feat: snappier timeline — instant discard, progressive load, restore preview origin
- Discard yanks photos from the grid optimistically (cache strip + active cursor advance) instead of waiting for the mutation round-trip; wired through the X hotkey, RightSidebar bulk discard, LeftSidebar discard drop, and DiscardActionBar restore/delete. - usePhotosQuery resolves on the first 500-photo page and streams the remaining pages into the cache in the background, so the first thumbnails paint immediately on large libraries. - Closing preview restores the photo it was opened on (snapshot ref in PreviewView, written directly to the store) and Timeline scrolls that row back into view. Escape is handled on the dialog with stopPropagation so Timeline's window-level Esc handler doesn't wipe the restored selection. - Preview overlay bumped to z-[1000] so it covers Leaflet map tiles, and the right sidebar no longer collapses during preview — both fix visible layout shifts on close. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,8 @@ import { ActiveHeapCard } from '../heaps/ActiveHeapCard'
|
||||
import { PHOTO_DRAG_MIME } from '../timeline/PhotoThumbnail'
|
||||
import { useFolderTreeQuery } from '../../hooks/useFolderTreeQuery'
|
||||
import { useTagsQuery } from '../../hooks/useTagsQuery'
|
||||
import { usePhotoStore } from '../../store/photoStore'
|
||||
import { stripPhotosFromCache } from '../../hooks/usePhotosQuery'
|
||||
import {
|
||||
useLibraryStatsQuery,
|
||||
LIBRARY_STATS_QUERY_KEY,
|
||||
@@ -101,6 +103,13 @@ export function LeftSidebar({ onCollapse, onOpenSettings }: LeftSidebarProps) {
|
||||
// Bulk discard mutation for the drag-onto-Discarded interaction.
|
||||
const discardDropMutation = useMutation({
|
||||
mutationFn: (photoIds: string[]) => photosApi.bulkDiscard(photoIds),
|
||||
// Optimistically pull the dropped photos out of the timeline so the
|
||||
// grid reflows the moment the drop lands, instead of waiting for
|
||||
// the network round-trip + invalidation refetch.
|
||||
onMutate: (photoIds) => {
|
||||
usePhotoStore.getState().removePhotosFromTimeline(photoIds)
|
||||
stripPhotosFromCache(queryClient, photoIds)
|
||||
},
|
||||
onSuccess: (_data, photoIds) => {
|
||||
registerUndoable(
|
||||
`Discarded ${photoIds.length} photo${photoIds.length === 1 ? '' : 's'}`,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useActiveHeapMembers } from '../../hooks/useActiveHeapMembersQuery'
|
||||
import { HEAPS_QUERY_KEY } from '../../hooks/useHeapsQuery'
|
||||
import { LIBRARY_STATS_QUERY_KEY } from '../../hooks/useLibraryStatsQuery'
|
||||
import { useTagsQuery, TAGS_QUERY_KEY } from '../../hooks/useTagsQuery'
|
||||
import { stripPhotosFromCache } from '../../hooks/usePhotosQuery'
|
||||
import { toast } from '../ToastContainer'
|
||||
import { PhotoInfoPanel } from '../sidebar/PhotoInfoPanel'
|
||||
import { COLOR_LABEL_OPTIONS } from '../../constants/colorLabels'
|
||||
@@ -48,6 +49,13 @@ export function RightSidebar({ onCollapse }: RightSidebarProps) {
|
||||
})
|
||||
const bulkDiscardMutation = useMutation({
|
||||
mutationFn: (ids: string[]) => photosApi.bulkDiscard(ids),
|
||||
// Yank the photos from the timeline before the network round-trip
|
||||
// so the grid reflows immediately. Same pattern as the X hotkey
|
||||
// path in useKeyboardShortcuts.
|
||||
onMutate: (ids) => {
|
||||
usePhotoStore.getState().removePhotosFromTimeline(ids)
|
||||
stripPhotosFromCache(queryClient, ids)
|
||||
},
|
||||
onSuccess: invalidatePhotoQueries,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user