Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112<script lang="ts"> import { page } from "$app/state"; 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.ts"; import { loginHrefFor } from "#lib/auth/guards.ts"; import { createSubscription, deleteSubscription } from "#lib/api/graph.ts"; import { createBobbinClient } from "#lib/api/client.ts"; import { awaitDeletedRecord, awaitIndexedOrSaved } from "#lib/api/awaitRecord.ts"; 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"; import { createOptimisticRelation, seededValue } from "#lib/optimistic.svelte.ts";
interface Props { repoDid: string; initialRkey?: string | null | Promise<string | null>; class?: string; }
let { repoDid, initialRkey, class: className }: Props = $props();
const auth = getAuth(); const signedIn = $derived(Boolean(auth.currentDid)); const loginHref = $derived(loginHrefFor(page.url));
const rkeySeed = seededValue( () => initialRkey, (rkey) => rkey, null ); const relation = createOptimisticRelation({ key: () => `${auth.currentDid ?? ""}:${repoDid}`, loadedRkey: () => rkeySeed.value }); const subscribed = $derived(relation.active); const error = $derived(relation.failed ? "Failed to update subscription" : null);
$effect(() => { if (!relation.failed) return; const timer = setTimeout(() => relation.resetFailure(), 2000); return () => clearTimeout(timer); });
const toggle = async () => { const agent = auth.agent; if (!agent || !repoDid || !relation.known || relation.loading) return; const wasSubscribed = relation.active; const existingRkey = relation.rkey; relation.begin(!wasSubscribed); const bobbinUrl = page.data.publicConfig?.bobbinUrl; const ctx = bobbinUrl ? createBobbinClient({ serviceUrl: bobbinUrl }) : null; try { if (wasSubscribed && existingRkey) { const { uri } = await deleteSubscription(agent, existingRkey); relation.deleted(); if (ctx) { awaitDeletedRecord(ctx, uri).catch((cause) => console.error("failed to wait for unsubscribe index", cause) ); } } else { const written = await createSubscription(agent, repoDid); relation.created(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 { relation.fail(); } };</script>
<Tooltip content={error ?? ""} disabled={!error} class={className}> <ButtonGroup class="w-full"> <Button data-testid="watch" data-subscribed={subscribed} variant={error ? "danger" : "default"} href={signedIn ? undefined : loginHref} disabled={signedIn && !relation.known} aria-busy={relation.loading} class="flex-1 max-sm:px-xs! sm: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>