Skip to content

Commit

Permalink
Fixes #2321. (#2333)
Browse files Browse the repository at this point in the history
This implements the following changes:

  * Increase KV notification status memory to 2 months
  * Set Vuex notification memory to 6 months
  * Automatically mark notifications as read after the notification status memory window (e.g. after 2 months)
  * Purge notifications from Vuex store that are older than 6 months
  • Loading branch information
corrideat authored Sep 7, 2024
1 parent 41ff6b7 commit b1f60db
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 10 deletions.
6 changes: 4 additions & 2 deletions frontend/model/notifications/storageConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { DAYS_MILLIS } from '~/frontend/model/contracts/shared/time.js'

// The maximum allowed age of read (resp. unread) stored notifications.
// Not to be confused with maximum storage duration.
export const MAX_AGE_READ = 30 * DAYS_MILLIS
export const MAX_AGE_UNREAD = Infinity
// Notifications older than `MAX_AGE_READ` are automatically marked as read
export const MAX_AGE_READ = 60 * DAYS_MILLIS
// Notifications older than `MAX_AGE_UNREAD` are automatically discarded.
export const MAX_AGE_UNREAD = 180 * DAYS_MILLIS
// The maximum allowed number of stored notifications.
export const MAX_COUNT = 30
// The maximum allowed number of read (resp. unread) stored notifications.
Expand Down
7 changes: 5 additions & 2 deletions frontend/model/notifications/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export function age (notification: Notification): number {
/*
* Creates a copy of the given notification list, possibly sorted and/or truncated to
* make it suitable for offline storage.
* The second argument, `status` contains separarately-stored parameters for that
* notification hash to reflect how notifications are used in the app
* (specifically, the `read` parameter is stored there).
*
* Algorithm steps taken on the copy:
*
Expand All @@ -40,9 +43,9 @@ export function age (notification: Notification): number {
* 5f. discard unread notifications, older ones first.
* 6. Return the remaining notifications.
*/
export function applyStorageRules (notifications: Notification[]): Notification[] {
export function applyStorageRules (notifications: Notification[], status: { [string]: Notification } = {}): Notification[] {
// Apply the MAX_AGE constraint by selecting items that have not yet expired.
let items = notifications.filter(item => !isExpired(item))
let items = notifications.filter(item => !isExpired({ ...item, ...status[item.hash] }))

if (items.length > MAX_COUNT) {
// Sort the list by descending priority order.
Expand Down
22 changes: 17 additions & 5 deletions frontend/model/notifications/vuexModule.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
'use strict'

import { Vue } from '@common/common.js'
import type { Notification } from './types.flow.js'
import './selectors.js'
import { compareOnTimestamp, isNew, isOlder } from './utils.js'
import * as keys from './mutationKeys.js'
import { cloneDeep } from '~/frontend/model/contracts/shared/giLodash.js'
import * as keys from './mutationKeys.js'
import './selectors.js'
import { MAX_AGE_READ, MAX_AGE_UNREAD } from './storageConstants.js'
import type { Notification } from './types.flow.js'
import { age, compareOnTimestamp, isNew, isOlder } from './utils.js'

const defaultState = {
items: [], status: {}
}

const getters = {
notifications (state, getters, rootState) {
return state.items.map(item => ({ ...item, ...state.status[item.hash] }))
return state.items.map(item => {
const notification = { ...item, ...state.status[item.hash] }
// Notifications older than MAX_AGE_UNREAD are discarded
if (age(notification) > MAX_AGE_UNREAD) {
return null
} else if (!notification.read && age(notification) > MAX_AGE_READ) {
// Unread notifications older than MAX_AGE_READ are automatically
// marked as read
notification.read = true
}
return notification
}).filter(Boolean)
},
// Notifications relevant to the current group only.
currentGroupNotifications (state, getters, rootState) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/model/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ sbp('sbp/selectors/register', {
}

const { identityContractID, encryptionParams } = state.loggedIn
state.notifications.items = applyStorageRules(state.notifications.items || [])
state.notifications.items = applyStorageRules(state.notifications.items || [], state.notifications.status || {})
if (encrypted) {
await sbp('gi.db/settings/saveEncrypted', identityContractID, state, encryptionParams)
} else {
Expand Down

0 comments on commit b1f60db

Please sign in to comment.