Merge pull request #3337 from bluesky-social/samuel/scrollable-loggedoutlayout

Move scrollview to `LoggedOutLayout` to fix scrolling on web
zio/stable
Samuel Newman 2024-03-22 15:36:10 +00:00 committed by GitHub
commit b27a0b8c97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 81 deletions

View File

@ -1,13 +1,6 @@
import React from 'react' import React from 'react'
import { import {type StyleProp, View, type ViewStyle} from 'react-native'
ScrollView,
type StyleProp,
StyleSheet,
View,
type ViewStyle,
} from 'react-native'
import {isWeb} from '#/platform/detection'
import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
@ -16,23 +9,18 @@ export function FormContainer({
title, title,
children, children,
style, style,
contentContainerStyle,
}: { }: {
testID?: string testID?: string
title?: React.ReactNode title?: React.ReactNode
children: React.ReactNode children: React.ReactNode
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
contentContainerStyle?: StyleProp<ViewStyle>
}) { }) {
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const t = useTheme() const t = useTheme()
return ( return (
<ScrollView
testID={testID}
style={[styles.maxHeight, contentContainerStyle]}
keyboardShouldPersistTaps="handled">
<View <View
style={[a.gap_md, a.flex_1, !gtMobile && [a.px_lg, a.pt_md], style]}> testID={testID}
style={[a.gap_md, a.flex_1, !gtMobile && [a.px_lg, a.py_md], style]}>
{title && !gtMobile && ( {title && !gtMobile && (
<Text style={[a.text_xl, a.font_bold, t.atoms.text_contrast_high]}> <Text style={[a.text_xl, a.font_bold, t.atoms.text_contrast_high]}>
{title} {title}
@ -40,14 +28,5 @@ export function FormContainer({
)} )}
{children} {children}
</View> </View>
</ScrollView>
) )
} }
const styles = StyleSheet.create({
maxHeight: {
// @ts-ignore web only -prf
maxHeight: isWeb ? '100vh' : undefined,
height: !isWeb ? '100%' : undefined,
},
})

View File

@ -164,7 +164,11 @@ export const Login = ({onPressBack}: {onPressBack: () => void}) => {
return ( return (
<KeyboardAvoidingView testID="signIn" behavior="padding" style={a.flex_1}> <KeyboardAvoidingView testID="signIn" behavior="padding" style={a.flex_1}>
<LoggedOutLayout leadin="" title={title} description={description}> <LoggedOutLayout
leadin=""
title={title}
description={description}
scrollable>
<LayoutAnimationConfig skipEntering skipExiting> <LayoutAnimationConfig skipEntering skipExiting>
<ScreenTransition key={currentForm}>{content}</ScreenTransition> <ScreenTransition key={currentForm}>{content}</ScreenTransition>
</LayoutAnimationConfig> </LayoutAnimationConfig>

View File

@ -1,11 +1,10 @@
import React from 'react' import React from 'react'
import {ActivityIndicator, StyleSheet, View} from 'react-native' import {ActivityIndicator, View} from 'react-native'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
import {createFullHandle} from '#/lib/strings/handles' import {createFullHandle} from '#/lib/strings/handles'
import {isWeb} from '#/platform/detection'
import {ScreenTransition} from '#/screens/Login/ScreenTransition' import {ScreenTransition} from '#/screens/Login/ScreenTransition'
import {useSignupContext, useSubmitSignup} from '#/screens/Signup/state' import {useSignupContext, useSubmitSignup} from '#/screens/Signup/state'
import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView' import {CaptchaWebView} from '#/screens/Signup/StepCaptcha/CaptchaWebView'
@ -54,7 +53,14 @@ export function StepCaptcha() {
return ( return (
<ScreenTransition> <ScreenTransition>
<View style={[a.gap_lg]}> <View style={[a.gap_lg]}>
<View style={[styles.container, completed && styles.center]}> <View
style={[
a.w_full,
a.pb_xl,
a.overflow_hidden,
{minHeight: 500},
completed && [a.align_center, a.justify_center],
]}>
{!completed ? ( {!completed ? (
<CaptchaWebView <CaptchaWebView
url={url} url={url}
@ -72,24 +78,3 @@ export function StepCaptcha() {
</ScreenTransition> </ScreenTransition>
) )
} }
const styles = StyleSheet.create({
error: {
borderRadius: 6,
marginTop: 10,
},
// @ts-expect-error: Suppressing error due to incomplete `ViewStyle` type definition in react-native-web, missing `cursor` prop as discussed in https://github.com/necolas/react-native-web/issues/832.
touchable: {
...(isWeb && {cursor: 'pointer'}),
},
container: {
minHeight: 500,
width: '100%',
paddingBottom: 20,
overflow: 'hidden',
},
center: {
alignItems: 'center',
justifyContent: 'center',
},
})

View File

@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import {ScrollView, View} from 'react-native' import {View} from 'react-native'
import {LayoutAnimationConfig} from 'react-native-reanimated'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@ -20,7 +21,7 @@ import {
import {StepCaptcha} from '#/screens/Signup/StepCaptcha' import {StepCaptcha} from '#/screens/Signup/StepCaptcha'
import {StepHandle} from '#/screens/Signup/StepHandle' import {StepHandle} from '#/screens/Signup/StepHandle'
import {StepInfo} from '#/screens/Signup/StepInfo' import {StepInfo} from '#/screens/Signup/StepInfo'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button' import {Button, ButtonText} from '#/components/Button'
import {Divider} from '#/components/Divider' import {Divider} from '#/components/Divider'
import {InlineLink} from '#/components/Link' import {InlineLink} from '#/components/Link'
@ -32,6 +33,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
const {screen} = useAnalytics() const {screen} = useAnalytics()
const [state, dispatch] = React.useReducer(reducer, initialState) const [state, dispatch] = React.useReducer(reducer, initialState)
const submit = useSubmitSignup({state, dispatch}) const submit = useSubmitSignup({state, dispatch})
const {gtMobile} = useBreakpoints()
const { const {
data: serviceInfo, data: serviceInfo,
@ -125,13 +127,16 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
<LoggedOutLayout <LoggedOutLayout
leadin="" leadin=""
title={_(msg`Create Account`)} title={_(msg`Create Account`)}
description={_(msg`We're so excited to have you join us!`)}> description={_(msg`We're so excited to have you join us!`)}
<ScrollView scrollable>
testID="createAccount" <View testID="createAccount" style={a.flex_1}>
keyboardShouldPersistTaps="handled" <View
style={a.h_full} style={[
keyboardDismissMode="on-drag"> a.flex_1,
<View style={[a.flex_1, a.px_xl, a.pt_2xl, {paddingBottom: 100}]}> a.px_xl,
a.pt_2xl,
!gtMobile && {paddingBottom: 100},
]}>
<View style={[a.gap_sm, a.pb_3xl]}> <View style={[a.gap_sm, a.pb_3xl]}>
<Text style={[a.font_semibold, t.atoms.text_contrast_medium]}> <Text style={[a.font_semibold, t.atoms.text_contrast_medium]}>
<Trans>Step</Trans> {state.activeStep + 1} <Trans>of</Trans>{' '} <Trans>Step</Trans> {state.activeStep + 1} <Trans>of</Trans>{' '}
@ -152,6 +157,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
</View> </View>
<View style={[a.pb_3xl]}> <View style={[a.pb_3xl]}>
<LayoutAnimationConfig skipEntering skipExiting>
{state.activeStep === SignupStep.INFO ? ( {state.activeStep === SignupStep.INFO ? (
<StepInfo /> <StepInfo />
) : state.activeStep === SignupStep.HANDLE ? ( ) : state.activeStep === SignupStep.HANDLE ? (
@ -159,6 +165,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
) : ( ) : (
<StepCaptcha /> <StepCaptcha />
)} )}
</LayoutAnimationConfig>
</View> </View>
<View style={[a.flex_row, a.justify_between, a.pb_lg]}> <View style={[a.flex_row, a.justify_between, a.pb_lg]}>
@ -208,7 +215,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
</Text> </Text>
</View> </View>
</View> </View>
</ScrollView> </View>
</LoggedOutLayout> </LoggedOutLayout>
</SignupContext.Provider> </SignupContext.Provider>
) )

View File

@ -1,19 +1,24 @@
import React from 'react' import React from 'react'
import {StyleSheet, View} from 'react-native' import {ScrollView, StyleSheet, View} from 'react-native'
import {Text} from '../text/Text'
import {isWeb} from '#/platform/detection'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' import {atoms as a} from '#/alf'
import {Text} from '../text/Text'
export const LoggedOutLayout = ({ export const LoggedOutLayout = ({
leadin, leadin,
title, title,
description, description,
children, children,
scrollable,
}: React.PropsWithChildren<{ }: React.PropsWithChildren<{
leadin: string leadin: string
title: string title: string
description: string description: string
scrollable?: boolean
}>) => { }>) => {
const {isMobile, isTabletOrMobile} = useWebMediaQueries() const {isMobile, isTabletOrMobile} = useWebMediaQueries()
const pal = usePalette('default') const pal = usePalette('default')
@ -25,7 +30,18 @@ export const LoggedOutLayout = ({
}) })
if (isMobile) { if (isMobile) {
return <View style={{paddingTop: 10}}>{children}</View> if (scrollable) {
return (
<ScrollView
style={styles.scrollview}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag">
<View style={a.pt_md}>{children}</View>
</ScrollView>
)
} else {
return <View style={a.pt_md}>{children}</View>
}
} }
return ( return (
<View style={styles.container}> <View style={styles.container}>
@ -50,9 +66,23 @@ export const LoggedOutLayout = ({
{description} {description}
</Text> </Text>
</View> </View>
{scrollable ? (
<View style={[styles.scrollableContent, contentBg]}>
<ScrollView
style={styles.scrollview}
contentContainerStyle={styles.scrollViewContentContainer}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag">
<View style={[styles.contentWrapper, isWeb && a.my_auto]}>
{children}
</View>
</ScrollView>
</View>
) : (
<View style={[styles.content, contentBg]}> <View style={[styles.content, contentBg]}>
<View style={styles.contentWrapper}>{children}</View> <View style={styles.contentWrapper}>{children}</View>
</View> </View>
)}
</View> </View>
) )
} }
@ -74,7 +104,16 @@ const styles = StyleSheet.create({
paddingHorizontal: 40, paddingHorizontal: 40,
justifyContent: 'center', justifyContent: 'center',
}, },
scrollableContent: {
flex: 2,
},
scrollview: {
flex: 1,
},
scrollViewContentContainer: {
flex: 1,
paddingHorizontal: 40,
},
leadinText: { leadinText: {
fontSize: 36, fontSize: 36,
fontWeight: '800', fontWeight: '800',