Monorepo for Tangled
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143<script lang="ts"> import { page } from "$app/state"; import { resolve } from "$app/paths"; import Eye from "$icon/eye"; import EyeFilled from "$lib/components/icons/EyeFilled.svelte"; import TriangleAlert from "$icon/triangle-alert"; import { getAuth } from "$lib/auth.svelte"; import { loginWithReturn } from "$lib/auth/guards"; import { createSubscription, deleteSubscription, getSubscriptionRkey } from "$lib/api/graph"; import { createBobbinClient } from "$lib/api/client"; import { awaitDeletedRecord, awaitIndexedOrSaved } from "$lib/api/awaitRecord"; import Button from "$lib/components/ui/Button.svelte"; import ButtonGroup from "$lib/components/ui/ButtonGroup.svelte"; import ToggleFaces from "$lib/components/ui/ToggleFaces.svelte"; import Tooltip from "$lib/components/ui/Tooltip.svelte";
interface Props { repoDid: string; }
let { repoDid }: Props = $props();
const auth = getAuth(); const signedIn = $derived(Boolean(auth.currentDid)); const loginHref = $derived( resolve(loginWithReturn(page.url.pathname + page.url.search) as "/login") );
let subscribed = $state(false); let known = $state(false); let toggling = $state(false); let subRkey = $state<string | null>(null); let error = $state<string | null>(null);
$effect(() => { const agent = auth.agent; const did = auth.currentDid; if (!agent || !did || !repoDid) { subscribed = false; subRkey = null; known = true; return; } let cancelled = false; known = false; error = null; const bobbinUrl = (page.data as { publicConfig?: { bobbinUrl?: string } })?.publicConfig ?.bobbinUrl; if (!bobbinUrl) return; const ctx = createBobbinClient({ serviceUrl: bobbinUrl }); getSubscriptionRkey(ctx, did, repoDid) .then((rkey) => { if (cancelled) return; subRkey = rkey; subscribed = !!rkey; known = true; }) .catch(() => { if (!cancelled) error = "Could not load subscription state"; }); return () => { cancelled = true; }; });
$effect(() => { if (!error) return; const timer = setTimeout(() => { error = null; }, 2000); return () => clearTimeout(timer); });
const toggle = async () => { const agent = auth.agent; if (!agent || !repoDid || !known || toggling) return; const wasSubscribed = subscribed; const wasRkey = subRkey; error = null; toggling = true; subscribed = !wasSubscribed; const bobbinUrl = (page.data as { publicConfig?: { bobbinUrl?: string } })?.publicConfig ?.bobbinUrl; const ctx = bobbinUrl ? createBobbinClient({ serviceUrl: bobbinUrl }) : null; try { if (wasSubscribed && wasRkey) { const { uri } = await deleteSubscription(agent, wasRkey); subRkey = null; if (ctx) { awaitDeletedRecord(ctx, uri).catch((cause) => console.error("failed to wait for unsubscribe index", cause) ); } } else { const written = await createSubscription(agent, repoDid); subRkey = written.rkey; if (ctx) { // todo: needs a design pass for surfacing index wait failures awaitIndexedOrSaved(ctx, written).catch((cause) => console.error("failed to wait for subscribe index", cause) ); } } } catch { subscribed = wasSubscribed; subRkey = wasRkey; error = "Failed to update subscription"; } finally { toggling = false; } };</script>
<Tooltip content={error ?? ""} disabled={!error}> <ButtonGroup> <Button variant={error ? "danger" : "default"} insetShadow={true} href={signedIn ? undefined : loginHref} class="min-w-24" onclick={signedIn ? toggle : undefined} > <ToggleFaces on={subscribed} failed={Boolean(error)} offLabel="Watch" onLabel="Watching" > {#snippet off(cls)} <Eye class={cls} /> {/snippet} {#snippet onIcon(cls)} <EyeFilled class={cls} /> {/snippet} {#snippet err(cls)} <TriangleAlert class={cls} /> {/snippet} </ToggleFaces> </Button> </ButtonGroup></Tooltip><span role="status" aria-live="polite" class="sr-only">{error ?? ""}</span>