Composer - Use sheet presentation on iOS (#4278)

* use sheet presentation + tweak spacing

* line up elements + add hitslop to cancel

* fixing spacing on replies
zio/stable
Samuel Newman 2024-05-30 14:39:36 +03:00 committed by GitHub
parent 76f860dad2
commit 3bdceac2fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 53 additions and 27 deletions

View File

@ -7,19 +7,21 @@ import {useThemePrefs} from 'state/shell'
import {dark, dim, light, ThemeName} from '#/alf/themes' import {dark, dim, light, ThemeName} from '#/alf/themes'
export function useColorModeTheme(): ThemeName { export function useColorModeTheme(): ThemeName {
const theme = useThemeName()
React.useLayoutEffect(() => {
updateDocument(theme)
SystemUI.setBackgroundColorAsync(getBackgroundColor(theme))
}, [theme])
return theme
}
export function useThemeName(): ThemeName {
const colorScheme = useColorScheme() const colorScheme = useColorScheme()
const {colorMode, darkTheme} = useThemePrefs() const {colorMode, darkTheme} = useThemePrefs()
React.useLayoutEffect(() => { return getThemeName(colorScheme, colorMode, darkTheme)
const theme = getThemeName(colorScheme, colorMode, darkTheme)
updateDocument(theme)
SystemUI.setBackgroundColorAsync(getBackgroundColor(theme))
}, [colorMode, colorScheme, darkTheme])
return React.useMemo(
() => getThemeName(colorScheme, colorMode, darkTheme),
[colorScheme, colorMode, darkTheme],
)
} }
function getThemeName( function getThemeName(
@ -53,7 +55,7 @@ function updateDocument(theme: ThemeName) {
} }
} }
function getBackgroundColor(theme: ThemeName): string { export function getBackgroundColor(theme: ThemeName): string {
switch (theme) { switch (theme) {
case 'light': case 'light':
return light.atoms.bg.backgroundColor return light.atoms.bg.backgroundColor

View File

@ -54,7 +54,7 @@ import {useAgent, useSession} from '#/state/session'
import {useComposerControls} from '#/state/shell/composer' import {useComposerControls} from '#/state/shell/composer'
import {useAnalytics} from 'lib/analytics/analytics' import {useAnalytics} from 'lib/analytics/analytics'
import * as apilib from 'lib/api/index' import * as apilib from 'lib/api/index'
import {MAX_GRAPHEME_LENGTH} from 'lib/constants' import {HITSLOP_10, MAX_GRAPHEME_LENGTH} from 'lib/constants'
import {useIsKeyboardVisible} from 'lib/hooks/useIsKeyboardVisible' import {useIsKeyboardVisible} from 'lib/hooks/useIsKeyboardVisible'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
@ -165,9 +165,8 @@ export const ComposePost = observer(function ComposePost({
() => ({ () => ({
paddingBottom: paddingBottom:
isAndroid || (isIOS && !isKeyboardVisible) ? insets.bottom : 0, isAndroid || (isIOS && !isKeyboardVisible) ? insets.bottom : 0,
paddingTop: isMobile && isWeb ? 15 : insets.top,
}), }),
[insets, isKeyboardVisible, isMobile], [insets, isKeyboardVisible],
) )
const hasScrolled = useSharedValue(0) const hasScrolled = useSharedValue(0)
@ -422,7 +421,8 @@ export const ComposePost = observer(function ComposePost({
accessibilityLabel={_(msg`Cancel`)} accessibilityLabel={_(msg`Cancel`)}
accessibilityHint={_( accessibilityHint={_(
msg`Closes post composer and discards post draft`, msg`Closes post composer and discards post draft`,
)}> )}
hitSlop={HITSLOP_10}>
<Text style={[pal.link, s.f18]}> <Text style={[pal.link, s.f18]}>
<Trans>Cancel</Trans> <Trans>Cancel</Trans>
</Text> </Text>
@ -622,10 +622,8 @@ const styles = StyleSheet.create({
topbar: { topbar: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginTop: -10,
paddingHorizontal: 4,
marginHorizontal: 16, marginHorizontal: 16,
height: 44, height: 54,
gap: 4, gap: 4,
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
}, },
@ -633,7 +631,6 @@ const styles = StyleSheet.create({
paddingTop: 10, paddingTop: 10,
paddingBottom: 10, paddingBottom: 10,
height: 50, height: 50,
marginTop: 0,
}, },
postBtn: { postBtn: {
borderRadius: 20, borderRadius: 20,
@ -676,7 +673,7 @@ const styles = StyleSheet.create({
}, },
textInputLayout: { textInputLayout: {
flexDirection: 'row', flexDirection: 'row',
paddingTop: 16, paddingTop: 4,
}, },
textInputLayoutMobile: { textInputLayoutMobile: {
flex: 1, flex: 1,

View File

@ -223,8 +223,9 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
alignItems: 'flex-start', alignItems: 'flex-start',
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomWidth: StyleSheet.hairlineWidth,
paddingTop: 16, paddingTop: 4,
paddingBottom: 16, paddingBottom: 16,
marginBottom: 12,
}, },
replyToPost: { replyToPost: {
flex: 1, flex: 1,

View File

@ -1,11 +1,15 @@
import React from 'react' import React, {useLayoutEffect} from 'react'
import {Modal, View} from 'react-native' 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 {observer} from 'mobx-react-lite'
import {isIOS} from '#/platform/detection'
import {Provider as LegacyModalProvider} from '#/state/modals' import {Provider as LegacyModalProvider} from '#/state/modals'
import {useComposerState} from 'state/shell/composer' import {useComposerState} from '#/state/shell/composer'
import {ModalsContainer as LegacyModalsContainer} from '#/view/com/modals/Modal' import {ModalsContainer as LegacyModalsContainer} from '#/view/com/modals/Modal'
import {useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {getBackgroundColor, useThemeName} from '#/alf/util/useColorModeTheme'
import { import {
Outlet as PortalOutlet, Outlet as PortalOutlet,
Provider as PortalProvider, Provider as PortalProvider,
@ -19,15 +23,17 @@ export const Composer = observer(function ComposerImpl({}: {
const state = useComposerState() const state = useComposerState()
const ref = useComposerCancelRef() const ref = useComposerCancelRef()
const open = !!state
return ( return (
<Modal <Modal
aria-modal aria-modal
accessibilityViewIsModal accessibilityViewIsModal
visible={!!state} visible={open}
presentationStyle="overFullScreen" presentationStyle="formSheet"
animationType="slide" animationType="slide"
onRequestClose={() => ref.current?.onPressCancel()}> onRequestClose={() => ref.current?.onPressCancel()}>
<View style={[t.atoms.bg, {flex: 1}]}> <View style={[t.atoms.bg, a.flex_1]}>
<LegacyModalProvider> <LegacyModalProvider>
<PortalProvider> <PortalProvider>
<ComposePost <ComposePost
@ -43,7 +49,27 @@ export const Composer = observer(function ComposerImpl({}: {
<PortalOutlet /> <PortalOutlet />
</PortalProvider> </PortalProvider>
</LegacyModalProvider> </LegacyModalProvider>
{isIOS && <IOSModalBackground active={open} />}
</View> </View>
</Modal> </Modal>
) )
}) })
// 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
}