Allow nested sheets without boilerplate (#5660)
Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
@@ -107,9 +107,9 @@ 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 {createPortalGroup} from '#/components/Portal'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {Text as NewText} from '#/components/Typography'
|
||||
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
|
||||
import {
|
||||
composerReducer,
|
||||
createComposerState,
|
||||
@@ -117,8 +117,6 @@ import {
|
||||
} from './state/composer'
|
||||
import {NO_VIDEO, NoVideoState, processVideo, VideoState} from './state/video'
|
||||
|
||||
const Portal = createPortalGroup()
|
||||
|
||||
type CancelRef = {
|
||||
onPressCancel: () => void
|
||||
}
|
||||
@@ -522,7 +520,7 @@ export const ComposePost = ({
|
||||
const keyboardVerticalOffset = useKeyboardVerticalOffset()
|
||||
|
||||
return (
|
||||
<Portal.Provider>
|
||||
<BottomSheetPortalProvider>
|
||||
<KeyboardAvoidingView
|
||||
testID="composePostView"
|
||||
behavior={isIOS ? 'padding' : 'height'}
|
||||
@@ -666,11 +664,7 @@ export const ComposePost = ({
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Gallery
|
||||
images={images}
|
||||
dispatch={dispatch}
|
||||
Portal={Portal.Portal}
|
||||
/>
|
||||
<Gallery images={images} dispatch={dispatch} />
|
||||
|
||||
{extGif && (
|
||||
<View style={a.relative} key={extGif.url}>
|
||||
@@ -684,7 +678,6 @@ export const ComposePost = ({
|
||||
gif={extGif}
|
||||
altText={extGifAlt ?? ''}
|
||||
onSubmit={handleChangeGifAltText}
|
||||
Portal={Portal.Portal}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
@@ -744,7 +737,6 @@ export const ComposePost = ({
|
||||
},
|
||||
})
|
||||
}}
|
||||
Portal={Portal.Portal}
|
||||
/>
|
||||
</Animated.View>
|
||||
)}
|
||||
@@ -782,7 +774,6 @@ export const ComposePost = ({
|
||||
})
|
||||
}}
|
||||
style={bottomBarAnimatedStyle}
|
||||
Portal={Portal.Portal}
|
||||
/>
|
||||
)}
|
||||
<View
|
||||
@@ -819,7 +810,6 @@ export const ComposePost = ({
|
||||
onClose={focusTextInput}
|
||||
onSelectGif={onSelectGif}
|
||||
disabled={hasMedia}
|
||||
Portal={Portal.Portal}
|
||||
/>
|
||||
{!isMobile ? (
|
||||
<Button
|
||||
@@ -849,11 +839,9 @@ export const ComposePost = ({
|
||||
onConfirm={onClose}
|
||||
confirmButtonCta={_(msg`Discard`)}
|
||||
confirmButtonColor="negative"
|
||||
Portal={Portal.Portal}
|
||||
/>
|
||||
</KeyboardAvoidingView>
|
||||
<Portal.Outlet />
|
||||
</Portal.Provider>
|
||||
</BottomSheetPortalProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import * as TextField from '#/components/forms/TextField'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import {PortalComponent} from '#/components/Portal'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {GifEmbed} from '../util/post-embeds/GifEmbed'
|
||||
import {AltTextReminder} from './photos/Gallery'
|
||||
@@ -30,12 +29,10 @@ export function GifAltTextDialog({
|
||||
gif,
|
||||
altText,
|
||||
onSubmit,
|
||||
Portal,
|
||||
}: {
|
||||
gif: Gif
|
||||
altText: string
|
||||
onSubmit: (alt: string) => void
|
||||
Portal: PortalComponent
|
||||
}) {
|
||||
const {data} = useResolveGifQuery(gif)
|
||||
const vendorAltText = parseAltFromGIFDescription(data?.description ?? '').alt
|
||||
@@ -50,7 +47,6 @@ export function GifAltTextDialog({
|
||||
thumb={data.thumb?.source.path}
|
||||
params={params}
|
||||
onSubmit={onSubmit}
|
||||
Portal={Portal}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -61,14 +57,12 @@ export function GifAltTextDialogLoaded({
|
||||
onSubmit,
|
||||
params,
|
||||
thumb,
|
||||
Portal,
|
||||
}: {
|
||||
vendorAltText: string
|
||||
altText: string
|
||||
onSubmit: (alt: string) => void
|
||||
params: EmbedPlayerParams
|
||||
thumb: string | undefined
|
||||
Portal: PortalComponent
|
||||
}) {
|
||||
const control = Dialog.useDialogControl()
|
||||
const {_} = useLingui()
|
||||
@@ -113,8 +107,7 @@ export function GifAltTextDialogLoaded({
|
||||
control={control}
|
||||
onClose={() => {
|
||||
onSubmit(altTextDraft)
|
||||
}}
|
||||
Portal={Portal}>
|
||||
}}>
|
||||
<Dialog.Handle />
|
||||
<AltTextInner
|
||||
vendorAltText={vendorAltText}
|
||||
|
||||
@@ -21,7 +21,6 @@ import {ComposerImage, cropImage} from '#/state/gallery'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {PortalComponent} from '#/components/Portal'
|
||||
import {ComposerAction} from '../state/composer'
|
||||
import {EditImageDialog} from './EditImageDialog'
|
||||
import {ImageAltTextDialog} from './ImageAltTextDialog'
|
||||
@@ -31,7 +30,6 @@ const IMAGE_GAP = 8
|
||||
interface GalleryProps {
|
||||
images: ComposerImage[]
|
||||
dispatch: (action: ComposerAction) => void
|
||||
Portal: PortalComponent
|
||||
}
|
||||
|
||||
export let Gallery = (props: GalleryProps): React.ReactNode => {
|
||||
@@ -59,12 +57,7 @@ interface GalleryInnerProps extends GalleryProps {
|
||||
containerInfo: Dimensions
|
||||
}
|
||||
|
||||
const GalleryInner = ({
|
||||
images,
|
||||
containerInfo,
|
||||
dispatch,
|
||||
Portal,
|
||||
}: GalleryInnerProps) => {
|
||||
const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => {
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
|
||||
const {altTextControlStyle, imageControlsStyle, imageStyle} =
|
||||
@@ -118,7 +111,6 @@ const GalleryInner = ({
|
||||
onRemove={() => {
|
||||
dispatch({type: 'embed_remove_image', image})
|
||||
}}
|
||||
Portal={Portal}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
@@ -135,7 +127,6 @@ type GalleryItemProps = {
|
||||
imageStyle?: ViewStyle
|
||||
onChange: (next: ComposerImage) => void
|
||||
onRemove: () => void
|
||||
Portal: PortalComponent
|
||||
}
|
||||
|
||||
const GalleryItem = ({
|
||||
@@ -145,7 +136,6 @@ const GalleryItem = ({
|
||||
imageStyle,
|
||||
onChange,
|
||||
onRemove,
|
||||
Portal,
|
||||
}: GalleryItemProps): React.ReactNode => {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
@@ -240,7 +230,6 @@ const GalleryItem = ({
|
||||
control={altTextControl}
|
||||
image={image}
|
||||
onChange={onChange}
|
||||
Portal={Portal}
|
||||
/>
|
||||
|
||||
<EditImageDialog
|
||||
|
||||
@@ -15,21 +15,18 @@ import * as Dialog from '#/components/Dialog'
|
||||
import {DialogControlProps} from '#/components/Dialog'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||
import {PortalComponent} from '#/components/Portal'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
type Props = {
|
||||
control: Dialog.DialogOuterProps['control']
|
||||
image: ComposerImage
|
||||
onChange: (next: ComposerImage) => void
|
||||
Portal: PortalComponent
|
||||
}
|
||||
|
||||
export const ImageAltTextDialog = ({
|
||||
control,
|
||||
image,
|
||||
onChange,
|
||||
Portal,
|
||||
}: Props): React.ReactNode => {
|
||||
const [altText, setAltText] = React.useState(image.alt)
|
||||
|
||||
@@ -41,8 +38,7 @@ export const ImageAltTextDialog = ({
|
||||
...image,
|
||||
alt: enforceLen(altText, MAX_ALT_TEXT, true),
|
||||
})
|
||||
}}
|
||||
Portal={Portal}>
|
||||
}}>
|
||||
<Dialog.Handle />
|
||||
<ImageAltTextInner
|
||||
control={control}
|
||||
|
||||
@@ -9,16 +9,14 @@ import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {GifSelectDialog} from '#/components/dialogs/GifSelect'
|
||||
import {GifSquare_Stroke2_Corner0_Rounded as GifIcon} from '#/components/icons/Gif'
|
||||
import {PortalComponent} from '#/components/Portal'
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
onSelectGif: (gif: Gif) => void
|
||||
disabled?: boolean
|
||||
Portal?: PortalComponent
|
||||
}
|
||||
|
||||
export function SelectGifBtn({onClose, onSelectGif, disabled, Portal}: Props) {
|
||||
export function SelectGifBtn({onClose, onSelectGif, disabled}: Props) {
|
||||
const {_} = useLingui()
|
||||
const ref = useRef<{open: () => void}>(null)
|
||||
const t = useTheme()
|
||||
@@ -48,7 +46,6 @@ export function SelectGifBtn({onClose, onSelectGif, disabled, Portal}: Props) {
|
||||
controlRef={ref}
|
||||
onClose={onClose}
|
||||
onSelectGif={onSelectGif}
|
||||
Portal={Portal}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -13,7 +13,6 @@ import * as Dialog from '#/components/Dialog'
|
||||
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'
|
||||
import {PortalComponent} from '#/components/Portal'
|
||||
|
||||
export function ThreadgateBtn({
|
||||
postgate,
|
||||
@@ -21,7 +20,6 @@ export function ThreadgateBtn({
|
||||
threadgateAllowUISettings,
|
||||
onChangeThreadgateAllowUISettings,
|
||||
style,
|
||||
Portal,
|
||||
}: {
|
||||
postgate: AppBskyFeedPostgate.Record
|
||||
onChangePostgate: (v: AppBskyFeedPostgate.Record) => void
|
||||
@@ -30,8 +28,6 @@ export function ThreadgateBtn({
|
||||
onChangeThreadgateAllowUISettings: (v: ThreadgateAllowUISetting[]) => void
|
||||
|
||||
style?: StyleProp<AnimatedStyle<ViewStyle>>
|
||||
|
||||
Portal: PortalComponent
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
@@ -81,7 +77,6 @@ export function ThreadgateBtn({
|
||||
onChangePostgate={onChangePostgate}
|
||||
threadgateAllowUISettings={threadgateAllowUISettings}
|
||||
onChangeThreadgateAllowUISettings={onChangeThreadgateAllowUISettings}
|
||||
Portal={Portal}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -17,7 +17,6 @@ import {CC_Stroke2_Corner0_Rounded as CCIcon} from '#/components/icons/CC'
|
||||
import {PageText_Stroke2_Corner0_Rounded as PageTextIcon} from '#/components/icons/PageText'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
||||
import {PortalComponent} from '#/components/Portal'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {SubtitleFilePicker} from './SubtitleFilePicker'
|
||||
|
||||
@@ -30,7 +29,6 @@ interface Props {
|
||||
captions: CaptionsTrack[]
|
||||
saveAltText: (altText: string) => void
|
||||
setCaptions: (updater: (prev: CaptionsTrack[]) => CaptionsTrack[]) => void
|
||||
Portal: PortalComponent
|
||||
}
|
||||
|
||||
export function SubtitleDialogBtn(props: Props) {
|
||||
@@ -58,7 +56,7 @@ export function SubtitleDialogBtn(props: Props) {
|
||||
{isWeb ? <Trans>Captions & alt text</Trans> : <Trans>Alt text</Trans>}
|
||||
</ButtonText>
|
||||
</Button>
|
||||
<Dialog.Outer control={control} Portal={props.Portal}>
|
||||
<Dialog.Outer control={control}>
|
||||
<Dialog.Handle />
|
||||
<SubtitleDialogInner {...props} />
|
||||
</Dialog.Outer>
|
||||
|
||||
@@ -8,13 +8,10 @@ import {atoms as a} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {createPortalGroup} from '#/components/Portal'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {H3, P, Text} from '#/components/Typography'
|
||||
import {PlatformInfo} from '../../../../modules/expo-bluesky-swiss-army'
|
||||
|
||||
const Portal = createPortalGroup()
|
||||
|
||||
export function Dialogs() {
|
||||
const scrollable = Dialog.useDialogControl()
|
||||
const basic = Dialog.useDialogControl()
|
||||
@@ -201,41 +198,34 @@ export function Dialogs() {
|
||||
</Dialog.Outer>
|
||||
|
||||
<Dialog.Outer control={withMenu}>
|
||||
<Portal.Provider>
|
||||
<Dialog.Inner label="test">
|
||||
<H3 nativeID="dialog-title">Dialog with Menu</H3>
|
||||
<Menu.Root>
|
||||
<Menu.Trigger label="Open menu">
|
||||
{({props}) => (
|
||||
<Button
|
||||
style={a.mt_2xl}
|
||||
label="Open menu"
|
||||
color="primary"
|
||||
variant="solid"
|
||||
size="large"
|
||||
{...props}>
|
||||
<ButtonText>Open Menu</ButtonText>
|
||||
</Button>
|
||||
)}
|
||||
</Menu.Trigger>
|
||||
<Menu.Outer Portal={Portal.Portal}>
|
||||
<Menu.Group>
|
||||
<Menu.Item
|
||||
label="Item 1"
|
||||
onPress={() => console.log('item 1')}>
|
||||
<Menu.ItemText>Item 1</Menu.ItemText>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
label="Item 2"
|
||||
onPress={() => console.log('item 2')}>
|
||||
<Menu.ItemText>Item 2</Menu.ItemText>
|
||||
</Menu.Item>
|
||||
</Menu.Group>
|
||||
</Menu.Outer>
|
||||
</Menu.Root>
|
||||
</Dialog.Inner>
|
||||
<Portal.Outlet />
|
||||
</Portal.Provider>
|
||||
<Dialog.Inner label="test">
|
||||
<H3 nativeID="dialog-title">Dialog with Menu</H3>
|
||||
<Menu.Root>
|
||||
<Menu.Trigger label="Open menu">
|
||||
{({props}) => (
|
||||
<Button
|
||||
style={a.mt_2xl}
|
||||
label="Open menu"
|
||||
color="primary"
|
||||
variant="solid"
|
||||
size="large"
|
||||
{...props}>
|
||||
<ButtonText>Open Menu</ButtonText>
|
||||
</Button>
|
||||
)}
|
||||
</Menu.Trigger>
|
||||
<Menu.Outer>
|
||||
<Menu.Group>
|
||||
<Menu.Item label="Item 1" onPress={() => console.log('item 1')}>
|
||||
<Menu.ItemText>Item 1</Menu.ItemText>
|
||||
</Menu.Item>
|
||||
<Menu.Item label="Item 2" onPress={() => console.log('item 2')}>
|
||||
<Menu.ItemText>Item 2</Menu.ItemText>
|
||||
</Menu.Item>
|
||||
</Menu.Group>
|
||||
</Menu.Outer>
|
||||
</Menu.Root>
|
||||
</Dialog.Inner>
|
||||
</Dialog.Outer>
|
||||
|
||||
<Dialog.Outer control={scrollable}>
|
||||
|
||||
@@ -34,6 +34,7 @@ import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
||||
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
|
||||
import {SigninDialog} from '#/components/dialogs/Signin'
|
||||
import {Outlet as PortalOutlet} from '#/components/Portal'
|
||||
import {BottomSheetOutlet} from '../../../modules/bottom-sheet'
|
||||
import {updateActiveViewAsync} from '../../../modules/expo-bluesky-swiss-army/src/VisibilityView'
|
||||
import {RoutesContainer, TabsNavigator} from '../../Navigation'
|
||||
import {Composer} from './Composer'
|
||||
@@ -119,6 +120,7 @@ function ShellInner() {
|
||||
<SigninDialog />
|
||||
<Lightbox />
|
||||
<PortalOutlet />
|
||||
<BottomSheetOutlet />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user