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