Skip to content

Commit 41bbc9b

Browse files
committed
refactor(web): evict notification list on archival instead of manually modifying cache
1 parent 133053f commit 41bbc9b

File tree

1 file changed

+30
-34
lines changed

1 file changed

+30
-34
lines changed

web/helpers/apollo-cache/index.ts

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { InMemoryCache, type InMemoryCacheConfig } from '@apollo/client/core/index.js';
2-
import {
3-
getNotifications,
4-
NOTIFICATION_FRAGMENT,
5-
} from '~/components/Notifications/graphql/notification.query';
2+
import { getNotifications } from '~/components/Notifications/graphql/notification.query';
63
import { NotificationType, type NotificationOverview } from '~/composables/gql/graphql';
74
import { NotificationType as NotificationCacheType } from '../../composables/gql/typename';
85
import { mergeAndDedup } from './merge';
@@ -112,47 +109,46 @@ const defaultCacheConfig: InMemoryCacheConfig = {
112109
},
113110
archiveNotification: {
114111
/**
115-
* Ensures newly archived notifications appear in the archive list immediately.
112+
* Ensures newly archived notifications appear in the archive list without a page reload.
116113
*
117-
* When a notification is archived, we need to manually prepend it to the archive list
118-
* in the cache if that list has already been queried. Otherwise, the archived notification
119-
* won't appear until the next refetch (usually a page refresh).
120-
* Note: the prepended notification may not be in the correct order. This is probably ok.
114+
* When a notification is archived, we need to evict the cached archive list to force a refetch.
115+
* This ensures the archived notification appears in the correct sorted position.
116+
*
117+
* If the archive list is empty, we evict the entire notifications cache since evicting an empty
118+
* list has no effect. This forces a full refetch of all notification data.
119+
* Note: This may cause temporary jitter with infinite scroll.
121120
*
122121
* This function:
123-
* 1. Gets the archived notification's data from the cache using its ID
124-
* 2. If the archive list exists in the cache, adds the notification to the beginning of that list
125-
* 3. Returns the original mutation result
122+
* 1. Checks if the cache has an archive list. If not, this function is a no-op.
123+
* 2. If the list has items, evicts just the archive list
124+
* 3. If it is empty, evicts the entire notifications cache
125+
* 4. Runs garbage collection to clean up orphaned references
126+
* 5. Returns the original mutation result
126127
*
127128
* @param _ - Existing cache value (unused)
128129
* @param incoming - Result (i.e. the archived notification) from the server after archiving
129130
* @param cache - Apollo cache instance
130-
* @param args - Mutation arguments containing the notification ID
131131
* @returns The incoming result to be cached
132132
*/
133-
merge(_, incoming, { cache, args }) {
134-
if (!args?.id) return incoming;
135-
const id = cache.identify({ id: args.id, __typename: NotificationCacheType });
136-
if (!id) return incoming;
137-
138-
const notification = cache.readFragment({ id, fragment: NOTIFICATION_FRAGMENT });
139-
if (!notification) return incoming;
133+
merge(_, incoming, { cache }) {
134+
const archiveQuery = cache.readQuery({
135+
query: getNotifications,
136+
// @ts-expect-error the cache only uses the filter type; the limit & offset are superfluous.
137+
variables: { filter: { type: NotificationType.Archive } },
138+
});
139+
if (!archiveQuery) return incoming;
140140

141-
cache.updateQuery(
142-
{
143-
query: getNotifications,
144-
// @ts-expect-error the cache only uses the filter type; the limit & offset are superfluous.
145-
variables: { filter: { type: NotificationType.Archive } },
146-
},
147-
(data) => {
148-
// no data means the archive hasn't been queried yet, in which case this operation is unnecessary
149-
if (!data) return;
150-
const updated = structuredClone(data);
151-
updated.notifications.list.unshift(notification);
152-
return updated;
153-
}
154-
);
141+
if (archiveQuery.notifications.list.length === 0) {
142+
cache.evict({ fieldName: 'notifications' });
143+
} else {
144+
cache.evict({
145+
id: archiveQuery.notifications.id,
146+
fieldName: 'list',
147+
args: { filter: { type: NotificationType.Archive } },
148+
});
149+
}
155150

151+
cache.gc();
156152
return incoming;
157153
},
158154
},

0 commit comments

Comments
 (0)