Download src/lib/stores/data.ts from Fraser/piclets: direct link, hf CLI and curl.
- Browser
- Download file 3.93 kB
-
https://huggingface.co/spaces/Fraser/piclets/resolve/main/src/lib/stores/data.ts
- Command line
-
hf download hf://spaces/Fraser/piclets/src/lib/stores/data.ts
-
curl -L -o data.ts https://huggingface.co/spaces/Fraser/piclets/resolve/main/src/lib/stores/data.ts
3.93 kB
| import { writable, get } from 'svelte/store'; | |
| import { authStore } from './auth'; | |
| import { getDex, getFeed, getLeaderboard, getStats, getUser } from '$lib/services/dataset'; | |
| import type { | |
| Monster, | |
| MonsterSummary, | |
| LeaderboardEntry, | |
| UserRecord, | |
| Stats, | |
| } from '$lib/types'; | |
| interface DataState { | |
| dex: MonsterSummary[] | null; | |
| feed: MonsterSummary[] | null; | |
| leaderboard: LeaderboardEntry[] | null; | |
| stats: Stats | null; | |
| myUser: UserRecord | null; | |
| loading: boolean; | |
| error: string | null; | |
| } | |
| const initialState: DataState = { | |
| dex: null, | |
| feed: null, | |
| leaderboard: null, | |
| stats: null, | |
| myUser: null, | |
| loading: false, | |
| error: null, | |
| }; | |
| function createDataStore() { | |
| const { subscribe, set, update } = writable<DataState>(initialState); | |
| /** Fetch all index files + the current user's record. Used by PullToRefresh | |
| * and on first load. No cache-busting — stale reads for other people's | |
| * finds are fine; monsters are immutable. */ | |
| async function refreshAll(): Promise<void> { | |
| update((s) => ({ ...s, loading: true, error: null })); | |
| try { | |
| const [dex, feed, leaderboard, stats] = await Promise.all([ | |
| getDex(), | |
| getFeed(), | |
| getLeaderboard(), | |
| getStats(), | |
| ]); | |
| const auth = get(authStore); | |
| let myUser: UserRecord | null = null; | |
| if (auth.userInfo?.sub) { | |
| try { | |
| myUser = await getUser(auth.userInfo.sub); | |
| } catch (err) { | |
| console.error('[dataStore] Failed to fetch user record for sub:', auth.userInfo.sub, err); | |
| } | |
| } | |
| set({ dex, feed, leaderboard, stats, myUser, loading: false, error: null }); | |
| } catch (err) { | |
| update((s) => ({ | |
| ...s, | |
| loading: false, | |
| error: err instanceof Error ? err.message : 'Failed to load data', | |
| })); | |
| } | |
| } | |
| /** Re-fetch just the current user's record. */ | |
| async function refreshMine(): Promise<void> { | |
| const auth = get(authStore); | |
| if (!auth.userInfo?.sub) return; | |
| try { | |
| const myUser = await getUser(auth.userInfo.sub); | |
| update((s) => ({ ...s, myUser })); | |
| } catch { | |
| // ignore — user may not exist in dataset yet | |
| } | |
| } | |
| /** Optimistic insert after a successful scan. Mutates in-memory state only — | |
| * no refetch. The /scan response already contains the full monster, so we | |
| * can show it instantly regardless of CDN propagation lag. | |
| * | |
| * - Always ensures the monster is in `dex` (fixes CDN lag for both new | |
| * and existing scans). | |
| * - Prepends to `feed` only for genuinely new discoveries. | |
| * - Updates `myUser` (discoveries, total_rarity, discovery_count) only | |
| * when the current user is the discoverer (status === 'new'). */ | |
| function optimisticInsert(monster: Monster, isNew: boolean): void { | |
| update((s) => { | |
| const summary: MonsterSummary = { | |
| key: monster.key, | |
| name: monster.name, | |
| type: monster.type, | |
| rarity: monster.rarity, | |
| image_url: monster.image_url, | |
| discoverer_username: monster.discoverer.username, | |
| discovered_at: monster.discovered_at, | |
| }; | |
| const dex = | |
| s.dex && s.dex.some((m) => m.key === monster.key) | |
| ? s.dex | |
| : [summary, ...(s.dex || [])]; | |
| const feed = | |
| isNew && s.feed && !s.feed.some((m) => m.key === monster.key) | |
| ? [summary, ...s.feed].slice(0, 50) | |
| : s.feed; | |
| let myUser = s.myUser; | |
| if (isNew && myUser && !myUser.discoveries.includes(monster.key)) { | |
| myUser = { | |
| ...myUser, | |
| discoveries: [...myUser.discoveries, monster.key], | |
| total_rarity: myUser.total_rarity + monster.rarity, | |
| discovery_count: myUser.discovery_count + 1, | |
| }; | |
| } | |
| return { ...s, dex, feed, myUser }; | |
| }); | |
| } | |
| return { subscribe, set, update, refreshAll, refreshMine, optimisticInsert }; | |
| } | |
| export const dataStore = createDataStore(); | |