From 058ddb0c53d969d7b3c4bf312158a1901bbc57ec Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 2 Oct 2024 22:03:52 -0700 Subject: [PATCH] [Sheets] [Pt. 14] Portaling behaviors for nested dialogs (#5588) Co-authored-by: Eric Bailey Co-authored-by: Samuel Newman --- src/components/Dialog/index.tsx | 65 +-- src/components/Dialog/types.ts | 2 + src/components/Portal.tsx | 2 + src/components/Prompt.tsx | 22 +- src/components/dialogs/MutedWords.tsx | 634 ++++++++++++++------------ 5 files changed, 376 insertions(+), 349 deletions(-) diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index effe781071..bdbfb24d01 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -16,7 +16,7 @@ import { DialogOuterProps, } from '#/components/Dialog/types' import {createInput} from '#/components/forms/TextField' -import {Portal} from '#/components/Portal' +import {Portal as DefaultPortal} from '#/components/Portal' import { BottomSheetSnapPoint, BottomSheetView, @@ -34,26 +34,7 @@ export function Outer({ onClose, nativeOptions, testID, -}: React.PropsWithChildren) { - return ( - - - {children} - - - ) -} - -export function OuterWithoutPortal({ - children, - control, - onClose, - nativeOptions, - testID, + Portal = DefaultPortal, }: React.PropsWithChildren) { const t = useTheme() const ref = React.useRef(null) @@ -119,26 +100,28 @@ export function OuterWithoutPortal({ const Wrapper = isIOS ? View : GestureHandlerRootView return ( - - { - setSnapPoint(e.nativeEvent.snapPoint) - }} - onStateChange={e => { - if (e.nativeEvent.state === 'closed') { - onCloseAnimationComplete() - } - }} - cornerRadius={20} - {...nativeOptions}> - - {children} - - - + + + { + setSnapPoint(e.nativeEvent.snapPoint) + }} + onStateChange={e => { + if (e.nativeEvent.state === 'closed') { + onCloseAnimationComplete() + } + }} + cornerRadius={20} + {...nativeOptions}> + + {children} + + + + ) } diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts index 2fd4d7c05a..cf698bf5b0 100644 --- a/src/components/Dialog/types.ts +++ b/src/components/Dialog/types.ts @@ -6,6 +6,7 @@ import type { } from 'react-native' import {ViewStyleProp} from '#/alf' +import {PortalComponent} from '#/components/Portal' import {BottomSheetViewProps} from '../../../modules/bottom-sheet' import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomSheet.types' @@ -58,6 +59,7 @@ export type DialogOuterProps = { nativeOptions?: Omit webOptions?: {} testID?: string + Portal?: PortalComponent } type DialogInnerPropsBase = React.PropsWithChildren & T diff --git a/src/components/Portal.tsx b/src/components/Portal.tsx index 03b397b2b8..7441df005c 100644 --- a/src/components/Portal.tsx +++ b/src/components/Portal.tsx @@ -12,6 +12,8 @@ type ComponentMap = { [id: string]: Component } +export type PortalComponent = ({children}: {children?: React.ReactNode}) => null + export function createPortalGroup() { const Context = React.createContext({ outlet: null, diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx index 3e877a6630..a38a55174f 100644 --- a/src/components/Prompt.tsx +++ b/src/components/Prompt.tsx @@ -3,10 +3,10 @@ import {GestureResponderEvent, View} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {isNative} from '#/platform/detection' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {Button, ButtonColor, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' +import {PortalComponent} from '#/components/Portal' import {Text} from '#/components/Typography' export { @@ -26,11 +26,11 @@ export function Outer({ children, control, testID, - withoutPortal, + Portal, }: React.PropsWithChildren<{ control: Dialog.DialogControlProps testID?: string - withoutPortal?: boolean + Portal?: PortalComponent }>) { const {gtMobile} = useBreakpoints() const titleId = React.useId() @@ -41,11 +41,8 @@ export function Outer({ [titleId, descriptionId], ) - const Wrapper = - withoutPortal && isNative ? Dialog.OuterWithoutPortal : Dialog.Outer - return ( - + - + ) } @@ -185,7 +182,7 @@ export function Basic({ onConfirm, confirmButtonColor, showCancel = true, - withoutPortal, + Portal, }: React.PropsWithChildren<{ control: Dialog.DialogOuterProps['control'] title: string @@ -202,13 +199,10 @@ export function Basic({ onConfirm: (e: GestureResponderEvent) => void confirmButtonColor?: ButtonColor showCancel?: boolean - withoutPortal?: boolean + Portal?: PortalComponent }>) { return ( - + {title} {description} diff --git a/src/components/dialogs/MutedWords.tsx b/src/components/dialogs/MutedWords.tsx index 55fd98d711..38571e6b8d 100644 --- a/src/components/dialogs/MutedWords.tsx +++ b/src/components/dialogs/MutedWords.tsx @@ -31,11 +31,14 @@ 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 {NormalizedRNGHPressable} from '#/components/NormalizedRNGHPressable' +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,314 +108,356 @@ function MutedWordsInner() { }, [_, field, targets, addMutedWord, setField, durations, excludeFollowing]) return ( - - - - Add muted words and tags - - - - 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. - - - - - { - if (error) { - setError('') - } - setField(value) - }} - onSubmitEditing={submit} - /> - - - - - - Duration: - - - - - - - - - - Forever - - - - - - - - - - - 24 hours - - - - - - - - - - - - - 7 days - - - - - - - - - - - 30 days - - - - - - - - - - - Mute in: - - - - - - - - - Text & tags - - - - - - - - - - - - Tags only - - - - - - - - - - - Options: - - - - - - - Exclude users you follow - - - - - - - - - - - {error && ( - - - {error} - - - )} - - - - - + + + - Your muted words + Add muted words and tags + + + + 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. + - {isPreferencesLoading ? ( - - ) : preferencesError || !preferences ? ( - - - - We're sorry, but we weren't able to load your muted words at - this time. Please try again. - + + { + if (error) { + setError('') + } + setField(value) + }} + onSubmitEditing={submit} + /> + + + + + + Duration: - - ) : preferences.moderationPrefs.mutedWords.length ? ( - [...preferences.moderationPrefs.mutedWords] - .reverse() - .map((word, i) => ( - - )) - ) : ( - - - You haven't muted any words or tags yet + + + + + + + + + Forever + + + + + + + + + + + 24 hours + + + + + + + + + + + + + 7 days + + + + + + + + + + + 30 days + + + + + + + + + + + Mute in: + + + + + + + + Text & tags + + + + + + + + + + + + Tags only + + + + + + + + + + + Options: + + + + + + + Exclude users you follow + + + + - )} + + + + + + {error && ( + + + {error} + + + )} + + + + + + + Your muted words + + + {isPreferencesLoading ? ( + + ) : preferencesError || !preferences ? ( + + + + We're sorry, but we weren't able to load your muted words at + this time. Please try again. + + + + ) : preferences.moderationPrefs.mutedWords.length ? ( + [...preferences.moderationPrefs.mutedWords] + .reverse() + .map((word, i) => ( + + )) + ) : ( + + + You haven't muted any words or tags yet + + + )} + + + {isNative && } - {isNative && } - + + - - + + ) } @@ -444,6 +489,7 @@ function MutedWordRow({ onConfirm={remove} confirmButtonCta={_(msg`Remove`)} confirmButtonColor="negative" + Portal={Portal.Portal} />