This repository has no description
1import type { ReactionGroup } from "$lib/components/reaction/reactions";
2
3export interface CommentView {
4 uri: string;
5 cid?: string;
6 rkey: string;
7 authorDid: string;
8 authorHandle: string;
9 createdAt: string;
10 body: string;
11 bodyHtml: string | null;
12 reactions?: ReactionGroup[];
13 deleted?: boolean;
14}
15
16export interface CommentThread {
17 self: CommentView;
18 replies: CommentView[];
19}
20
21export interface ThreadInput {
22 comment: CommentView;
23 replyTo: string | null;
24 // lets us reply to a parent even after it's deleted
25 replyToCid?: string;
26}
27
28const byCreatedAt = (a: CommentView, b: CommentView) => a.createdAt.localeCompare(b.createdAt);
29
30// stand-in for a deleted parent
31export function deletedComment(uri: string, createdAt: string, cid?: string): CommentView {
32 return {
33 uri,
34 cid,
35 rkey: uri.split("/").pop() ?? "",
36 authorDid: "",
37 authorHandle: "",
38 createdAt,
39 body: "",
40 bodyHtml: null,
41 deleted: true
42 };
43}
44
45export function buildCommentThreads(inputs: ThreadInput[]): CommentThread[] {
46 const threads = new Map<string, CommentThread>();
47
48 for (const { comment, replyTo } of inputs) {
49 if (replyTo === null) {
50 threads.set(comment.uri, { self: comment, replies: [] });
51 }
52 }
53
54 for (const { comment, replyTo, replyToCid } of inputs) {
55 if (replyTo === null) continue;
56 let parent = threads.get(replyTo);
57 if (!parent) {
58 parent = { self: deletedComment(replyTo, comment.createdAt), replies: [] };
59 threads.set(replyTo, parent);
60 }
61 if (parent.self.deleted && replyToCid && !parent.self.cid) {
62 parent.self.cid = replyToCid;
63 }
64 parent.replies.push(comment);
65 }
66
67 const list = [...threads.values()].sort((a, b) => byCreatedAt(a.self, b.self));
68 for (const thread of list) thread.replies.sort(byCreatedAt);
69 return list;
70}