tracks lexicons and how many times they appeared on the jetstream

feat(client): load data in server function so we dont have weird empty gap with no data

ptr.pet a41cbdc9 452aff95

verified
Changed files
+39 -29
.tangled
workflows
client
-3
.tangled/workflows/build.yml
··· 11 11 - gnused 12 12 13 13 steps: 14 - - name: test server 15 - command: | 16 - cd server && cargo test 17 14 - name: build client 18 15 command: | 19 16 export PUBLIC_API_URL="localhost:3713"
+2 -1
client/src/routes/+layout.ts
··· 1 1 export const prerender = true; 2 - export const ssr = false; 2 + export const ssr = true; 3 + export const csr = true;
+7
client/src/routes/+page.server.ts
··· 1 + import { fetchEvents, fetchTrackingSince } from "$lib/api"; 2 + 3 + export const load = async () => { 4 + const events = await fetchEvents(); 5 + const trackingSince = await fetchTrackingSince(); 6 + return { events, trackingSince }; 7 + };
+30 -25
client/src/routes/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { dev } from "$app/environment"; 3 - import type { EventRecord, NsidCount, SortOption } from "$lib/types"; 3 + import type { 4 + EventRecord, 5 + Events, 6 + NsidCount, 7 + Since, 8 + SortOption, 9 + } from "$lib/types"; 4 10 import { onMount, onDestroy } from "svelte"; 5 11 import { writable } from "svelte/store"; 6 12 import { PUBLIC_API_URL } from "$env/static/public"; ··· 15 21 import RefreshControl from "$lib/components/RefreshControl.svelte"; 16 22 import { formatTimestamp } from "$lib/format"; 17 23 18 - const events = writable(new Map<string, EventRecord>()); 24 + type Props = { 25 + data: { events: Events; trackingSince: Since }; 26 + }; 27 + 28 + const { data }: Props = $props(); 29 + 30 + const events = writable( 31 + new Map<string, EventRecord>(Object.entries(data.events.events)), 32 + ); 19 33 const pendingUpdates = new Map<string, EventRecord>(); 20 34 let eventsList: NsidCount[] = $state([]); 21 35 let updateTimer: NodeJS.Timeout | null = null; ··· 28 42 })) 29 43 .toArray(); 30 44 }); 31 - let per_second = $state(0); 32 - let tracking_since = $state(0); 45 + let per_second = $state(data.events.per_second); 46 + let tracking_since = $state(data.trackingSince.since); 47 + 48 + const applyEvents = (newEvents: Record<string, EventRecord>) => { 49 + events.update((map) => { 50 + for (const [nsid, event] of Object.entries(newEvents)) { 51 + map.set(nsid, event); 52 + } 53 + return map; 54 + }); 55 + }; 33 56 34 57 let all: EventRecord = $derived( 35 58 eventsList.reduce( ··· 76 99 }; 77 100 websocket.onmessage = async (event) => { 78 101 const jsonData = JSON.parse(event.data); 79 - 80 - if (jsonData.per_second > 0) { 81 - per_second = jsonData.per_second; 82 - } 83 - 84 - // Store updates in pending map if refresh rate is set 102 + per_second = jsonData.per_second; 85 103 if (refreshRate) { 86 104 for (const [nsid, event] of Object.entries(jsonData.events)) { 87 105 pendingUpdates.set(nsid, event as EventRecord); 88 106 } 89 107 } else { 90 - // Apply updates immediately if no refresh rate 91 - events.update((map) => { 92 - for (const [nsid, event] of Object.entries( 93 - jsonData.events, 94 - )) { 95 - map.set(nsid, event as EventRecord); 96 - } 97 - return map; 98 - }); 108 + applyEvents(jsonData.events); 99 109 } 100 110 }; 101 111 websocket.onerror = (error) => { ··· 114 124 error = null; 115 125 const data = await fetchEvents(); 116 126 per_second = data.per_second; 117 - events.update((map) => { 118 - for (const [nsid, event] of Object.entries(data.events)) { 119 - map.set(nsid, event); 120 - } 121 - return map; 122 - }); 127 + applyEvents(data.events); 123 128 tracking_since = (await fetchTrackingSince()).since; 124 129 } catch (err) { 125 130 error =