diff options
Diffstat (limited to 'src/view/com/composer')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 69 | ||||
-rw-r--r-- | src/view/com/composer/threadgate/ThreadgateBtn.tsx | 51 | ||||
-rw-r--r-- | src/view/com/composer/useExternalLinkFetch.ts | 18 |
3 files changed, 99 insertions, 39 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 0efbe70e6..eefd0affc 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -58,9 +58,11 @@ import { useLanguagePrefs, useLanguagePrefsApi, } from '#/state/preferences/languages' +import {createPostgateRecord} from '#/state/queries/postgate/util' import {useProfileQuery} from '#/state/queries/profile' import {Gif} from '#/state/queries/tenor' -import {ThreadgateSetting} from '#/state/queries/threadgate' +import {ThreadgateAllowUISetting} from '#/state/queries/threadgate' +import {threadgateViewToAllowUISetting} from '#/state/queries/threadgate/util' import {useUploadVideo} from '#/state/queries/video/video' import {useAgent, useSession} from '#/state/session' import {useComposerControls} from '#/state/shell/composer' @@ -81,9 +83,12 @@ import {State as VideoUploadState} from 'state/queries/video/video' import {ComposerOpts} from 'state/shell/composer' import {ComposerReplyTo} from 'view/com/composer/ComposerReplyTo' import {atoms as a, useTheme} from '#/alf' -import {Button, ButtonText} from '#/components/Button' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' +import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import * as Prompt from '#/components/Prompt' +import {Text as NewText} from '#/components/Typography' import {QuoteEmbed, QuoteX} from '../util/post-embeds/QuoteEmbed' import {Text} from '../util/text/Text' import * as Toast from '../util/Toast' @@ -182,10 +187,14 @@ export const ComposePost = observer(function ComposePost({ }) const [publishOnUpload, setPublishOnUpload] = useState(false) - const {extLink, setExtLink} = useExternalLinkFetch({setQuote}) + const {extLink, setExtLink} = useExternalLinkFetch({setQuote, setError}) const [extGif, setExtGif] = useState<Gif>() const [labels, setLabels] = useState<string[]>([]) - const [threadgate, setThreadgate] = useState<ThreadgateSetting[]>([]) + const [threadgateAllowUISettings, onChangeThreadgateAllowUISettings] = + useState<ThreadgateAllowUISetting[]>( + threadgateViewToAllowUISetting(undefined), + ) + const [postgate, setPostgate] = useState(createPostgateRecord({post: ''})) const gallery = useMemo( () => new GalleryModel(initImageUris), @@ -335,7 +344,8 @@ export const ComposePost = observer(function ComposePost({ quote, extLink, labels, - threadgate, + threadgate: threadgateAllowUISettings, + postgate, onStateChange: setProcessingState, langs: toPostLanguages(langPrefs.postLanguage), }) @@ -581,15 +591,40 @@ export const ComposePost = observer(function ComposePost({ </View> )} {error !== '' && ( - <View style={styles.errorLine}> - <View style={styles.errorIcon}> - <FontAwesomeIcon - icon="exclamation" - style={{color: colors.red4}} - size={10} - /> + <View style={[a.px_lg, a.pb_sm]}> + <View + style={[ + a.px_md, + a.py_sm, + a.rounded_sm, + a.flex_row, + a.gap_sm, + t.atoms.bg_contrast_25, + { + paddingRight: 48, + }, + ]}> + <CircleInfo fill={t.palette.negative_400} /> + <NewText style={[a.flex_1, a.leading_snug, {paddingTop: 1}]}> + {error} + </NewText> + <Button + label={_(msg`Dismiss error`)} + size="tiny" + color="secondary" + variant="ghost" + shape="round" + style={[ + a.absolute, + { + top: a.py_sm.paddingTop, + right: a.px_md.paddingRight, + }, + ]} + onPress={() => setError('')}> + <ButtonIcon icon={X} /> + </Button> </View> - <Text style={[s.red4, a.flex_1]}>{error}</Text> </View> )} </Animated.View> @@ -680,8 +715,12 @@ export const ComposePost = observer(function ComposePost({ {replyTo ? null : ( <ThreadgateBtn - threadgate={threadgate} - onChange={setThreadgate} + postgate={postgate} + onChangePostgate={setPostgate} + threadgateAllowUISettings={threadgateAllowUISettings} + onChangeThreadgateAllowUISettings={ + onChangeThreadgateAllowUISettings + } style={bottomBarAnimatedStyle} /> )} diff --git a/src/view/com/composer/threadgate/ThreadgateBtn.tsx b/src/view/com/composer/threadgate/ThreadgateBtn.tsx index 6cf2eea2c..666473afd 100644 --- a/src/view/com/composer/threadgate/ThreadgateBtn.tsx +++ b/src/view/com/composer/threadgate/ThreadgateBtn.tsx @@ -1,27 +1,33 @@ import React from 'react' import {Keyboard, StyleProp, ViewStyle} from 'react-native' import Animated, {AnimatedStyle} from 'react-native-reanimated' +import {AppBskyFeedPostgate} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {isNative} from '#/platform/detection' -import {ThreadgateSetting} from '#/state/queries/threadgate' +import {ThreadgateAllowUISetting} from '#/state/queries/threadgate' import {useAnalytics} from 'lib/analytics/analytics' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' -import {ThreadgateEditorDialog} from '#/components/dialogs/ThreadgateEditor' -import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign' +import {PostInteractionSettingsControlledDialog} from '#/components/dialogs/PostInteractionSettingsDialog' import {Earth_Stroke2_Corner0_Rounded as Earth} from '#/components/icons/Globe' import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group' export function ThreadgateBtn({ - threadgate, - onChange, + postgate, + onChangePostgate, + threadgateAllowUISettings, + onChangeThreadgateAllowUISettings, style, }: { - threadgate: ThreadgateSetting[] - onChange: (v: ThreadgateSetting[]) => void + postgate: AppBskyFeedPostgate.Record + onChangePostgate: (v: AppBskyFeedPostgate.Record) => void + + threadgateAllowUISettings: ThreadgateAllowUISetting[] + onChangeThreadgateAllowUISettings: (v: ThreadgateAllowUISetting[]) => void + style?: StyleProp<AnimatedStyle<ViewStyle>> }) { const {track} = useAnalytics() @@ -38,13 +44,15 @@ export function ThreadgateBtn({ control.open() } - const isEverybody = threadgate.length === 0 - const isNobody = !!threadgate.find(gate => gate.type === 'nobody') - const label = isEverybody - ? _(msg`Everybody can reply`) - : isNobody - ? _(msg`Nobody can reply`) - : _(msg`Some people can reply`) + const anyoneCanReply = + threadgateAllowUISettings.length === 1 && + threadgateAllowUISettings[0].type === 'everybody' + const anyoneCanQuote = + !postgate.embeddingRules || postgate.embeddingRules.length === 0 + const anyoneCanInteract = anyoneCanReply && anyoneCanQuote + const label = anyoneCanInteract + ? _(msg`Anybody can interact`) + : _(msg`Interaction limited`) return ( <> @@ -59,16 +67,19 @@ export function ThreadgateBtn({ accessibilityHint={_( msg`Opens a dialog to choose who can reply to this thread`, )}> - <ButtonIcon - icon={isEverybody ? Earth : isNobody ? CircleBanSign : Group} - /> + <ButtonIcon icon={anyoneCanInteract ? Earth : Group} /> <ButtonText>{label}</ButtonText> </Button> </Animated.View> - <ThreadgateEditorDialog + <PostInteractionSettingsControlledDialog control={control} - threadgate={threadgate} - onChange={onChange} + onSave={() => { + control.close() + }} + postgate={postgate} + onChangePostgate={onChangePostgate} + threadgateAllowUISettings={threadgateAllowUISettings} + onChangeThreadgateAllowUISettings={onChangeThreadgateAllowUISettings} /> </> ) diff --git a/src/view/com/composer/useExternalLinkFetch.ts b/src/view/com/composer/useExternalLinkFetch.ts index 2938ea25a..317514437 100644 --- a/src/view/com/composer/useExternalLinkFetch.ts +++ b/src/view/com/composer/useExternalLinkFetch.ts @@ -1,4 +1,6 @@ import {useEffect, useState} from 'react' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' import {logger} from '#/logger' import {useFetchDid} from '#/state/queries/handle' @@ -7,6 +9,7 @@ import {useAgent} from '#/state/session' import * as apilib from 'lib/api/index' import {POST_IMG_MAX} from 'lib/constants' import { + EmbeddingDisabledError, getFeedAsEmbed, getListAsEmbed, getPostAsQuote, @@ -28,9 +31,12 @@ import {ComposerOpts} from 'state/shell/composer' export function useExternalLinkFetch({ setQuote, + setError, }: { setQuote: (opts: ComposerOpts['quote']) => void + setError: (err: string) => void }) { + const {_} = useLingui() const [extLink, setExtLink] = useState<apilib.ExternalEmbedDraft | undefined>( undefined, ) @@ -57,9 +63,13 @@ export function useExternalLinkFetch({ setExtLink(undefined) }, err => { - logger.error('Failed to fetch post for quote embedding', { - message: err.toString(), - }) + if (err instanceof EmbeddingDisabledError) { + setError(_(msg`This post's author has disabled quote posts.`)) + } else { + logger.error('Failed to fetch post for quote embedding', { + message: err.toString(), + }) + } setExtLink(undefined) }, ) @@ -170,7 +180,7 @@ export function useExternalLinkFetch({ }) } return cleanup - }, [extLink, setQuote, getPost, fetchDid, agent]) + }, [_, extLink, setQuote, getPost, fetchDid, agent, setError]) return {extLink, setExtLink} } |