Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { awaitDeletedRecord, awaitIndexedOrSaved } from "#lib/api/awaitRecord.ts";import { createBobbinClient, type BobbinContext, type XrpcRequestInit } from "#lib/api/client.ts";import { createCob, eraseCob, readCob } from "#lib/api/cob.ts";import type * as OrgTangledLabelDefinition from "#lib/api/lexicons/types/org/tangled/label/definition.ts";import type * as OrgTangledLabelDefs from "#lib/api/lexicons/types/org/tangled/label/defs.ts";import { collect } from "#lib/api/pagination.ts";import type { ResolvedPathname } from "$app/types";import type { Label } from "#lib/components/repo/LabelPanel.svelte";import { labelHref } from "#lib/ticketQuery.ts";import { atUriOf, datetimeOf, didOf, labelKeyOf, nsidOf, recordKeyOf, type AtUri, type Did, type KnotOrigin, type LabelKey} from "#lib/api/syntax.ts";
export type LabelDefinition = OrgTangledLabelDefinition.Main;export type LabelToken = OrgTangledLabelDefs.LabelToken;
const LABEL_DEFINITION_NSID = "org.tangled.label.definition";const LABEL_DEFINITION_COLLECTION = nsidOf(LABEL_DEFINITION_NSID);
export const BASIC_LABEL_VALUE = "null";
export interface LabelView { uri: string; value: LabelDefinition;}
export const listLabelDefinitions = async ( ctx: BobbinContext, repoDid: string, init?: XrpcRequestInit): Promise<LabelView[]> => { const items = await collect( ctx, "org.tangled.label.listDefinitions", { subject: didOf(repoDid), limit: 100 }, { ...init, maxPages: 5 } ); return items.map((item) => ({ uri: item.uri, value: item.value as LabelDefinition }));};
export interface CustomLabelInput { name: string; color: string;}
// the knot wants a slug as a label def's rkey and refuses a tidconst labelSlugOf = (name: string): LabelKey => labelKeyOf( name .trim() .toLowerCase() .replace(/[^a-z0-9._~-]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 64) );
// only the owner can make these, and an erased key can't be used againexport const createRepoLabel = async ( agent: OAuthUserAgent, knot: KnotOrigin, repoDid: Did, bobbinUrl: string, { name, color }: CustomLabelInput): Promise<LabelView> => { const value: LabelDefinition = { $type: "org.tangled.label.definition", name, color, createdAt: datetimeOf(new Date().toISOString()) }; const written = await createCob( agent, knot, repoDid, LABEL_DEFINITION_COLLECTION, value, recordKeyOf(labelSlugOf(name)) ); await awaitIndexedOrSaved(createBobbinClient({ serviceUrl: bobbinUrl }), written); return { uri: written.uri, value };};
export const deleteRepoLabel = async ( agent: OAuthUserAgent, knot: KnotOrigin, bobbinUrl: string, uri: string): Promise<void> => { const current = await readCob<LabelDefinition>(knot, atUriOf(uri)); await eraseCob(agent, knot, current); await awaitDeletedRecord(createBobbinClient({ serviceUrl: bobbinUrl }), current.uri);};
export type AppliedLabels = Record<string, string[]>;
export interface SubjectLabels { definitions: LabelView[]; applied: AppliedLabels;}
export const NO_SUBJECT_LABELS: SubjectLabels = { definitions: [], applied: {} };
export const labelDefinitionUri = (repoDid: string, key: string): AtUri => atUriOf(`at://${repoDid}/${LABEL_DEFINITION_NSID}/${key}`);
export const loadSubjectLabels = async ( ctx: BobbinContext, repoDid: string, tokens: readonly LabelToken[], init?: XrpcRequestInit): Promise<SubjectLabels> => ({ definitions: await listLabelDefinitions(ctx, repoDid, init), applied: Object.fromEntries( tokens.map((token) => [labelDefinitionUri(repoDid, token.key), [BASIC_LABEL_VALUE]]) )});
export const labelsOfTokens = ( definitions: readonly LabelView[], repoDid: string, tokens: readonly LabelToken[] | undefined, listHref: string): Label[] => { const byUri = new Map(definitions.map((definition) => [definition.uri, definition])); return (tokens ?? []).flatMap((token) => { const definition = byUri.get(labelDefinitionUri(repoDid, token.key)); if (!definition) return []; return [ { key: definition.uri, name: definition.value.name, color: definition.value.color ?? "", href: labelHref(listHref, definition.value.name) as ResolvedPathname } ]; });};