Monorepo for Tangled
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101<script lang="ts"> import { page } from "$app/state"; import { now as tidNow } from "@atcute/tid"; import SmilePlus from "$icon/smile-plus"; import { createAction } from "$lib/action.svelte"; import { putReaction, deleteReaction } from "$lib/api/reaction"; import { createBobbinClient } from "$lib/api/client"; import { awaitDeletedRecord, awaitIndexedOrSaved } from "$lib/api/awaitRecord"; import { getAuth } from "$lib/auth.svelte"; import type { ReactionRecord } from "$lib/api/reaction"; import Button from "$lib/components/ui/Button.svelte"; import { ORDERED_REACTION_KINDS, type ReactionKind } from "$lib/components/reaction/reactions"; import Dropdown, { type DropdownVariants } from "$lib/components/ui/Dropdown.svelte"; import type { PopoverOrigin } from "$lib/components/ui/popover.svelte";
interface Props { subjectUri: string; reacted?: Map<ReactionKind, string>; onreacted?: (kind: ReactionKind, rkey: string) => void; onunreacted?: (kind: ReactionKind) => void; dropdownAlign?: DropdownVariants["align"]; dropdownOrigin?: PopoverOrigin; }
let { subjectUri, reacted, onreacted, onunreacted, dropdownAlign, dropdownOrigin }: Props = $props();
const auth = getAuth();
let panel = $state<HTMLElement>();
const pick = createAction(async (kind: ReactionKind) => { const agent = auth?.agent; if (!agent) return;
const bobbinUrl = page.data.publicConfig?.bobbinUrl; const ctx = bobbinUrl ? createBobbinClient({ serviceUrl: bobbinUrl }) : null;
const existingRkey = reacted?.get(kind); if (existingRkey) { const { uri } = await deleteReaction(agent, existingRkey); onunreacted?.(kind); if (ctx) { awaitDeletedRecord(ctx, uri).catch((cause) => console.error("failed to wait for unreaction index", cause) ); } } else { const rkey = tidNow(); const record: ReactionRecord = { $type: "sh.tangled.feed.reaction", subject: subjectUri as ReactionRecord["subject"], reaction: kind, createdAt: new Date().toISOString() }; const written = await putReaction(agent, rkey, record); onreacted?.(kind, rkey); if (ctx) { // todo: needs a design pass for surfacing index wait failures awaitIndexedOrSaved(ctx, written).catch((cause) => console.error("failed to wait for reaction index", cause) ); } } panel?.hidePopover(); });
const handlePick = (kind: ReactionKind) => { if (!pick.loading) void pick.run(kind); };</script>
<Dropdown variant="button" buttonVariant="ghost" size="small" label="React" title="React" menuClass="w-fit" align={dropdownAlign} origin={dropdownOrigin}> {#snippet trigger()} <SmilePlus class="size-3.5" /> {/snippet}
<div class="grid grid-cols-4 p-2xs"> {#each ORDERED_REACTION_KINDS as kind (kind)} <Button variant="ghost" aria-label={`React with ${kind}`} aria-pressed={reacted?.has(kind) ?? false} disabled={pick.loading} onclick={() => handlePick(kind)} > <span class="typography-paragraph-regular leading-none">{kind}</span> </Button> {/each} </div></Dropdown>