Composer - Use sheet presentation on iOS (#4278)
* use sheet presentation + tweak spacing * line up elements + add hitslop to cancel * fixing spacing on replieszio/stable
parent
76f860dad2
commit
3bdceac2fb
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue