<script lang="ts">
import { page } from "$app/state";
import SmilePlus from "$icon/smile-plus";
import { createAction } from "#lib/action.svelte.ts";
import type { KnotOrigin } from "#lib/api/syntax.ts";
import { createBobbinClient } from "#lib/api/client.ts";
import { getAuth } from "#lib/auth.svelte.ts";
import Button from "#lib/components/ui/Button.svelte";
import {
ORDERED_REACTION_KINDS,
toggleReaction,
type ReactionGroup,
type ReactionKind
} from "#lib/components/reaction/reactions.ts";
import Dropdown, { type DropdownVariants } from "#lib/components/ui/Dropdown.svelte";
import type { PopoverOrigin } from "#lib/components/ui/popover.svelte.ts";
import Tooltip from "#lib/components/ui/Tooltip.svelte";
import { resetFailureSoon } from "#lib/optimistic.svelte.ts";
interface Props {
subjectUri: string;
subjectCid?: string;
knot?: KnotOrigin;
groups: ReactionGroup[];
onchange: (next: ReactionGroup[]) => void;
dropdownAlign?: DropdownVariants["align"];
dropdownOrigin?: PopoverOrigin;
triggerClass?: string;
}
let {
subjectUri,
subjectCid,
knot,
groups,
onchange,
dropdownAlign,
dropdownOrigin,
triggerClass
}: Props = $props();
const auth = getAuth();
let dropdown = $state<ReturnType<typeof Dropdown>>();
let failed = $state(false);
resetFailureSoon(
() => failed,
() => (failed = false)
);
const pick = createAction(async (kind: ReactionKind) => {
const agent = auth?.agent;
const viewer = auth?.currentUser;
if (!agent || !viewer) return;
const bobbinUrl = page.data.publicConfig?.bobbinUrl;
await toggleReaction({
agent,
knot,
subject: { uri: subjectUri, cid: subjectCid },
kind,
viewer,
bobbin: bobbinUrl ? createBobbinClient({ serviceUrl: bobbinUrl }) : null,
groups: () => groups,
commit: onchange
});
});
const handlePick = async (kind: ReactionKind) => {
if (pick.loading) return;
dropdown?.hideMenu();
failed = false;
await pick.run(kind);
failed = pick.error !== undefined;
};
</script>
<Tooltip content="Something went wrong. Try again." disabled={!failed}>
<Dropdown
bind:this={dropdown}
square
variant="button"
buttonVariant={failed ? "danger" : "ghost"}
size="small"
label="React"
title="React"
menuClass="w-fit"
align={dropdownAlign}
origin={dropdownOrigin}
{triggerClass}
>
{#snippet trigger()}
<SmilePlus class="size-3.5" />
{/snippet}
<div class="grid grid-cols-4">
{#each ORDERED_REACTION_KINDS as kind (kind)}
<Button
data-kind={kind}
variant="ghost"
class="[--btn-radius:0.25rem]"
aria-label={`React with ${kind}`}
aria-pressed={groups.some((g) => g.kind === kind && g.isReacted)}
disabled={pick.loading}
onclick={() => handlePick(kind)}
>
<span class="typography-paragraph-regular leading-none">{kind}</span>
</Button>
{/each}
</div>
</Dropdown>
</Tooltip>