From 076c6efb38da0d29d0408cc9f7f80f4b76dc6996 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Thu, 23 Apr 2026 15:37:38 +0200 Subject: [PATCH] perf(faith/calendar): trim yearDays, send pre-filtered feastDots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit yearDays was a 365-entry array (one per day in the LY window) with {iso, name, rank, color, seasonKey} on each — the client only needed the color (for the needle pin on the currently-selected day; RingView re-did the feast filter itself). Split into: - yearDays: {iso, color} — unchanged count, but ~60% smaller per entry (drops name, rank, seasonKey) - feastDots: {iso, name, rank, color} — new, pre-filtered to rank > ferial server-side (~150 entries instead of 365) RingView's `feastDots` derivation shrinks to filtering out just the currently-selected day, and `activeFeasts` filters `feastDots` by arc bounds instead of re-scanning yearDays. needleDay's color lookup still works with the trimmed YearDay. Also collapses a stray `locals.session ?? (locals.session ?? …)` the earlier #5 sweep introduced in both calendar page loaders. --- TODO.md | 2 +- package.json | 2 +- src/lib/calendarTypes.ts | 17 ++++++++--- .../[[dd=calendarDay]]/+page.server.ts | 29 ++++++++++++++----- .../[[dd=calendarDay]]/+page.svelte | 2 ++ .../[[dd=calendarDay]]/RingView.svelte | 29 ++++++------------- .../[dd=calendarDay]/+page.server.ts | 2 +- 7 files changed, 48 insertions(+), 35 deletions(-) diff --git a/TODO.md b/TODO.md index 42eba662..68f83ba9 100644 --- a/TODO.md +++ b/TODO.md @@ -11,7 +11,7 @@ Order = impact. Font items + app.html preload intentionally skipped. - [x] 5. Replace redundant `locals.auth()` with `locals.session` across all routes (68 files, 107 sites — loaders, actions, API endpoints) - [x] 6. Stream fitness stats loader — muscleHeatmap, nutritionStats, periods, sharedPeriods now stream via `{#await}`. `stats` still awaited (too many chart $deriveds depend on it) - [x] 7. Muscle-heatmap endpoint — add projection + O(1) bucket math. Overview already had a projection; set-subfield narrowing was attempted but reverted (returned malformed sets). Timeseries cap not feasible: totals are lifetime-scoped. -- [ ] 8. Calendar payload trim — drop `name` from `yearDays`, pre-filter `feastDots` server-side +- [x] 8. Calendar payload trim — `yearDays` narrowed to `{iso, color}` (needle lookup only), new pre-filtered `feastDots` array carries feast-specific metadata. Also fixed a stray double `locals.session ?? (locals.session ?? …)` in both calendar page loaders. - [ ] 9. History sessions endpoint — slim exercise payload for list view - [ ] 10. `Cache-Control` headers on stable API endpoints (all_brief, calendar, exercises metadata) - [ ] 11. Search — debounce 200 ms + server-side pre-normalized `_searchKey` diff --git a/package.json b/package.json index 1972bf5f..65385e74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.46.21", + "version": "1.46.22", "private": true, "type": "module", "scripts": { diff --git a/src/lib/calendarTypes.ts b/src/lib/calendarTypes.ts index a6a9d80e..c5857622 100644 --- a/src/lib/calendarTypes.ts +++ b/src/lib/calendarTypes.ts @@ -16,14 +16,23 @@ export interface CalendarDay { rite1962?: Rite1962Detail; } -// Compact per-day shape returned for the full year so the ring / month-grid -// overview views can render without refetching. Kept small on purpose. +// Compact per-day shape returned for the full window of the liturgical year. +// Kept to the bare minimum needed client-side: the ring needs a color for the +// needle on the selected day (which may be a ferial with no rank metadata), +// everything else goes through the separate `feastDots` array. export interface YearDay { + iso: string; + color: string; // primary color key (WHITE/RED/...) +} + +// Pre-filtered list of days that render a feast dot on the ring — rank > feria +// — with the metadata the ring and side panel need for each. Sent alongside +// YearDay so clients don't have to filter 365 entries themselves. +export interface FeastDot { iso: string; name: string; rank: string; - color: string; // primary color key (WHITE/RED/...) - seasonKey: string | null; + color: string; } export interface SeasonArc { diff --git a/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.server.ts b/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.server.ts index 6baeb45f..bce04cc9 100644 --- a/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.server.ts +++ b/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.server.ts @@ -13,16 +13,17 @@ import { type Diocese1969, type Rite } from '../../../../calendarI18n'; -import { seasonColorFor } from '../../../../calendarColors'; +import { rankDotSize, seasonColorFor } from '../../../../calendarColors'; import { getYear, getYear1962, isoFor } from '$lib/server/liturgicalCalendar'; -import type { CalendarDay, SeasonArc, YearDay } from '$lib/calendarTypes'; +import type { CalendarDay, FeastDot, SeasonArc, YearDay } from '$lib/calendarTypes'; export type { CalendarDay, + FeastDot, ProperSection, Rite1962Commem, Rite1962Detail, @@ -234,14 +235,25 @@ export const load: PageServerLoad = async ({ params, url, locals, fetch }) => { } } - const yearDays: YearDay[] = sortedYear.map((d, i) => ({ + // `yearDays` only carries what the ring's needle-color lookup needs for any + // day (feast or ferial). Feast metadata (name, rank) moves into `feastDots` + // below so the client can iterate it directly without filtering 365 entries. + const yearDays: YearDay[] = sortedYear.map((d) => ({ iso: d.iso, - name: d.name, - rank: d.rank, - color: d.colorKeys[0] ?? 'GREEN', - seasonKey: filledSeasons[i] + color: d.colorKeys[0] ?? 'GREEN' })); + const feastDots: FeastDot[] = []; + for (const d of sortedYear) { + if (rankDotSize(d.rank) === 0) continue; + feastDots.push({ + iso: d.iso, + name: d.name, + rank: d.rank, + color: d.colorKeys[0] ?? 'GREEN' + }); + } + const seasonArcs: SeasonArc[] = []; let cur: SeasonArc | null = null; for (let i = 0; i < sortedYear.length; i++) { @@ -282,6 +294,7 @@ export const load: PageServerLoad = async ({ params, url, locals, fetch }) => { month, monthDays, yearDays, + feastDots, seasonArcs, windowStart, windowEnd, @@ -291,6 +304,6 @@ export const load: PageServerLoad = async ({ params, url, locals, fetch }) => { todayIso, selected: selectedEntry, selectedIso, - session: locals.session ?? (locals.session ?? await locals.auth()) + session: locals.session ?? await locals.auth() }; }; diff --git a/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.svelte b/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.svelte index 466ef4a7..12115f3e 100644 --- a/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.svelte +++ b/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/+page.svelte @@ -28,6 +28,7 @@ const month = $derived(data.month); const monthDays = $derived(data.monthDays); const yearDays = $derived(data.yearDays); + const feastDots = $derived(data.feastDots); const seasonArcs = $derived(data.seasonArcs); const today = $derived(data.today); const todayIso = $derived(data.todayIso); @@ -267,6 +268,7 @@ {year} {liturgicalYear} {yearDays} + {feastDots} {seasonArcs} {todayIso} {selectedIso} diff --git a/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/RingView.svelte b/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/RingView.svelte index da1ac5ac..33da570a 100644 --- a/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/RingView.svelte +++ b/src/routes/[faithLang=faithLang]/[calendar=calendarLang]/[rite=calendarRite]/[[yyyy=calendarYear]]/[[mm=calendarMonth]]/[[dd=calendarDay]]/RingView.svelte @@ -1,5 +1,5 @@