diff options
Diffstat (limited to 'src/components/dialogs')
-rw-r--r-- | src/components/dialogs/BirthDateSettings.tsx | 1 | ||||
-rw-r--r-- | src/components/dialogs/EmbedConsent.tsx | 1 | ||||
-rw-r--r-- | src/components/dialogs/GifSelect.ios.tsx | 255 | ||||
-rw-r--r-- | src/components/dialogs/GifSelect.shared.tsx | 53 | ||||
-rw-r--r-- | src/components/dialogs/GifSelect.tsx | 75 | ||||
-rw-r--r-- | src/components/dialogs/MutedWords.tsx | 528 | ||||
-rw-r--r-- | src/components/dialogs/PostInteractionSettingsDialog.tsx | 7 | ||||
-rw-r--r-- | src/components/dialogs/SwitchAccount.tsx | 1 | ||||
-rw-r--r-- | src/components/dialogs/nuxs/NeueTypography.tsx | 1 |
9 files changed, 357 insertions, 565 deletions
diff --git a/src/components/dialogs/BirthDateSettings.tsx b/src/components/dialogs/BirthDateSettings.tsx index 08608f9d8..81d0c6740 100644 --- a/src/components/dialogs/BirthDateSettings.tsx +++ b/src/components/dialogs/BirthDateSettings.tsx @@ -31,7 +31,6 @@ export function BirthDateSettingsDialog({ return ( <Dialog.Outer control={control}> <Dialog.Handle /> - <Dialog.ScrollableInner label={_(msg`My Birthday`)}> <View style={[a.gap_sm, a.pb_lg]}> <Text style={[a.text_2xl, a.font_bold]}> diff --git a/src/components/dialogs/EmbedConsent.tsx b/src/components/dialogs/EmbedConsent.tsx index 765b8adc7..824155d8b 100644 --- a/src/components/dialogs/EmbedConsent.tsx +++ b/src/components/dialogs/EmbedConsent.tsx @@ -50,7 +50,6 @@ export function EmbedConsentDialog({ return ( <Dialog.Outer control={control}> <Dialog.Handle /> - <Dialog.ScrollableInner label={_(msg`External Media`)} style={[gtMobile ? {width: 'auto', maxWidth: 400} : a.w_full]}> diff --git a/src/components/dialogs/GifSelect.ios.tsx b/src/components/dialogs/GifSelect.ios.tsx deleted file mode 100644 index 2f867e865..000000000 --- a/src/components/dialogs/GifSelect.ios.tsx +++ /dev/null @@ -1,255 +0,0 @@ -import React, { - useCallback, - useImperativeHandle, - useMemo, - useRef, - useState, -} from 'react' -import {Modal, ScrollView, TextInput, View} from 'react-native' -import {msg, Trans} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {cleanError} from '#/lib/strings/errors' -import { - Gif, - useFeaturedGifsQuery, - useGifSearchQuery, -} from '#/state/queries/tenor' -import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' -import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' -import {FlatList_INTERNAL} from '#/view/com/util/Views' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import * as TextField from '#/components/forms/TextField' -import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' -import {Button, ButtonText} from '../Button' -import {Handle} from '../Dialog' -import {useThrottledValue} from '../hooks/useThrottledValue' -import {ListFooter, ListMaybePlaceholder} from '../Lists' -import {GifPreview} from './GifSelect.shared' - -export function GifSelectDialog({ - controlRef, - onClose, - onSelectGif: onSelectGifProp, -}: { - controlRef: React.RefObject<{open: () => void}> - onClose: () => void - onSelectGif: (gif: Gif) => void -}) { - const t = useTheme() - const [open, setOpen] = useState(false) - - useImperativeHandle(controlRef, () => ({ - open: () => setOpen(true), - })) - - const close = useCallback(() => { - setOpen(false) - onClose() - }, [onClose]) - - const onSelectGif = useCallback( - (gif: Gif) => { - onSelectGifProp(gif) - close() - }, - [onSelectGifProp, close], - ) - - const renderErrorBoundary = useCallback( - (error: any) => <ModalError details={String(error)} close={close} />, - [close], - ) - - return ( - <Modal - visible={open} - animationType="slide" - presentationStyle="formSheet" - onRequestClose={close} - aria-modal - accessibilityViewIsModal> - <View style={[a.flex_1, t.atoms.bg]}> - <Handle /> - <ErrorBoundary renderError={renderErrorBoundary}> - <GifList onSelectGif={onSelectGif} close={close} /> - </ErrorBoundary> - </View> - </Modal> - ) -} - -function GifList({ - onSelectGif, -}: { - close: () => void - onSelectGif: (gif: Gif) => void -}) { - const {_} = useLingui() - const t = useTheme() - const {gtMobile} = useBreakpoints() - const textInputRef = useRef<TextInput>(null) - const listRef = useRef<FlatList_INTERNAL>(null) - const [undeferredSearch, setSearch] = useState('') - const search = useThrottledValue(undeferredSearch, 500) - - const isSearching = search.length > 0 - - const trendingQuery = useFeaturedGifsQuery() - const searchQuery = useGifSearchQuery(search) - - const { - data, - fetchNextPage, - isFetchingNextPage, - hasNextPage, - error, - isLoading, - isError, - refetch, - } = isSearching ? searchQuery : trendingQuery - - const flattenedData = useMemo(() => { - return data?.pages.flatMap(page => page.results) || [] - }, [data]) - - const renderItem = useCallback( - ({item}: {item: Gif}) => { - return <GifPreview gif={item} onSelectGif={onSelectGif} /> - }, - [onSelectGif], - ) - - const onEndReached = React.useCallback(() => { - if (isFetchingNextPage || !hasNextPage || error) return - fetchNextPage() - }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) - - const hasData = flattenedData.length > 0 - - const onGoBack = useCallback(() => { - if (isSearching) { - // clear the input and reset the state - textInputRef.current?.clear() - setSearch('') - } else { - close() - } - }, [isSearching]) - - const listHeader = useMemo(() => { - return ( - <View style={[a.relative, a.mb_lg, a.pt_4xl, a.flex_row, a.align_center]}> - {/* cover top corners */} - <View - style={[ - a.absolute, - a.inset_0, - { - borderBottomLeftRadius: 8, - borderBottomRightRadius: 8, - }, - t.atoms.bg, - ]} - /> - - <TextField.Root> - <TextField.Icon icon={Search} /> - <TextField.Input - label={_(msg`Search GIFs`)} - placeholder={_(msg`Search Tenor`)} - onChangeText={text => { - setSearch(text) - listRef.current?.scrollToOffset({offset: 0, animated: false}) - }} - returnKeyType="search" - clearButtonMode="while-editing" - inputRef={textInputRef} - maxLength={50} - /> - </TextField.Root> - </View> - ) - }, [t.atoms.bg, _]) - - return ( - <FlatList_INTERNAL - ref={listRef} - key={gtMobile ? '3 cols' : '2 cols'} - data={flattenedData} - renderItem={renderItem} - numColumns={gtMobile ? 3 : 2} - columnWrapperStyle={a.gap_sm} - contentContainerStyle={a.px_lg} - ListHeaderComponent={ - <> - {listHeader} - {!hasData && ( - <ListMaybePlaceholder - isLoading={isLoading} - isError={isError} - onRetry={refetch} - onGoBack={onGoBack} - emptyType="results" - sideBorders={false} - topBorder={false} - errorTitle={_(msg`Failed to load GIFs`)} - errorMessage={_(msg`There was an issue connecting to Tenor.`)} - emptyMessage={ - isSearching - ? _(msg`No search results found for "${search}".`) - : _( - msg`No featured GIFs found. There may be an issue with Tenor.`, - ) - } - /> - )} - </> - } - stickyHeaderIndices={[0]} - onEndReached={onEndReached} - onEndReachedThreshold={4} - keyExtractor={(item: Gif) => item.id} - keyboardDismissMode="on-drag" - ListFooterComponent={ - hasData ? ( - <ListFooter - isFetchingNextPage={isFetchingNextPage} - error={cleanError(error)} - onRetry={fetchNextPage} - style={{borderTopWidth: 0}} - /> - ) : null - } - /> - ) -} - -function ModalError({details, close}: {details?: string; close: () => void}) { - const {_} = useLingui() - - return ( - <ScrollView - style={[a.flex_1, a.gap_md]} - centerContent - contentContainerStyle={a.px_lg}> - <ErrorScreen - title={_(msg`Oh no!`)} - message={_( - msg`There was an unexpected issue in the application. Please let us know if this happened to you!`, - )} - details={details} - /> - <Button - label={_(msg`Close dialog`)} - onPress={close} - color="primary" - size="large" - variant="solid"> - <ButtonText> - <Trans>Close</Trans> - </ButtonText> - </Button> - </ScrollView> - ) -} diff --git a/src/components/dialogs/GifSelect.shared.tsx b/src/components/dialogs/GifSelect.shared.tsx deleted file mode 100644 index 90b2abaa8..000000000 --- a/src/components/dialogs/GifSelect.shared.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, {useCallback} from 'react' -import {Image} from 'expo-image' -import {msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {logEvent} from '#/lib/statsig/statsig' -import {Gif} from '#/state/queries/tenor' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' -import {Button} from '../Button' - -export function GifPreview({ - gif, - onSelectGif, -}: { - gif: Gif - onSelectGif: (gif: Gif) => void -}) { - const {gtTablet} = useBreakpoints() - const {_} = useLingui() - const t = useTheme() - - const onPress = useCallback(() => { - logEvent('composer:gif:select', {}) - onSelectGif(gif) - }, [onSelectGif, gif]) - - return ( - <Button - label={_(msg`Select GIF "${gif.title}"`)} - style={[a.flex_1, gtTablet ? {maxWidth: '33%'} : {maxWidth: '50%'}]} - onPress={onPress}> - {({pressed}) => ( - <Image - style={[ - a.flex_1, - a.mb_sm, - a.rounded_sm, - {aspectRatio: 1, opacity: pressed ? 0.8 : 1}, - t.atoms.bg_contrast_25, - ]} - source={{ - uri: gif.media_formats.tinygif.url, - }} - contentFit="cover" - accessibilityLabel={gif.title} - accessibilityHint="" - cachePolicy="none" - accessibilityIgnoresInvertColors - /> - )} - </Button> - ) -} diff --git a/src/components/dialogs/GifSelect.tsx b/src/components/dialogs/GifSelect.tsx index 1afc588da..6023b5808 100644 --- a/src/components/dialogs/GifSelect.tsx +++ b/src/components/dialogs/GifSelect.tsx @@ -6,10 +6,12 @@ import React, { useState, } from 'react' import {TextInput, View} from 'react-native' -import {BottomSheetFlatListMethods} from '@discord/bottom-sheet' +import {useWindowDimensions} from 'react-native' +import {Image} from 'expo-image' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {logEvent} from '#/lib/statsig/statsig' import {cleanError} from '#/lib/strings/errors' import {isWeb} from '#/platform/detection' import { @@ -19,7 +21,8 @@ import { } from '#/state/queries/tenor' import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' -import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {ListMethods} from '#/view/com/util/List' +import {atoms as a, ios, native, useBreakpoints, useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {useThrottledValue} from '#/components/hooks/useThrottledValue' @@ -27,16 +30,18 @@ import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arr import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2' import {Button, ButtonIcon, ButtonText} from '../Button' import {ListFooter, ListMaybePlaceholder} from '../Lists' -import {GifPreview} from './GifSelect.shared' +import {PortalComponent} from '../Portal' export function GifSelectDialog({ controlRef, onClose, onSelectGif: onSelectGifProp, + Portal, }: { controlRef: React.RefObject<{open: () => void}> onClose: () => void onSelectGif: (gif: Gif) => void + Portal?: PortalComponent }) { const control = Dialog.useDialogControl() @@ -59,8 +64,13 @@ export function GifSelectDialog({ return ( <Dialog.Outer control={control} - nativeOptions={{sheet: {snapPoints: ['100%']}}} - onClose={onClose}> + onClose={onClose} + Portal={Portal} + nativeOptions={{ + bottomInset: 0, + // use system corner radius on iOS + ...ios({cornerRadius: undefined}), + }}> <Dialog.Handle /> <ErrorBoundary renderError={renderErrorBoundary}> <GifList control={control} onSelectGif={onSelectGif} /> @@ -80,9 +90,10 @@ function GifList({ const t = useTheme() const {gtMobile} = useBreakpoints() const textInputRef = useRef<TextInput>(null) - const listRef = useRef<BottomSheetFlatListMethods>(null) + const listRef = useRef<ListMethods>(null) const [undeferredSearch, setSearch] = useState('') const search = useThrottledValue(undeferredSearch, 500) + const {height} = useWindowDimensions() const isSearching = search.length > 0 @@ -95,7 +106,7 @@ function GifList({ isFetchingNextPage, hasNextPage, error, - isLoading, + isPending, isError, refetch, } = isSearching ? searchQuery : trendingQuery @@ -132,6 +143,7 @@ function GifList({ return ( <View style={[ + native(a.pt_4xl), a.relative, a.mb_lg, a.flex_row, @@ -196,13 +208,14 @@ function GifList({ data={flattenedData} renderItem={renderItem} numColumns={gtMobile ? 3 : 2} - columnWrapperStyle={a.gap_sm} + columnWrapperStyle={[a.gap_sm]} + contentContainerStyle={[native([a.px_xl, {minHeight: height}])]} ListHeaderComponent={ <> {listHeader} {!hasData && ( <ListMaybePlaceholder - isLoading={isLoading} + isLoading={isPending} isError={isError} onRetry={refetch} onGoBack={onGoBack} @@ -273,3 +286,47 @@ function DialogError({details}: {details?: string}) { </Dialog.ScrollableInner> ) } + +export function GifPreview({ + gif, + onSelectGif, +}: { + gif: Gif + onSelectGif: (gif: Gif) => void +}) { + const {gtTablet} = useBreakpoints() + const {_} = useLingui() + const t = useTheme() + + const onPress = useCallback(() => { + logEvent('composer:gif:select', {}) + onSelectGif(gif) + }, [onSelectGif, gif]) + + return ( + <Button + label={_(msg`Select GIF "${gif.title}"`)} + style={[a.flex_1, gtTablet ? {maxWidth: '33%'} : {maxWidth: '50%'}]} + onPress={onPress}> + {({pressed}) => ( + <Image + style={[ + a.flex_1, + a.mb_sm, + a.rounded_sm, + {aspectRatio: 1, opacity: pressed ? 0.8 : 1}, + t.atoms.bg_contrast_25, + ]} + source={{ + uri: gif.media_formats.tinygif.url, + }} + contentFit="cover" + accessibilityLabel={gif.title} + accessibilityHint="" + cachePolicy="none" + accessibilityIgnoresInvertColors + /> + )} + </Button> + ) +} diff --git a/src/components/dialogs/MutedWords.tsx b/src/components/dialogs/MutedWords.tsx index 81a614103..c3aae8f0d 100644 --- a/src/components/dialogs/MutedWords.tsx +++ b/src/components/dialogs/MutedWords.tsx @@ -30,11 +30,14 @@ import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/P import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {Loader} from '#/components/Loader' +import {createPortalGroup} from '#/components/Portal' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' const ONE_DAY = 24 * 60 * 60 * 1000 +const Portal = createPortalGroup() + export function MutedWordsDialog() { const {mutedWordsDialogControl: control} = useGlobalDialogsControlContext() return ( @@ -105,307 +108,349 @@ function MutedWordsInner() { }, [_, field, targets, addMutedWord, setField, durations, excludeFollowing]) return ( - <Dialog.ScrollableInner label={_(msg`Manage your muted words and tags`)}> - <View> - <Text - style={[a.text_md, a.font_bold, a.pb_sm, t.atoms.text_contrast_high]}> - <Trans>Add muted words and tags</Trans> - </Text> - <Text style={[a.pb_lg, a.leading_snug, t.atoms.text_contrast_medium]}> - <Trans> - Posts can be muted based on their text, their tags, or both. We - recommend avoiding common words that appear in many posts, since it - can result in no posts being shown. - </Trans> - </Text> - - <View style={[a.pb_sm]}> - <Dialog.Input - autoCorrect={false} - autoCapitalize="none" - autoComplete="off" - label={_(msg`Enter a word or tag`)} - placeholder={_(msg`Enter a word or tag`)} - value={field} - onChangeText={value => { - if (error) { - setError('') - } - setField(value) - }} - onSubmitEditing={submit} - /> - </View> + <Portal.Provider> + <Dialog.ScrollableInner label={_(msg`Manage your muted words and tags`)}> + <View> + <Text + style={[ + a.text_md, + a.font_bold, + a.pb_sm, + t.atoms.text_contrast_high, + ]}> + <Trans>Add muted words and tags</Trans> + </Text> + <Text style={[a.pb_lg, a.leading_snug, t.atoms.text_contrast_medium]}> + <Trans> + Posts can be muted based on their text, their tags, or both. We + recommend avoiding common words that appear in many posts, since + it can result in no posts being shown. + </Trans> + </Text> - <View style={[a.pb_xl, a.gap_sm]}> - <Toggle.Group - label={_(msg`Select how long to mute this word for.`)} - type="radio" - values={durations} - onChange={setDurations}> - <Text - style={[ - a.pb_xs, - a.text_sm, - a.font_bold, - t.atoms.text_contrast_medium, - ]}> - <Trans>Duration:</Trans> - </Text> + <View style={[a.pb_sm]}> + <Dialog.Input + autoCorrect={false} + autoCapitalize="none" + autoComplete="off" + label={_(msg`Enter a word or tag`)} + placeholder={_(msg`Enter a word or tag`)} + value={field} + onChangeText={value => { + if (error) { + setError('') + } + setField(value) + }} + onSubmitEditing={submit} + /> + </View> + + <View style={[a.pb_xl, a.gap_sm]}> + <Toggle.Group + label={_(msg`Select how long to mute this word for.`)} + type="radio" + values={durations} + onChange={setDurations}> + <Text + style={[ + a.pb_xs, + a.text_sm, + a.font_bold, + t.atoms.text_contrast_medium, + ]}> + <Trans>Duration:</Trans> + </Text> - <View - style={[ - gtMobile && [a.flex_row, a.align_center, a.justify_start], - a.gap_sm, - ]}> <View style={[ - a.flex_1, - a.flex_row, - a.justify_start, - a.align_center, + gtMobile && [a.flex_row, a.align_center, a.justify_start], a.gap_sm, ]}> - <Toggle.Item - label={_(msg`Mute this word until you unmute it`)} - name="forever" - style={[a.flex_1]}> - <TargetToggle> - <View - style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> - <Toggle.Radio /> - <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>Forever</Trans> - </Toggle.LabelText> - </View> - </TargetToggle> - </Toggle.Item> + <View + style={[ + a.flex_1, + a.flex_row, + a.justify_start, + a.align_center, + a.gap_sm, + ]}> + <Toggle.Item + label={_(msg`Mute this word until you unmute it`)} + name="forever" + style={[a.flex_1]}> + <TargetToggle> + <View + style={[ + a.flex_1, + a.flex_row, + a.align_center, + a.gap_sm, + ]}> + <Toggle.Radio /> + <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> + <Trans>Forever</Trans> + </Toggle.LabelText> + </View> + </TargetToggle> + </Toggle.Item> + + <Toggle.Item + label={_(msg`Mute this word for 24 hours`)} + name="24_hours" + style={[a.flex_1]}> + <TargetToggle> + <View + style={[ + a.flex_1, + a.flex_row, + a.align_center, + a.gap_sm, + ]}> + <Toggle.Radio /> + <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> + <Trans>24 hours</Trans> + </Toggle.LabelText> + </View> + </TargetToggle> + </Toggle.Item> + </View> - <Toggle.Item - label={_(msg`Mute this word for 24 hours`)} - name="24_hours" - style={[a.flex_1]}> - <TargetToggle> - <View - style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> - <Toggle.Radio /> - <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>24 hours</Trans> - </Toggle.LabelText> - </View> - </TargetToggle> - </Toggle.Item> + <View + style={[ + a.flex_1, + a.flex_row, + a.justify_start, + a.align_center, + a.gap_sm, + ]}> + <Toggle.Item + label={_(msg`Mute this word for 7 days`)} + name="7_days" + style={[a.flex_1]}> + <TargetToggle> + <View + style={[ + a.flex_1, + a.flex_row, + a.align_center, + a.gap_sm, + ]}> + <Toggle.Radio /> + <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> + <Trans>7 days</Trans> + </Toggle.LabelText> + </View> + </TargetToggle> + </Toggle.Item> + + <Toggle.Item + label={_(msg`Mute this word for 30 days`)} + name="30_days" + style={[a.flex_1]}> + <TargetToggle> + <View + style={[ + a.flex_1, + a.flex_row, + a.align_center, + a.gap_sm, + ]}> + <Toggle.Radio /> + <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> + <Trans>30 days</Trans> + </Toggle.LabelText> + </View> + </TargetToggle> + </Toggle.Item> + </View> </View> + </Toggle.Group> - <View + <Toggle.Group + label={_( + msg`Select what content this mute word should apply to.`, + )} + type="radio" + values={targets} + onChange={setTargets}> + <Text style={[ - a.flex_1, - a.flex_row, - a.justify_start, - a.align_center, - a.gap_sm, + a.pb_xs, + a.text_sm, + a.font_bold, + t.atoms.text_contrast_medium, ]}> + <Trans>Mute in:</Trans> + </Text> + + <View style={[a.flex_row, a.align_center, a.gap_sm, a.flex_wrap]}> <Toggle.Item - label={_(msg`Mute this word for 7 days`)} - name="7_days" + label={_(msg`Mute this word in post text and tags`)} + name="content" style={[a.flex_1]}> <TargetToggle> <View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> <Toggle.Radio /> <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>7 days</Trans> + <Trans>Text & tags</Trans> </Toggle.LabelText> </View> + <PageText size="sm" /> </TargetToggle> </Toggle.Item> <Toggle.Item - label={_(msg`Mute this word for 30 days`)} - name="30_days" + label={_(msg`Mute this word in tags only`)} + name="tag" style={[a.flex_1]}> <TargetToggle> <View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> <Toggle.Radio /> <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>30 days</Trans> + <Trans>Tags only</Trans> </Toggle.LabelText> </View> + <Hashtag size="sm" /> </TargetToggle> </Toggle.Item> </View> - </View> - </Toggle.Group> + </Toggle.Group> - <Toggle.Group - label={_(msg`Select what content this mute word should apply to.`)} - type="radio" - values={targets} - onChange={setTargets}> - <Text - style={[ - a.pb_xs, - a.text_sm, - a.font_bold, - t.atoms.text_contrast_medium, - ]}> - <Trans>Mute in:</Trans> - </Text> - - <View style={[a.flex_row, a.align_center, a.gap_sm, a.flex_wrap]}> + <View> + <Text + style={[ + a.pb_xs, + a.text_sm, + a.font_bold, + t.atoms.text_contrast_medium, + ]}> + <Trans>Options:</Trans> + </Text> <Toggle.Item - label={_(msg`Mute this word in post text and tags`)} - name="content" - style={[a.flex_1]}> + label={_(msg`Do not apply this mute word to users you follow`)} + name="exclude_following" + style={[a.flex_row, a.justify_between]} + value={excludeFollowing} + onChange={setExcludeFollowing}> <TargetToggle> <View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> - <Toggle.Radio /> + <Toggle.Checkbox /> <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>Text & tags</Trans> + <Trans>Exclude users you follow</Trans> </Toggle.LabelText> </View> - <PageText size="sm" /> </TargetToggle> </Toggle.Item> + </View> - <Toggle.Item - label={_(msg`Mute this word in tags only`)} - name="tag" - style={[a.flex_1]}> - <TargetToggle> - <View - style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> - <Toggle.Radio /> - <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>Tags only</Trans> - </Toggle.LabelText> - </View> - <Hashtag size="sm" /> - </TargetToggle> - </Toggle.Item> + <View style={[a.pt_xs]}> + <Button + disabled={isPending || !field} + label={_(msg`Add mute word for configured settings`)} + size="large" + color="primary" + variant="solid" + style={[]} + onPress={submit}> + <ButtonText> + <Trans>Add</Trans> + </ButtonText> + <ButtonIcon icon={isPending ? Loader : Plus} position="right" /> + </Button> </View> - </Toggle.Group> - <View> + {error && ( + <View + style={[ + a.mb_lg, + a.flex_row, + a.rounded_sm, + a.p_md, + a.mb_xs, + t.atoms.bg_contrast_25, + { + backgroundColor: t.palette.negative_400, + }, + ]}> + <Text + style={[ + a.italic, + {color: t.palette.white}, + native({marginTop: 2}), + ]}> + {error} + </Text> + </View> + )} + </View> + + <Divider /> + + <View style={[a.pt_2xl]}> <Text style={[ - a.pb_xs, - a.text_sm, + a.text_md, a.font_bold, - t.atoms.text_contrast_medium, + a.pb_md, + t.atoms.text_contrast_high, ]}> - <Trans>Options:</Trans> + <Trans>Your muted words</Trans> </Text> - <Toggle.Item - label={_(msg`Do not apply this mute word to users you follow`)} - name="exclude_following" - style={[a.flex_row, a.justify_between]} - value={excludeFollowing} - onChange={setExcludeFollowing}> - <TargetToggle> - <View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> - <Toggle.Checkbox /> - <Toggle.LabelText style={[a.flex_1, a.leading_tight]}> - <Trans>Exclude users you follow</Trans> - </Toggle.LabelText> - </View> - </TargetToggle> - </Toggle.Item> - </View> - - <View style={[a.pt_xs]}> - <Button - disabled={isPending || !field} - label={_(msg`Add mute word for configured settings`)} - size="large" - color="primary" - variant="solid" - style={[]} - onPress={submit}> - <ButtonText> - <Trans>Add</Trans> - </ButtonText> - <ButtonIcon icon={isPending ? Loader : Plus} position="right" /> - </Button> - </View> - {error && ( - <View - style={[ - a.mb_lg, - a.flex_row, - a.rounded_sm, - a.p_md, - a.mb_xs, - t.atoms.bg_contrast_25, - { - backgroundColor: t.palette.negative_400, - }, - ]}> - <Text + {isPreferencesLoading ? ( + <Loader /> + ) : preferencesError || !preferences ? ( + <View style={[ - a.italic, - {color: t.palette.white}, - native({marginTop: 2}), + a.py_md, + a.px_lg, + a.rounded_md, + t.atoms.bg_contrast_25, ]}> - {error} - </Text> - </View> - )} - </View> - - <Divider /> - - <View style={[a.pt_2xl]}> - <Text - style={[ - a.text_md, - a.font_bold, - a.pb_md, - t.atoms.text_contrast_high, - ]}> - <Trans>Your muted words</Trans> - </Text> + <Text style={[a.italic, t.atoms.text_contrast_high]}> + <Trans> + We're sorry, but we weren't able to load your muted words at + this time. Please try again. + </Trans> + </Text> + </View> + ) : preferences.moderationPrefs.mutedWords.length ? ( + [...preferences.moderationPrefs.mutedWords] + .reverse() + .map((word, i) => ( + <MutedWordRow + key={word.value + i} + word={word} + style={[i % 2 === 0 && t.atoms.bg_contrast_25]} + /> + )) + ) : ( + <View + style={[ + a.py_md, + a.px_lg, + a.rounded_md, + t.atoms.bg_contrast_25, + ]}> + <Text style={[a.italic, t.atoms.text_contrast_high]}> + <Trans>You haven't muted any words or tags yet</Trans> + </Text> + </View> + )} + </View> - {isPreferencesLoading ? ( - <Loader /> - ) : preferencesError || !preferences ? ( - <View - style={[a.py_md, a.px_lg, a.rounded_md, t.atoms.bg_contrast_25]}> - <Text style={[a.italic, t.atoms.text_contrast_high]}> - <Trans> - We're sorry, but we weren't able to load your muted words at - this time. Please try again. - </Trans> - </Text> - </View> - ) : preferences.moderationPrefs.mutedWords.length ? ( - [...preferences.moderationPrefs.mutedWords] - .reverse() - .map((word, i) => ( - <MutedWordRow - key={word.value + i} - word={word} - style={[i % 2 === 0 && t.atoms.bg_contrast_25]} - /> - )) - ) : ( - <View - style={[a.py_md, a.px_lg, a.rounded_md, t.atoms.bg_contrast_25]}> - <Text style={[a.italic, t.atoms.text_contrast_high]}> - <Trans>You haven't muted any words or tags yet</Trans> - </Text> - </View> - )} + {isNative && <View style={{height: 20}} />} </View> - {isNative && <View style={{height: 20}} />} - </View> + <Dialog.Close /> + </Dialog.ScrollableInner> - <Dialog.Close /> - </Dialog.ScrollableInner> + <Portal.Outlet /> + </Portal.Provider> ) } @@ -437,6 +482,7 @@ function MutedWordRow({ onConfirm={remove} confirmButtonCta={_(msg`Remove`)} confirmButtonColor="negative" + Portal={Portal.Portal} /> <View diff --git a/src/components/dialogs/PostInteractionSettingsDialog.tsx b/src/components/dialogs/PostInteractionSettingsDialog.tsx index 47eefae6f..bddc49968 100644 --- a/src/components/dialogs/PostInteractionSettingsDialog.tsx +++ b/src/components/dialogs/PostInteractionSettingsDialog.tsx @@ -37,6 +37,7 @@ import * as Toggle from '#/components/forms/Toggle' import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {Loader} from '#/components/Loader' +import {PortalComponent} from '#/components/Portal' import {Text} from '#/components/Typography' export type PostInteractionSettingsFormProps = { @@ -54,13 +55,15 @@ export type PostInteractionSettingsFormProps = { export function PostInteractionSettingsControlledDialog({ control, + Portal, ...rest }: PostInteractionSettingsFormProps & { control: Dialog.DialogControlProps + Portal?: PortalComponent }) { const {_} = useLingui() return ( - <Dialog.Outer control={control}> + <Dialog.Outer control={control} Portal={Portal}> <Dialog.Handle /> <Dialog.ScrollableInner label={_(msg`Edit post interaction settings`)} @@ -231,7 +234,6 @@ export function PostInteractionSettingsForm({ }: PostInteractionSettingsFormProps) { const t = useTheme() const {_} = useLingui() - const control = Dialog.useDialogContext() const {data: lists} = useMyListsQuery('curate') const [quotesEnabled, setQuotesEnabled] = React.useState( !( @@ -437,7 +439,6 @@ export function PostInteractionSettingsForm({ <Button label={_(msg`Save`)} onPress={onSave} - onAccessibilityEscape={control.close} color="primary" size="large" variant="solid" diff --git a/src/components/dialogs/SwitchAccount.tsx b/src/components/dialogs/SwitchAccount.tsx index 0bd4bcb8c..ea870e2da 100644 --- a/src/components/dialogs/SwitchAccount.tsx +++ b/src/components/dialogs/SwitchAccount.tsx @@ -43,7 +43,6 @@ export function SwitchAccountDialog({ return ( <Dialog.Outer control={control}> <Dialog.Handle /> - <Dialog.ScrollableInner label={_(msg`Switch Account`)}> <View style={[a.gap_lg]}> <Text style={[a.text_2xl, a.font_bold]}> diff --git a/src/components/dialogs/nuxs/NeueTypography.tsx b/src/components/dialogs/nuxs/NeueTypography.tsx index f160c8774..f29dc356d 100644 --- a/src/components/dialogs/nuxs/NeueTypography.tsx +++ b/src/components/dialogs/nuxs/NeueTypography.tsx @@ -44,7 +44,6 @@ export function NeueTypography() { return ( <Dialog.Outer control={control} onClose={onClose}> <Dialog.Handle /> - <Dialog.ScrollableInner label={_(msg`Introducing new font settings`)}> <View style={[a.gap_xl]}> <View style={[a.gap_md]}> |