diff options
Diffstat (limited to 'src/view/com/composer')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 570 | ||||
-rw-r--r-- | src/view/com/composer/GifAltText.tsx | 9 | ||||
-rw-r--r-- | src/view/com/composer/photos/EditImageDialog.web.tsx | 1 | ||||
-rw-r--r-- | src/view/com/composer/photos/Gallery.tsx | 13 | ||||
-rw-r--r-- | src/view/com/composer/photos/ImageAltTextDialog.tsx | 9 | ||||
-rw-r--r-- | src/view/com/composer/photos/SelectGifBtn.tsx | 5 | ||||
-rw-r--r-- | src/view/com/composer/photos/SelectPhotoBtn.tsx | 14 | ||||
-rw-r--r-- | src/view/com/composer/threadgate/ThreadgateBtn.tsx | 5 | ||||
-rw-r--r-- | src/view/com/composer/videos/SubtitleDialog.tsx | 8 |
9 files changed, 342 insertions, 292 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 227964907..e03c64a42 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -114,11 +114,14 @@ 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 {composerReducer, createComposerState} from './state/composer' import {NO_VIDEO, NoVideoState, processVideo, VideoState} from './state/video' +const Portal = createPortalGroup() + const MAX_IMAGES = 4 type CancelRef = { @@ -613,296 +616,313 @@ export const ComposePost = ({ const keyboardVerticalOffset = useKeyboardVerticalOffset() return ( - <KeyboardAvoidingView - testID="composePostView" - behavior={isIOS ? 'padding' : 'height'} - keyboardVerticalOffset={keyboardVerticalOffset} - style={a.flex_1}> - <View style={[a.flex_1, viewStyles]} aria-modal accessibilityViewIsModal> - <Animated.View - style={topBarAnimatedStyle} - layout={native(LinearTransition)}> - <View style={styles.topbarInner}> - <Button - label={_(msg`Cancel`)} - variant="ghost" - color="primary" - shape="default" - size="small" - style={[ - a.rounded_full, - a.py_sm, - {paddingLeft: 7, paddingRight: 7}, - ]} - onPress={onPressCancel} - accessibilityHint={_( - msg`Closes post composer and discards post draft`, - )}> - <ButtonText style={[a.text_md]}> - <Trans>Cancel</Trans> - </ButtonText> - </Button> - <View style={a.flex_1} /> - {isProcessing ? ( - <> - <Text style={pal.textLight}>{processingState}</Text> - <View style={styles.postBtn}> - <ActivityIndicator /> - </View> - </> - ) : ( - <View style={[styles.postBtnWrapper]}> - <LabelsBtn - labels={labels} - onChange={setLabels} - hasMedia={hasMedia} - /> - {canPost ? ( - <Button - testID="composerPublishBtn" - label={ - replyTo ? _(msg`Publish reply`) : _(msg`Publish post`) - } - variant="solid" - color="primary" - shape="default" - size="small" - style={[a.rounded_full, a.py_sm]} - onPress={() => onPressPublish()} - disabled={videoState.status !== 'idle' && publishOnUpload}> - <ButtonText style={[a.text_md]}> - {replyTo ? ( - <Trans context="action">Reply</Trans> - ) : ( - <Trans context="action">Post</Trans> - )} - </ButtonText> - </Button> - ) : ( - <View style={[styles.postBtn, pal.btn]}> - <Text style={[pal.textLight, s.f16, s.bold]}> - <Trans context="action">Post</Trans> - </Text> + <Portal.Provider> + <KeyboardAvoidingView + testID="composePostView" + behavior={isIOS ? 'padding' : 'height'} + keyboardVerticalOffset={keyboardVerticalOffset} + style={a.flex_1}> + <View + style={[a.flex_1, viewStyles]} + aria-modal + accessibilityViewIsModal> + <Animated.View + style={topBarAnimatedStyle} + layout={native(LinearTransition)}> + <View style={styles.topbarInner}> + <Button + label={_(msg`Cancel`)} + variant="ghost" + color="primary" + shape="default" + size="small" + style={[ + a.rounded_full, + a.py_sm, + {paddingLeft: 7, paddingRight: 7}, + ]} + onPress={onPressCancel} + accessibilityHint={_( + msg`Closes post composer and discards post draft`, + )}> + <ButtonText style={[a.text_md]}> + <Trans>Cancel</Trans> + </ButtonText> + </Button> + <View style={a.flex_1} /> + {isProcessing ? ( + <> + <Text style={pal.textLight}>{processingState}</Text> + <View style={styles.postBtn}> + <ActivityIndicator /> </View> - )} - </View> - )} - </View> - - {isAltTextRequiredAndMissing && ( - <View style={[styles.reminderLine, pal.viewLight]}> - <View style={styles.errorIcon}> - <FontAwesomeIcon - icon="exclamation" - style={{color: colors.red4}} - size={10} - /> - </View> - <Text style={[pal.text, a.flex_1]}> - <Trans>One or more images is missing alt text.</Trans> - </Text> + </> + ) : ( + <View style={[styles.postBtnWrapper]}> + <LabelsBtn + labels={labels} + onChange={setLabels} + hasMedia={hasMedia} + /> + {canPost ? ( + <Button + testID="composerPublishBtn" + label={ + replyTo ? _(msg`Publish reply`) : _(msg`Publish post`) + } + variant="solid" + color="primary" + shape="default" + size="small" + style={[a.rounded_full, a.py_sm]} + onPress={() => onPressPublish()} + disabled={ + videoState.status !== 'idle' && publishOnUpload + }> + <ButtonText style={[a.text_md]}> + {replyTo ? ( + <Trans context="action">Reply</Trans> + ) : ( + <Trans context="action">Post</Trans> + )} + </ButtonText> + </Button> + ) : ( + <View style={[styles.postBtn, pal.btn]}> + <Text style={[pal.textLight, s.f16, s.bold]}> + <Trans context="action">Post</Trans> + </Text> + </View> + )} + </View> + )} </View> - )} - <ErrorBanner - error={error} - videoState={videoState} - clearError={() => setError('')} - clearVideo={clearVideo} - /> - </Animated.View> - <Animated.ScrollView - layout={native(LinearTransition)} - onScroll={scrollHandler} - style={styles.scrollView} - keyboardShouldPersistTaps="always" - onContentSizeChange={onScrollViewContentSizeChange} - onLayout={onScrollViewLayout}> - {replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined} - <View - style={[ - styles.textInputLayout, - isNative && styles.textInputLayoutMobile, - ]}> - <UserAvatar - avatar={currentProfile?.avatar} - size={50} - type={currentProfile?.associated?.labeler ? 'labeler' : 'user'} - /> - <TextInput - ref={textInput} - richtext={richtext} - placeholder={selectTextInputPlaceholder} - autoFocus - setRichText={setRichText} - onPhotoPasted={onPhotoPasted} - onPressPublish={() => onPressPublish()} - onNewLink={onNewLink} - onError={setError} - accessible={true} - accessibilityLabel={_(msg`Write post`)} - accessibilityHint={_( - msg`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`, - )} + {isAltTextRequiredAndMissing && ( + <View style={[styles.reminderLine, pal.viewLight]}> + <View style={styles.errorIcon}> + <FontAwesomeIcon + icon="exclamation" + style={{color: colors.red4}} + size={10} + /> + </View> + <Text style={[pal.text, a.flex_1]}> + <Trans>One or more images is missing alt text.</Trans> + </Text> + </View> + )} + <ErrorBanner + error={error} + videoState={videoState} + clearError={() => setError('')} + clearVideo={clearVideo} /> - </View> - - <Gallery images={images} dispatch={dispatch} /> - {images.length === 0 && extLink && ( - <View style={a.relative}> - <ExternalEmbed - link={extLink} - gif={extGif} - onRemove={() => { - if (extGif) { - dispatch({type: 'embed_remove_gif'}) - } else { - dispatch({type: 'embed_remove_link'}) - } - setExtLink(undefined) - setExtGif(undefined) - }} + </Animated.View> + <Animated.ScrollView + layout={native(LinearTransition)} + onScroll={scrollHandler} + style={styles.scrollView} + keyboardShouldPersistTaps="always" + onContentSizeChange={onScrollViewContentSizeChange} + onLayout={onScrollViewLayout}> + {replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined} + + <View + style={[ + styles.textInputLayout, + isNative && styles.textInputLayoutMobile, + ]}> + <UserAvatar + avatar={currentProfile?.avatar} + size={50} + type={currentProfile?.associated?.labeler ? 'labeler' : 'user'} /> - <GifAltText - link={extLink} - gif={extGif} - onSubmit={handleChangeGifAltText} + <TextInput + ref={textInput} + richtext={richtext} + placeholder={selectTextInputPlaceholder} + autoFocus + setRichText={setRichText} + onPhotoPasted={onPhotoPasted} + onPressPublish={() => onPressPublish()} + onNewLink={onNewLink} + onError={setError} + accessible={true} + accessibilityLabel={_(msg`Write post`)} + accessibilityHint={_( + msg`Compose posts up to ${MAX_GRAPHEME_LENGTH} characters in length`, + )} /> </View> - )} - <LayoutAnimationConfig skipExiting> - {hasVideo && ( - <Animated.View - style={[a.w_full, a.mt_lg]} - entering={native(ZoomIn)} - exiting={native(ZoomOut)}> - {videoState.asset && - (videoState.status === 'compressing' ? ( - <VideoTranscodeProgress - asset={videoState.asset} - progress={videoState.progress} - clear={clearVideo} - /> - ) : videoState.video ? ( - <VideoPreview - asset={videoState.asset} - video={videoState.video} - setDimensions={updateVideoDimensions} - clear={clearVideo} - /> - ) : null)} - <SubtitleDialogBtn - defaultAltText={videoState.altText} - saveAltText={altText => - dispatch({ - type: 'embed_update_video', - videoAction: { - type: 'update_alt_text', - altText, - signal: videoState.abortController.signal, - }, - }) - } - captions={videoState.captions} - setCaptions={updater => { - dispatch({ - type: 'embed_update_video', - videoAction: { - type: 'update_captions', - updater, - signal: videoState.abortController.signal, - }, - }) + + <Gallery + images={images} + dispatch={dispatch} + Portal={Portal.Portal} + /> + {images.length === 0 && extLink && ( + <View style={a.relative}> + <ExternalEmbed + link={extLink} + gif={extGif} + onRemove={() => { + if (extGif) { + dispatch({type: 'embed_remove_gif'}) + } else { + dispatch({type: 'embed_remove_link'}) + } + setExtLink(undefined) + setExtGif(undefined) }} /> - </Animated.View> + <GifAltText + link={extLink} + gif={extGif} + onSubmit={handleChangeGifAltText} + Portal={Portal.Portal} + /> + </View> )} - </LayoutAnimationConfig> - <View style={!hasVideo ? [a.mt_md] : []}> - {quote ? ( - <View style={[s.mt5, s.mb2, isWeb && s.mb10]}> - <View style={{pointerEvents: 'none'}}> - <QuoteEmbed quote={quote} /> - </View> - {quote.uri !== initQuote?.uri && ( - <QuoteX - onRemove={() => { - dispatch({type: 'embed_remove_quote'}) - setQuote(undefined) + <LayoutAnimationConfig skipExiting> + {hasVideo && ( + <Animated.View + style={[a.w_full, a.mt_lg]} + entering={native(ZoomIn)} + exiting={native(ZoomOut)}> + {videoState.asset && + (videoState.status === 'compressing' ? ( + <VideoTranscodeProgress + asset={videoState.asset} + progress={videoState.progress} + clear={clearVideo} + /> + ) : videoState.video ? ( + <VideoPreview + asset={videoState.asset} + video={videoState.video} + setDimensions={updateVideoDimensions} + clear={clearVideo} + /> + ) : null)} + <SubtitleDialogBtn + defaultAltText={videoState.altText} + saveAltText={altText => + dispatch({ + type: 'embed_update_video', + videoAction: { + type: 'update_alt_text', + altText, + signal: videoState.abortController.signal, + }, + }) + } + captions={videoState.captions} + setCaptions={updater => { + dispatch({ + type: 'embed_update_video', + videoAction: { + type: 'update_captions', + updater, + signal: videoState.abortController.signal, + }, + }) }} + Portal={Portal.Portal} /> - )} - </View> - ) : null} - </View> - </Animated.ScrollView> - <SuggestedLanguage text={richtext.text} /> - - {replyTo ? null : ( - <ThreadgateBtn - postgate={postgate} - onChangePostgate={setPostgate} - threadgateAllowUISettings={threadgateAllowUISettings} - onChangeThreadgateAllowUISettings={ - onChangeThreadgateAllowUISettings - } - style={bottomBarAnimatedStyle} - /> - )} - <View - style={[ - t.atoms.bg, - t.atoms.border_contrast_medium, - styles.bottomBar, - ]}> - {videoState.status !== 'idle' && videoState.status !== 'done' ? ( - <VideoUploadToolbar state={videoState} /> - ) : ( - <ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}> - <SelectPhotoBtn - size={images.length} - disabled={!canSelectImages} - onAdd={onImageAdd} - /> - <SelectVideoBtn - onSelectVideo={selectVideo} - disabled={!canSelectImages || images?.length > 0} - setError={setError} - /> - <OpenCameraBtn disabled={!canSelectImages} onAdd={onImageAdd} /> - <SelectGifBtn - onClose={focusTextInput} - onSelectGif={onSelectGif} - disabled={hasMedia} - /> - {!isMobile ? ( - <Button - onPress={onEmojiButtonPress} - style={a.p_sm} - label={_(msg`Open emoji picker`)} - accessibilityHint={_(msg`Open emoji picker`)} - variant="ghost" - shape="round" - color="primary"> - <EmojiSmile size="lg" /> - </Button> + </Animated.View> + )} + </LayoutAnimationConfig> + <View style={!hasVideo ? [a.mt_md] : []}> + {quote ? ( + <View style={[s.mt5, s.mb2, isWeb && s.mb10]}> + <View style={{pointerEvents: 'none'}}> + <QuoteEmbed quote={quote} /> + </View> + {quote.uri !== initQuote?.uri && ( + <QuoteX + onRemove={() => { + dispatch({type: 'embed_remove_quote'}) + setQuote(undefined) + }} + /> + )} + </View> ) : null} - </ToolbarWrapper> + </View> + </Animated.ScrollView> + <SuggestedLanguage text={richtext.text} /> + + {replyTo ? null : ( + <ThreadgateBtn + postgate={postgate} + onChangePostgate={setPostgate} + threadgateAllowUISettings={threadgateAllowUISettings} + onChangeThreadgateAllowUISettings={ + onChangeThreadgateAllowUISettings + } + style={bottomBarAnimatedStyle} + Portal={Portal.Portal} + /> )} - <View style={a.flex_1} /> - <SelectLangBtn /> - <CharProgress count={graphemeLength} /> + <View + style={[ + t.atoms.bg, + t.atoms.border_contrast_medium, + styles.bottomBar, + ]}> + {videoState.status !== 'idle' && videoState.status !== 'done' ? ( + <VideoUploadToolbar state={videoState} /> + ) : ( + <ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}> + <SelectPhotoBtn + size={images.length} + disabled={!canSelectImages} + onAdd={onImageAdd} + /> + <SelectVideoBtn + onSelectVideo={selectVideo} + disabled={!canSelectImages || images?.length > 0} + setError={setError} + /> + <OpenCameraBtn disabled={!canSelectImages} onAdd={onImageAdd} /> + <SelectGifBtn + onClose={focusTextInput} + onSelectGif={onSelectGif} + disabled={hasMedia} + Portal={Portal.Portal} + /> + {!isMobile ? ( + <Button + onPress={onEmojiButtonPress} + style={a.p_sm} + label={_(msg`Open emoji picker`)} + accessibilityHint={_(msg`Open emoji picker`)} + variant="ghost" + shape="round" + color="primary"> + <EmojiSmile size="lg" /> + </Button> + ) : null} + </ToolbarWrapper> + )} + <View style={a.flex_1} /> + <SelectLangBtn /> + <CharProgress count={graphemeLength} /> + </View> </View> - </View> - <Prompt.Basic - control={discardPromptControl} - title={_(msg`Discard draft?`)} - description={_(msg`Are you sure you'd like to discard this draft?`)} - onConfirm={onClose} - confirmButtonCta={_(msg`Discard`)} - confirmButtonColor="negative" - /> - </KeyboardAvoidingView> + <Prompt.Basic + control={discardPromptControl} + title={_(msg`Discard draft?`)} + description={_(msg`Are you sure you'd like to discard this draft?`)} + onConfirm={onClose} + confirmButtonCta={_(msg`Discard`)} + confirmButtonColor="negative" + Portal={Portal.Portal} + /> + </KeyboardAvoidingView> + <Portal.Outlet /> + </Portal.Provider> ) } diff --git a/src/view/com/composer/GifAltText.tsx b/src/view/com/composer/GifAltText.tsx index a05607c76..3479fb973 100644 --- a/src/view/com/composer/GifAltText.tsx +++ b/src/view/com/composer/GifAltText.tsx @@ -20,6 +20,7 @@ import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' 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' @@ -28,10 +29,12 @@ export function GifAltText({ link: linkProp, gif, onSubmit, + Portal, }: { link: ExternalEmbedDraft gif?: Gif onSubmit: (alt: string) => void + Portal: PortalComponent }) { const control = Dialog.useDialogControl() const {_} = useLingui() @@ -96,9 +99,7 @@ export function GifAltText({ <AltTextReminder /> - <Dialog.Outer - control={control} - nativeOptions={isAndroid ? {sheet: {snapPoints: ['100%']}} : {}}> + <Dialog.Outer control={control} Portal={Portal}> <Dialog.Handle /> <AltTextInner onSubmit={onPressSubmit} @@ -185,6 +186,8 @@ function AltTextInner({ </View> </View> <Dialog.Close /> + {/* Maybe fix this later -h */} + {isAndroid ? <View style={{height: 300}} /> : null} </Dialog.ScrollableInner> ) } diff --git a/src/view/com/composer/photos/EditImageDialog.web.tsx b/src/view/com/composer/photos/EditImageDialog.web.tsx index 0afb83ed9..ebe528abc 100644 --- a/src/view/com/composer/photos/EditImageDialog.web.tsx +++ b/src/view/com/composer/photos/EditImageDialog.web.tsx @@ -20,6 +20,7 @@ import {EditImageDialogProps} from './EditImageDialog' export const EditImageDialog = (props: EditImageDialogProps) => { return ( <Dialog.Outer control={props.control}> + <Dialog.Handle /> <EditImageInner key={props.image.source.id} {...props} /> </Dialog.Outer> ) diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 5ff7042bc..3958a85c0 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -21,6 +21,7 @@ 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' @@ -30,6 +31,7 @@ const IMAGE_GAP = 8 interface GalleryProps { images: ComposerImage[] dispatch: (action: ComposerAction) => void + Portal: PortalComponent } export let Gallery = (props: GalleryProps): React.ReactNode => { @@ -57,7 +59,12 @@ interface GalleryInnerProps extends GalleryProps { containerInfo: Dimensions } -const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { +const GalleryInner = ({ + images, + containerInfo, + dispatch, + Portal, +}: GalleryInnerProps) => { const {isMobile} = useWebMediaQueries() const {altTextControlStyle, imageControlsStyle, imageStyle} = @@ -111,6 +118,7 @@ const GalleryInner = ({images, containerInfo, dispatch}: GalleryInnerProps) => { onRemove={() => { dispatch({type: 'embed_remove_image', image}) }} + Portal={Portal} /> ) })} @@ -127,6 +135,7 @@ type GalleryItemProps = { imageStyle?: ViewStyle onChange: (next: ComposerImage) => void onRemove: () => void + Portal: PortalComponent } const GalleryItem = ({ @@ -136,6 +145,7 @@ const GalleryItem = ({ imageStyle, onChange, onRemove, + Portal, }: GalleryItemProps): React.ReactNode => { const {_} = useLingui() const t = useTheme() @@ -230,6 +240,7 @@ const GalleryItem = ({ control={altTextControl} image={image} onChange={onChange} + Portal={Portal} /> <EditImageDialog diff --git a/src/view/com/composer/photos/ImageAltTextDialog.tsx b/src/view/com/composer/photos/ImageAltTextDialog.tsx index 123e1066a..16ce4351a 100644 --- a/src/view/com/composer/photos/ImageAltTextDialog.tsx +++ b/src/view/com/composer/photos/ImageAltTextDialog.tsx @@ -5,25 +5,26 @@ import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {MAX_ALT_TEXT} from '#/lib/constants' -import {isWeb} from '#/platform/detection' +import {isAndroid, isWeb} from '#/platform/detection' import {ComposerImage} from '#/state/gallery' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import * as TextField from '#/components/forms/TextField' +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 = (props: Props): React.ReactNode => { return ( - <Dialog.Outer control={props.control}> + <Dialog.Outer control={props.control} Portal={props.Portal}> <Dialog.Handle /> - <ImageAltTextInner {...props} /> </Dialog.Outer> ) @@ -116,6 +117,8 @@ const ImageAltTextInner = ({ </ButtonText> </Button> </View> + {/* Maybe fix this later -h */} + {isAndroid ? <View style={{height: 300}} /> : null} </Dialog.ScrollableInner> ) } diff --git a/src/view/com/composer/photos/SelectGifBtn.tsx b/src/view/com/composer/photos/SelectGifBtn.tsx index d13df0a11..d482e0783 100644 --- a/src/view/com/composer/photos/SelectGifBtn.tsx +++ b/src/view/com/composer/photos/SelectGifBtn.tsx @@ -9,14 +9,16 @@ 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}: Props) { +export function SelectGifBtn({onClose, onSelectGif, disabled, Portal}: Props) { const {_} = useLingui() const ref = useRef<{open: () => void}>(null) const t = useTheme() @@ -46,6 +48,7 @@ export function SelectGifBtn({onClose, onSelectGif, disabled}: Props) { controlRef={ref} onClose={onClose} onSelectGif={onSelectGif} + Portal={Portal} /> </> ) diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx index 34ead3d9a..37bfbafe6 100644 --- a/src/view/com/composer/photos/SelectPhotoBtn.tsx +++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx @@ -9,6 +9,7 @@ import {isNative} from '#/platform/detection' import {ComposerImage, createComposerImage} from '#/state/gallery' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' +import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' import {Image_Stroke2_Corner0_Rounded as Image} from '#/components/icons/Image' type Props = { @@ -21,23 +22,26 @@ export function SelectPhotoBtn({size, disabled, onAdd}: Props) { const {_} = useLingui() const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() const t = useTheme() + const sheetWrapper = useSheetWrapper() const onPressSelectPhotos = useCallback(async () => { if (isNative && !(await requestPhotoAccessIfNeeded())) { return } - const images = await openPicker({ - selectionLimit: 4 - size, - allowsMultipleSelection: true, - }) + const images = await sheetWrapper( + openPicker({ + selectionLimit: 4 - size, + allowsMultipleSelection: true, + }), + ) const results = await Promise.all( images.map(img => createComposerImage(img)), ) onAdd(results) - }, [requestPhotoAccessIfNeeded, size, onAdd]) + }, [requestPhotoAccessIfNeeded, size, onAdd, sheetWrapper]) return ( <Button diff --git a/src/view/com/composer/threadgate/ThreadgateBtn.tsx b/src/view/com/composer/threadgate/ThreadgateBtn.tsx index b0806180c..7e57a57d4 100644 --- a/src/view/com/composer/threadgate/ThreadgateBtn.tsx +++ b/src/view/com/composer/threadgate/ThreadgateBtn.tsx @@ -13,6 +13,7 @@ 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, @@ -20,6 +21,7 @@ export function ThreadgateBtn({ threadgateAllowUISettings, onChangeThreadgateAllowUISettings, style, + Portal, }: { postgate: AppBskyFeedPostgate.Record onChangePostgate: (v: AppBskyFeedPostgate.Record) => void @@ -28,6 +30,8 @@ export function ThreadgateBtn({ onChangeThreadgateAllowUISettings: (v: ThreadgateAllowUISetting[]) => void style?: StyleProp<AnimatedStyle<ViewStyle>> + + Portal: PortalComponent }) { const {_} = useLingui() const t = useTheme() @@ -77,6 +81,7 @@ export function ThreadgateBtn({ onChangePostgate={onChangePostgate} threadgateAllowUISettings={threadgateAllowUISettings} onChangeThreadgateAllowUISettings={onChangeThreadgateAllowUISettings} + Portal={Portal} /> </> ) diff --git a/src/view/com/composer/videos/SubtitleDialog.tsx b/src/view/com/composer/videos/SubtitleDialog.tsx index f66684d4e..04522ee1d 100644 --- a/src/view/com/composer/videos/SubtitleDialog.tsx +++ b/src/view/com/composer/videos/SubtitleDialog.tsx @@ -7,7 +7,7 @@ import {useLingui} from '@lingui/react' import {MAX_ALT_TEXT} from '#/lib/constants' import {useEnforceMaxGraphemeCount} from '#/lib/strings/helpers' import {LANGUAGES} from '#/locale/languages' -import {isAndroid, isWeb} from '#/platform/detection' +import {isWeb} from '#/platform/detection' import {useLanguagePrefs} from '#/state/preferences' import {atoms as a, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' @@ -17,6 +17,7 @@ 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' @@ -29,6 +30,7 @@ interface Props { captions: CaptionsTrack[] saveAltText: (altText: string) => void setCaptions: (updater: (prev: CaptionsTrack[]) => CaptionsTrack[]) => void + Portal: PortalComponent } export function SubtitleDialogBtn(props: Props) { @@ -56,9 +58,7 @@ export function SubtitleDialogBtn(props: Props) { {isWeb ? <Trans>Captions & alt text</Trans> : <Trans>Alt text</Trans>} </ButtonText> </Button> - <Dialog.Outer - control={control} - nativeOptions={isAndroid ? {sheet: {snapPoints: ['60%']}} : {}}> + <Dialog.Outer control={control} Portal={props.Portal}> <Dialog.Handle /> <SubtitleDialogInner {...props} /> </Dialog.Outer> |