import { Client, RecordKeyString } from "@atproto/lex"; import { AtprotoDid } from "@atproto/oauth-client"; import { blue, dev } from "../lexicons"; import { Constellation } from "../util/constellation"; import { Post } from "./post"; import { AtUri } from "@atproto/api"; import { strongRef } from "../lexicons/com/atproto/repo"; export type PostInteractions = { liked: RecordKeyString | undefined; lastReblog: RecordKeyString | undefined; }; export async function fetchUserPostInteractions(post: Post, did: string) { const rootUri = post.reblog?.root.uri ?? post.uri // Like is based on the root post const likeResults = await Constellation.call( blue.microcosm.links.getBacklinks, { subject: rootUri, source: `${dev.maelstrom.bramblr.like.$nsid}:root.uri`, did: [did as AtprotoDid], limit: 1, }, ); const reblogResults = await Constellation.call( blue.microcosm.links.getBacklinks, { // For a more accurate behavior, this should be from the root post // subject: rootUri // source: `${dev.maelstrom.bramblr.post.$nsid}:reblog.root.uri`, subject: post.uri, source: `${dev.maelstrom.bramblr.post.$nsid}:reblog.parent.uri`, did: [did as AtprotoDid], limit: 1, }, ); return { liked: likeResults.total > 0 ? likeResults.records[0].rkey : undefined, lastReblog: reblogResults.total > 0 ? reblogResults.records[0].rkey : undefined, } as PostInteractions; } export async function fetchPostNotesCount(post: Post) { const rootUri = post.reblog?.root.uri ?? post.uri // Likes are unique per post, only via is stored for notifications const { total: likeTotal } = await Constellation.call( blue.microcosm.links.getBacklinkDids, { subject: rootUri, source: `${dev.maelstrom.bramblr.like.$nsid}:root.uri`, }, ); const { total: reblogTotal } = await Constellation.call( blue.microcosm.links.getBacklinksCount, { subject: rootUri, source: `${dev.maelstrom.bramblr.post.$nsid}:reblog.root.uri`, }, ); return likeTotal + reblogTotal; } // Also mutates input postInteractions export async function setLikePost( like: boolean, post: Post, postInteractions: PostInteractions, client: Client, ) { const rootRef = post.reblog?.root ?? { uri: post.uri, cid: post.cid } if (like && postInteractions.liked === undefined) { const response = await client.create(dev.maelstrom.bramblr.like, { root: rootRef, // Store via for notifications via: { uri: post.uri, cid: post.cid, }, createdAt: new Date().toISOString() as any, // TODO: Fix this }); console.log(`Committed change: ${response.commit}`); // No change if (!response) { return postInteractions; } return { ...postInteractions, liked: new AtUri(response.uri).rkey }; } else if (!like && postInteractions.liked !== undefined) { const response = await client.delete(dev.maelstrom.bramblr.like, { repo: client.did!, rkey: postInteractions.liked!, }); console.log(`Deleted: ${postInteractions.liked}`) console.log(`Committed change: ${response.commit}`); return { ...postInteractions, liked: undefined }; } return postInteractions; } // Also mutates input postInteractions export async function addPostReblog( post: Post, postInteractions: PostInteractions, client: Client, ) { const postRef: strongRef.Main = { uri: post.uri, cid: post.cid } const parentRef = post.reblog?.parent ?? postRef const rootRef = post.reblog?.root ?? postRef // TODO: Validate that thread and reblog match, otherwise we can get broken posts const response = await client.create(dev.maelstrom.bramblr.post, { text: "", reblog: { root: rootRef, parent: parentRef, }, thread: [ ...post.thread, postRef, ], createdAt: new Date().toISOString() as any, // TODO: Fix this }); console.log(`Committed change: ${response.commit}`); // No change if (!response) { return postInteractions; } return { ...postInteractions, lastReblog: new AtUri(response.uri).rkey }; }