aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/block_card/block_card.js2
-rw-r--r--src/components/media_modal/media_modal.vue15
-rw-r--r--src/components/mobile_post_status_modal/mobile_post_status_modal.js91
-rw-r--r--src/components/mobile_post_status_modal/mobile_post_status_modal.vue76
-rw-r--r--src/components/mute_card/mute_card.js2
-rw-r--r--src/components/post_status_form/post_status_form.js3
-rw-r--r--src/components/post_status_form/post_status_form.vue1
-rw-r--r--src/components/side_drawer/side_drawer.vue15
-rw-r--r--src/components/status/status.js8
-rw-r--r--src/components/user_avatar/user_avatar.js5
-rw-r--r--src/components/user_card/user_card.js3
-rw-r--r--src/components/user_card/user_card.vue14
-rw-r--r--src/components/user_profile/user_profile.js94
-rw-r--r--src/components/user_profile/user_profile.vue4
14 files changed, 258 insertions, 75 deletions
diff --git a/src/components/block_card/block_card.js b/src/components/block_card/block_card.js
index 11fa27b4..c459ff1b 100644
--- a/src/components/block_card/block_card.js
+++ b/src/components/block_card/block_card.js
@@ -9,7 +9,7 @@ const BlockCard = {
},
computed: {
user () {
- return this.$store.getters.userById(this.userId)
+ return this.$store.getters.findUser(this.userId)
},
blocked () {
return this.user.statusnet_blocking
diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue
index 427bf12b..7f666603 100644
--- a/src/components/media_modal/media_modal.vue
+++ b/src/components/media_modal/media_modal.vue
@@ -1,5 +1,5 @@
<template>
- <div class="modal-view" v-if="showing" @click.prevent="hide">
+ <div class="modal-view media-modal-view" v-if="showing" @click.prevent="hide">
<img class="modal-image" v-if="type === 'image'" :src="currentMedia.url"></img>
<VideoAttachment
class="modal-image"
@@ -32,18 +32,7 @@
<style lang="scss">
@import '../../_variables.scss';
-.modal-view {
- z-index: 1000;
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- background-color: rgba(0, 0, 0, 0.5);
-
+.media-modal-view {
&:hover {
.modal-view-button-arrow {
opacity: 0.75;
diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.js b/src/components/mobile_post_status_modal/mobile_post_status_modal.js
new file mode 100644
index 00000000..2f24dd08
--- /dev/null
+++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.js
@@ -0,0 +1,91 @@
+import PostStatusForm from '../post_status_form/post_status_form.vue'
+import { throttle } from 'lodash'
+
+const MobilePostStatusModal = {
+ components: {
+ PostStatusForm
+ },
+ data () {
+ return {
+ hidden: false,
+ postFormOpen: false,
+ scrollingDown: false,
+ inputActive: false,
+ oldScrollPos: 0,
+ amountScrolled: 0
+ }
+ },
+ created () {
+ window.addEventListener('scroll', this.handleScroll)
+ window.addEventListener('resize', this.handleOSK)
+ },
+ destroyed () {
+ window.removeEventListener('scroll', this.handleScroll)
+ window.removeEventListener('resize', this.handleOSK)
+ },
+ computed: {
+ currentUser () {
+ return this.$store.state.users.currentUser
+ },
+ isHidden () {
+ return this.hidden || this.inputActive
+ }
+ },
+ methods: {
+ openPostForm () {
+ this.postFormOpen = true
+ this.hidden = true
+
+ const el = this.$el.querySelector('textarea')
+ this.$nextTick(function () {
+ el.focus()
+ })
+ },
+ closePostForm () {
+ this.postFormOpen = false
+ this.hidden = false
+ },
+ handleOSK () {
+ // This is a big hack: we're guessing from changed window sizes if the
+ // on-screen keyboard is active or not. This is only really important
+ // for phones in portrait mode and it's more important to show the button
+ // in normal scenarios on all phones, than it is to hide it when the
+ // keyboard is active.
+ // Guesswork based on https://www.mydevice.io/#compare-devices
+
+ // for example, iphone 4 and android phones from the same time period
+ const smallPhone = window.innerWidth < 350
+ const smallPhoneKbOpen = smallPhone && window.innerHeight < 345
+
+ const biggerPhone = !smallPhone && window.innerWidth < 450
+ const biggerPhoneKbOpen = biggerPhone && window.innerHeight < 560
+ if (smallPhoneKbOpen || biggerPhoneKbOpen) {
+ this.inputActive = true
+ } else {
+ this.inputActive = false
+ }
+ },
+ handleScroll: throttle(function () {
+ const scrollAmount = window.scrollY - this.oldScrollPos
+ const scrollingDown = scrollAmount > 0
+
+ if (scrollingDown !== this.scrollingDown) {
+ this.amountScrolled = 0
+ this.scrollingDown = scrollingDown
+ if (!scrollingDown) {
+ this.hidden = false
+ }
+ } else if (scrollingDown) {
+ this.amountScrolled += scrollAmount
+ if (this.amountScrolled > 100 && !this.hidden) {
+ this.hidden = true
+ }
+ }
+
+ this.oldScrollPos = window.scrollY
+ this.scrollingDown = scrollingDown
+ }, 100)
+ }
+}
+
+export default MobilePostStatusModal
diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
new file mode 100644
index 00000000..0a451c28
--- /dev/null
+++ b/src/components/mobile_post_status_modal/mobile_post_status_modal.vue
@@ -0,0 +1,76 @@
+<template>
+<div v-if="currentUser">
+ <div
+ class="post-form-modal-view modal-view"
+ v-show="postFormOpen"
+ @click="closePostForm"
+ >
+ <div class="post-form-modal-panel panel" @click.stop="">
+ <div class="panel-heading">{{$t('post_status.new_status')}}</div>
+ <PostStatusForm class="panel-body" @posted="closePostForm"/>
+ </div>
+ </div>
+ <button
+ class="new-status-button"
+ :class="{ 'hidden': isHidden }"
+ @click="openPostForm"
+ >
+ <i class="icon-edit" />
+ </button>
+</div>
+</template>
+
+<script src="./mobile_post_status_modal.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.post-form-modal-view {
+ max-height: 100%;
+ display: block;
+}
+
+.post-form-modal-panel {
+ flex-shrink: 0;
+ margin: 25% 0 4em 0;
+ width: 100%;
+}
+
+.new-status-button {
+ width: 5em;
+ height: 5em;
+ border-radius: 100%;
+ position: fixed;
+ bottom: 1.5em;
+ right: 1.5em;
+ // TODO: this needs its own color, it has to stand out enough and link color
+ // is not very optimal for this particular use.
+ background-color: $fallback--fg;
+ background-color: var(--btn, $fallback--fg);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3), 0px 4px 6px rgba(0, 0, 0, 0.3);
+ z-index: 10;
+
+ transition: 0.35s transform;
+ transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
+
+ &.hidden {
+ transform: translateY(150%);
+ }
+
+ i {
+ font-size: 1.5em;
+ color: $fallback--text;
+ color: var(--text, $fallback--text);
+ }
+}
+
+@media all and (min-width: 801px) {
+ .new-status-button {
+ display: none;
+ }
+}
+
+</style>
diff --git a/src/components/mute_card/mute_card.js b/src/components/mute_card/mute_card.js
index 5dd0a9e5..65c9cfb5 100644
--- a/src/components/mute_card/mute_card.js
+++ b/src/components/mute_card/mute_card.js
@@ -9,7 +9,7 @@ const MuteCard = {
},
computed: {
user () {
- return this.$store.getters.userById(this.userId)
+ return this.$store.getters.findUser(this.userId)
},
muted () {
return this.user.muted
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 23a2c7e2..1f0df35a 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -222,6 +222,9 @@ const PostStatusForm = {
this.highlighted = 0
}
},
+ onKeydown (e) {
+ e.stopPropagation()
+ },
setCaret ({target: {selectionStart}}) {
this.caret = selectionStart
},
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 0ddde4ea..3d1df91b 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -20,6 +20,7 @@
ref="textarea"
@click="setCaret"
@keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control"
+ @keydown="onKeydown"
@keydown.down="cycleForward"
@keydown.up="cycleBackward"
@keydown.shift.tab="cycleBackward"
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index b608b008..95ee21b4 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -15,12 +15,7 @@
</div>
</div>
<ul>
- <li v-if="currentUser" @click="toggleDrawer">
- <router-link :to="{ name: 'new-status', params: { username: currentUser.screen_name } }">
- {{ $t("post_status.new_status") }}
- </router-link>
- </li>
- <li v-else @click="toggleDrawer">
+ <li v-if="!currentUser" @click="toggleDrawer">
<router-link :to="{ name: 'login' }">
{{ $t("login.login") }}
</router-link>
@@ -119,14 +114,14 @@
}
.side-drawer-container-open {
- transition-delay: 0.0s;
- transition-property: left;
+ transition: 0.35s;
+ transition-property: background-color;
+ background-color: rgba(0, 0, 0, 0.5);
}
.side-drawer-container-closed {
left: -100%;
- transition-delay: 0.5s;
- transition-property: left;
+ background-color: rgba(0, 0, 0, 0);
}
.side-drawer-click-outside {
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 9e18fe15..c90da6d4 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -145,11 +145,11 @@ const Status = {
return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id)
},
replyToName () {
- const user = this.$store.state.users.usersObject[this.status.in_reply_to_user_id]
- if (user) {
- return user.screen_name
- } else {
+ if (this.status.in_reply_to_screen_name) {
return this.status.in_reply_to_screen_name
+ } else {
+ const user = this.$store.getters.findUser(this.status.in_reply_to_user_id)
+ return user && user.screen_name
}
},
hideReply () {
diff --git a/src/components/user_avatar/user_avatar.js b/src/components/user_avatar/user_avatar.js
index e513b993..e6fed3b5 100644
--- a/src/components/user_avatar/user_avatar.js
+++ b/src/components/user_avatar/user_avatar.js
@@ -23,6 +23,11 @@ const UserAvatar = {
imageLoadError () {
this.showPlaceholder = true
}
+ },
+ watch: {
+ src () {
+ this.showPlaceholder = false
+ }
}
}
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
index 80d15a27..43a77f45 100644
--- a/src/components/user_card/user_card.js
+++ b/src/components/user_card/user_card.js
@@ -15,6 +15,9 @@ export default {
betterShadow: this.$store.state.interface.browserSupport.cssFilter
}
},
+ created () {
+ this.$store.dispatch('fetchUserRelationship', this.user.id)
+ },
computed: {
classes () {
return [{
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index cc2ce6b8..690e1bde 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -11,7 +11,7 @@
<div :title="user.name" class='user-name' v-if="user.name_html" v-html="user.name_html"></div>
<div :title="user.name" class='user-name' v-else>{{user.name}}</div>
<router-link :to="{ name: 'user-settings' }" v-if="!isOtherUser">
- <i class="button-icon icon-cog usersettings" :title="$t('tool_tip.user_settings')"></i>
+ <i class="button-icon icon-pencil usersettings" :title="$t('tool_tip.user_settings')"></i>
</router-link>
<a :href="user.statusnet_profile_url" target="_blank" v-if="isOtherUser && !user.is_local">
<i class="icon-link-ext usersettings"></i>
@@ -159,6 +159,18 @@
&-bio {
text-align: center;
+
+ img {
+ object-fit: contain;
+ vertical-align: middle;
+ max-width: 100%;
+ max-height: 400px;
+
+ .emoji {
+ width: 32px;
+ height: 32px;
+ }
+ }
}
// Modifiers
diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js
index 54126514..82df4510 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -9,7 +9,7 @@ import withList from '../../hocs/with_list/with_list'
const FollowerList = compose(
withLoadMore({
fetch: (props, $store) => $store.dispatch('addFollowers', props.userId),
- select: (props, $store) => get($store.getters.userById(props.userId), 'followers', []),
+ select: (props, $store) => get($store.getters.findUser(props.userId), 'followers', []),
destory: (props, $store) => $store.dispatch('clearFollowers', props.userId),
childPropName: 'entries',
additionalPropNames: ['userId']
@@ -20,7 +20,7 @@ const FollowerList = compose(
const FriendList = compose(
withLoadMore({
fetch: (props, $store) => $store.dispatch('addFriends', props.userId),
- select: (props, $store) => get($store.getters.userById(props.userId), 'friends', []),
+ select: (props, $store) => get($store.getters.findUser(props.userId), 'friends', []),
destory: (props, $store) => $store.dispatch('clearFriends', props.userId),
childPropName: 'entries',
additionalPropNames: ['userId']
@@ -31,28 +31,16 @@ const FriendList = compose(
const UserProfile = {
data () {
return {
- error: false
+ error: false,
+ fetchedUserId: null
}
},
created () {
- this.$store.commit('clearTimeline', { timeline: 'user' })
- this.$store.commit('clearTimeline', { timeline: 'favorites' })
- this.$store.commit('clearTimeline', { timeline: 'media' })
- this.$store.dispatch('startFetching', { timeline: 'user', userId: this.fetchBy })
- this.$store.dispatch('startFetching', { timeline: 'media', userId: this.fetchBy })
- this.startFetchFavorites()
if (!this.user.id) {
- this.$store.dispatch('fetchUser', this.fetchBy)
- .catch((reason) => {
- const errorMessage = get(reason, 'error.error')
- if (errorMessage === 'No user with such user_id') { // Known error
- this.error = this.$t('user_profile.profile_does_not_exist')
- } else if (errorMessage) {
- this.error = errorMessage
- } else {
- this.error = this.$t('user_profile.profile_loading_error')
- }
- })
+ this.fetchUserId()
+ .then(() => this.startUp())
+ } else {
+ this.startUp()
}
},
destroyed () {
@@ -69,7 +57,7 @@ const UserProfile = {
return this.$store.state.statuses.timelines.media
},
userId () {
- return this.$route.params.id || this.user.id
+ return this.$route.params.id || this.user.id || this.fetchedUserId
},
userName () {
return this.$route.params.name || this.user.screen_name
@@ -79,10 +67,9 @@ const UserProfile = {
this.userId === this.$store.state.users.currentUser.id
},
userInStore () {
- if (this.isExternal) {
- return this.$store.getters.userById(this.userId)
- }
- return this.$store.getters.userByName(this.userName)
+ const routeParams = this.$route.params
+ // This needs fetchedUserId so that computed will be refreshed when user is fetched
+ return this.$store.getters.findUser(this.fetchedUserId || routeParams.name || routeParams.id)
},
user () {
if (this.timeline.statuses[0]) {
@@ -93,9 +80,6 @@ const UserProfile = {
}
return {}
},
- fetchBy () {
- return this.isExternal ? this.userId : this.userName
- },
isExternal () {
return this.$route.name === 'external-user-profile'
},
@@ -109,14 +93,38 @@ const UserProfile = {
methods: {
startFetchFavorites () {
if (this.isUs) {
- this.$store.dispatch('startFetching', { timeline: 'favorites', userId: this.fetchBy })
+ this.$store.dispatch('startFetching', { timeline: 'favorites', userId: this.userId })
+ }
+ },
+ fetchUserId () {
+ let fetchPromise
+ if (this.userId && !this.$route.params.name) {
+ fetchPromise = this.$store.dispatch('fetchUser', this.userId)
+ } else {
+ fetchPromise = this.$store.dispatch('fetchUser', this.userName)
+ .then(({ id }) => {
+ this.fetchedUserId = id
+ })
}
+ return fetchPromise
+ .catch((reason) => {
+ const errorMessage = get(reason, 'error.error')
+ if (errorMessage === 'No user with such user_id') { // Known error
+ this.error = this.$t('user_profile.profile_does_not_exist')
+ } else if (errorMessage) {
+ this.error = errorMessage
+ } else {
+ this.error = this.$t('user_profile.profile_loading_error')
+ }
+ })
+ .then(() => this.startUp())
},
startUp () {
- this.$store.dispatch('startFetching', { timeline: 'user', userId: this.fetchBy })
- this.$store.dispatch('startFetching', { timeline: 'media', userId: this.fetchBy })
-
- this.startFetchFavorites()
+ if (this.userId) {
+ this.$store.dispatch('startFetching', { timeline: 'user', userId: this.userId })
+ this.$store.dispatch('startFetching', { timeline: 'media', userId: this.userId })
+ this.startFetchFavorites()
+ }
},
cleanUp () {
this.$store.dispatch('stopFetching', 'user')
@@ -128,19 +136,19 @@ const UserProfile = {
}
},
watch: {
- userName () {
- if (this.isExternal) {
- return
+ // userId can be undefined if we don't know it yet
+ userId (newVal) {
+ if (newVal) {
+ this.cleanUp()
+ this.startUp()
}
- this.cleanUp()
- this.startUp()
},
- userId () {
- if (!this.isExternal) {
- return
+ userName () {
+ if (this.$route.params.name) {
+ this.fetchUserId()
+ this.cleanUp()
+ this.startUp()
}
- this.cleanUp()
- this.startUp()
},
$route () {
this.$refs.tabSwitcher.activateTab(0)()
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 7d4a8b1f..d449eb85 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -11,7 +11,7 @@
:title="$t('user_profile.timeline_title')"
:timeline="timeline"
:timeline-name="'user'"
- :user-id="fetchBy"
+ :user-id="userId"
/>
<div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count">
<FriendList :userId="userId" />
@@ -25,7 +25,7 @@
:embedded="true" :title="$t('user_card.media')"
timeline-name="media"
:timeline="media"
- :user-id="fetchBy"
+ :user-id="userId"
/>
<Timeline
v-if="isUs"