<script lang="ts">
import type { ResolvedPathname } from "$app/types";
import {
BASIC_LABEL_VALUE,
createRepoLabel,
type AppliedLabels,
type LabelView,
type SubjectLabels
} from "#lib/api/labels.ts";
import { didOf, labelKeyOf, type KnotOrigin } from "#lib/api/syntax.ts";
import { cobRefOf } from "#lib/api/cob.ts";
import { setTicketLabels } from "#lib/api/ticket.ts";
import StaleReload from "#lib/components/ui/StaleReload.svelte";
import { didFromUri, rkeyFromUri } from "#lib/api/uri.ts";
import { getAuth } from "#lib/auth.svelte.ts";
import LabelPanel, { type Label } from "#lib/components/repo/LabelPanel.svelte";
import { labelHref } from "#lib/ticketQuery.ts";
interface Props {
subject: string;
subjectCid?: string | null;
// on a knot repo the uri authority is the repo did, not the owner
isOwner: boolean;
knot?: KnotOrigin;
labels: SubjectLabels;
listHref: string;
settingsHref: string;
canEdit?: boolean;
summarize?: boolean;
onwritten?: (cid: string) => void;
}
let {
subject,
subjectCid,
isOwner,
knot,
labels,
listHref,
settingsHref,
canEdit = false,
summarize = false,
onwritten
}: Props = $props();
const auth = getAuth();
let applied = $derived<AppliedLabels>(labels.applied);
let created = $state<LabelView[]>([]);
let failure = $state<unknown>();
const allLabels = $derived<Label[]>(
[...labels.definitions, ...created].map((definition) => ({
key: definition.uri,
name: definition.value.name,
color: definition.value.color ?? "",
href: labelHref(listHref, definition.value.name) as ResolvedPathname
}))
);
const picked = $derived(allLabels.filter((label) => applied[label.key]?.length));
const change = async (keys: string[]) => {
const agent = auth.agent;
if (!agent || !knot) return;
const before = applied;
const current = picked.map((label) => label.key);
const add = keys.filter((key) => !current.includes(key));
const remove = current.filter((key) => !keys.includes(key));
if (add.length === 0 && remove.length === 0) return;
const next = { ...before };
for (const key of add) next[key] = [BASIC_LABEL_VALUE];
for (const key of remove) delete next[key];
applied = next;
failure = undefined;
try {
const written = await setTicketLabels(
agent,
knot,
cobRefOf({ uri: subject, cid: subjectCid }),
keys.map((key) => labelKeyOf(rkeyFromUri(key)))
);
onwritten?.(written.cid);
} catch (cause) {
applied = before;
failure = cause;
}
};
const createLabel = async (name: string, color: string) => {
const agent = auth.agent;
if (!agent || !knot) return;
const repoDid = didOf(didFromUri(subject));
const label = await createRepoLabel(agent, knot, repoDid, auth.bobbinUrl, {
name,
color
}).catch(() => null);
if (!label) return;
created = [...created, label];
await change([...picked.map((current) => current.key), label.uri]);
};
</script>
<LabelPanel
labels={picked}
{allLabels}
canEdit={canEdit && !!auth.agent}
onLabelsChange={change}
onCreateLabel={canEdit && isOwner ? createLabel : undefined}
settingsHref={canEdit && isOwner ? settingsHref : undefined}
{summarize}
/>
<StaleReload cause={failure} message />