Feed: eager-load only the year shards needed for the active date filter

This commit is contained in:
Davide Scaini
2026-05-15 09:32:12 +02:00
parent 8a06227243
commit d3bce49445
+39 -13
View File
@@ -132,19 +132,41 @@
$: if (sport || datePre || query || customFrom || customTo) shown = PAGE_SIZE; // reset pagination on filter change $: if (sport || datePre || query || customFrom || customTo) shown = PAGE_SIZE; // reset pagination on filter change
// When a search query or any date filter is active, eagerly load all // Eager-load shards / feed-pages when a filter needs data not yet in memory.
// remaining shards so results aren't limited to the initially-loaded year.
// Use only primary let-variables here — Svelte 5 doesn't reliably track
// derived $: variables as dependencies of side-effect $: blocks.
let loadingAllShards = false; let loadingAllShards = false;
let loadingAllFeedPages = false; let loadingAllFeedPages = false;
$: if ((query.trim() || customFrom || customTo || datePre !== 'all') && pendingShards.length > 0 && !loadingAllShards) { function _yearFromShard(url: string): number | null {
const m = url.match(/index-(\d{4})\.json$/);
return m ? parseInt(m[1], 10) : null;
}
// Returns [minYear, maxYear] for year-specific filters (year preset or custom
// date range). Returns null for open-ended filters (all, 7d, 30d, 6mo).
function _neededYearRange(pre: string, from: string, to: string): [number, number] | null {
if (from || to) {
const fy = from ? parseInt(from.slice(0, 4), 10) : 0;
const ty = to ? parseInt(to.slice(0, 4), 10) : 9999;
return [fy, ty];
}
if (/^\d{4}$/.test(pre)) { const y = parseInt(pre, 10); return [y, y]; }
return null;
}
$: {
const yr = _neededYearRange(datePre, customFrom, customTo);
const needEager = !!query.trim() || yr !== null;
if (needEager && pendingShards.length > 0 && !loadingAllShards) {
loadingAllShards = true; loadingAllShards = true;
// When year-specific filter (no search), load only shards that cover
// the needed range; unneeded shards stay in pendingShards for "Load more".
// When search is active, load everything so full-text search works.
const toLoad = (yr && !query.trim())
? pendingShards.filter(url => { const y = _yearFromShard(url); return y !== null && y >= yr[0] && y <= yr[1]; })
: [...pendingShards];
(async () => { (async () => {
while (pendingShards.length > 0) { for (const url of toLoad) {
const url = pendingShards[0]; pendingShards = pendingShards.filter(u => u !== url);
pendingShards = pendingShards.slice(1);
try { try {
const fresh = await loadShardActivities(url); const fresh = await loadShardActivities(url);
const existing = new Map(all.map(a => [a.id, a])); const existing = new Map(all.map(a => [a.id, a]));
@@ -157,12 +179,15 @@
loadingAllShards = false; loadingAllShards = false;
})(); })();
} }
}
$: if ((query.trim() || customFrom || customTo || datePre !== 'all') && feedNextPage > 0 && !loadingAllFeedPages) { $: {
const yr = _neededYearRange(datePre, customFrom, customTo);
if ((!!query.trim() || yr !== null) && feedNextPage > 0 && !loadingAllFeedPages) {
loadingAllFeedPages = true; loadingAllFeedPages = true;
// Capture at loop start — dateFrom is reactive and may change mid-fetch.
const effectiveFrom = dateFrom;
(async () => { (async () => {
// Snapshot at loop start — customFrom may change while we're fetching.
const fromFilter = customFrom;
while (feedNextPage > 0) { while (feedNextPage > 0) {
const page = feedNextPage; const page = feedNextPage;
feedNextPage = page < feedTotalPages ? page + 1 : 0; feedNextPage = page < feedTotalPages ? page + 1 : 0;
@@ -175,15 +200,16 @@
); );
// Feed is sorted newest-first. Once the oldest activity in this page // Feed is sorted newest-first. Once the oldest activity in this page
// predates our from-filter, everything needed is already loaded. // predates our from-filter, everything needed is already loaded.
if (fromFilter && fresh.length > 0) { if (effectiveFrom && fresh.length > 0) {
const oldest = fresh.reduce((m, a) => (a.started_at ?? '') < (m.started_at ?? '') ? a : m); const oldest = fresh.reduce((m, a) => (a.started_at ?? '') < (m.started_at ?? '') ? a : m);
if ((oldest.started_at ?? '') < fromFilter) { feedNextPage = 0; break; } if ((oldest.started_at ?? '') < effectiveFrom) { feedNextPage = 0; break; }
} }
} catch { /* ignore — partial results still useful */ } } catch { /* ignore — partial results still useful */ }
} }
loadingAllFeedPages = false; loadingAllFeedPages = false;
})(); })();
} }
}
$: if (mounted) { $: if (mounted) {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);