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