fix(web): edge case where archived notifications don't appear

if the archive has already been fetched/loaded.
This commit is contained in:
Pujit Mehrotra
2024-12-11 12:08:45 -05:00
committed by Pujit Mehrotra
parent bc4708f405
commit e40a9ebecd

View File

@@ -1,6 +1,10 @@
import { InMemoryCache, type InMemoryCacheConfig } from '@apollo/client/core/index.js'; import { InMemoryCache, type InMemoryCacheConfig } from '@apollo/client/core/index.js';
import type { NotificationOverview } from '~/composables/gql/graphql'; import {
import { NotificationType } from '../../composables/gql/typename'; getNotifications,
NOTIFICATION_FRAGMENT,
} from '~/components/Notifications/graphql/notification.query';
import { NotificationType, type NotificationOverview } from '~/composables/gql/graphql';
import { NotificationType as NotificationCacheType } from '../../composables/gql/typename';
import { mergeAndDedup } from './merge'; import { mergeAndDedup } from './merge';
/**------------------------------------------------------------------------ /**------------------------------------------------------------------------
@@ -60,9 +64,9 @@ const defaultCacheConfig: InMemoryCacheConfig = {
overview: { overview: {
/** /**
* Busts notification cache when new unread notifications are detected. * Busts notification cache when new unread notifications are detected.
* *
* This allows incoming notifications to appear in the sidebar without reloading the page. * This allows incoming notifications to appear in the sidebar without reloading the page.
* *
* @param existing - Existing overview data in cache * @param existing - Existing overview data in cache
* @param incoming - New overview data from server * @param incoming - New overview data from server
* @param context - Apollo context containing cache instance * @param context - Apollo context containing cache instance
@@ -106,6 +110,52 @@ const defaultCacheConfig: InMemoryCacheConfig = {
return incoming; // Return the incoming data so Apollo knows the result of the mutation return incoming; // Return the incoming data so Apollo knows the result of the mutation
}, },
}, },
archiveNotification: {
/**
* Ensures newly archived notifications appear in the archive list immediately.
*
* When a notification is archived, we need to manually prepend it to the archive list
* in the cache if that list has already been queried. Otherwise, the archived notification
* won't appear until the next refetch (usually a page refresh).
* Note: the prepended notification may not be in the correct order. This is probably ok.
*
* This function:
* 1. Gets the archived notification's data from the cache using its ID
* 2. If the archive list exists in the cache, adds the notification to the beginning of that list
* 3. Returns the original mutation result
*
* @param _ - Existing cache value (unused)
* @param incoming - Result (i.e. the archived notification) from the server after archiving
* @param cache - Apollo cache instance
* @param args - Mutation arguments containing the notification ID
* @returns The incoming result to be cached
*/
merge(_, incoming, { cache, args }) {
if (!args?.id) return incoming;
const id = cache.identify({ id: args.id, __typename: NotificationCacheType });
if (!id) return incoming;
const notification = cache.readFragment({ id, fragment: NOTIFICATION_FRAGMENT });
if (!notification) return incoming;
cache.updateQuery(
{
query: getNotifications,
// @ts-expect-error the cache only uses the filter type; the limit & offset are superfluous.
variables: { filter: { type: NotificationType.Archive } },
},
(data) => {
// no data means the archive hasn't been queried yet, in which case this operation is unnecessary
if (!data) return;
const updated = structuredClone(data);
updated.notifications.list.unshift(notification);
return updated;
}
);
return incoming;
},
},
deleteNotification: { deleteNotification: {
/** /**
* Ensures that a deleted notification is removed from the cache + * Ensures that a deleted notification is removed from the cache +
@@ -119,10 +169,7 @@ const defaultCacheConfig: InMemoryCacheConfig = {
*/ */
merge(_, incoming, { cache, args }) { merge(_, incoming, { cache, args }) {
if (args?.id) { if (args?.id) {
const id = cache.identify({ const id = cache.identify({ id: args.id, __typename: NotificationCacheType });
id: args.id,
__typename: NotificationType,
});
cache.evict({ id }); cache.evict({ id });
} }
// Removes references to evicted notification, preventing dangling references // Removes references to evicted notification, preventing dangling references