mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {AppBskyFeedDefs, AppBskyFeedThreadgate} from '@atproto/api'
2
3import {ThreadgateAllowUISetting} from '#/state/queries/threadgate/types'
4
5export function threadgateViewToAllowUISetting(
6 threadgateView: AppBskyFeedDefs.ThreadgateView | undefined,
7): ThreadgateAllowUISetting[] {
8 const threadgate =
9 threadgateView &&
10 AppBskyFeedThreadgate.isRecord(threadgateView.record) &&
11 AppBskyFeedThreadgate.validateRecord(threadgateView.record).success
12 ? threadgateView.record
13 : undefined
14 return threadgateRecordToAllowUISetting(threadgate)
15}
16
17/**
18 * Converts a full {@link AppBskyFeedThreadgate.Record} to a list of
19 * {@link ThreadgateAllowUISetting}, for use by app UI.
20 */
21export function threadgateRecordToAllowUISetting(
22 threadgate: AppBskyFeedThreadgate.Record | undefined,
23): ThreadgateAllowUISetting[] {
24 /*
25 * If `threadgate` doesn't exist (default), or if `threadgate.allow === undefined`, it means
26 * anyone can reply.
27 *
28 * If `threadgate.allow === []` it means no one can reply, and we translate to UI code
29 * here. This was a historical choice, and we have no lexicon representation
30 * for 'replies disabled' other than an empty array.
31 */
32 if (!threadgate || threadgate.allow === undefined) {
33 return [{type: 'everybody'}]
34 }
35 if (threadgate.allow.length === 0) {
36 return [{type: 'nobody'}]
37 }
38
39 const settings: ThreadgateAllowUISetting[] = threadgate.allow
40 .map(allow => {
41 let setting: ThreadgateAllowUISetting | undefined
42 if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') {
43 setting = {type: 'mention'}
44 } else if (allow.$type === 'app.bsky.feed.threadgate#followingRule') {
45 setting = {type: 'following'}
46 } else if (allow.$type === 'app.bsky.feed.threadgate#listRule') {
47 setting = {type: 'list', list: allow.list}
48 }
49 return setting
50 })
51 .filter(n => !!n)
52 return settings
53}
54
55/**
56 * Converts an array of {@link ThreadgateAllowUISetting} to the `allow` prop on
57 * {@link AppBskyFeedThreadgate.Record}.
58 *
59 * If the `allow` property on the record is undefined, we infer that to mean
60 * that everyone can reply. If it's an empty array, we infer that to mean that
61 * no one can reply.
62 */
63export function threadgateAllowUISettingToAllowRecordValue(
64 threadgate: ThreadgateAllowUISetting[],
65): AppBskyFeedThreadgate.Record['allow'] {
66 if (threadgate.find(v => v.type === 'everybody')) {
67 return undefined
68 }
69
70 let allow: (
71 | AppBskyFeedThreadgate.MentionRule
72 | AppBskyFeedThreadgate.FollowingRule
73 | AppBskyFeedThreadgate.ListRule
74 )[] = []
75
76 if (!threadgate.find(v => v.type === 'nobody')) {
77 for (const rule of threadgate) {
78 if (rule.type === 'mention') {
79 allow.push({$type: 'app.bsky.feed.threadgate#mentionRule'})
80 } else if (rule.type === 'following') {
81 allow.push({$type: 'app.bsky.feed.threadgate#followingRule'})
82 } else if (rule.type === 'list') {
83 allow.push({
84 $type: 'app.bsky.feed.threadgate#listRule',
85 list: rule.list,
86 })
87 }
88 }
89 }
90
91 return allow
92}
93
94/**
95 * Merges two {@link AppBskyFeedThreadgate.Record} objects, combining their
96 * `allow` and `hiddenReplies` arrays and de-deduplicating them.
97 *
98 * Note: `allow` can be undefined here, be sure you don't accidentally set it
99 * to an empty array. See other comments in this file.
100 */
101export function mergeThreadgateRecords(
102 prev: AppBskyFeedThreadgate.Record,
103 next: Partial<AppBskyFeedThreadgate.Record>,
104): AppBskyFeedThreadgate.Record {
105 // can be undefined if everyone can reply!
106 const allow: AppBskyFeedThreadgate.Record['allow'] | undefined =
107 prev.allow || next.allow
108 ? [...(prev.allow || []), ...(next.allow || [])].filter(
109 (v, i, a) => a.findIndex(t => t.$type === v.$type) === i,
110 )
111 : undefined
112 const hiddenReplies = Array.from(
113 new Set([...(prev.hiddenReplies || []), ...(next.hiddenReplies || [])]),
114 )
115
116 return createThreadgateRecord({
117 post: prev.post,
118 allow, // can be undefined!
119 hiddenReplies,
120 })
121}
122
123/**
124 * Create a new {@link AppBskyFeedThreadgate.Record} object with the given
125 * properties.
126 */
127export function createThreadgateRecord(
128 threadgate: Partial<AppBskyFeedThreadgate.Record>,
129): AppBskyFeedThreadgate.Record {
130 if (!threadgate.post) {
131 throw new Error('Cannot create a threadgate record without a post URI')
132 }
133
134 return {
135 $type: 'app.bsky.feed.threadgate',
136 post: threadgate.post,
137 createdAt: new Date().toISOString(),
138 allow: threadgate.allow, // can be undefined!
139 hiddenReplies: threadgate.hiddenReplies || [],
140 }
141}