From 62e9525724cfff0c3e26b8325b019e926baed1ca Mon Sep 17 00:00:00 2001
From: shpuld <shp@cock.li>
Date: Tue, 29 Jan 2019 21:04:52 +0200
Subject: [PATCH] Add loading indicator for notifications, make timelines
 indicate bottoming out when no more statuses

---
 src/components/notifications/notifications.js      | 14 ++++++++++++++
 src/components/notifications/notifications.vue     |  9 +++++++--
 src/components/timeline/timeline.js                | 10 ++++++++--
 src/components/timeline/timeline.vue               |  9 +++++++--
 src/i18n/en.json                                   |  6 ++++--
 src/i18n/fi.json                                   |  6 ++++--
 src/modules/statuses.js                            |  7 +++++++
 .../notifications_fetcher.service.js               |  1 +
 .../timeline_fetcher/timeline_fetcher.service.js   |  1 +
 9 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index ea32bbd0..5e95631a 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -13,6 +13,11 @@ const Notifications = {
 
     notificationsFetcher.startFetching({ store, credentials })
   },
+  data () {
+    return {
+      bottomedOut: false
+    }
+  },
   computed: {
     notifications () {
       return notificationsFromStore(this.$store)
@@ -28,6 +33,9 @@ const Notifications = {
     },
     unseenCount () {
       return this.unseenNotifications.length
+    },
+    loading () {
+      return this.$store.state.statuses.notifications.loading
     }
   },
   components: {
@@ -49,10 +57,16 @@ const Notifications = {
     fetchOlderNotifications () {
       const store = this.$store
       const credentials = store.state.users.currentUser.credentials
+      store.commit('setNotificationsLoading', { value: true })
       notificationsFetcher.fetchAndUpdate({
         store,
         credentials,
         older: true
+      }).then(notifs => {
+        store.commit('setNotificationsLoading', { value: false })
+        if (notifs.length === 0) {
+          this.bottomedOut = true
+        }
       })
     }
   }
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index 64f18720..6f162b62 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -18,10 +18,15 @@
         </div>
       </div>
       <div class="panel-footer">
-        <a href="#" v-on:click.prevent='fetchOlderNotifications()' v-if="!notifications.loading">
+        <div v-if="bottomedOut" class="new-status-notification text-center panel-footer faint">
+          {{$t('notifications.no_more_notifications')}}
+        </div>
+        <a v-else-if="!loading" href="#" v-on:click.prevent="fetchOlderNotifications()">
           <div class="new-status-notification text-center panel-footer">{{$t('notifications.load_older')}}</div>
         </a>
-        <div class="new-status-notification text-center panel-footer" v-else>...</div>
+        <div v-else class="new-status-notification text-center panel-footer">
+          <i class="icon-spin3 animate-spin"/>
+        </div>
       </div>
     </div>
   </div>
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 98da8660..85e0a055 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -16,7 +16,8 @@ const Timeline = {
   data () {
     return {
       paused: false,
-      unfocused: false
+      unfocused: false,
+      bottomedOut: false
     }
   },
   computed: {
@@ -95,7 +96,12 @@ const Timeline = {
         showImmediately: true,
         userId: this.userId,
         tag: this.tag
-      }).then(() => store.commit('setLoading', { timeline: this.timelineName, value: false }))
+      }).then(statuses => {
+        store.commit('setLoading', { timeline: this.timelineName, value: false })
+        if (statuses.length === 0) {
+          this.bottomedOut = true
+        }
+      })
     }, 1000, this),
     scrollLoad (e) {
       const bodyBRect = document.body.getBoundingClientRect()
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 6ba598c5..e3eea3bd 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -20,10 +20,15 @@
       </div>
     </div>
     <div :class="classes.footer">
-      <a href="#" v-on:click.prevent='fetchOlderStatuses()' v-if="!timeline.loading">
+      <div v-if="bottomedOut" class="new-status-notification text-center panel-footer faint">
+        {{$t('timeline.no_more_statuses')}}
+      </div>
+      <a v-else-if="!timeline.loading" href="#" v-on:click.prevent='fetchOlderStatuses()'>
         <div class="new-status-notification text-center panel-footer">{{$t('timeline.load_older')}}</div>
       </a>
-      <div class="new-status-notification text-center panel-footer" v-else>...</div>
+      <div v-else class="new-status-notification text-center panel-footer">
+        <i class="icon-spin3 animate-spin"/>
+      </div>
     </div>
   </div>
 </template>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 3ff98ab0..c2f48450 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -49,7 +49,8 @@
     "load_older": "Load older notifications",
     "notifications": "Notifications",
     "read": "Read!",
-    "repeated_you": "repeated your status"
+    "repeated_you": "repeated your status",
+    "no_more_notifications": "No more notifications"
   },
   "post_status": {
     "new_status": "Post new status",
@@ -317,7 +318,8 @@
     "no_retweet_hint": "Post is marked as followers-only or direct and cannot be repeated",
     "repeated": "repeated",
     "show_new": "Show new",
-    "up_to_date": "Up-to-date"
+    "up_to_date": "Up-to-date",
+    "no_more_statuses": "No more statuses"
   },
   "user_card": {
     "approve": "Approve",
diff --git a/src/i18n/fi.json b/src/i18n/fi.json
index 08cfb617..ee7cd4d2 100644
--- a/src/i18n/fi.json
+++ b/src/i18n/fi.json
@@ -26,7 +26,8 @@
     "followed_you": "seuraa sinua",
     "notifications": "Ilmoitukset",
     "read": "Lue!",
-    "repeated_you": "toisti viestisi"
+    "repeated_you": "toisti viestisi",
+    "no_more_notifications": "Ei enempää ilmoituksia"
   },
   "post_status": {
     "default": "Tulin juuri saunasta.",
@@ -77,7 +78,8 @@
     "load_older": "Lataa vanhempia viestejä",
     "repeated": "toisti",
     "show_new": "Näytä uudet",
-    "up_to_date": "Ajantasalla"
+    "up_to_date": "Ajantasalla",
+    "no_more_statuses": "Ei enempää viestejä"
   },
   "user_card": {
     "follow": "Seuraa",
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 04ec4dbc..56619455 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -28,6 +28,7 @@ export const defaultState = {
     minId: Number.POSITIVE_INFINITY,
     data: [],
     idStore: {},
+    loading: false,
     error: false
   },
   favorites: new Set(),
@@ -348,6 +349,9 @@ export const mutations = {
   setError (state, { value }) {
     state.error = value
   },
+  setNotificationsLoading (state, { value }) {
+    state.notifications.loading = value
+  },
   setNotificationsError (state, { value }) {
     state.notifications.error = value
   },
@@ -376,6 +380,9 @@ const statuses = {
     setError ({ rootState, commit }, { value }) {
       commit('setError', { value })
     },
+    setNotificationsLoading ({ rootState, commit }, { value }) {
+      commit('setNotificationsLoading', { value })
+    },
     setNotificationsError ({ rootState, commit }, { value }) {
       commit('setNotificationsError', { value })
     },
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
index 1480cded..b69ec643 100644
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ b/src/services/notifications_fetcher/notifications_fetcher.service.js
@@ -24,6 +24,7 @@ const fetchAndUpdate = ({store, credentials, older = false}) => {
   return apiService.fetchTimeline(args)
     .then((notifications) => {
       update({store, notifications, older})
+      return notifications
     }, () => store.dispatch('setNotificationsError', { value: true }))
     .catch(() => store.dispatch('setNotificationsError', { value: true }))
 }
diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js
index 727f6c60..08d91b5a 100644
--- a/src/services/timeline_fetcher/timeline_fetcher.service.js
+++ b/src/services/timeline_fetcher/timeline_fetcher.service.js
@@ -35,6 +35,7 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false
         store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })
       }
       update({store, statuses, timeline, showImmediately, userId})
+      return statuses
     }, () => store.dispatch('setError', { value: true }))
 }