import React from 'react' import {Keyboard, StyleProp, View, ViewStyle} from 'react-native' import { AppBskyFeedDefs, AppBskyFeedGetPostThread, AppBskyGraphDefs, AtUri, BskyAgent, } from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {useAnalytics} from '#/lib/analytics/analytics' import {createThreadgate} from '#/lib/api' import {until} from '#/lib/async/until' import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' import {usePalette} from '#/lib/hooks/usePalette' import {makeListLink, makeProfileLink} from '#/lib/routes/links' import {colors} from '#/lib/styles' import {logger} from '#/logger' import {isNative} from '#/platform/detection' import {useModalControls} from '#/state/modals' import {RQKEY_ROOT as POST_THREAD_RQKEY_ROOT} from '#/state/queries/post-thread' import { ThreadgateSetting, threadgateViewToSettings, } from '#/state/queries/threadgate' import {useAgent} from '#/state/session' import * as Toast from 'view/com/util/Toast' import {Button} from '#/components/Button' import {TextLink} from '../util/Link' import {Text} from '../util/text/Text' export function WhoCanReply({ post, isThreadAuthor, style, }: { post: AppBskyFeedDefs.PostView isThreadAuthor: boolean style?: StyleProp }) { const {track} = useAnalytics() const {_} = useLingui() const pal = usePalette('default') const agent = useAgent() const queryClient = useQueryClient() const {openModal} = useModalControls() const containerStyles = useColorSchemeStyle( { backgroundColor: pal.colors.unreadNotifBg, }, { backgroundColor: pal.colors.unreadNotifBg, }, ) const textStyles = useColorSchemeStyle( {color: colors.blue5}, {color: colors.blue1}, ) const hoverStyles = useColorSchemeStyle( { backgroundColor: colors.white, }, { backgroundColor: pal.colors.background, }, ) const settings = React.useMemo( () => threadgateViewToSettings(post.threadgate), [post], ) const isRootPost = !('reply' in post.record) const onPressEdit = () => { track('Post:EditThreadgateOpened') if (isNative && Keyboard.isVisible()) { Keyboard.dismiss() } openModal({ name: 'threadgate', settings, async onConfirm(newSettings: ThreadgateSetting[]) { try { if (newSettings.length) { await createThreadgate(agent, post.uri, newSettings) } else { await agent.api.com.atproto.repo.deleteRecord({ repo: agent.session!.did, collection: 'app.bsky.feed.threadgate', rkey: new AtUri(post.uri).rkey, }) } await whenAppViewReady(agent, post.uri, res => { const thread = res.data.thread if (AppBskyFeedDefs.isThreadViewPost(thread)) { const fetchedSettings = threadgateViewToSettings( thread.post.threadgate, ) return ( JSON.stringify(fetchedSettings) === JSON.stringify(newSettings) ) } return false }) Toast.show('Thread settings updated') queryClient.invalidateQueries({ queryKey: [POST_THREAD_RQKEY_ROOT], }) track('Post:ThreadgateEdited') } catch (err) { Toast.show( 'There was an issue. Please check your internet connection and try again.', ) logger.error('Failed to edit threadgate', {message: err}) } }, }) } if (!isRootPost) { return null } if (!settings.length && !isThreadAuthor) { return null } return ( {!settings.length ? ( Everybody can reply. ) : settings[0].type === 'nobody' ? ( Replies to this thread are disabled. ) : ( Only{' '} {settings.map((rule, i) => ( ))}{' '} can reply. )} {isThreadAuthor && ( )} ) } function Rule({ rule, post, lists, }: { rule: ThreadgateSetting post: AppBskyFeedDefs.PostView lists: AppBskyGraphDefs.ListViewBasic[] | undefined }) { const pal = usePalette('default') if (rule.type === 'mention') { return mentioned users } if (rule.type === 'following') { return ( users followed by{' '} ) } if (rule.type === 'list') { const list = lists?.find(l => l.uri === rule.list) if (list) { const listUrip = new AtUri(list.uri) return ( {' '} members ) } } } function Separator({i, length}: {i: number; length: number}) { if (length < 2 || i === length - 1) { return null } if (i === length - 2) { return ( <> {length > 2 ? ',' : ''} and{' '} ) } return <>, } async function whenAppViewReady( agent: BskyAgent, uri: string, fn: (res: AppBskyFeedGetPostThread.Response) => boolean, ) { await until( 5, // 5 tries 1e3, // 1s delay between tries fn, () => agent.app.bsky.feed.getPostThread({ uri, depth: 0, }), ) }