diff options
Diffstat (limited to 'src/components')
48 files changed, 619 insertions, 296 deletions
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index ec326c45..af16e302 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -190,6 +190,7 @@ .video { width: 100%; + height: 100%; } .play-icon { @@ -286,7 +287,7 @@ } img { - image-orientation: from-image; + image-orientation: from-image; // NOTE: only FF supports this } } } diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 568e9359..8a02174e 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -87,6 +87,7 @@ &-expanded-content { flex: 1; margin-left: 0.7em; + min-width: 0; } } </style> diff --git a/src/components/conversation-page/conversation-page.js b/src/components/conversation-page/conversation-page.js index 1da70ce9..8f996be1 100644 --- a/src/components/conversation-page/conversation-page.js +++ b/src/components/conversation-page/conversation-page.js @@ -5,12 +5,8 @@ const conversationPage = { Conversation }, computed: { - statusoid () { - const id = this.$route.params.id - const statuses = this.$store.state.statuses.allStatusesObject - const status = statuses[id] - - return status + statusId () { + return this.$route.params.id } } } diff --git a/src/components/conversation-page/conversation-page.vue b/src/components/conversation-page/conversation-page.vue index 532f785c..8cc0a55f 100644 --- a/src/components/conversation-page/conversation-page.vue +++ b/src/components/conversation-page/conversation-page.vue @@ -2,7 +2,7 @@ <conversation :collapsable="false" is-page="true" - :statusoid="statusoid" + :status-id="statusId" /> </template> diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index a2b3aeab..72ee9c39 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,4 +1,4 @@ -import { reduce, filter, findIndex, clone } from 'lodash' +import { reduce, filter, findIndex, clone, get } from 'lodash' import Status from '../status/status.vue' const sortById = (a, b) => { @@ -39,10 +39,11 @@ const conversation = { } }, props: [ - 'statusoid', + 'statusId', 'collapsable', 'isPage', - 'showPinned' + 'pinnedStatusIdsObject', + 'inProfile' ], created () { if (this.isPage) { @@ -51,21 +52,17 @@ const conversation = { }, computed: { status () { - return this.statusoid + return this.$store.state.statuses.allStatusesObject[this.statusId] }, - statusId () { - if (this.statusoid.retweeted_status) { - return this.statusoid.retweeted_status.id + originalStatusId () { + if (this.status.retweeted_status) { + return this.status.retweeted_status.id } else { - return this.statusoid.id + return this.statusId } }, conversationId () { - if (this.statusoid.retweeted_status) { - return this.statusoid.retweeted_status.statusnet_conversation_id - } else { - return this.statusoid.statusnet_conversation_id - } + return this.getConversationId(this.statusId) }, conversation () { if (!this.status) { @@ -77,7 +74,7 @@ const conversation = { } const conversation = clone(this.$store.state.statuses.conversationsObject[this.conversationId]) - const statusIndex = findIndex(conversation, { id: this.statusId }) + const statusIndex = findIndex(conversation, { id: this.originalStatusId }) if (statusIndex !== -1) { conversation[statusIndex] = this.status } @@ -110,7 +107,15 @@ const conversation = { Status }, watch: { - '$route': 'fetchConversation', + statusId (newVal, oldVal) { + const newConversationId = this.getConversationId(newVal) + const oldConversationId = this.getConversationId(oldVal) + if (newConversationId && oldConversationId && newConversationId === oldConversationId) { + this.setHighlight(this.originalStatusId) + } else { + this.fetchConversation() + } + }, expanded (value) { if (value) { this.fetchConversation() @@ -120,24 +125,25 @@ const conversation = { methods: { fetchConversation () { if (this.status) { - this.$store.state.api.backendInteractor.fetchConversation({ id: this.status.id }) + this.$store.state.api.backendInteractor.fetchConversation({ id: this.statusId }) .then(({ ancestors, descendants }) => { this.$store.dispatch('addNewStatuses', { statuses: ancestors }) this.$store.dispatch('addNewStatuses', { statuses: descendants }) + this.setHighlight(this.originalStatusId) }) - .then(() => this.setHighlight(this.statusId)) } else { - const id = this.$route.params.id - this.$store.state.api.backendInteractor.fetchStatus({ id }) - .then((status) => this.$store.dispatch('addNewStatuses', { statuses: [status] })) - .then(() => this.fetchConversation()) + this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId }) + .then((status) => { + this.$store.dispatch('addNewStatuses', { statuses: [status] }) + this.fetchConversation() + }) } }, getReplies (id) { return this.replies[id] || [] }, focused (id) { - return (this.isExpanded) && id === this.status.id + return (this.isExpanded) && id === this.statusId }, setHighlight (id) { if (!id) return @@ -149,9 +155,10 @@ const conversation = { }, toggleExpanded () { this.expanded = !this.expanded - if (!this.expanded) { - this.setHighlight(null) - } + }, + getConversationId (statusId) { + const status = this.$store.state.statuses.allStatusesObject[statusId] + return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id')) } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 5a900607..0f1de55f 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -21,11 +21,12 @@ :inline-expanded="collapsable && isExpanded" :statusoid="status" :expandable="!isExpanded" - :show-pinned="showPinned" + :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]" :focused="focused(status.id)" :in-conversation="isExpanded" :highlight="getHighlight()" :replies="getReplies(status.id)" + :in-profile="inProfile" class="status-fadein panel-body" @goto="setHighlight" @toggleExpanded="toggleExpanded" diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 2ec72729..5ac73e97 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -16,6 +16,16 @@ const ExtraButtons = { this.$store.dispatch('unpinStatus', this.status.id) .then(() => this.$emit('onSuccess')) .catch(err => this.$emit('onError', err.error.error)) + }, + muteConversation () { + this.$store.dispatch('muteConversation', this.status.id) + .then(() => this.$emit('onSuccess')) + .catch(err => this.$emit('onError', err.error.error)) + }, + unmuteConversation () { + this.$store.dispatch('unmuteConversation', this.status.id) + .then(() => this.$emit('onSuccess')) + .catch(err => this.$emit('onError', err.error.error)) } }, computed: { @@ -31,8 +41,8 @@ const ExtraButtons = { canPin () { return this.ownStatus && (this.status.visibility === 'public' || this.status.visibility === 'unlisted') }, - enabled () { - return this.canPin || this.canDelete + canMute () { + return !!this.currentUser } } } diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index cdad1666..6781a4f8 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -1,6 +1,6 @@ <template> <v-popover - v-if="enabled" + v-if="canDelete || canMute || canPin" trigger="click" placement="top" class="extra-button-popover" @@ -10,6 +10,20 @@ <div slot="popover"> <div class="dropdown-menu"> <button + v-if="canMute && !status.thread_muted" + class="dropdown-item dropdown-item-icon" + @click.prevent="muteConversation" + > + <i class="icon-eye-off" /><span>{{ $t("status.mute_conversation") }}</span> + </button> + <button + v-if="canMute && status.thread_muted" + class="dropdown-item dropdown-item-icon" + @click.prevent="unmuteConversation" + > + <i class="icon-eye-off" /><span>{{ $t("status.unmute_conversation") }}</span> + </button> + <button v-if="!status.pinned && canPin" v-close-popover class="dropdown-item dropdown-item-icon" diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js index 5f0b7b25..5f80a079 100644 --- a/src/components/features_panel/features_panel.js +++ b/src/components/features_panel/features_panel.js @@ -1,8 +1,6 @@ const FeaturesPanel = { computed: { - chat: function () { - return this.$store.state.instance.chatAvailable && (!this.$store.state.chatDisabled) - }, + chat: function () { return this.$store.state.instance.chatAvailable }, gopher: function () { return this.$store.state.instance.gopherAvailable }, whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled }, mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable }, diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue index 6adfb76c..6169d294 100644 --- a/src/components/gallery/gallery.vue +++ b/src/components/gallery/gallery.vue @@ -61,13 +61,17 @@ } &.contain-fit { - img, video { + img, + video, + canvas { object-fit: contain; } } &.cover-fit { - img, video { + img, + video, + canvas { object-fit: cover; } } diff --git a/src/components/instance_specific_panel/instance_specific_panel.js b/src/components/instance_specific_panel/instance_specific_panel.js index 9bb5e945..09e3d055 100644 --- a/src/components/instance_specific_panel/instance_specific_panel.js +++ b/src/components/instance_specific_panel/instance_specific_panel.js @@ -2,9 +2,6 @@ const InstanceSpecificPanel = { computed: { instanceSpecificPanelContent () { return this.$store.state.instance.instanceSpecificPanelContent - }, - show () { - return !this.$store.state.config.hideISP } } } diff --git a/src/components/instance_specific_panel/instance_specific_panel.vue b/src/components/instance_specific_panel/instance_specific_panel.vue index a7cf6b48..7448ca06 100644 --- a/src/components/instance_specific_panel/instance_specific_panel.vue +++ b/src/components/instance_specific_panel/instance_specific_panel.vue @@ -1,8 +1,5 @@ <template> - <div - v-if="show" - class="instance-specific-panel" - > + <div class="instance-specific-panel"> <div class="panel panel-default"> <div class="panel-body"> <!-- eslint-disable vue/no-v-html --> @@ -14,6 +11,3 @@ </template> <script src="./instance_specific_panel.js" ></script> - -<style lang="scss"> -</style> diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js index d4e3cc17..1f8a9de9 100644 --- a/src/components/interactions/interactions.js +++ b/src/components/interactions/interactions.js @@ -13,8 +13,8 @@ const Interactions = { } }, methods: { - onModeSwitch (index, dataset) { - this.filterMode = tabModeDict[dataset.filter] + onModeSwitch (key) { + this.filterMode = tabModeDict[key] } }, components: { diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue index d71c99d5..08cee343 100644 --- a/src/components/interactions/interactions.vue +++ b/src/components/interactions/interactions.vue @@ -10,18 +10,15 @@ :on-switch="onModeSwitch" > <span - data-tab-dummy - data-filter="mentions" + key="mentions" :label="$t('nav.mentions')" /> <span - data-tab-dummy - data-filter="likes+repeats" + key="likes+repeats" :label="$t('interactions.favs_repeats')" /> <span - data-tab-dummy - data-filter="follows" + key="follows" :label="$t('interactions.follows')" /> </tab-switcher> diff --git a/src/components/link-preview/link-preview.js b/src/components/link-preview/link-preview.js index 2f6da55e..444aafbe 100644 --- a/src/components/link-preview/link-preview.js +++ b/src/components/link-preview/link-preview.js @@ -5,6 +5,11 @@ const LinkPreview = { 'size', 'nsfw' ], + data () { + return { + imageLoaded: false + } + }, computed: { useImage () { // Currently BE shoudn't give cards if tagged NSFW, this is a bit paranoid @@ -15,6 +20,15 @@ const LinkPreview = { useDescription () { return this.card.description && /\S/.test(this.card.description) } + }, + created () { + if (this.useImage) { + const newImg = new Image() + newImg.onload = () => { + this.imageLoaded = true + } + newImg.src = this.card.image + } } } diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue index 493774c2..69171977 100644 --- a/src/components/link-preview/link-preview.vue +++ b/src/components/link-preview/link-preview.vue @@ -7,7 +7,7 @@ rel="noopener" > <div - v-if="useImage" + v-if="useImage && imageLoaded" class="card-image" :class="{ 'small-image': size === 'small' }" > diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue index 3ec7fe0c..b4fdcefb 100644 --- a/src/components/login_form/login_form.vue +++ b/src/components/login_form/login_form.vue @@ -33,6 +33,11 @@ type="password" > </div> + <div class="form-group"> + <router-link :to="{name: 'password-reset'}"> + {{ $t('password_reset.forgot_password') }} + </router-link> + </div> </template> <div diff --git a/src/components/media_modal/media_modal.vue b/src/components/media_modal/media_modal.vue index 0543e677..ab5a36a5 100644 --- a/src/components/media_modal/media_modal.vue +++ b/src/components/media_modal/media_modal.vue @@ -63,6 +63,7 @@ max-width: 90%; max-height: 90%; box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5); + image-orientation: from-image; // NOTE: only FF supports this } .modal-view-button-arrow { diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index 9b341a3b..c2bb76ee 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -1,14 +1,12 @@ import SideDrawer from '../side_drawer/side_drawer.vue' import Notifications from '../notifications/notifications.vue' -import MobilePostStatusModal from '../mobile_post_status_modal/mobile_post_status_modal.vue' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' import GestureService from '../../services/gesture_service/gesture_service' const MobileNav = { components: { SideDrawer, - Notifications, - MobilePostStatusModal + Notifications }, data: () => ({ notificationsCloseGesture: undefined, diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index f67b7ff8..d1c24e56 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -70,7 +70,6 @@ ref="sideDrawer" :logout="logout" /> - <MobilePostStatusModal /> </div> </template> diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.js b/src/components/mobile_post_status_button/mobile_post_status_button.js index 3cec23c6..3e77148a 100644 --- a/src/components/mobile_post_status_modal/mobile_post_status_modal.js +++ b/src/components/mobile_post_status_button/mobile_post_status_button.js @@ -1,14 +1,9 @@ -import PostStatusForm from '../post_status_form/post_status_form.vue' import { debounce } from 'lodash' -const MobilePostStatusModal = { - components: { - PostStatusForm - }, +const MobilePostStatusButton = { data () { return { hidden: false, - postFormOpen: false, scrollingDown: false, inputActive: false, oldScrollPos: 0, @@ -28,8 +23,8 @@ const MobilePostStatusModal = { window.removeEventListener('resize', this.handleOSK) }, computed: { - currentUser () { - return this.$store.state.users.currentUser + isLoggedIn () { + return !!this.$store.state.users.currentUser }, isHidden () { return this.autohideFloatingPostButton && (this.hidden || this.inputActive) @@ -57,17 +52,7 @@ const MobilePostStatusModal = { window.removeEventListener('scroll', this.handleScrollEnd) }, 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 + this.$store.dispatch('openPostStatusModal') }, handleOSK () { // This is a big hack: we're guessing from changed window sizes if the @@ -105,4 +90,4 @@ const MobilePostStatusModal = { } } -export default MobilePostStatusModal +export default MobilePostStatusButton diff --git a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue index 5db7584b..9cf45de3 100644 --- a/src/components/mobile_post_status_modal/mobile_post_status_modal.vue +++ b/src/components/mobile_post_status_button/mobile_post_status_button.vue @@ -1,23 +1,5 @@ <template> - <div v-if="currentUser"> - <div - v-show="postFormOpen" - class="post-form-modal-view modal-view" - @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> + <div v-if="isLoggedIn"> <button class="new-status-button" :class="{ 'hidden': isHidden }" @@ -28,22 +10,11 @@ </div> </template> -<script src="./mobile_post_status_modal.js"></script> +<script src="./mobile_post_status_button.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; diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 896c6d52..8e817f3b 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -9,7 +9,8 @@ const Notification = { data () { return { userExpanded: false, - betterShadow: this.$store.state.interface.browserSupport.cssFilter + betterShadow: this.$store.state.interface.browserSupport.cssFilter, + unmuted: false } }, props: [ 'notification' ], @@ -23,11 +24,14 @@ const Notification = { toggleUserExpanded () { this.userExpanded = !this.userExpanded }, - userProfileLink (user) { + generateUserProfileLink (user) { return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) }, getUser (notification) { return this.$store.state.users.usersObject[notification.from_profile.id] + }, + toggleMute () { + this.unmuted = !this.unmuted } }, computed: { @@ -47,6 +51,12 @@ const Notification = { return this.userInStore } return this.notification.from_profile + }, + userProfileLink () { + return this.generateUserProfileLink(this.user) + }, + needMute () { + return this.user.muted } } } diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index bafcd026..1f192c77 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -4,104 +4,126 @@ :compact="true" :statusoid="notification.status" /> - <div - v-else - class="non-mention" - :class="[userClass, { highlighted: userStyle }]" - :style="[ userStyle ]" - > - <a - class="avatar-container" - :href="notification.from_profile.statusnet_profile_url" - @click.stop.prevent.capture="toggleUserExpanded" + <div v-else> + <div + v-if="needMute && !unmuted" + class="container muted" > - <UserAvatar - :compact="true" - :better-shadow="betterShadow" - :user="notification.from_profile" - /> - </a> - <div class="notification-right"> - <UserCard - v-if="userExpanded" - :user="getUser(notification)" - :rounded="true" - :bordered="true" - /> - <span class="notification-details"> - <div class="name-and-action"> - <!-- eslint-disable vue/no-v-html --> - <span - v-if="!!notification.from_profile.name_html" - class="username" - :title="'@'+notification.from_profile.screen_name" - v-html="notification.from_profile.name_html" - /> - <!-- eslint-enable vue/no-v-html --> - <span - v-else - class="username" - :title="'@'+notification.from_profile.screen_name" - >{{ notification.from_profile.name }}</span> - <span v-if="notification.type === 'like'"> - <i class="fa icon-star lit" /> - <small>{{ $t('notifications.favorited_you') }}</small> - </span> - <span v-if="notification.type === 'repeat'"> - <i - class="fa icon-retweet lit" - :title="$t('tool_tip.repeat')" + <small> + <router-link :to="userProfileLink"> + {{ notification.from_profile.screen_name }} + </router-link> + </small> + <a + href="#" + class="unmute" + @click.prevent="toggleMute" + ><i class="button-icon icon-eye-off" /></a> + </div> + <div + v-else + class="non-mention" + :class="[userClass, { highlighted: userStyle }]" + :style="[ userStyle ]" + > + <a + class="avatar-container" + :href="notification.from_profile.statusnet_profile_url" + @click.stop.prevent.capture="toggleUserExpanded" + > + <UserAvatar + :compact="true" + :better-shadow="betterShadow" + :user="notification.from_profile" + /> + </a> + <div class="notification-right"> + <UserCard + v-if="userExpanded" + :user="getUser(notification)" + :rounded="true" + :bordered="true" + /> + <span class="notification-details"> + <div class="name-and-action"> + <!-- eslint-disable vue/no-v-html --> + <span + v-if="!!notification.from_profile.name_html" + class="username" + :title="'@'+notification.from_profile.screen_name" + v-html="notification.from_profile.name_html" /> - <small>{{ $t('notifications.repeated_you') }}</small> - </span> - <span v-if="notification.type === 'follow'"> - <i class="fa icon-user-plus lit" /> - <small>{{ $t('notifications.followed_you') }}</small> - </span> - </div> + <!-- eslint-enable vue/no-v-html --> + <span + v-else + class="username" + :title="'@'+notification.from_profile.screen_name" + >{{ notification.from_profile.name }}</span> + <span v-if="notification.type === 'like'"> + <i class="fa icon-star lit" /> + <small>{{ $t('notifications.favorited_you') }}</small> + </span> + <span v-if="notification.type === 'repeat'"> + <i + class="fa icon-retweet lit" + :title="$t('tool_tip.repeat')" + /> + <small>{{ $t('notifications.repeated_you') }}</small> + </span> + <span v-if="notification.type === 'follow'"> + <i class="fa icon-user-plus lit" /> + <small>{{ $t('notifications.followed_you') }}</small> + </span> + </div> + <div + v-if="notification.type === 'follow'" + class="timeago" + > + <span class="faint"> + <Timeago + :time="notification.created_at" + :auto-update="240" + /> + </span> + </div> + <div + v-else + class="timeago" + > + <router-link + v-if="notification.status" + :to="{ name: 'conversation', params: { id: notification.status.id } }" + class="faint-link" + > + <Timeago + :time="notification.created_at" + :auto-update="240" + /> + </router-link> + </div> + <a + v-if="needMute" + href="#" + @click.prevent="toggleMute" + ><i class="button-icon icon-eye-off" /></a> + </span> <div v-if="notification.type === 'follow'" - class="timeago" - > - <span class="faint"> - <Timeago - :time="notification.created_at" - :auto-update="240" - /> - </span> - </div> - <div - v-else - class="timeago" + class="follow-text" > - <router-link - v-if="notification.status" - :to="{ name: 'conversation', params: { id: notification.status.id } }" - class="faint-link" - > - <Timeago - :time="notification.created_at" - :auto-update="240" - /> + <router-link :to="userProfileLink"> + @{{ notification.from_profile.screen_name }} </router-link> </div> - </span> - <div - v-if="notification.type === 'follow'" - class="follow-text" - > - <router-link :to="userProfileLink(notification.from_profile)"> - @{{ notification.from_profile.screen_name }} - </router-link> + <template v-else> + <status + class="faint" + :compact="true" + :statusoid="notification.action" + :no-heading="true" + /> + </template> </div> - <template v-else> - <status - class="faint" - :compact="true" - :statusoid="notification.action" - :no-heading="true" - /> - </template> </div> </div> </template> diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 622d12f4..71876b14 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -33,7 +33,6 @@ .notification { box-sizing: border-box; - display: flex; border-bottom: 1px solid; border-color: $fallback--border; border-color: var(--border, $fallback--border); @@ -47,6 +46,10 @@ } } + .muted { + padding: .25em .6em; + } + .non-mention { display: flex; flex: 1; diff --git a/src/components/password_reset/password_reset.js b/src/components/password_reset/password_reset.js new file mode 100644 index 00000000..fa71e07a --- /dev/null +++ b/src/components/password_reset/password_reset.js @@ -0,0 +1,62 @@ +import { mapState } from 'vuex' +import passwordResetApi from '../../services/new_api/password_reset.js' + +const passwordReset = { + data: () => ({ + user: { + email: '' + }, + isPending: false, + success: false, + throttled: false, + error: null + }), + computed: { + ...mapState({ + signedIn: (state) => !!state.users.currentUser, + instance: state => state.instance + }), + mailerEnabled () { + return this.instance.mailerEnabled + } + }, + created () { + if (this.signedIn) { + this.$router.push({ name: 'root' }) + } + }, + methods: { + dismissError () { + this.error = null + }, + submit () { + this.isPending = true + const email = this.user.email + const instance = this.instance.server + + passwordResetApi({ instance, email }).then(({ status }) => { + this.isPending = false + this.user.email = '' + + if (status === 204) { + this.success = true + this.error = null + } else if (status === 404 || status === 400) { + this.error = this.$t('password_reset.not_found') + this.$nextTick(() => { + this.$refs.email.focus() + }) + } else if (status === 429) { + this.throttled = true + this.error = this.$t('password_reset.too_many_requests') + } + }).catch(() => { + this.isPending = false + this.user.email = '' + this.error = this.$t('general.generic_error') + }) + } + } +} + +export default passwordReset diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue new file mode 100644 index 00000000..00474e95 --- /dev/null +++ b/src/components/password_reset/password_reset.vue @@ -0,0 +1,116 @@ +<template> + <div class="settings panel panel-default"> + <div class="panel-heading"> + {{ $t('password_reset.password_reset') }} + </div> + <div class="panel-body"> + <form + class="password-reset-form" + @submit.prevent="submit" + > + <div class="container"> + <div v-if="!mailerEnabled"> + <p> + {{ $t('password_reset.password_reset_disabled') }} + </p> + </div> + <div v-else-if="success || throttled"> + <p v-if="success"> + {{ $t('password_reset.check_email') }} + </p> + <div class="form-group text-center"> + <router-link :to="{name: 'root'}"> + {{ $t('password_reset.return_home') }} + </router-link> + </div> + </div> + <div v-else> + <p> + {{ $t('password_reset.instruction') }} + </p> + <div class="form-group"> + <input + ref="email" + v-model="user.email" + :disabled="isPending" + :placeholder="$t('password_reset.placeholder')" + class="form-control" + type="input" + > + </div> + <div class="form-group"> + <button + :disabled="isPending" + type="submit" + class="btn btn-default btn-block" + > + {{ $t('general.submit') }} + </button> + </div> + </div> + <p + v-if="error" + class="alert error notice-dismissible" + > + <span>{{ error }}</span> + <a + class="button-icon dismiss" + @click.prevent="dismissError()" + > + <i class="icon-cancel" /> + </a> + </p> + </div> + </form> + </div> + </div> +</template> + +<script src="./password_reset.js"></script> +<style lang="scss"> +@import '../../_variables.scss'; + +.password-reset-form { + display: flex; + flex-direction: column; + align-items: center; + margin: 0.6em; + + .container { + display: flex; + flex: 1 0; + flex-direction: column; + margin-top: 0.6em; + max-width: 18rem; + } + + .form-group { + display: flex; + flex-direction: column; + margin-bottom: 1em; + padding: 0.3em 0.0em 0.3em; + line-height: 24px; + } + + .error { + text-align: center; + animation-name: shakeError; + animation-duration: 0.4s; + animation-timing-function: ease-in-out; + } + + .alert { + padding: 0.5em; + margin: 0.3em 0.0em 1em; + } + + .notice-dismissible { + padding-right: 2rem; + } + + .icon-cancel { + cursor: pointer; + } +} + +</style> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 40bbf6d4..dc4b419c 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -8,7 +8,7 @@ import fileTypeService from '../../services/file_type/file_type.service.js' import { reject, map, uniqBy } from 'lodash' import suggestor from '../emoji-input/suggestor.js' -const buildMentionsString = ({ user, attentions }, currentUser) => { +const buildMentionsString = ({ user, attentions = [] }, currentUser) => { let allAttentions = [...attentions] allAttentions.unshift(user) diff --git a/src/components/post_status_modal/post_status_modal.js b/src/components/post_status_modal/post_status_modal.js new file mode 100644 index 00000000..1033ba11 --- /dev/null +++ b/src/components/post_status_modal/post_status_modal.js @@ -0,0 +1,32 @@ +import PostStatusForm from '../post_status_form/post_status_form.vue' + +const PostStatusModal = { + components: { + PostStatusForm + }, + computed: { + isLoggedIn () { + return !!this.$store.state.users.currentUser + }, + isOpen () { + return this.isLoggedIn && this.$store.state.postStatus.modalActivated + }, + params () { + return this.$store.state.postStatus.params || {} + } + }, + watch: { + isOpen (val) { + if (val) { + this.$nextTick(() => this.$el.querySelector('textarea').focus()) + } + } + }, + methods: { + closeModal () { + this.$store.dispatch('closePostStatusModal') + } + } +} + +export default PostStatusModal diff --git a/src/components/post_status_modal/post_status_modal.vue b/src/components/post_status_modal/post_status_modal.vue new file mode 100644 index 00000000..3f8eec69 --- /dev/null +++ b/src/components/post_status_modal/post_status_modal.vue @@ -0,0 +1,43 @@ +<template> + <div + v-if="isOpen" + class="post-form-modal-view modal-view" + @click="closeModal" + > + <div + class="post-form-modal-panel panel" + @click.stop="" + > + <div class="panel-heading"> + {{ $t('post_status.new_status') }} + </div> + <PostStatusForm + class="panel-body" + v-bind="params" + @posted="closeModal" + /> + </div> + </div> +</template> + +<script src="./post_status_modal.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.post-form-modal-view { + align-items: flex-start; +} + +.post-form-modal-panel { + flex-shrink: 0; + margin-top: 25%; + margin-bottom: 2em; + width: 100%; + max-width: 700px; + + @media (orientation: landscape) { + margin-top: 8%; + } +} +</style> diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index e0fa214a..5bb06a4f 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -268,6 +268,7 @@ $validations-cRed: #f04124; textarea { min-height: 100px; + resize: vertical; } .form-group { diff --git a/src/components/search/search.js b/src/components/search/search.js index b434e127..8e903052 100644 --- a/src/components/search/search.js +++ b/src/components/search/search.js @@ -75,8 +75,8 @@ const Search = { const length = this[tabName].length return length === 0 ? '' : ` (${length})` }, - onResultTabSwitch (_index, dataset) { - this.currenResultTab = dataset.filter + onResultTabSwitch (key) { + this.currenResultTab = key }, getActiveTab () { if (this.visibleStatuses.length > 0) { diff --git a/src/components/search/search.vue b/src/components/search/search.vue index 4350e672..746bbaa2 100644 --- a/src/components/search/search.vue +++ b/src/components/search/search.vue @@ -31,21 +31,18 @@ <tab-switcher ref="tabSwitcher" :on-switch="onResultTabSwitch" - :custom-active="currenResultTab" + :active-tab="currenResultTab" > <span - data-tab-dummy - data-filter="statuses" + key="statuses" :label="$t('user_card.statuses') + resultCount('visibleStatuses')" /> <span - data-tab-dummy - data-filter="people" + key="people" :label="$t('search.people') + resultCount('users')" /> <span - data-tab-dummy - data-filter="hashtags" + key="hashtags" :label="$t('search.hashtags') + resultCount('hashtags')" /> </tab-switcher> diff --git a/src/components/status/status.js b/src/components/status/status.js index 3c172e5b..d17ba318 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -29,7 +29,8 @@ const Status = { 'isPreview', 'noHeading', 'inlineExpanded', - 'showPinned' + 'showPinned', + 'inProfile' ], data () { return { @@ -117,7 +118,7 @@ const Status = { return hits }, - muted () { return !this.unmuted && (this.status.user.muted || this.muteWordHits.length > 0) }, + muted () { return !this.unmuted && ((!this.inProfile && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) }, hideFilteredStatuses () { return typeof this.$store.state.config.hideFilteredStatuses === 'undefined' ? this.$store.state.instance.hideFilteredStatuses @@ -335,7 +336,7 @@ const Status = { return } } - if (target.className.match(/hashtag/)) { + if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) { // Extract tag name from link url const tag = extractTagFromUrl(target.href) if (tag) { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index ab506632..64218f6e 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -32,7 +32,7 @@ </template> <template v-else> <div - v-if="showPinned && statusoid.pinned" + v-if="showPinned" class="status-pin" > <i class="fa icon-pin faint" /> diff --git a/src/components/still-image/still-image.vue b/src/components/still-image/still-image.vue index 3fff63f9..4137bd59 100644 --- a/src/components/still-image/still-image.vue +++ b/src/components/still-image/still-image.vue @@ -7,8 +7,10 @@ v-if="animated" ref="canvas" /> + <!-- NOTE: key is required to force to re-render img tag when src is changed --> <img ref="src" + :key="src" :src="src" :referrerpolicy="referrerpolicy" @load="onLoad" diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index a5fe019c..08d5d08f 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -4,12 +4,22 @@ import './tab_switcher.scss' export default Vue.component('tab-switcher', { name: 'TabSwitcher', - props: ['renderOnlyFocused', 'onSwitch', 'customActive'], + props: ['renderOnlyFocused', 'onSwitch', 'activeTab'], data () { return { active: this.$slots.default.findIndex(_ => _.tag) } }, + computed: { + activeIndex () { + // In case of controlled component + if (this.activeTab) { + return this.$slots.default.findIndex(slot => this.activeTab === slot.key) + } else { + return this.active + } + } + }, beforeUpdate () { const currentSlot = this.$slots.default[this.active] if (!currentSlot.tag) { @@ -17,21 +27,13 @@ export default Vue.component('tab-switcher', { } }, methods: { - activateTab (index, dataset) { + activateTab (index) { return () => { if (typeof this.onSwitch === 'function') { - this.onSwitch.call(null, index, this.$slots.default[index].elm.dataset) + this.onSwitch.call(null, this.$slots.default[index].key) } this.active = index } - }, - isActiveTab (index) { - const customActiveIndex = this.$slots.default.findIndex(slot => { - const dataFilter = slot.data && slot.data.attrs && slot.data.attrs['data-filter'] - return this.customActive && this.customActive === dataFilter - }) - - return customActiveIndex > -1 ? customActiveIndex === index : index === this.active } }, render (h) { @@ -41,13 +43,13 @@ export default Vue.component('tab-switcher', { const classesTab = ['tab'] const classesWrapper = ['tab-wrapper'] - if (this.isActiveTab(index)) { + if (this.activeIndex === index) { classesTab.push('active') classesWrapper.push('active') } if (slot.data.attrs.image) { return ( - <div class={ classesWrapper.join(' ')}> + <div class={classesWrapper.join(' ')}> <button disabled={slot.data.attrs.disabled} onClick={this.activateTab(index)} @@ -59,7 +61,7 @@ export default Vue.component('tab-switcher', { ) } return ( - <div class={ classesWrapper.join(' ')}> + <div class={classesWrapper.join(' ')}> <button disabled={slot.data.attrs.disabled} onClick={this.activateTab(index)} @@ -71,7 +73,7 @@ export default Vue.component('tab-switcher', { const contents = this.$slots.default.map((slot, index) => { if (!slot.tag) return - const active = index === this.active + const active = this.activeIndex === index if (this.renderOnlyFocused) { return active ? <div class="active">{slot}</div> diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index aac3869f..0594576c 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -25,7 +25,8 @@ const Timeline = { 'tag', 'embedded', 'count', - 'pinnedStatusIds' + 'pinnedStatusIds', + 'inProfile' ], data () { return { @@ -58,7 +59,10 @@ const Timeline = { excludedStatusIdsObject () { const ids = getExcludedStatusIdsByPinning(this.timeline.visibleStatuses, this.pinnedStatusIds) // Convert id array to object - return keyBy(ids, id => id) + return keyBy(ids) + }, + pinnedStatusIdsObject () { + return keyBy(this.pinnedStatusIds) } }, components: { diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 0cb4b3ef..f1d3903a 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -33,9 +33,10 @@ v-if="timeline.statusesObject[statusId]" :key="statusId + '-pinned'" class="status-fadein" - :statusoid="timeline.statusesObject[statusId]" + :status-id="statusId" :collapsable="true" - :show-pinned="true" + :pinned-status-ids-object="pinnedStatusIdsObject" + :in-profile="inProfile" /> </template> <template v-for="status in timeline.visibleStatuses"> @@ -43,8 +44,9 @@ v-if="!excludedStatusIdsObject[status.id]" :key="status.id" class="status-fadein" - :statusoid="status" + :status-id="status.id" :collapsable="true" + :in-profile="inProfile" /> </template> </div> diff --git a/src/components/user_avatar/user_avatar.js b/src/components/user_avatar/user_avatar.js index a42b9c71..4adf8211 100644 --- a/src/components/user_avatar/user_avatar.js +++ b/src/components/user_avatar/user_avatar.js @@ -16,7 +16,7 @@ const UserAvatar = { }, computed: { imgSrc () { - return this.showPlaceholder ? '/images/avi.png' : this.src + return this.showPlaceholder ? '/images/avi.png' : this.user.profile_image_url_original } }, methods: { diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue index 811efd3c..9ffb28d8 100644 --- a/src/components/user_avatar/user_avatar.vue +++ b/src/components/user_avatar/user_avatar.vue @@ -3,7 +3,7 @@ class="avatar" :alt="user.screen_name" :title="user.screen_name" - :src="user.profile_image_url_original" + :src="imgSrc" :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" :image-load-error="imageLoadError" /> diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 82d3b835..0c200ad1 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -11,7 +11,6 @@ export default { data () { return { followRequestInProgress: false, - followRequestSent: false, hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined' ? this.$store.state.instance.hideUserStats : this.$store.state.config.hideUserStats, @@ -112,9 +111,8 @@ export default { followUser () { const store = this.$store this.followRequestInProgress = true - requestFollow(this.user, store).then(({ sent }) => { + requestFollow(this.user, store).then(() => { this.followRequestInProgress = false - this.followRequestSent = sent }) }, unfollowUser () { @@ -170,6 +168,9 @@ export default { } this.$store.dispatch('setMedia', [attachment]) this.$store.dispatch('setCurrent', attachment) + }, + mentionUser () { + this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user }) } } } diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index fc18e240..f25d16d3 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -135,13 +135,13 @@ <button class="btn btn-default btn-block" :disabled="followRequestInProgress" - :title="followRequestSent ? $t('user_card.follow_again') : ''" + :title="user.requested ? $t('user_card.follow_again') : ''" @click="followUser" > <template v-if="followRequestInProgress"> {{ $t('user_card.follow_progress') }} </template> - <template v-else-if="followRequestSent"> + <template v-else-if="user.requested"> {{ $t('user_card.follow_sent') }} </template> <template v-else> @@ -190,6 +190,15 @@ <div> <button + class="btn btn-default btn-block" + @click="mentionUser" + > + {{ $t('user_card.mention') }} + </button> + </div> + + <div> + <button v-if="user.muted" class="btn btn-default btn-block pressed" @click="unmuteUser" diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue index c92630e3..e859d612 100644 --- a/src/components/user_panel/user_panel.vue +++ b/src/components/user_panel/user_panel.vue @@ -11,7 +11,7 @@ rounded="top" /> <div class="panel-footer"> - <post-status-form v-if="user" /> + <post-status-form /> </div> </div> <auth-form diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 39b99dac..00055707 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -22,21 +22,23 @@ const FriendList = withLoadMore({ additionalPropNames: ['userId'] })(List) +const defaultTabKey = 'statuses' + const UserProfile = { data () { return { error: false, - userId: null + userId: null, + tab: defaultTabKey } }, created () { - // Make sure that timelines used in this page are empty - this.cleanUp() const routeParams = this.$route.params this.load(routeParams.name || routeParams.id) + this.tab = get(this.$route, 'query.tab', defaultTabKey) }, destroyed () { - this.cleanUp() + this.stopFetching() }, computed: { timeline () { @@ -67,17 +69,36 @@ const UserProfile = { }, methods: { load (userNameOrId) { + const startFetchingTimeline = (timeline, userId) => { + // Clear timeline only if load another user's profile + if (userId !== this.$store.state.statuses.timelines[timeline].userId) { + this.$store.commit('clearTimeline', { timeline }) + } + this.$store.dispatch('startFetchingTimeline', { timeline, userId }) + } + + const loadById = (userId) => { + this.userId = userId + startFetchingTimeline('user', userId) + startFetchingTimeline('media', userId) + if (this.isUs) { + startFetchingTimeline('favorites', userId) + } + // Fetch all pinned statuses immediately + this.$store.dispatch('fetchPinnedStatuses', userId) + } + + // Reset view + this.userId = null + this.error = false + // Check if user data is already loaded in store const user = this.$store.getters.findUser(userNameOrId) if (user) { - this.userId = user.id - this.fetchTimelines() + loadById(user.id) } else { this.$store.dispatch('fetchUser', userNameOrId) - .then(({ id }) => { - this.userId = id - this.fetchTimelines() - }) + .then(({ id }) => loadById(id)) .catch((reason) => { const errorMessage = get(reason, 'error.error') if (errorMessage === 'No user with such user_id') { // Known error @@ -90,40 +111,33 @@ const UserProfile = { }) } }, - fetchTimelines () { - const userId = this.userId - this.$store.dispatch('startFetchingTimeline', { timeline: 'user', userId }) - this.$store.dispatch('startFetchingTimeline', { timeline: 'media', userId }) - if (this.isUs) { - this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId }) - } - // Fetch all pinned statuses immediately - this.$store.dispatch('fetchPinnedStatuses', userId) - }, - cleanUp () { + stopFetching () { this.$store.dispatch('stopFetching', 'user') this.$store.dispatch('stopFetching', 'favorites') this.$store.dispatch('stopFetching', 'media') - this.$store.commit('clearTimeline', { timeline: 'user' }) - this.$store.commit('clearTimeline', { timeline: 'favorites' }) - this.$store.commit('clearTimeline', { timeline: 'media' }) + }, + switchUser (userNameOrId) { + this.stopFetching() + this.load(userNameOrId) + }, + onTabSwitch (tab) { + this.tab = tab + this.$router.replace({ query: { tab } }) } }, watch: { '$route.params.id': function (newVal) { if (newVal) { - this.cleanUp() - this.load(newVal) + this.switchUser(newVal) } }, '$route.params.name': function (newVal) { if (newVal) { - this.cleanUp() - this.load(newVal) + this.switchUser(newVal) } }, - $route () { - this.$refs.tabSwitcher.activateTab(0)() + '$route.query': function (newVal) { + this.tab = newVal.tab || defaultTabKey } }, components: { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index cffa28f1..14082e83 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -12,22 +12,25 @@ rounded="top" /> <tab-switcher - ref="tabSwitcher" + :active-tab="tab" :render-only-focused="true" + :on-switch="onTabSwitch" > - <div :label="$t('user_card.statuses')"> - <Timeline - :count="user.statuses_count" - :embedded="true" - :title="$t('user_profile.timeline_title')" - :timeline="timeline" - timeline-name="user" - :user-id="userId" - :pinned-status-ids="user.pinnedStatusIds" - /> - </div> + <Timeline + key="statuses" + :label="$t('user_card.statuses')" + :count="user.statuses_count" + :embedded="true" + :title="$t('user_profile.timeline_title')" + :timeline="timeline" + timeline-name="user" + :user-id="userId" + :pinned-status-ids="user.pinnedStatusIds" + :in-profile="true" + /> <div v-if="followsTabVisible" + key="followees" :label="$t('user_card.followees')" :disabled="!user.friends_count" > @@ -42,6 +45,7 @@ </div> <div v-if="followersTabVisible" + key="followers" :label="$t('user_card.followers')" :disabled="!user.followers_count" > @@ -58,6 +62,7 @@ </FollowerList> </div> <Timeline + key="media" :label="$t('user_card.media')" :disabled="!media.visibleStatuses.length" :embedded="true" @@ -65,15 +70,18 @@ timeline-name="media" :timeline="media" :user-id="userId" + :in-profile="true" /> <Timeline v-if="isUs" + key="favorites" :label="$t('user_card.favorites')" :disabled="!favorites.visibleStatuses.length" :embedded="true" :title="$t('user_card.favorites')" timeline-name="favorites" :timeline="favorites" + :in-profile="true" /> </tab-switcher> </div> diff --git a/src/components/who_to_follow/who_to_follow.js b/src/components/who_to_follow/who_to_follow.js index f8100257..1aa3a4cd 100644 --- a/src/components/who_to_follow/who_to_follow.js +++ b/src/components/who_to_follow/who_to_follow.js @@ -21,11 +21,12 @@ const WhoToFollow = { name: i.display_name, screen_name: i.acct, profile_image_url: i.avatar || '/images/avi.png', - profile_image_url_original: i.avatar || '/images/avi.png' + profile_image_url_original: i.avatar || '/images/avi.png', + statusnet_profile_url: i.url } this.users.push(user) - this.$store.state.api.backendInteractor.externalProfile(user.screen_name) + this.$store.state.api.backendInteractor.fetchUser({ id: user.screen_name }) .then((externalUser) => { if (!externalUser.error) { this.$store.commit('addNewUsers', [externalUser]) diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js index 7d01678b..dcb56106 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.js +++ b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -13,7 +13,7 @@ function showWhoToFollow (panel, reply) { toFollow.img = img toFollow.name = name - panel.$store.state.api.backendInteractor.externalProfile(name) + panel.$store.state.api.backendInteractor.fetchUser({ id: name }) .then((externalUser) => { if (!externalUser.error) { panel.$store.commit('addNewUsers', [externalUser]) |
