feat(home): Sort-Chip 'Zuletzt angesehen' + Profile-Switch-Refetch

Neuer Wert 'viewed' im AllSort-Enum + ALL_SORTS-Array. localStorage-
Whitelist ergaenzt. Reactive $effect lauscht auf profileStore.active
und refetcht offset=0 nur wenn aktueller Sort 'viewed' ist — andere
Sortierungen sind profilunabhaengig.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-22 14:35:28 +02:00
parent a5321d620a
commit 01d29bff0e

View File

@@ -27,12 +27,13 @@
let favorites = $state<SearchHit[]>([]);
const ALL_PAGE = 10;
type AllSort = 'name' | 'rating' | 'cooked' | 'created';
type AllSort = 'name' | 'rating' | 'cooked' | 'created' | 'viewed';
const ALL_SORTS: { value: AllSort; label: string }[] = [
{ value: 'name', label: 'Name' },
{ value: 'rating', label: 'Bewertung' },
{ value: 'cooked', label: 'Zuletzt gekocht' },
{ value: 'created', label: 'Hinzugefügt' }
{ value: 'created', label: 'Hinzugefügt' },
{ value: 'viewed', label: 'Zuletzt angesehen' }
];
function buildAllUrl(sort: AllSort, limit: number, offset: number): string {
const profileId = profileStore.active?.id;
@@ -166,7 +167,7 @@
void loadRecent();
void searchFilterStore.load();
const saved = localStorage.getItem('kochwas.allSort');
if (saved && ['name', 'rating', 'cooked', 'created'].includes(saved)) {
if (saved && ['name', 'rating', 'cooked', 'created', 'viewed'].includes(saved)) {
allSort = saved as AllSort;
}
// Fresh-mount: kick off the initial 10. On back-nav, snapshot.restore
@@ -205,6 +206,33 @@
store.reSearch();
});
// 'viewed' sort depends on the active profile. When the user switches
// profiles, refetch with the new profile_id so the list reflects what
// the *current* profile has viewed. Other sorts are profile-agnostic
// and don't need this.
$effect(() => {
const active = profileStore.active;
if (allSort !== 'viewed') return;
if (allLoading) return;
// Re-fetch the first page; rehydrate would re-load the previous
// depth, but a sort-context change should reset to page 1 anyway.
void (async () => {
allLoading = true;
try {
const res = await fetch(buildAllUrl('viewed', ALL_PAGE, 0));
if (!res.ok) return;
const body = await res.json();
const hits = body.hits as SearchHit[];
allRecipes = hits;
allExhausted = hits.length < ALL_PAGE;
} finally {
allLoading = false;
}
// 'active' is referenced so $effect tracks it as a dep:
void active;
})();
});
// Sync current query back into the URL as ?q=... via replaceState,
// without spamming the history stack. Pushing a new entry happens only
// when the user clicks a result or otherwise navigates away.