mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {
2 type $Typed,
3 AppBskyEmbedRecord,
4 AppBskyEmbedRecordWithMedia,
5 type AppBskyFeedDefs,
6 type AppBskyFeedPostgate,
7 AtUri,
8} from '@atproto/api'
9
10export const POSTGATE_COLLECTION = 'app.bsky.feed.postgate'
11
12export function createPostgateRecord(
13 postgate: Partial<AppBskyFeedPostgate.Record> & {
14 post: AppBskyFeedPostgate.Record['post']
15 },
16): AppBskyFeedPostgate.Record {
17 return {
18 $type: POSTGATE_COLLECTION,
19 createdAt: new Date().toISOString(),
20 post: postgate.post,
21 detachedEmbeddingUris: postgate.detachedEmbeddingUris || [],
22 embeddingRules: postgate.embeddingRules || [],
23 }
24}
25
26export function mergePostgateRecords(
27 prev: AppBskyFeedPostgate.Record,
28 next: Partial<AppBskyFeedPostgate.Record>,
29) {
30 const detachedEmbeddingUris = Array.from(
31 new Set([
32 ...(prev.detachedEmbeddingUris || []),
33 ...(next.detachedEmbeddingUris || []),
34 ]),
35 )
36 const embeddingRules = [
37 ...(prev.embeddingRules || []),
38 ...(next.embeddingRules || []),
39 ].filter(
40 (rule, i, all) => all.findIndex(_rule => _rule.$type === rule.$type) === i,
41 )
42 return createPostgateRecord({
43 post: prev.post,
44 detachedEmbeddingUris,
45 embeddingRules,
46 })
47}
48
49export function createEmbedViewDetachedRecord({
50 uri,
51}: {
52 uri: string
53}): $Typed<AppBskyEmbedRecord.View> {
54 const record: $Typed<AppBskyEmbedRecord.ViewDetached> = {
55 $type: 'app.bsky.embed.record#viewDetached',
56 uri,
57 detached: true,
58 }
59 return {
60 $type: 'app.bsky.embed.record#view',
61 record,
62 }
63}
64
65export function createMaybeDetachedQuoteEmbed({
66 post,
67 quote,
68 quoteUri,
69 detached,
70}:
71 | {
72 post: AppBskyFeedDefs.PostView
73 quote: AppBskyFeedDefs.PostView
74 quoteUri: undefined
75 detached: false
76 }
77 | {
78 post: AppBskyFeedDefs.PostView
79 quote: undefined
80 quoteUri: string
81 detached: true
82 }): AppBskyEmbedRecord.View | AppBskyEmbedRecordWithMedia.View | undefined {
83 if (AppBskyEmbedRecord.isView(post.embed)) {
84 if (detached) {
85 return createEmbedViewDetachedRecord({uri: quoteUri})
86 } else {
87 return createEmbedRecordView({post: quote})
88 }
89 } else if (AppBskyEmbedRecordWithMedia.isView(post.embed)) {
90 if (detached) {
91 return {
92 ...post.embed,
93 record: createEmbedViewDetachedRecord({uri: quoteUri}),
94 }
95 } else {
96 return createEmbedRecordWithMediaView({post, quote})
97 }
98 }
99}
100
101export function createEmbedViewRecordFromPost(
102 post: AppBskyFeedDefs.PostView,
103): $Typed<AppBskyEmbedRecord.ViewRecord> {
104 return {
105 $type: 'app.bsky.embed.record#viewRecord',
106 uri: post.uri,
107 cid: post.cid,
108 author: post.author,
109 value: post.record,
110 labels: post.labels,
111 replyCount: post.replyCount,
112 repostCount: post.repostCount,
113 likeCount: post.likeCount,
114 quoteCount: post.quoteCount,
115 indexedAt: post.indexedAt,
116 embeds: post.embed ? [post.embed] : [],
117 }
118}
119
120export function createEmbedRecordView({
121 post,
122}: {
123 post: AppBskyFeedDefs.PostView
124}): AppBskyEmbedRecord.View {
125 return {
126 $type: 'app.bsky.embed.record#view',
127 record: createEmbedViewRecordFromPost(post),
128 }
129}
130
131export function createEmbedRecordWithMediaView({
132 post,
133 quote,
134}: {
135 post: AppBskyFeedDefs.PostView
136 quote: AppBskyFeedDefs.PostView
137}): AppBskyEmbedRecordWithMedia.View | undefined {
138 if (!AppBskyEmbedRecordWithMedia.isView(post.embed)) return
139 return {
140 ...(post.embed || {}),
141 record: {
142 record: createEmbedViewRecordFromPost(quote),
143 },
144 }
145}
146
147export function getMaybeDetachedQuoteEmbed({
148 viewerDid,
149 post,
150}: {
151 viewerDid: string
152 post: AppBskyFeedDefs.PostView
153}) {
154 if (AppBskyEmbedRecord.isView(post.embed)) {
155 // detached
156 if (AppBskyEmbedRecord.isViewDetached(post.embed.record)) {
157 const urip = new AtUri(post.embed.record.uri)
158 return {
159 embed: post.embed,
160 uri: urip.toString(),
161 isOwnedByViewer: urip.host === viewerDid,
162 isDetached: true,
163 }
164 }
165
166 // post
167 if (AppBskyEmbedRecord.isViewRecord(post.embed.record)) {
168 const urip = new AtUri(post.embed.record.uri)
169 return {
170 embed: post.embed,
171 uri: urip.toString(),
172 isOwnedByViewer: urip.host === viewerDid,
173 isDetached: false,
174 }
175 }
176 } else if (AppBskyEmbedRecordWithMedia.isView(post.embed)) {
177 // detached
178 if (AppBskyEmbedRecord.isViewDetached(post.embed.record.record)) {
179 const urip = new AtUri(post.embed.record.record.uri)
180 return {
181 embed: post.embed,
182 uri: urip.toString(),
183 isOwnedByViewer: urip.host === viewerDid,
184 isDetached: true,
185 }
186 }
187
188 // post
189 if (AppBskyEmbedRecord.isViewRecord(post.embed.record.record)) {
190 const urip = new AtUri(post.embed.record.record.uri)
191 return {
192 embed: post.embed,
193 uri: urip.toString(),
194 isOwnedByViewer: urip.host === viewerDid,
195 isDetached: false,
196 }
197 }
198 }
199}
200
201export const embeddingRules = {
202 disableRule: {$type: 'app.bsky.feed.postgate#disableRule'},
203}