fix: optimistic membership cache so P toggle is instant and reliable

P already toggled membership in the active heap (remove if every
selected photo is a member, otherwise add the missing ones), but the
mutation only invalidate-then-refetched the heap-photo-ids cache on
success. Pressing P twice in quick succession could read the stale
cache and mis-toggle.

Both heap-toggle mutations (useKeyboardShortcuts P shortcut and the
RightSidebar Pick button) now do an optimistic update in onMutate:
- Read the current ['heap-photo-ids', heapId] cache
- Add or remove the affected ids in a Set
- Write the new array back via setQueryData
- Roll back from the captured `previous` on error
- Re-sync via invalidateQueries in onSettled (heap counts in particular
  still need server truth)

Result: the basket affordance flips the moment you press P, and a
quick second press always reads the new state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 23:24:29 +02:00
parent 324cc0298b
commit ee6b49952e
2 changed files with 39 additions and 4 deletions

View File

@@ -146,14 +146,29 @@ export function RightSidebar() {
? heapsApi.removePhotos(activeHeap.id, [activePhotoId])
: heapsApi.addPhotos(activeHeap.id, [activePhotoId])
},
onSuccess: () => {
// Optimistic flip so the badge / button label update instantly.
onMutate: ({ remove }) => {
if (!activeHeap || !activePhotoId) return { previous: undefined }
const key = ['heap-photo-ids', activeHeap.id] as const
const previous = queryClient.getQueryData<string[]>(key)
const set = new Set(previous ?? [])
if (remove) set.delete(activePhotoId)
else set.add(activePhotoId)
queryClient.setQueryData<string[]>(key, Array.from(set))
return { previous }
},
onError: (_e, _vars, ctx) => {
if (activeHeap && ctx?.previous) {
queryClient.setQueryData(['heap-photo-ids', activeHeap.id], ctx.previous)
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: HEAPS_QUERY_KEY })
if (activeHeap) {
queryClient.invalidateQueries({
queryKey: ['heap-photo-ids', activeHeap.id],
})
}
queryClient.invalidateQueries({ queryKey: ['photos'] })
},
})