perf(frontend): load favorite/share badges once per session, not per navigation

The files browser re-fetched the first 200 favorites AND the first 200 outgoing
shares on every folder navigation (two round-trips each time) just to render the
star / shared badges — work that grew with how much the user browsed, for data
that barely changes.

Move both id sets into a session-scoped `badges` store: `ensureLoaded()` fetches
once (concurrent callers share one in-flight request) and every later navigation
reads from cache, so browsing costs zero extra requests. Mutations keep the cache
in sync optimistically:
- favorite toggle / batch-favorite → `setFavorite` (revert on failure),
- share creation → `markShared`, wired through a new optional `onshared` callback
  on ShareDialog (fired when a grant or public link is created). This also makes
  the shared badge appear immediately instead of only after re-navigating.

Net effect per session: badge fetches drop from O(navigations) × 2 to 2 total.
The 200-item ceiling is unchanged from before; the fully-correct fix is per-item
flags on the listing endpoint (a backend change, noted in the store).

Verified: new badges store unit tests (load-once, concurrent de-dupe, optimistic
favorite/share, reset) and a headless render of the real files route in list and
grid (virtualization intact, no runtime errors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8Vb9QHmLZnEMzHz7MrFy6
This commit is contained in:
Claude
2026-06-19 15:00:14 +00:00
parent 8794324c3d
commit 546dcef305
4 changed files with 176 additions and 46 deletions
@@ -43,9 +43,11 @@
interface Props {
open: boolean;
item: Target | null;
/** Fired with the item id when an outgoing share (grant or link) is created. */
onshared?: (id: string) => void;
}
let { open = $bindable(false), item }: Props = $props();
let { open = $bindable(false), item, onshared }: Props = $props();
let tab = $state<'people' | 'link'>('people');
let directoryAvailable = $state(true);
@@ -159,6 +161,7 @@
query = '';
results = [];
summarizeNotifications(res.notification.outcomes);
onshared?.(item.id);
await loadGrants();
} catch (e) {
errorToast(e);
@@ -280,6 +283,7 @@
newLinkName = '';
password = '';
expiresAt = null;
onshared?.(item.id);
await loadShares();
ui.notify(t('share.created', 'Public link created'), 'success');
} catch (e) {