From 8c0d385772b0cc581c2de754faa83cf59d5feae4 Mon Sep 17 00:00:00 2001 From: Fred Boniface Date: Tue, 21 Apr 2026 19:54:00 +0100 Subject: [PATCH] Add QuickLinks based on 'frecency' pattern --- src/lib/components/ui/TimezoneWarning.svelte | 38 +++++----- .../components/ui/cards/QuickLinksCard.svelte | 71 +++++++++++++++++++ src/lib/quick-links.svelte.ts | 60 ++++++++++++++++ src/routes/+page.svelte | 2 + src/routes/board/+page.svelte | 21 ++++++ src/routes/board/+page.ts | 12 ++-- 6 files changed, 181 insertions(+), 23 deletions(-) create mode 100644 src/lib/components/ui/cards/QuickLinksCard.svelte create mode 100644 src/lib/quick-links.svelte.ts diff --git a/src/lib/components/ui/TimezoneWarning.svelte b/src/lib/components/ui/TimezoneWarning.svelte index c2ebf57..c64ccca 100644 --- a/src/lib/components/ui/TimezoneWarning.svelte +++ b/src/lib/components/ui/TimezoneWarning.svelte @@ -1,6 +1,6 @@ {#if isNotLondon} -

- All times are shown in {londonZone} -

+
+

+ All times are shown in {londonZone} +

+
{/if} \ No newline at end of file + .tzText { + width: 80%; + text-align: center; + margin: auto; + font-family: 'URW Gothic', sans-serif; + font-size: 1.2rem; + } + diff --git a/src/lib/components/ui/cards/QuickLinksCard.svelte b/src/lib/components/ui/cards/QuickLinksCard.svelte new file mode 100644 index 0000000..49165c5 --- /dev/null +++ b/src/lib/components/ui/cards/QuickLinksCard.svelte @@ -0,0 +1,71 @@ + + + +
+ {#if quickLinks.list.length === 0} +

Your most viewed stations will appear here

+ {:else} +
+ {#each quickLinks.list as station (station.id)} +
+ +
+ {/each} +
+ {/if} +
+
+ + diff --git a/src/lib/quick-links.svelte.ts b/src/lib/quick-links.svelte.ts new file mode 100644 index 0000000..8fd07e1 --- /dev/null +++ b/src/lib/quick-links.svelte.ts @@ -0,0 +1,60 @@ +export interface QuickLink { + id: string; + score: number; + lastAccessed: number; +} + +const RETURNED_LENGTH: number = 9; +const MAX_SCORE: number = 50; +const MAX_ENTRIES: number = RETURNED_LENGTH * 4; + +class QuickLinksService { + #links = $state([]); + + constructor() { + if (typeof window !== 'undefined') { + const saved = localStorage.getItem('ql'); + if (saved) { + this.#links = JSON.parse(saved); + } + } + } + + get list(): QuickLink[] { + return this.#links.slice(0, RETURNED_LENGTH); + } + + recordVisit(id: string) { + if (id == '') return; + const existing = this.#links.find((l) => l.id === id); + + if (existing) { + existing.score += 1; + existing.lastAccessed = Date.now(); + } else { + this.#links.push({ + id: id, + score: 1, + lastAccessed: Date.now() + }); + } + + // Score decay - if MAX_SCORE reached, divide all by two + if (this.#links.some((l) => l.score > MAX_SCORE)) { + this.#links.forEach((l) => { + l.score = Math.max(1, Math.floor(l.score / 2)); + }); + } + + // Sort & Prune + const sorted = [...this.#links].sort( + (a, b) => b.score - a.score || b.lastAccessed - a.lastAccessed + ); + + this.#links = sorted.slice(0, MAX_ENTRIES); + + localStorage.setItem('ql', JSON.stringify(this.#links)); + } +} + +export const quickLinks = new QuickLinksService(); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index c18f801..031b100 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,11 +1,13 @@
+