diff options
Diffstat (limited to 'src/view/com/threadgate')
-rw-r--r-- | src/view/com/threadgate/WhoCanReply.tsx | 234 |
1 files changed, 139 insertions, 95 deletions
diff --git a/src/view/com/threadgate/WhoCanReply.tsx b/src/view/com/threadgate/WhoCanReply.tsx index c1e36d481..3ffbaa7ae 100644 --- a/src/view/com/threadgate/WhoCanReply.tsx +++ b/src/view/com/threadgate/WhoCanReply.tsx @@ -1,128 +1,172 @@ import React from 'react' -import {StyleProp, View, ViewStyle} from 'react-native' -import { - AppBskyFeedDefs, - AppBskyFeedThreadgate, - AppBskyGraphDefs, - AtUri, -} from '@atproto/api' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {Trans} from '@lingui/macro' +import {Keyboard, StyleProp, View, ViewStyle} from 'react-native' +import {AppBskyFeedDefs, AppBskyGraphDefs, AtUri} 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 {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 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<ViewStyle> }) { + const {track} = useAnalytics() + const {_} = useLingui() const pal = usePalette('default') - const {isMobile} = useWebMediaQueries() + const agent = useAgent() + const queryClient = useQueryClient() + const {openModal} = useModalControls() const containerStyles = useColorSchemeStyle( { - borderColor: pal.colors.unreadNotifBorder, backgroundColor: pal.colors.unreadNotifBg, }, { - borderColor: pal.colors.unreadNotifBorder, backgroundColor: pal.colors.unreadNotifBg, }, ) - const iconStyles = useColorSchemeStyle( + const textStyles = useColorSchemeStyle( + {color: colors.blue5}, + {color: colors.blue1}, + ) + const hoverStyles = useColorSchemeStyle( { - backgroundColor: colors.blue3, + backgroundColor: colors.white, }, { - backgroundColor: colors.blue3, + backgroundColor: pal.colors.background, }, ) - const textStyles = useColorSchemeStyle( - {color: colors.gray7}, - {color: colors.blue1}, - ) - const record = React.useMemo( - () => - post.threadgate && - AppBskyFeedThreadgate.isRecord(post.threadgate.record) && - AppBskyFeedThreadgate.validateRecord(post.threadgate.record).success - ? post.threadgate.record - : null, + const settings = React.useMemo( + () => threadgateViewToSettings(post.threadgate), [post], ) - if (record) { - return ( - <View - style={[ - { - flexDirection: 'row', - alignItems: 'center', - gap: isMobile ? 8 : 10, - paddingHorizontal: isMobile ? 16 : 18, - paddingVertical: 12, - borderWidth: 1, - borderLeftWidth: isMobile ? 0 : 1, - borderRightWidth: isMobile ? 0 : 1, - }, - containerStyles, - style, - ]}> - <View - style={[ - { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - width: 32, - height: 32, - borderRadius: 19, - }, - iconStyles, - ]}> - <FontAwesomeIcon - icon={['far', 'comments']} - size={16} - color={'#fff'} - /> - </View> - <View style={{flex: 1}}> - <Text type="sm" style={[{flexWrap: 'wrap'}, textStyles]}> - {!record.allow?.length ? ( - <Trans>Replies to this thread are disabled</Trans> - ) : ( - <Trans> - Only{' '} - {record.allow.map((rule, i) => ( - <> - <Rule - key={`rule-${i}`} - rule={rule} - post={post} - lists={post.threadgate!.lists} - /> - <Separator - key={`sep-${i}`} - i={i} - length={record.allow!.length} - /> - </> - ))}{' '} - can reply. - </Trans> + 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, + }) + } + 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 ( + <View + style={[ + { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + paddingLeft: 18, + paddingRight: 14, + paddingVertical: 10, + borderTopWidth: 1, + }, + pal.border, + containerStyles, + style, + ]}> + <View style={{flex: 1, paddingVertical: 6}}> + <Text type="sm" style={[{flexWrap: 'wrap'}, textStyles]}> + {!settings.length ? ( + <Trans>Everybody can reply.</Trans> + ) : settings[0].type === 'nobody' ? ( + <Trans>Replies to this thread are disabled.</Trans> + ) : ( + <Trans> + Only{' '} + {settings.map((rule, i) => ( + <> + <Rule + key={`rule-${i}`} + rule={rule} + post={post} + lists={post.threadgate!.lists} + /> + <Separator key={`sep-${i}`} i={i} length={settings.length} /> + </> + ))}{' '} + can reply. + </Trans> + )} + </Text> + </View> + {isThreadAuthor && ( + <View> + <Button label={_(msg`Edit`)} onPress={onPressEdit}> + {({hovered}) => ( + <View + style={[ + hovered && hoverStyles, + {paddingVertical: 6, paddingHorizontal: 8, borderRadius: 8}, + ]}> + <Text type="sm" style={pal.link}> + <Trans>Edit</Trans> + </Text> + </View> )} - </Text> + </Button> </View> - </View> - ) - } - return null + )} + </View> + ) } function Rule({ @@ -130,15 +174,15 @@ function Rule({ post, lists, }: { - rule: any + rule: ThreadgateSetting post: AppBskyFeedDefs.PostView lists: AppBskyGraphDefs.ListViewBasic[] | undefined }) { const pal = usePalette('default') - if (AppBskyFeedThreadgate.isMentionRule(rule)) { + if (rule.type === 'mention') { return <Trans>mentioned users</Trans> } - if (AppBskyFeedThreadgate.isFollowingRule(rule)) { + if (rule.type === 'following') { return ( <Trans> users followed by{' '} @@ -151,7 +195,7 @@ function Rule({ </Trans> ) } - if (AppBskyFeedThreadgate.isListRule(rule)) { + if (rule.type === 'list') { const list = lists?.find(l => l.uri === rule.list) if (list) { const listUrip = new AtUri(list.uri) |