Monorepo for Tangled
Something went wrong. Try again.
Svelte
at sl/sv-pull
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142<script lang="ts"> import { untrack, type Component } from "svelte"; import type { SvelteHTMLElements } from "svelte/elements"; import { now as tidNow } from "@atcute/tid"; import X from "$icon/x"; import { putComment } from "$lib/api/comment"; import { getAuth } from "$lib/auth.svelte"; import Button from "$lib/components/ui/Button.svelte"; import ErrorAlert from "$lib/components/ui/Error.svelte"; import MarkdownEditor from "$lib/components/ui/MarkdownEditor.svelte"; import Spinner from "$lib/components/ui/Spinner.svelte"; import { renderMarkup, type MarkupContext } from "$lib/markup"; import type { CommentRecord } from "$lib/api/records"; import { createAction } from "$lib/action.svelte"; import type { ThreadInput } from "./comments";
interface Props { subjectUri: string; subjectCid?: string; // when set, the comment strongRefs this parent comment (threaded reply) replyToUri?: string; replyToCid?: string; authorDid: string; authorHandle: string; // reused on edit so the record keeps its identity; omitted when composing rkey?: string; createdAt?: string; body?: string; markup: MarkupContext; rows?: number; placeholder?: string; submitLabel: string; submitIcon: Component<SvelteHTMLElements["svg"]>; autofocus?: boolean; onsubmitted?: (submitted: ThreadInput) => void; oncancel?: () => void; }
let { subjectUri, subjectCid, replyToUri, replyToCid, authorDid, authorHandle, rkey, createdAt, body: initialBody = "", markup, rows = 6, placeholder, submitLabel, submitIcon, autofocus = false, onsubmitted, oncancel }: Props = $props();
const auth = getAuth();
let body = $state(untrack(() => initialBody)); let tab = $state<"write" | "preview">("write");
const publish = createAction(async (event: SubmitEvent) => { event.preventDefault(); const agent = auth.agent; if (!agent || body.trim() === "") return; if (!subjectCid) throw new Error("Cannot comment: the subject record is missing a cid."); const createdAtValue = createdAt ?? new Date().toISOString(); const targetRkey = rkey ?? tidNow(); const record: CommentRecord = { $type: "sh.tangled.feed.comment", subject: { uri: subjectUri, cid: subjectCid } as CommentRecord["subject"], body: { $type: "sh.tangled.markup.markdown", text: body }, createdAt: createdAtValue }; if (replyToUri && replyToCid) { record.replyTo = { uri: replyToUri, cid: replyToCid } as CommentRecord["replyTo"]; } const saved = await putComment(agent, targetRkey, record); // render markdown client-side so the optimistic comment matches a real one const bodyHtml = await renderMarkup(body, markup).catch(() => null); onsubmitted?.({ comment: { uri: saved.uri, cid: saved.cid, rkey: targetRkey, authorDid, authorHandle, createdAt: createdAtValue, body, bodyHtml }, replyTo: replyToUri ?? null }); // keep the prefilled body when editing; reset when composing a fresh comment if (!rkey) { body = ""; tab = "write"; } }); const canSubmit = $derived(body.trim() !== "" && !publish.loading);</script>
<form onsubmit={publish.run} class="flex flex-col gap-2"> <MarkdownEditor name="body" {rows} {placeholder} {markup} {autofocus} transparent bind:value={body} bind:tab disabled={publish.loading} />
<ErrorAlert label={publish.error} />
<div class="flex items-center gap-2"> <Button type="submit" variant="primary" icon={publish.loading ? Spinner : submitIcon} disabled={!canSubmit} > {submitLabel} </Button> {#if oncancel} <Button type="button" variant="default" icon={X} disabled={publish.loading} onclick={oncancel} > Cancel </Button> {/if} </div></form>