~james/core

core/web/src/lib/api/comment.ts -rw-r--r-- 3.7 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 15 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import {
	atUriOf,
	datetimeOf,
	didOf,
	nsidOf,
	type AtUri,
	type Datetime,
	type Did,
	type GitOid,
	type KnotOrigin,
	type Rkey,
	type RawResourceUri
} from "#lib/api/syntax.ts";
import { parseCanonicalResourceUri } from "@atcute/lexicons/syntax";
import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
import type { BobbinContext, XrpcRequestInit } from "#lib/api/client.ts";
import { jsonGet } from "#lib/api/_request.ts";
import { createCob, eraseCob, updateCob, type CobRecord, type CobRef } from "#lib/api/cob.ts";
import { filterBlobsFromMarkdown } from "#lib/api/markupBlobs.ts";
import type { Blob } from "@atcute/lexicons/interfaces";
import type { RecordView } from "#lib/api/records.ts";
import { deleteRecord, putRecord } from "#lib/api/write.ts";
import type * as OrgTangledFeedComment from "#lib/api/lexicons/types/org/tangled/feed/comment.ts";
import type * as OrgTangledFeedDefs from "#lib/api/lexicons/types/org/tangled/feed/defs.ts";

export type CommentRecord = OrgTangledFeedComment.Main;

export const COMMENT_COLLECTION = nsidOf("org.tangled.feed.comment");

// subject and replyTo have to name the current version or the knot refuses
export interface CommentDraft {
	subject: CobRef;
	replyTo?: CobRef;
	text: string;
	blobs?: readonly Blob[];
	embed?: { repo: Did; commit: GitOid };
}

const commentRecord = (
	{ subject, replyTo, text, blobs, embed }: CommentDraft,
	createdAt = datetimeOf(new Date().toISOString())
): CommentRecord => ({
	$type: "org.tangled.feed.comment",
	subject: { $type: "com.atproto.repo.strongRef", ...subject },
	replyTo,
	body: {
		$type: "org.tangled.markup.markdown",
		text,
		blobs: filterBlobsFromMarkdown(undefined, blobs, text)
	},
	...(embed
		? {
				embed: {
					$type: "org.tangled.embed.commit",
					repo: embed.repo,
					commit: { $type: "org.tangled.git.oid", oid: embed.commit }
				}
			}
		: {}),
	createdAt
});

export const putComment = async (
	agent: OAuthUserAgent,
	rkey: Rkey,
	draft: CommentDraft,
	createdAt?: Datetime
): Promise<RecordView<CommentRecord> & { uri: RawResourceUri }> => {
	const record = commentRecord(draft, createdAt);
	const { uri, cid } = await putRecord(agent, COMMENT_COLLECTION, rkey, record);
	return { uri, cid, value: record };
};

export const deleteComment = async (agent: OAuthUserAgent, rkey: Rkey): Promise<{ uri: AtUri }> => {
	const uri = atUriOf(`at://${agent.sub}/${COMMENT_COLLECTION}/${rkey}`);
	await deleteRecord(agent, COMMENT_COLLECTION, rkey);
	return { uri };
};

export const createComment = async (
	agent: OAuthUserAgent,
	knot: KnotOrigin,
	draft: CommentDraft
): Promise<CobRecord<CommentRecord>> => {
	const record = commentRecord(draft);
	const { repo } = parseCanonicalResourceUri(draft.subject.uri);
	return {
		...(await createCob(agent, knot, didOf(repo), COMMENT_COLLECTION, record)),
		value: record
	};
};

export const editComment = (
	agent: OAuthUserAgent,
	knot: KnotOrigin,
	from: CobRef,
	text: string,
	blobs?: readonly Blob[]
): Promise<CobRecord<CommentRecord>> =>
	updateCob<CommentRecord>(agent, knot, from, (current) => ({
		...current,
		body: {
			...current.body,
			text,
			blobs: filterBlobsFromMarkdown(current.body.blobs, blobs, text)
		}
	}));

export const eraseComment = (
	agent: OAuthUserAgent,
	knot: KnotOrigin,
	from: CobRef
): Promise<void> => eraseCob(agent, knot, from);

export interface CommentListPage {
	cursor?: string | null;
	items: OrgTangledFeedDefs.CommentView[];
	total?: number;
}

export const listComments = (
	ctx: BobbinContext,
	subject: AtUri,
	filter: { limit?: number; cursor?: string; order?: "asc" | "desc" } = {},
	init?: XrpcRequestInit
) => jsonGet<CommentListPage>(ctx, "org.tangled.feed.listComments", { subject, ...filter }, init);