feat(frontend): restore files grid position when returning from a file
Opening a file now snapshots the grid (loaded pages, cursor, scroll offset, opened id) into a shared store, and the viewer derives prev/next from that list instead of a separate anchored request. Returning to the grid restores the cached list and scroll-centres the last-viewed file rather than reloading page 1 from the top. This also fixes two issues: - The viewer's "previous" arrow never appeared: the backend anchor window is forward-inclusive, so the anchor was always item 0 and prev was null. Neighbors now come from the cached list, so paging is symmetric. - Paging forward in the viewer prefetches further pages into the snapshot, so navigation continues past the initially loaded set and the grid still restores correctly. A deep link straight to a file (empty cache) falls back to the anchored API window as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,12 @@
|
||||
import { parseDslFilter } from '$lib/utils/dsl';
|
||||
import type { File, FileCursorPage, Pool, PoolOffsetPage } from '$lib/api/types';
|
||||
import { appSettings } from '$lib/stores/appSettings';
|
||||
import {
|
||||
saveFilesSnapshot,
|
||||
peekFilesSnapshot,
|
||||
queryKey,
|
||||
type FilesSnapshot,
|
||||
} from '$lib/stores/filesCache';
|
||||
|
||||
let scrollContainer = $state<HTMLElement | undefined>();
|
||||
|
||||
@@ -93,8 +99,21 @@
|
||||
let prevKey = $state('');
|
||||
|
||||
$effect(() => {
|
||||
if (resetKey !== prevKey) {
|
||||
prevKey = resetKey;
|
||||
const key = resetKey;
|
||||
if (key === prevKey) return;
|
||||
const firstRun = prevKey === '';
|
||||
prevKey = key;
|
||||
|
||||
// On the first mount, restore the grid the user left when opening a file
|
||||
// (same sort/order/filter) so back-navigation keeps their place. Any later
|
||||
// change means the query itself changed → reset and reload from the top.
|
||||
const snap = peekFilesSnapshot();
|
||||
if (firstRun && snap && queryKey(snap.query) === key) {
|
||||
files = snap.files;
|
||||
nextCursor = snap.nextCursor;
|
||||
hasMore = snap.hasMore;
|
||||
void tick().then(() => restoreScroll(snap));
|
||||
} else {
|
||||
files = [];
|
||||
nextCursor = null;
|
||||
hasMore = true;
|
||||
@@ -102,6 +121,21 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll the grid so the last-opened file is centred; fall back to the saved
|
||||
// scroll offset if that card isn't present (e.g. nothing was opened).
|
||||
function restoreScroll(snap: FilesSnapshot) {
|
||||
if (!scrollContainer) return;
|
||||
const idx = snap.lastOpenedId ? files.findIndex((f) => f.id === snap.lastOpenedId) : -1;
|
||||
if (idx >= 0) {
|
||||
const card = scrollContainer.querySelector<HTMLElement>(`[data-file-index="${idx}"]`);
|
||||
if (card) {
|
||||
card.scrollIntoView({ block: 'center' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
scrollContainer.scrollTop = snap.scrollTop;
|
||||
}
|
||||
|
||||
async function loadMore() {
|
||||
if (loading || !hasMore) return;
|
||||
loading = true;
|
||||
@@ -144,7 +178,18 @@
|
||||
}
|
||||
|
||||
function openFile(file: File) {
|
||||
if (file.id) goto(`/files/${file.id}`);
|
||||
if (!file.id) return;
|
||||
// Snapshot the grid so returning from the viewer restores this exact list
|
||||
// and scroll position instead of reloading page 1 from the top.
|
||||
saveFilesSnapshot({
|
||||
query: { sort: sortState.sort, order: sortState.order, filter: filterParam },
|
||||
files,
|
||||
nextCursor,
|
||||
hasMore,
|
||||
scrollTop: scrollContainer?.scrollTop ?? 0,
|
||||
lastOpenedId: file.id,
|
||||
});
|
||||
goto(`/files/${file.id}`);
|
||||
}
|
||||
|
||||
// ---- Selection logic ----
|
||||
|
||||
Reference in New Issue
Block a user