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:
@@ -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'] })
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -77,6 +77,25 @@ export function useKeyboardShortcuts(props: KeyboardShortcutsProps) {
|
||||
remove
|
||||
? heapsApi.removePhotos(heapId, photoIds)
|
||||
: heapsApi.addPhotos(heapId, photoIds),
|
||||
// Optimistically flip the membership cache so the basket affordance
|
||||
// updates instantly and a quick second P press reads the new state
|
||||
// (otherwise invalidate-then-refetch leaves a brief stale window).
|
||||
onMutate: ({ heapId, photoIds, remove }) => {
|
||||
const key = ['heap-photo-ids', heapId] as const
|
||||
const previous = queryClient.getQueryData<string[]>(key)
|
||||
const set = new Set(previous ?? [])
|
||||
if (remove) photoIds.forEach((id) => set.delete(id))
|
||||
else photoIds.forEach((id) => set.add(id))
|
||||
queryClient.setQueryData<string[]>(key, Array.from(set))
|
||||
return { previous }
|
||||
},
|
||||
onError: (e: any, _vars, ctx) => {
|
||||
// Roll back the optimistic update on failure.
|
||||
if (ctx?.previous) {
|
||||
queryClient.setQueryData(['heap-photo-ids', _vars.heapId], ctx.previous)
|
||||
}
|
||||
toast.error('Heap update failed', e.message || 'Unknown error')
|
||||
},
|
||||
onSuccess: (data, vars) => {
|
||||
const heap = (queryClient.getQueryData<Heap[]>(HEAPS_QUERY_KEY) ?? []).find(
|
||||
(h) => h.id === vars.heapId
|
||||
@@ -95,11 +114,12 @@ export function useKeyboardShortcuts(props: KeyboardShortcutsProps) {
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onSettled: (_data, _err, vars) => {
|
||||
// Re-sync with server truth (heap counts in particular need this).
|
||||
queryClient.invalidateQueries({ queryKey: HEAPS_QUERY_KEY })
|
||||
queryClient.invalidateQueries({ queryKey: ['heap-photo-ids', vars.heapId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['photos'] })
|
||||
},
|
||||
onError: (e: any) => toast.error('Heap update failed', e.message || 'Unknown error'),
|
||||
})
|
||||
|
||||
const togglePickOnSelection = () => {
|
||||
|
||||
Reference in New Issue
Block a user