From 2c89d49a3d22ed2813a6a57fb6049341fa8624ba Mon Sep 17 00:00:00 2001
From: dave <starpumadev@gmail.com>
Date: Thu, 4 Apr 2019 15:10:34 -0400
Subject: #468 - show pinned timeline and add pinned label to the status

---
 src/components/user_profile/user_profile.vue | 29 ++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

(limited to 'src/components/user_profile/user_profile.vue')

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}">
-- 
cgit v1.2.3-70-g09d2


From cd0a7afa06cafc93510286d35a60e9e40ead36bf Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Wed, 24 Apr 2019 13:56:53 -0400
Subject: remove pinned timeline, instead, use simple entity of user object

---
 src/components/timeline/timeline.js                   |  3 +--
 src/components/timeline/timeline.vue                  |  3 +--
 src/components/user_profile/user_profile.js           | 13 +++++++------
 src/components/user_profile/user_profile.vue          | 19 ++++++++++---------
 src/modules/statuses.js                               | 14 ++++++++++----
 src/modules/users.js                                  |  3 +++
 .../backend_interactor_service.js                     |  2 ++
 .../entity_normalizer/entity_normalizer.service.js    |  2 ++
 8 files changed, 36 insertions(+), 23 deletions(-)

(limited to 'src/components/user_profile/user_profile.vue')

diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 953b4a06..19d9a9ac 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -11,8 +11,7 @@ const Timeline = {
     'userId',
     'tag',
     'embedded',
-    'count',
-    'noLoadMore'
+    'count'
   ],
   data () {
     return {
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 870fe4f5..e6a8d458 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -21,12 +21,11 @@
           class="status-fadein"
           :key="status.id"
           :statusoid="status"
-          :pinned="timelineName === 'pinned'"
           :collapsable="true"
         />
       </div>
     </div>
-    <div :class="classes.footer" v-if="!noLoadMore">
+    <div :class="classes.footer">
       <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 71a88dcf..0071fa89 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -2,6 +2,7 @@ import get from 'lodash/get'
 import UserCard from '../user_card/user_card.vue'
 import FollowCard from '../follow_card/follow_card.vue'
 import Timeline from '../timeline/timeline.vue'
+import Conversation from '../conversation/conversation.vue'
 import ModerationTools from '../moderation_tools/moderation_tools.vue'
 import List from '../list/list.vue'
 import withLoadMore from '../../hocs/with_load_more/with_load_more'
@@ -40,9 +41,6 @@ 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
     },
@@ -56,6 +54,9 @@ const UserProfile = {
     user () {
       return this.$store.getters.findUser(this.userId)
     },
+    pinnedStatuses () {
+      return this.user.pinnedStatusIds.map(id => this.$store.state.statuses.allStatusesObject[id])
+    },
     isExternal () {
       return this.$route.name === 'external-user-profile'
     },
@@ -94,15 +95,14 @@ 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 })
       }
+      this.$store.dispatch('fetchPinnedStatuses', userId)
     },
     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' })
@@ -133,7 +133,8 @@ const UserProfile = {
     FollowerList,
     FriendList,
     ModerationTools,
-    FollowCard
+    FollowCard,
+    Conversation
   }
 }
 
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index caf297c6..46561e2a 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -4,15 +4,16 @@
     <UserCard :user="user" :switcher="true" :selected="timeline.viewing" rounded="top"/>
     <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
       <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"
-        />
+        <div class="timeline">
+          <Conversation
+            v-for="status in pinnedStatuses"
+            class="status-fadein"
+            :key="status.id"
+            :statusoid="status"
+            :pinned="true"
+            :collapsable="true"
+          />
+        </div>
         <Timeline
           :count="user.statuses_count"
           :embedded="true"
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index b93f9cb5..3b3ae9e3 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -1,4 +1,4 @@
-import { remove, slice, each, findIndex, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash'
+import { remove, slice, each, findIndex, find, maxBy, minBy, merge, first, last, isArray, omitBy, map } from 'lodash'
 import { set } from 'vue'
 import apiService from '../services/api/api.service.js'
 // import parse from '../services/status_parser/status_parser.js'
@@ -47,8 +47,7 @@ export const defaultState = () => ({
     publicAndExternal: emptyTl(),
     friends: emptyTl(),
     tag: emptyTl(),
-    dms: emptyTl(),
-    pinned: emptyTl()
+    dms: emptyTl()
   }
 })
 
@@ -171,7 +170,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
   // This makes sure that user timeline won't get data meant for other
   // user. I.e. opening different user profiles makes request which could
   // return data late after user already viewing different user profile
-  if ((timeline === 'user' || timeline === 'media' || timeline === 'pinned') && timelineObject.userId !== userId) {
+  if ((timeline === 'user' || timeline === 'media') && timelineObject.userId !== userId) {
     return
   }
 
@@ -542,6 +541,13 @@ const statuses = {
       rootState.api.backendInteractor.unfavorite(status.id)
         .then(status => commit('setFavoritedConfirm', { status, user: rootState.users.currentUser }))
     },
+    fetchPinnedStatuses ({ rootState, dispatch, commit }, userId) {
+      rootState.api.backendInteractor.fetchPinnedStatuses(userId)
+        .then(statuses => {
+          dispatch('addNewStatuses', { statuses })
+          commit('savePinnedStatusIds', { userId, statusIds: map(statuses, 'id') })
+        })
+    },
     updatePinned ({ rootState, commit }, status) {
       commit('setPinned', { status })
       if (status.pinned) {
diff --git a/src/modules/users.js b/src/modules/users.js
index adcab233..def2b38c 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -160,6 +160,9 @@ export const mutations = {
   saveMuteIds (state, muteIds) {
     state.currentUser.muteIds = muteIds
   },
+  savePinnedStatusIds (state, { userId, statusIds }) {
+    state.usersObject[userId].pinnedStatusIds = statusIds
+  },
   addMuteId (state, muteId) {
     if (state.currentUser.muteIds.indexOf(muteId) === -1) {
       state.currentUser.muteIds.push(muteId)
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index 816e73c1..7a302f57 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -106,6 +106,7 @@ const backendInteractorService = (credentials) => {
   const fetchFollowRequests = () => apiService.fetchFollowRequests({credentials})
   const fetchOAuthTokens = () => apiService.fetchOAuthTokens({credentials})
   const revokeOAuthToken = (id) => apiService.revokeOAuthToken({id, credentials})
+  const fetchPinnedStatuses = (id) => apiService.fetchPinnedStatuses({ id, credentials })
 
   const getCaptcha = () => apiService.getCaptcha()
   const register = (params) => apiService.register(params)
@@ -154,6 +155,7 @@ const backendInteractorService = (credentials) => {
     fetchBlocks,
     fetchOAuthTokens,
     revokeOAuthToken,
+    fetchPinnedStatuses,
     tagUser,
     untagUser,
     addRight,
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 97e20b99..3636c05b 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -131,6 +131,8 @@ export const parseUser = (data) => {
   output.statuses_count = data.statuses_count
   output.friendIds = []
   output.followerIds = []
+  output.pinnedStatusIds = []
+
   if (data.pleroma) {
     output.follow_request_count = data.pleroma.follow_request_count
   }
-- 
cgit v1.2.3-70-g09d2


From 32be0e15d11c14513ff4fdc984aeaa4a5f462eac Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Fri, 26 Apr 2019 15:46:40 -0400
Subject: use pinned property from status directly

---
 src/components/conversation/conversation.js  | 3 +--
 src/components/conversation/conversation.vue | 1 -
 src/components/status/status.js              | 3 +--
 src/components/status/status.vue             | 2 +-
 src/components/user_profile/user_profile.vue | 1 -
 5 files changed, 3 insertions(+), 7 deletions(-)

(limited to 'src/components/user_profile/user_profile.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index fc239ee9..ffeb7244 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -41,8 +41,7 @@ const conversation = {
   props: [
     'statusoid',
     'collapsable',
-    'isPage',
-    'pinned'
+    'isPage'
   ],
   created () {
     if (this.isPage) {
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index 40011113..d04ff722 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -13,7 +13,6 @@
       :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 fa4b426c..09a2a010 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -26,8 +26,7 @@ const Status = {
     'replies',
     'isPreview',
     'noHeading',
-    'inlineExpanded',
-    'pinned'
+    'inlineExpanded'
   ],
   data () {
     return {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 1644f6b0..41662576 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -13,7 +13,7 @@
       </div>
     </template>
     <template v-else>
-      <div v-if="pinned" class="status-pin">
+      <div v-if="statusoid.pinned" class="status-pin">
         <i class="fa icon-pin faint"></i>
         <span class="faint">Pinned</span>
       </div>
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 46561e2a..1e104403 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -10,7 +10,6 @@
             class="status-fadein"
             :key="status.id"
             :statusoid="status"
-            :pinned="true"
             :collapsable="true"
           />
         </div>
-- 
cgit v1.2.3-70-g09d2


From 110c9d3b26e3871c8fc8157458be201ed4316d49 Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Tue, 30 Apr 2019 08:20:19 -0400
Subject: improve performance by caching pinned status ids into user object

---
 src/components/user_profile/user_profile.js             |  3 ---
 src/components/user_profile/user_profile.vue            | 16 +++++++++-------
 src/modules/statuses.js                                 |  6 +++---
 src/modules/users.js                                    | 17 +++++++++++++++--
 .../entity_normalizer/entity_normalizer.service.js      |  2 ++
 5 files changed, 29 insertions(+), 15 deletions(-)

(limited to 'src/components/user_profile/user_profile.vue')

diff --git a/src/components/user_profile/user_profile.js b/src/components/user_profile/user_profile.js
index 176ec228..8d660780 100644
--- a/src/components/user_profile/user_profile.js
+++ b/src/components/user_profile/user_profile.js
@@ -42,9 +42,6 @@ const UserProfile = {
     timeline () {
       return this.$store.state.statuses.timelines.user
     },
-    pinnedStatuses () {
-      return filter(this.timeline.statuses, { pinned: true })
-    },
     favorites () {
       return this.$store.state.statuses.timelines.favorites
     },
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 1e104403..36d75b9e 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -5,13 +5,15 @@
     <tab-switcher :renderOnlyFocused="true" ref="tabSwitcher">
       <div :label="$t('user_card.statuses')" :disabled="!user.statuses_count">
         <div class="timeline">
-          <Conversation
-            v-for="status in pinnedStatuses"
-            class="status-fadein"
-            :key="status.id"
-            :statusoid="status"
-            :collapsable="true"
-          />
+          <template v-for="statusId in user.pinnedStatuseIds">
+            <Conversation
+              v-if="timeline.statusesObject[statusId]"
+              class="status-fadein"
+              :key="statusId"
+              :statusoid="timeline.statusesObject[statusId]"
+              :collapsable="true"
+            />
+          </template>
         </div>
         <Timeline
           :count="user.statuses_count"
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 0650a86b..e6ee5447 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -424,7 +424,7 @@ export const mutations = {
       newStatus.favoritedBy.push(user)
     }
   },
-  setPinned (state, { status }) {
+  setPinned (state, status) {
     const newStatus = state.allStatusesObject[status.id]
     newStatus.pinned = status.pinned
   },
@@ -543,11 +543,11 @@ const statuses = {
     },
     pinStatus ({ rootState, commit }, statusId) {
       return rootState.api.backendInteractor.pinOwnStatus(statusId)
-        .then((status) => commit('setPinned', { status }))
+        .then((status) => commit('setPinned', status))
     },
     unpinStatus ({ rootState, commit }, statusId) {
       rootState.api.backendInteractor.unpinOwnStatus(statusId)
-        .then((status) => commit('setPinned', { status }))
+        .then((status) => commit('setPinned', status))
     },
     retweet ({ rootState, commit }, status) {
       // Optimistic retweeting...
diff --git a/src/modules/users.js b/src/modules/users.js
index adcab233..e72a657c 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -165,6 +165,15 @@ export const mutations = {
       state.currentUser.muteIds.push(muteId)
     }
   },
+  setPinned (state, status) {
+    const user = state.usersObject[status.user.id]
+    const index = user.pinnedStatuseIds.indexOf(status.id)
+    if (status.pinned && index === -1) {
+      user.pinnedStatuseIds.push(status.id)
+    } else if (!status.pinned && index !== -1) {
+      user.pinnedStatuseIds.splice(index, 1)
+    }
+  },
   setUserForStatus (state, status) {
     status.user = state.usersObject[status.user.id]
   },
@@ -318,13 +327,17 @@ const users = {
       store.commit('addNewUsers', users)
       store.commit('addNewUsers', retweetedUsers)
 
-      // Reconnect users to statuses
       each(statuses, (status) => {
+        // Reconnect users to statuses
         store.commit('setUserForStatus', status)
+        // Set pinned statuses to user
+        store.commit('setPinned', status)
       })
-      // Reconnect users to retweets
       each(compact(map(statuses, 'retweeted_status')), (status) => {
+        // Reconnect users to retweets
         store.commit('setUserForStatus', status)
+        // Set pinned retweets to user
+        store.commit('setPinned', status)
       })
     },
     addNewNotifications (store, { notifications }) {
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 5b8ad8a4..e3d1646a 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -131,6 +131,8 @@ export const parseUser = (data) => {
   output.statuses_count = data.statuses_count
   output.friendIds = []
   output.followerIds = []
+  output.pinnedStatuseIds = []
+
   if (data.pleroma) {
     output.follow_request_count = data.pleroma.follow_request_count
   }
-- 
cgit v1.2.3-70-g09d2


From f68653d0dd0b374aa28eca35b424b6c834c701d6 Mon Sep 17 00:00:00 2001
From: taehoon <th.dev91@gmail.com>
Date: Tue, 30 Apr 2019 11:06:22 -0400
Subject: show pinned state only in the user timeline

---
 src/components/conversation/conversation.js  | 3 ++-
 src/components/conversation/conversation.vue | 1 +
 src/components/status/status.js              | 3 ++-
 src/components/status/status.vue             | 2 +-
 src/components/user_profile/user_profile.vue | 1 +
 5 files changed, 7 insertions(+), 3 deletions(-)

(limited to 'src/components/user_profile/user_profile.vue')

diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
index ffeb7244..b3074590 100644
--- a/src/components/conversation/conversation.js
+++ b/src/components/conversation/conversation.js
@@ -41,7 +41,8 @@ const conversation = {
   props: [
     'statusoid',
     'collapsable',
-    'isPage'
+    'isPage',
+    'showPinned'
   ],
   created () {
     if (this.isPage) {
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index d04ff722..0b4998c3 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -14,6 +14,7 @@
       :inlineExpanded="collapsable && isExpanded"
       :statusoid="status"
       :expandable='!isExpanded'
+      :showPinned="showPinned"
       :focused="focused(status.id)"
       :inConversation="isExpanded"
       :highlight="getHighlight()"
diff --git a/src/components/status/status.js b/src/components/status/status.js
index a1ffb026..5b3d98c3 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -26,7 +26,8 @@ const Status = {
     'replies',
     'isPreview',
     'noHeading',
-    'inlineExpanded'
+    'inlineExpanded',
+    'showPinned'
   ],
   data () {
     return {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 8575a95e..997c1b31 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -16,7 +16,7 @@
       </div>
     </template>
     <template v-else>
-      <div v-if="statusoid.pinned" class="status-pin">
+      <div v-if="showPinned && statusoid.pinned" class="status-pin">
         <i class="fa icon-pin faint"></i>
         <span class="faint">{{$t('status.pinned')}}</span>
       </div>
diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue
index 36d75b9e..48b774ea 100644
--- a/src/components/user_profile/user_profile.vue
+++ b/src/components/user_profile/user_profile.vue
@@ -12,6 +12,7 @@
               :key="statusId"
               :statusoid="timeline.statusesObject[statusId]"
               :collapsable="true"
+              :showPinned="true"
             />
           </template>
         </div>
-- 
cgit v1.2.3-70-g09d2

