Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions web/helpers/apollo-cache/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { InMemoryCache, type InMemoryCacheConfig } from '@apollo/client/core/index.js';
import type { NotificationOverview } from '~/composables/gql/graphql';
import { NotificationType } from '../../composables/gql/typename';
import { getNotifications } 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';

/**------------------------------------------------------------------------
Expand Down Expand Up @@ -60,9 +61,9 @@ const defaultCacheConfig: InMemoryCacheConfig = {
overview: {
/**
* Busts notification cache when new unread notifications are detected.
*
*
* This allows incoming notifications to appear in the sidebar without reloading the page.
*
*
* @param existing - Existing overview data in cache
* @param incoming - New overview data from server
* @param context - Apollo context containing cache instance
Expand Down Expand Up @@ -106,6 +107,51 @@ const defaultCacheConfig: InMemoryCacheConfig = {
return incoming; // Return the incoming data so Apollo knows the result of the mutation
},
},
archiveNotification: {
/**
* Ensures newly archived notifications appear in the archive list without a page reload.
*
* When a notification is archived, we need to evict the cached archive list to force a refetch.
* This ensures the archived notification appears in the correct sorted position.
*
* If the archive list is empty, we evict the entire notifications cache since evicting an empty
* list has no effect. This forces a full refetch of all notification data.
* Note: This may cause temporary jitter with infinite scroll.
*
* This function:
* 1. Checks if the cache has an archive list. If not, this function is a no-op.
* 2. If the list has items, evicts just the archive list
* 3. If it is empty, evicts the entire notifications cache
* 4. Runs garbage collection to clean up orphaned references
* 5. 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
* @returns The incoming result to be cached
*/
merge(_, incoming, { cache }) {
const archiveQuery = cache.readQuery({
query: getNotifications,
// @ts-expect-error the cache only uses the filter type; the limit & offset are superfluous.
variables: { filter: { type: NotificationType.Archive } },
});
if (!archiveQuery) return incoming;

if (archiveQuery.notifications.list.length === 0) {
cache.evict({ fieldName: 'notifications' });
} else {
cache.evict({
id: archiveQuery.notifications.id,
fieldName: 'list',
args: { filter: { type: NotificationType.Archive } },
});
}

cache.gc();
return incoming;
},
},
deleteNotification: {
/**
* Ensures that a deleted notification is removed from the cache +
Expand All @@ -119,10 +165,7 @@ const defaultCacheConfig: InMemoryCacheConfig = {
*/
merge(_, incoming, { cache, args }) {
if (args?.id) {
const id = cache.identify({
id: args.id,
__typename: NotificationType,
});
const id = cache.identify({ id: args.id, __typename: NotificationCacheType });
cache.evict({ id });
}
// Removes references to evicted notification, preventing dangling references
Expand Down
Loading