feat: folder navigation from sidebar

Folders in the LeftSidebar were decorative — clicking one did
nothing. Now they actually filter the timeline.

filterStore: new folderId field, setFolderId, hasActiveFilters check,
filtersToParams sends folder_id to the backend (the param was already
declared and applied server-side, just nothing was setting it).
useFilterUrlSync round-trips ?folder_id= so the filter persists in
the URL. usePhotosQuery threads it through.

LeftSidebar:
- Clicking a folder row calls clearAllFilters() then setFolderId(id)
  so the user lands cleanly on that folder.
- Library virtual nodes (All Photos, Rated, Discarded) clear the
  folder filter as part of their normal action.
- The active-row visual highlight is now derived from the filter
  store: a folder row is selected when filterStore.folderId matches
  it, "All Photos" is selected when no folder is set. Keeps the
  sidebar in sync if filters change externally (URL hydrate, the
  ActiveFilterChips X button, FilterBar Clear all).

ActiveFilterChips: shows "Folder: {name}" and "Heap: {name}" chips,
looking up the names from the folders / heaps queries (lazy-enabled
only when the corresponding filter is set). Clicking the X clears
the filter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 23:35:59 +02:00
parent 61486a503e
commit 66ffd94c48
5 changed files with 76 additions and 4 deletions

View File

@@ -68,6 +68,9 @@ function parseUrl(): Partial<FilterState> {
const heapId = sp.get('heap_id')
if (heapId) out.heapId = heapId
const folderId = sp.get('folder_id')
if (folderId) out.folderId = folderId
const sortBy = sp.get('sort')
if (sortBy && ALLOWED_SORT_FIELDS.includes(sortBy as SortField)) {
out.sortBy = sortBy as SortField
@@ -91,6 +94,7 @@ function writeUrl(f: FilterState) {
if (f.colorLabel) sp.set('color_label', f.colorLabel)
if (f.flag !== 'any') sp.set('flag', f.flag)
if (f.heapId) sp.set('heap_id', f.heapId)
if (f.folderId) sp.set('folder_id', f.folderId)
if (f.sortBy !== 'taken_at') sp.set('sort', f.sortBy)
if (f.sortOrder !== 'desc') sp.set('order', f.sortOrder)

View File

@@ -19,6 +19,7 @@ export function usePhotosQuery() {
const colorLabel = useFilterStore((s) => s.colorLabel)
const flag = useFilterStore((s) => s.flag)
const heapId = useFilterStore((s) => s.heapId)
const folderId = useFilterStore((s) => s.folderId)
const sortBy = useFilterStore((s) => s.sortBy)
const sortOrder = useFilterStore((s) => s.sortOrder)
@@ -33,10 +34,11 @@ export function usePhotosQuery() {
colorLabel,
flag,
heapId,
folderId,
sortBy,
sortOrder,
}),
[q, dateFrom, dateTo, mediaTypes, ratingMin, colorLabel, flag, heapId, sortBy, sortOrder]
[q, dateFrom, dateTo, mediaTypes, ratingMin, colorLabel, flag, heapId, folderId, sortBy, sortOrder]
)
return useQuery({