From 86380f042976557d5260a3f5c2de0a9b0bcdbac6 Mon Sep 17 00:00:00 2001
From: Shpuld Shpludson <shp@cock.li>
Date: Tue, 14 Jan 2020 13:28:57 +0000
Subject: [PATCH] Optimize Notifications Rendering

---
 CHANGELOG.md                                  |  2 ++
 src/components/notifications/notifications.js | 27 ++++++++++++++++---
 .../notifications/notifications.vue           |  2 +-
 .../notification_utils/notification_utils.js  |  4 +--
 .../notification_utils.spec.js                |  4 +--
 5 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e17dc64..9f5a9305 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## [Unreleased]
+### Changed
+- Notifications column now cleans itself up to optimize performance when tab is left open for a long time
 ### Fixed
 - Single notifications left unread when hitting read on another device/tab
 
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index a89c0cdc..26ffbab6 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -2,10 +2,12 @@ import Notification from '../notification/notification.vue'
 import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
 import {
   notificationsFromStore,
-  visibleNotificationsFromStore,
+  filteredNotificationsFromStore,
   unseenNotificationsFromStore
 } from '../../services/notification_utils/notification_utils.js'
 
+const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30
+
 const Notifications = {
   props: {
     // Disables display of panel header
@@ -18,7 +20,11 @@ const Notifications = {
   },
   data () {
     return {
-      bottomedOut: false
+      bottomedOut: false,
+      // How many seen notifications to display in the list. The more there are,
+      // the heavier the page becomes. This count is increased when loading
+      // older notifications, and cut back to default whenever hitting "Read!".
+      seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
     }
   },
   computed: {
@@ -34,14 +40,17 @@ const Notifications = {
     unseenNotifications () {
       return unseenNotificationsFromStore(this.$store)
     },
-    visibleNotifications () {
-      return visibleNotificationsFromStore(this.$store, this.filterMode)
+    filteredNotifications () {
+      return filteredNotificationsFromStore(this.$store, this.filterMode)
     },
     unseenCount () {
       return this.unseenNotifications.length
     },
     loading () {
       return this.$store.state.statuses.notifications.loading
+    },
+    notificationsToDisplay () {
+      return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
     }
   },
   components: {
@@ -64,12 +73,21 @@ const Notifications = {
   methods: {
     markAsSeen () {
       this.$store.dispatch('markNotificationsAsSeen')
+      this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT
     },
     fetchOlderNotifications () {
       if (this.loading) {
         return
       }
 
+      const seenCount = this.filteredNotifications.length - this.unseenCount
+      if (this.seenToDisplayCount < seenCount) {
+        this.seenToDisplayCount = Math.min(this.seenToDisplayCount + 20, seenCount)
+        return
+      } else if (this.seenToDisplayCount > seenCount) {
+        this.seenToDisplayCount = seenCount
+      }
+
       const store = this.$store
       const credentials = store.state.users.currentUser.credentials
       store.commit('setNotificationsLoading', { value: true })
@@ -82,6 +100,7 @@ const Notifications = {
         if (notifs.length === 0) {
           this.bottomedOut = true
         }
+        this.seenToDisplayCount += notifs.length
       })
     }
   }
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index c42c35e6..d477a41b 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -32,7 +32,7 @@
       </div>
       <div class="panel-body">
         <div
-          v-for="notification in visibleNotifications"
+          v-for="notification in notificationsToDisplay"
           :key="notification.id"
           class="notification"
           :class="{&quot;unseen&quot;: !minimalMode && !notification.seen}"
diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js
index b08514da..860620fc 100644
--- a/src/services/notification_utils/notification_utils.js
+++ b/src/services/notification_utils/notification_utils.js
@@ -26,7 +26,7 @@ const sortById = (a, b) => {
   }
 }
 
-export const visibleNotificationsFromStore = (store, types) => {
+export const filteredNotificationsFromStore = (store, types) => {
   // map is just to clone the array since sort mutates it and it causes some issues
   let sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)
   sortedNotifications = sortBy(sortedNotifications, 'seen')
@@ -36,4 +36,4 @@ export const visibleNotificationsFromStore = (store, types) => {
 }
 
 export const unseenNotificationsFromStore = store =>
-  filter(visibleNotificationsFromStore(store), ({ seen }) => !seen)
+  filter(filteredNotificationsFromStore(store), ({ seen }) => !seen)
diff --git a/test/unit/specs/services/notification_utils/notification_utils.spec.js b/test/unit/specs/services/notification_utils/notification_utils.spec.js
index 1baa5fc9..00628d83 100644
--- a/test/unit/specs/services/notification_utils/notification_utils.spec.js
+++ b/test/unit/specs/services/notification_utils/notification_utils.spec.js
@@ -1,7 +1,7 @@
 import * as NotificationUtils from 'src/services/notification_utils/notification_utils.js'
 
 describe('NotificationUtils', () => {
-  describe('visibleNotificationsFromStore', () => {
+  describe('filteredNotificationsFromStore', () => {
     it('should return sorted notifications with configured types', () => {
       const store = {
         state: {
@@ -47,7 +47,7 @@ describe('NotificationUtils', () => {
           type: 'like'
         }
       ]
-      expect(NotificationUtils.visibleNotificationsFromStore(store)).to.eql(expected)
+      expect(NotificationUtils.filteredNotificationsFromStore(store)).to.eql(expected)
     })
   })