~james/core

core/web/src/lib/components/landing/guest.svelte.ts -rw-r--r-- 1.3 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 17 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import { getContext, setContext } from "svelte";
import { SvelteSet } from "svelte/reactivity";

/*
 * The visitor's side of the page: what they've tried. One per page, shared through
 * context — the visuals write to it (a round reviewed, a vouch given).
 */

export type BeatId = "pr" | "home" | "ci" | "vouch" | "agents" | "byo";

export interface FeedEvent {
	id: number;
	who: string;
	text: string;
	beat: BeatId;
	t: number;
	shield?: boolean;
	q?: string;
	sub?: string;
}

export class Guest {
	/** What the visitor has done, newest first — for the activity a later section shows. */
	feed = $state<FeedEvent[]>([]);
	done = new SvelteSet<BeatId>();

	#ids = 0;
	#timers: ReturnType<typeof setTimeout>[] = [];

	later(fn: () => void, ms: number) {
		const t = setTimeout(() => {
			this.#timers = this.#timers.filter((x) => x !== t);
			fn();
		}, ms);
		this.#timers.push(t);
	}

	destroy() {
		this.#timers.forEach(clearTimeout);
	}

	ev(beat: BeatId, who: string, text: string, o: Partial<FeedEvent> = {}, after = 0) {
		this.later(() => {
			this.feed.unshift({ who, text, beat, ...o, id: ++this.#ids, t: Date.now() });
		}, after);
	}

	markDone(b: BeatId) {
		this.done.add(b);
	}
}

const KEY = Symbol("guest");

export const setGuest = (guest: Guest) => setContext(KEY, guest);
export const getGuest = () => getContext<Guest>(KEY);