diff --git a/frontend/model/notifications/storageConstants.js b/frontend/model/notifications/storageConstants.js index aa187b3a08..8045ea5111 100644 --- a/frontend/model/notifications/storageConstants.js +++ b/frontend/model/notifications/storageConstants.js @@ -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. diff --git a/frontend/model/notifications/utils.js b/frontend/model/notifications/utils.js index c49082f3f9..01912a3c63 100644 --- a/frontend/model/notifications/utils.js +++ b/frontend/model/notifications/utils.js @@ -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: * @@ -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. diff --git a/frontend/model/notifications/vuexModule.js b/frontend/model/notifications/vuexModule.js index 16f845d52c..6f9e411776 100644 --- a/frontend/model/notifications/vuexModule.js +++ b/frontend/model/notifications/vuexModule.js @@ -1,11 +1,12 @@ '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: {} @@ -13,7 +14,18 @@ const defaultState = { 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) { diff --git a/frontend/model/state.js b/frontend/model/state.js index e9a48a280f..bfad20ac67 100644 --- a/frontend/model/state.js +++ b/frontend/model/state.js @@ -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 {