[Sheets] [Pt. 8] Allow nesting in other sheets or native modals (#5567)

This commit is contained in:
Hailey
2024-10-02 21:26:58 -07:00
committed by GitHub
parent ecf7215a34
commit c49182009a
11 changed files with 97 additions and 63 deletions
+1 -1
View File
@@ -68,7 +68,7 @@
"@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-native-fontawesome": "^0.3.2", "@fortawesome/react-native-fontawesome": "^0.3.2",
"@haileyok/bluesky-bottom-sheet": "^0.1.1-alpha.8", "@haileyok/bluesky-bottom-sheet": "^0.1.1-alpha.9",
"@haileyok/bluesky-video": "0.1.10", "@haileyok/bluesky-video": "0.1.10",
"@lingui/react": "^4.5.0", "@lingui/react": "^4.5.0",
"@mattermost/react-native-paste-input": "^0.7.1", "@mattermost/react-native-paste-input": "^0.7.1",
+26 -5
View File
@@ -1,12 +1,13 @@
import React, {useImperativeHandle} from 'react' import React, {useImperativeHandle} from 'react'
import {StyleProp, TextInput, View, ViewStyle} from 'react-native' import {StyleProp, TextInput, View, ViewStyle} from 'react-native'
import {GestureHandlerRootView, ScrollView} from 'react-native-gesture-handler'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {BlueskyBottomSheetView} from '@haileyok/bluesky-bottom-sheet' import {BlueskyBottomSheetView} from '@haileyok/bluesky-bottom-sheet'
import {logger} from '#/logger' import {logger} from '#/logger'
import {isIOS} from '#/platform/detection'
import {useDialogStateControlContext} from '#/state/dialogs' import {useDialogStateControlContext} from '#/state/dialogs'
import {List, ListMethods, ListProps} from '#/view/com/util/List' import {List, ListMethods, ListProps} from '#/view/com/util/List'
import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, flatten, useTheme} from '#/alf' import {atoms as a, flatten, useTheme} from '#/alf'
import {Context} from '#/components/Dialog/context' import {Context} from '#/components/Dialog/context'
import { import {
@@ -29,6 +30,26 @@ export function Outer({
onClose, onClose,
nativeOptions, nativeOptions,
testID, testID,
}: React.PropsWithChildren<DialogOuterProps>) {
return (
<Portal>
<OuterWithoutPortal
control={control}
onClose={onClose}
nativeOptions={nativeOptions}
testID={testID}>
{children}
</OuterWithoutPortal>
</Portal>
)
}
export function OuterWithoutPortal({
children,
control,
onClose,
nativeOptions,
testID,
}: React.PropsWithChildren<DialogOuterProps>) { }: React.PropsWithChildren<DialogOuterProps>) {
const t = useTheme() const t = useTheme()
const ref = React.useRef<BlueskyBottomSheetView>(null) const ref = React.useRef<BlueskyBottomSheetView>(null)
@@ -84,8 +105,9 @@ export function Outer({
const context = React.useMemo(() => ({close, insideDialog: true}), [close]) const context = React.useMemo(() => ({close, insideDialog: true}), [close])
const Wrapper = isIOS ? View : GestureHandlerRootView
return ( return (
<Portal>
<Context.Provider value={context}> <Context.Provider value={context}>
<BlueskyBottomSheetView <BlueskyBottomSheetView
ref={ref} ref={ref}
@@ -98,12 +120,11 @@ export function Outer({
}} }}
cornerRadius={20} cornerRadius={20}
{...nativeOptions}> {...nativeOptions}>
<View testID={testID} style={[t.atoms.bg]}> <Wrapper testID={testID} style={[t.atoms.bg]}>
{children} {children}
</View> </Wrapper>
</BlueskyBottomSheetView> </BlueskyBottomSheetView>
</Context.Provider> </Context.Provider>
</Portal>
) )
} }
+1
View File
@@ -103,6 +103,7 @@ export function Outer({
const context = React.useMemo( const context = React.useMemo(
() => ({ () => ({
close, close,
insideDialog: true,
}), }),
[close], [close],
) )
+2 -3
View File
@@ -23,7 +23,6 @@ import {shouldClickOpenNewTab} from '#/platform/urls'
import {useModalControls} from '#/state/modals' import {useModalControls} from '#/state/modals'
import {useOpenLink} from '#/state/preferences/in-app-browser' import {useOpenLink} from '#/state/preferences/in-app-browser'
import {atoms as a, flatten, TextStyleProp, useTheme, web} from '#/alf' import {atoms as a, flatten, TextStyleProp, useTheme, web} from '#/alf'
import {BottomSheetButton} from '#/components/BottomSheetButton'
import {Button, ButtonProps} from '#/components/Button' import {Button, ButtonProps} from '#/components/Button'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Text, TextProps} from '#/components/Typography' import {Text, TextProps} from '#/components/Typography'
@@ -258,7 +257,7 @@ export function BottomSheetLink({
}) })
return ( return (
<BottomSheetButton <Button
{...rest} {...rest}
style={[a.justify_start, flatten(rest.style)]} style={[a.justify_start, flatten(rest.style)]}
role="link" role="link"
@@ -277,7 +276,7 @@ export function BottomSheetLink({
}, },
})}> })}>
{children} {children}
</BottomSheetButton> </Button>
) )
} }
+17 -6
View File
@@ -3,8 +3,9 @@ import {GestureResponderEvent, View} from 'react-native'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {isNative} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonColor, ButtonProps, ButtonText} from '#/components/Button' import {Button, ButtonColor, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
@@ -25,9 +26,11 @@ export function Outer({
children, children,
control, control,
testID, testID,
withoutPortal,
}: React.PropsWithChildren<{ }: React.PropsWithChildren<{
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
testID?: string testID?: string
withoutPortal?: boolean
}>) { }>) {
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const titleId = React.useId() const titleId = React.useId()
@@ -38,8 +41,11 @@ export function Outer({
[titleId, descriptionId], [titleId, descriptionId],
) )
const Wrapper =
withoutPortal && isNative ? Dialog.OuterWithoutPortal : Dialog.Outer
return ( return (
<Dialog.Outer control={control} testID={testID}> <Wrapper control={control} testID={testID}>
<Context.Provider value={context}> <Context.Provider value={context}>
<Dialog.ScrollableInner <Dialog.ScrollableInner
accessibilityLabelledBy={titleId} accessibilityLabelledBy={titleId}
@@ -50,7 +56,7 @@ export function Outer({
{children} {children}
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
</Context.Provider> </Context.Provider>
</Dialog.Outer> </Wrapper>
) )
} }
@@ -139,7 +145,7 @@ export function Action({
* Note: The dialog will close automatically when the action is pressed, you * Note: The dialog will close automatically when the action is pressed, you
* should NOT close the dialog as a side effect of this method. * should NOT close the dialog as a side effect of this method.
*/ */
onPress: ButtonProps['onPress'] onPress: (e: GestureResponderEvent) => void
color?: ButtonColor color?: ButtonColor
/** /**
* Optional i18n string. If undefined, it will default to "Confirm". * Optional i18n string. If undefined, it will default to "Confirm".
@@ -179,6 +185,7 @@ export function Basic({
onConfirm, onConfirm,
confirmButtonColor, confirmButtonColor,
showCancel = true, showCancel = true,
withoutPortal,
}: React.PropsWithChildren<{ }: React.PropsWithChildren<{
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
title: string title: string
@@ -192,12 +199,16 @@ export function Basic({
* Note: The dialog will close automatically when the action is pressed, you * Note: The dialog will close automatically when the action is pressed, you
* should NOT close the dialog as a side effect of this method. * should NOT close the dialog as a side effect of this method.
*/ */
onConfirm: ButtonProps['onPress'] onConfirm: (e: GestureResponderEvent) => void
confirmButtonColor?: ButtonColor confirmButtonColor?: ButtonColor
showCancel?: boolean showCancel?: boolean
withoutPortal?: boolean
}>) { }>) {
return ( return (
<Outer control={control} testID="confirmModal"> <Outer
control={control}
testID="confirmModal"
withoutPortal={withoutPortal}>
<TitleText>{title}</TitleText> <TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText> <DescriptionText>{description}</DescriptionText>
<Actions> <Actions>
@@ -6,8 +6,7 @@ import {useLingui} from '@lingui/react'
import {getLabelingServiceTitle} from '#/lib/moderation' import {getLabelingServiceTitle} from '#/lib/moderation'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {BottomSheetButton} from '#/components/BottomSheetButton' import {Button, useButtonContext} from '#/components/Button'
import {useButtonContext} from '#/components/Button'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import * as LabelingServiceCard from '#/components/LabelingServiceCard' import * as LabelingServiceCard from '#/components/LabelingServiceCard'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
@@ -39,12 +38,12 @@ export function SelectLabelerView({
<View style={[a.gap_sm]}> <View style={[a.gap_sm]}>
{props.labelers.map(labeler => { {props.labelers.map(labeler => {
return ( return (
<BottomSheetButton <Button
key={labeler.creator.did} key={labeler.creator.did}
label={_(msg`Send report to ${labeler.creator.displayName}`)} label={_(msg`Send report to ${labeler.creator.displayName}`)}
onPress={() => props.onSelectLabeler(labeler.creator.did)}> onPress={() => props.onSelectLabeler(labeler.creator.did)}>
<LabelerButton labeler={labeler} /> <LabelerButton labeler={labeler} />
</BottomSheetButton> </Button>
) )
})} })}
</View> </View>
+2 -1
View File
@@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import {Pressable, ScrollView, View} from 'react-native' import {Pressable, View} from 'react-native'
import {ScrollView} from 'react-native-gesture-handler'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -13,9 +13,8 @@ import {logger} from '#/logger'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {BottomSheetButton} from '#/components/BottomSheetButton'
import {BottomSheetInlineLinkText} from '#/components/BottomSheetLink' import {BottomSheetInlineLinkText} from '#/components/BottomSheetLink'
import {ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
@@ -141,7 +140,7 @@ function Label({
</View> </View>
{!isSelfLabel && ( {!isSelfLabel && (
<View> <View>
<BottomSheetButton <Button
variant="solid" variant="solid"
color="secondary" color="secondary"
size="small" size="small"
@@ -150,7 +149,7 @@ function Label({
<ButtonText> <ButtonText>
<Trans>Appeal</Trans> <Trans>Appeal</Trans>
</ButtonText> </ButtonText>
</BottomSheetButton> </Button>
</View> </View>
)} )}
</View> </View>
@@ -279,7 +278,7 @@ function AppealForm({
? [a.flex_row, a.justify_between] ? [a.flex_row, a.justify_between]
: [{flexDirection: 'column-reverse'}, a.gap_sm] : [{flexDirection: 'column-reverse'}, a.gap_sm]
}> }>
<BottomSheetButton <Button
testID="backBtn" testID="backBtn"
variant="solid" variant="solid"
color="secondary" color="secondary"
@@ -287,8 +286,8 @@ function AppealForm({
onPress={onPressBack} onPress={onPressBack}
label={_(msg`Back`)}> label={_(msg`Back`)}>
<ButtonText>{_(msg`Back`)}</ButtonText> <ButtonText>{_(msg`Back`)}</ButtonText>
</BottomSheetButton> </Button>
<BottomSheetButton <Button
testID="submitBtn" testID="submitBtn"
variant="solid" variant="solid"
color="primary" color="primary"
@@ -297,7 +296,7 @@ function AppealForm({
label={_(msg`Submit`)}> label={_(msg`Submit`)}>
<ButtonText>{_(msg`Submit`)}</ButtonText> <ButtonText>{_(msg`Submit`)}</ButtonText>
{isPending && <ButtonIcon icon={Loader} />} {isPending && <ButtonIcon icon={Loader} />}
</BottomSheetButton> </Button>
</View> </View>
</> </>
) )
@@ -10,6 +10,7 @@ import {listUriToHref} from '#/lib/strings/url-helpers'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {BottomSheetInlineLinkText} from '#/components/BottomSheetLink'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
@@ -140,23 +141,24 @@ function ModerationDetailsDialogInner({
{modcause?.type === 'label' && ( {modcause?.type === 'label' && (
<View style={[a.pt_lg]}> <View style={[a.pt_lg]}>
<Divider /> <Divider />
<Text style={[t.atoms.text, a.text_md, a.leading_snug, a.mt_lg]}>
{modcause.source.type === 'user' ? ( {modcause.source.type === 'user' ? (
<Text style={[t.atoms.text, a.text_md, a.leading_snug, a.mt_lg]}>
<Trans>This label was applied by the author.</Trans> <Trans>This label was applied by the author.</Trans>
</Text>
) : ( ) : (
<Trans> <>
This label was applied by{' '} <Text style={[t.atoms.text, a.text_md, a.leading_snug, a.mt_lg]}>
<InlineLinkText <Trans>This label was applied by </Trans>
</Text>
<BottomSheetInlineLinkText
label={desc.source || _(msg`an unknown labeler`)} label={desc.source || _(msg`an unknown labeler`)}
to={makeProfileLink({did: modcause.label.src, handle: ''})} to={makeProfileLink({did: modcause.label.src, handle: ''})}
onPress={() => control.close()} onPress={() => control.close()}
style={a.text_md}> style={a.text_md}>
{desc.source || _(msg`an unknown labeler`)} {desc.source || _(msg`an unknown labeler`)}
</InlineLinkText> </BottomSheetInlineLinkText>
. </>
</Trans>
)} )}
</Text>
</View> </View>
)} )}
+1
View File
@@ -861,6 +861,7 @@ export const ComposePost = ({
onConfirm={onClose} onConfirm={onClose}
confirmButtonCta={_(msg`Discard`)} confirmButtonCta={_(msg`Discard`)}
confirmButtonColor="negative" confirmButtonColor="negative"
withoutPortal={true}
/> />
</KeyboardAvoidingView> </KeyboardAvoidingView>
) )
+4 -4
View File
@@ -4120,10 +4120,10 @@
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
"@haileyok/bluesky-bottom-sheet@^0.1.1-alpha.8": "@haileyok/bluesky-bottom-sheet@^0.1.1-alpha.9":
version "0.1.1-alpha.8" version "0.1.1-alpha.9"
resolved "https://registry.yarnpkg.com/@haileyok/bluesky-bottom-sheet/-/bluesky-bottom-sheet-0.1.1-alpha.8.tgz#c5fd503cdd5556a686a1f5c0974b1527fceda900" resolved "https://registry.yarnpkg.com/@haileyok/bluesky-bottom-sheet/-/bluesky-bottom-sheet-0.1.1-alpha.9.tgz#f23a9245d4fed5e1825222249f2ad64ec99896e6"
integrity sha512-305MSNscniLZpxd80QO4OCuMXkm+U/v/YW/x0fVQtBilVTikOOPIkiYge+kCUdryFPD3LU/GdrsQZL+y8qAiMQ== integrity sha512-duGQ/l20Mr/lmjS7lUu0T0QgARA/V47PrmpL7LZJMoK4IhSBHT4HMSjjOaBcSJhQg2I5/uSX3pShFzCfdGttVQ==
"@haileyok/bluesky-video@0.1.10": "@haileyok/bluesky-video@0.1.10":
version "0.1.10" version "0.1.10"