Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109<script lang="ts"> import { resolve } from "$app/paths"; import Button from "$lib/components/ui/Button.svelte"; import Spinner from "$lib/components/ui/Spinner.svelte"; import Tooltip from "$lib/components/ui/Tooltip.svelte"; import UserRoundPlus from "$icon/user-round-plus"; import UserRoundMinus from "$icon/user-round-minus"; import TriangleAlert from "$icon/triangle-alert"; import { getAuth } from "$lib/auth.svelte"; import { createFollow, deleteFollow } from "$lib/api/graph"; import { getProfileCounts } from "./counts.svelte"; import type { FollowChange } from "./types"; import { createOptimisticRelation } from "$lib/optimistic.svelte";
interface Props { profileDid: string; initialRkey?: string | null; onCommit?: (change: FollowChange) => void; }
let { profileDid, initialRkey, onCommit }: Props = $props();
const auth = getAuth(); const profileCounts = getProfileCounts(); const signedIn = $derived(Boolean(auth.currentDid)); const isSelf = $derived(auth.currentDid === profileDid); const relation = createOptimisticRelation({ key: () => `${auth.currentDid ?? ""}:${profileDid}`, loadedRkey: () => initialRkey });
const following = $derived(relation.active); const failed = $derived(relation.failed); $effect(() => { if (!failed) return; const timer = setTimeout(() => { relation.resetFailure(); }, 2000); return () => clearTimeout(timer); });
const commit = (change: FollowChange) => { profileCounts?.adjust(change.subjectDid, "followers", change.delta); profileCounts?.adjust(change.viewerDid, "following", change.delta); onCommit?.(change); };
const toggle = async () => { const agent = auth.agent; if (!agent || !relation.known || relation.loading) return; relation.begin(); try { if (relation.active && relation.rkey) { await deleteFollow(agent, relation.rkey); relation.deleted(); commit({ viewerDid: agent.sub, subjectDid: profileDid, following: false, rkey: null, delta: -1 }); } else { const rkey = await createFollow(agent, profileDid); relation.created(rkey); commit({ viewerDid: agent.sub, subjectDid: profileDid, following: true, rkey, delta: 1 }); } } catch { relation.fail(); } };</script>
{#if !isSelf} {#if !signedIn} <Button href={resolve("/login")} variant="default" class="w-full gap-2" insetShadow={true}> <UserRoundPlus class="size-4" aria-hidden="true" /><span>Follow</span> </Button> {:else} <Tooltip content="Something went wrong. Try again." disabled={!failed} class="w-full"> <Button variant={failed ? "danger" : "default"} class="w-full gap-2" insetShadow={true} disabled={!relation.known || relation.loading} aria-busy={relation.loading} onclick={toggle} > {#if relation.loading} <Spinner class="size-4 shrink-0" /> {:else if failed} <TriangleAlert class="size-4 shrink-0" aria-hidden="true" /> {:else if following} <UserRoundMinus class="size-4" aria-hidden="true" /> {:else} <UserRoundPlus class="size-4" aria-hidden="true" /> {/if} <span>{following ? "Unfollow" : "Follow"}</span> </Button> </Tooltip> {/if}{/if}