From 2c89d49a3d22ed2813a6a57fb6049341fa8624ba Mon Sep 17 00:00:00 2001 From: dave <starpumadev@gmail.com> Date: Thu, 4 Apr 2019 15:10:34 -0400 Subject: [PATCH] #468 - show pinned timeline and add pinned label to the status --- src/components/conversation/conversation.js | 3 +- src/components/conversation/conversation.vue | 1 + src/components/status/status.js | 3 +- src/components/status/status.vue | 17 +++++++++-- src/components/timeline/timeline.js | 3 +- src/components/timeline/timeline.vue | 3 +- src/components/user_profile/user_profile.js | 5 ++++ src/components/user_profile/user_profile.vue | 29 ++++++++++++------- src/modules/statuses.js | 3 +- src/services/api/api.service.js | 14 +++++++++ .../timeline_fetcher.service.js | 6 ++++ 11 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index ffeb7244..fc239ee9 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -41,7 +41,8 @@ const conversation = { props: [ 'statusoid', 'collapsable', - 'isPage' + 'isPage', + 'pinned' ], created () { if (this.isPage) { diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index d04ff722..40011113 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -13,6 +13,7 @@ :key="status.id" :inlineExpanded="collapsable && isExpanded" :statusoid="status" + :pinned="pinned" :expandable='!isExpanded' :focused="focused(status.id)" :inConversation="isExpanded" diff --git a/src/components/status/status.js b/src/components/status/status.js index a3596b69..f2881742 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -26,7 +26,8 @@ const Status = { 'replies', 'isPreview', 'noHeading', - 'inlineExpanded' + 'inlineExpanded', + 'pinned' ], data () { return { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 0443e758..a5614f59 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -12,6 +12,13 @@ </div> </template> <template v-else> + <div v-if="pinned" class="status-pin"> + <i class="fa icon-pin faint"></i> + <span class="faint">Pinned</span> + <div class="button-icon button-action-icon" v-if="status.pinned && ownStatus" @click.prevent="unpinStatus" title="Unpin"> + <i class="fa icon-cancel"></i> + </div> + </div> <div v-if="retweet && !noHeading && !inConversation" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info"> <UserAvatar class="media-left" v-if="retweet" :betterShadow="betterShadow" :user="statusoid.user"/> <div class="media-body faint"> @@ -55,9 +62,6 @@ <div class="button-icon button-action-icon" v-if="!status.pinned && ownStatus" @click.prevent="pinStatus" title="Pin"> <i class="fa icon-pin"></i> </div> - <div class="button-icon button-action-icon" v-if="status.pinned && ownStatus" @click.prevent="unpinStatus" title="Unpin"> - <i class="fa icon-cancel"></i> - </div> <div class="button-icon button-action-icon" v-if="expandable && !isPreview" @click.prevent="toggleExpanded" title="Expand"> <i class="button-icon icon-plus-squared"></i> </div> @@ -205,6 +209,13 @@ $status-margin: 0.75em; max-width: 100%; } +.status-pin { + padding: 0.75em 0.75em 0; + display: flex; + align-items: center; + justify-content: flex-end; +} + .status-preview { position: absolute; max-width: 95%; diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 19d9a9ac..953b4a06 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -11,7 +11,8 @@ const Timeline = { 'userId', 'tag', 'embedded', - 'count' + 'count', + 'noLoadMore' ], data () { return { diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index e6a8d458..870fe4f5 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -21,11 +21,12 @@ class="status-fadein" :key="status.id" :statusoid="status" + :pinned="timelineName === 'pinned'" :collapsable="true" /> </div> </div> - <div :class="classes.footer"> + <div :class="classes.footer" v-if="!noLoadMore"> <div v-if="count===0" class="new-status-notification text-center panel-footer faint"> {{$t('timeline.no_statuses')}} </div> diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js index 4eddb8b1..71a88dcf 100644 --- a/src/components/user_profile/user_profile.js +++ b/src/components/user_profile/user_profile.js @@ -40,6 +40,9 @@ const UserProfile = { timeline () { return this.$store.state.statuses.timelines.user }, + pinned () { + return this.$store.state.statuses.timelines.pinned + }, favorites () { return this.$store.state.statuses.timelines.favorites }, @@ -91,6 +94,7 @@ const UserProfile = { fetchTimelines () { const userId = this.userId this.$store.dispatch('startFetchingTimeline', { timeline: 'user', userId }) + this.$store.dispatch('startFetchingTimeline', { timeline: 'pinned', userId }) this.$store.dispatch('startFetchingTimeline', { timeline: 'media', userId }) if (this.isUs) { this.$store.dispatch('startFetchingTimeline', { timeline: 'favorites', userId }) @@ -98,6 +102,7 @@ const UserProfile = { }, cleanUp () { this.$store.dispatch('stopFetching', 'user') + this.$store.dispatch('stopFetching', 'pinned') this.$store.dispatch('stopFetching', 'favorites') this.$store.dispatch('stopFetching', 'media') this.$store.commit('clearTimeline', { timeline: 'user' }) diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index 71c625b7..caf297c6 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -3,16 +3,25 @@ <div v-if="user" class="user-profile panel panel-default"> <UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/> <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher"> - <Timeline - :label="$t('user_card.statuses')" - :disabled="!user.statuses_count" - :count="user.statuses_count" - :embedded="true" - :title="$t('user_profile.timeline_title')" - :timeline="timeline" - :timeline-name="'user'" - :user-id="userId" - /> + <div :label="$t('user_card.statuses')" :disabled="!user.statuses_count"> + <Timeline + :count="user.statuses_count" + :embedded="true" + :title="$t('user_profile.timeline_title')" + :timeline="pinned" + :timeline-name="'pinned'" + :user-id="userId" + :no-load-more="true" + /> + <Timeline + :count="user.statuses_count" + :embedded="true" + :title="$t('user_profile.timeline_title')" + :timeline="timeline" + :timeline-name="'user'" + :user-id="userId" + /> + </div> <div :label="$t('user_card.followees')" v-if="followsTabVisible" :disabled="!user.friends_count"> <FriendList :userId="userId"> <template slot="item" slot-scope="{item}"> diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 176e5f42..94d7f30b 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -47,7 +47,8 @@ export const defaultState = () => ({ publicAndExternal: emptyTl(), friends: emptyTl(), tag: emptyTl(), - dms: emptyTl() + dms: emptyTl(), + pinned: emptyTl() } }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 65cebe78..03137d20 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -506,6 +506,19 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use .then((data) => data.map(isNotifications ? parseNotification : parseStatus)) } +const fetchPinnedStatuses = ({ id, credentials }) => { + const url = MASTODON_USER_TIMELINE_URL(id) + '?pinned=true' + return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => { + if (data.ok) { + return data + } + throw new Error('Error fetching pinned timeline', data) + }) + .then((data) => data.json()) + .then((data) => data.map(parseStatus)) +} + const verifyCredentials = (user) => { return fetch(LOGIN_URL, { method: 'POST', @@ -726,6 +739,7 @@ const reportUser = ({credentials, userId, statusIds, comment, forward}) => { const apiService = { verifyCredentials, fetchTimeline, + fetchPinnedStatuses, fetchConversation, fetchStatus, fetchFriends, diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 8e954cdf..e8d9a2ed 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -16,6 +16,12 @@ const update = ({store, statuses, timeline, showImmediately, userId}) => { } const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false, showImmediately = false, userId = false, tag = false, until}) => { + if (timeline === 'pinned') { + return apiService.fetchPinnedStatuses({ id: userId, credentials }) + .then(statuses => { + update({ store, statuses, timeline, showImmediately, userId }) + }) + } const args = { timeline, credentials } const rootState = store.rootState || store.state const timelineData = rootState.statuses.timelines[camelCase(timeline)]