diff options
Diffstat (limited to 'src/view/shell')
-rw-r--r-- | src/view/shell/Composer.ios.tsx | 80 | ||||
-rw-r--r-- | src/view/shell/Composer.tsx | 159 | ||||
-rw-r--r-- | src/view/shell/Composer.web.tsx | 1 |
3 files changed, 138 insertions, 102 deletions
diff --git a/src/view/shell/Composer.ios.tsx b/src/view/shell/Composer.ios.tsx new file mode 100644 index 000000000..a732e0cde --- /dev/null +++ b/src/view/shell/Composer.ios.tsx @@ -0,0 +1,80 @@ +import React, {useLayoutEffect} from 'react' +import {Modal, View} from 'react-native' +import {StatusBar} from 'expo-status-bar' +import * as SystemUI from 'expo-system-ui' +import {observer} from 'mobx-react-lite' + +import {useComposerState} from '#/state/shell/composer' +import {atoms as a, useTheme} from '#/alf' +import {getBackgroundColor, useThemeName} from '#/alf/util/useColorModeTheme' +import {ComposePost, useComposerCancelRef} from '../com/composer/Composer' + +export const Composer = observer(function ComposerImpl({}: { + winHeight: number +}) { + const t = useTheme() + const state = useComposerState() + const ref = useComposerCancelRef() + + const open = !!state + + return ( + <Modal + aria-modal + accessibilityViewIsModal + visible={open} + presentationStyle="pageSheet" + animationType="slide" + onRequestClose={() => ref.current?.onPressCancel()}> + <View style={[t.atoms.bg, a.flex_1]}> + <Providers open={open}> + <ComposePost + cancelRef={ref} + replyTo={state?.replyTo} + onPost={state?.onPost} + quote={state?.quote} + mention={state?.mention} + text={state?.text} + imageUris={state?.imageUris} + /> + </Providers> + </View> + </Modal> + ) +}) + +function Providers({ + children, + open, +}: { + children: React.ReactNode + open: boolean +}) { + // on iOS, it's a native formSheet. We use FullWindowOverlay to make + // the dialogs appear over it + return ( + <> + {children} + <IOSModalBackground active={open} /> + </> + ) +} + +// Generally, the backdrop of the app is the theme color, but when this is open +// we want it to be black due to the modal being a form sheet. +function IOSModalBackground({active}: {active: boolean}) { + const theme = useThemeName() + + useLayoutEffect(() => { + SystemUI.setBackgroundColorAsync('black') + + return () => { + SystemUI.setBackgroundColorAsync(getBackgroundColor(theme)) + } + }, [theme]) + + // Set the status bar to light - however, only if the modal is active + // If we rely on this component being mounted to set this, + // there'll be a delay before it switches back to default. + return active ? <StatusBar style="light" animated /> : null +} diff --git a/src/view/shell/Composer.tsx b/src/view/shell/Composer.tsx index 1d656ca8f..b978d6b85 100644 --- a/src/view/shell/Composer.tsx +++ b/src/view/shell/Composer.tsx @@ -1,116 +1,73 @@ -import React, {useLayoutEffect, useState} from 'react' -import {Modal, View} from 'react-native' -import {GestureHandlerRootView} from 'react-native-gesture-handler' -import {RootSiblingParent} from 'react-native-root-siblings' -import {StatusBar} from 'expo-status-bar' -import * as SystemUI from 'expo-system-ui' +import React, {useEffect} from 'react' +import {Animated, Easing, StyleSheet, View} from 'react-native' import {observer} from 'mobx-react-lite' -import {isIOS} from '#/platform/detection' -import {Provider as LegacyModalProvider} from '#/state/modals' -import {useComposerState} from '#/state/shell/composer' -import {ModalsContainer as LegacyModalsContainer} from '#/view/com/modals/Modal' -import {atoms as a, useTheme} from '#/alf' -import {getBackgroundColor, useThemeName} from '#/alf/util/useColorModeTheme' -import { - Outlet as PortalOutlet, - Provider as PortalProvider, -} from '#/components/Portal' -import {ComposePost, useComposerCancelRef} from '../com/composer/Composer' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {usePalette} from 'lib/hooks/usePalette' +import {useComposerState} from 'state/shell/composer' +import {ComposePost} from '../com/composer/Composer' -export const Composer = observer(function ComposerImpl({}: { +export const Composer = observer(function ComposerImpl({ + winHeight, +}: { winHeight: number }) { - const t = useTheme() const state = useComposerState() - const ref = useComposerCancelRef() - const [isModalReady, setIsModalReady] = useState(false) + const pal = usePalette('default') + const initInterp = useAnimatedValue(0) - const open = !!state - const [prevOpen, setPrevOpen] = useState(open) - if (open !== prevOpen) { - setPrevOpen(open) - if (!open) { - setIsModalReady(false) + useEffect(() => { + if (state) { + Animated.timing(initInterp, { + toValue: 1, + duration: 300, + easing: Easing.out(Easing.exp), + useNativeDriver: true, + }).start() + } else { + initInterp.setValue(0) } + }, [initInterp, state]) + const wrapperAnimStyle = { + transform: [ + { + translateY: initInterp.interpolate({ + inputRange: [0, 1], + outputRange: [winHeight, 0], + }), + }, + ], + } + + // rendering + // = + + if (!state) { + return <View /> } return ( - <Modal + <Animated.View + style={[styles.wrapper, pal.view, wrapperAnimStyle]} aria-modal - accessibilityViewIsModal - visible={open} - presentationStyle="formSheet" - animationType="slide" - onShow={() => setIsModalReady(true)} - onRequestClose={() => ref.current?.onPressCancel()}> - <View style={[t.atoms.bg, a.flex_1]}> - <Providers open={open}> - <ComposePost - isModalReady={isModalReady} - cancelRef={ref} - replyTo={state?.replyTo} - onPost={state?.onPost} - quote={state?.quote} - mention={state?.mention} - text={state?.text} - imageUris={state?.imageUris} - /> - </Providers> - </View> - </Modal> + accessibilityViewIsModal> + <ComposePost + replyTo={state.replyTo} + onPost={state.onPost} + quote={state.quote} + mention={state.mention} + text={state.text} + imageUris={state.imageUris} + /> + </Animated.View> ) }) -function Providers({ - children, - open, -}: { - children: React.ReactNode - open: boolean -}) { - // on iOS, it's a native formSheet. We use FullWindowOverlay to make - // the dialogs appear over it - if (isIOS) { - return ( - <> - {children} - <IOSModalBackground active={open} /> - </> - ) - } else { - // on Android we just nest the dialogs within it - return ( - <GestureHandlerRootView style={a.flex_1}> - <RootSiblingParent> - <LegacyModalProvider> - <PortalProvider> - {children} - <LegacyModalsContainer /> - <PortalOutlet /> - </PortalProvider> - </LegacyModalProvider> - </RootSiblingParent> - </GestureHandlerRootView> - ) - } -} - -// Generally, the backdrop of the app is the theme color, but when this is open -// we want it to be black due to the modal being a form sheet. -function IOSModalBackground({active}: {active: boolean}) { - const theme = useThemeName() - - useLayoutEffect(() => { - SystemUI.setBackgroundColorAsync('black') - - return () => { - SystemUI.setBackgroundColorAsync(getBackgroundColor(theme)) - } - }, [theme]) - - // Set the status bar to light - however, only if the modal is active - // If we rely on this component being mounted to set this, - // there'll be a delay before it switches back to default. - return active ? <StatusBar style="light" animated /> : null -} +const styles = StyleSheet.create({ + wrapper: { + position: 'absolute', + top: 0, + bottom: 0, + width: '100%', + }, +}) diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx index 47322d4ea..64353db23 100644 --- a/src/view/shell/Composer.web.tsx +++ b/src/view/shell/Composer.web.tsx @@ -56,7 +56,6 @@ export function Composer({}: {winHeight: number}) { t.atoms.border_contrast_medium, ]}> <ComposePost - isModalReady={true} replyTo={state.replyTo} quote={state.quote} onPost={state.onPost} |