···11+import React from 'react'
22+import {View, ViewStyle} from 'react-native'
33+import {StyleProp} from 'react-native'
44+import {useSafeAreaInsets} from 'react-native-safe-area-context'
55+66+import {atoms as a} from '#/alf'
77+88+// Every screen should have a Layout component wrapping it.
99+// This component provides a default padding for the top of the screen.
1010+// This allows certain screens to avoid the top padding if they want to.
1111+//
1212+// In a future PR I will add a unified header component to this file and
1313+// things like a preconfigured scrollview.
1414+1515+/**
1616+ * Every screen should have a Layout.Screen component wrapping it.
1717+ * This component provides a default padding for the top of the screen
1818+ * and height/minHeight
1919+ */
2020+let Screen = ({
2121+ disableTopPadding,
2222+ style,
2323+ ...props
2424+}: React.ComponentProps<typeof View> & {
2525+ disableTopPadding?: boolean
2626+ style?: StyleProp<ViewStyle>
2727+}): React.ReactNode => {
2828+ const {top} = useSafeAreaInsets()
2929+ return (
3030+ <View
3131+ style={[
3232+ {paddingTop: disableTopPadding ? 0 : top},
3333+ a.util_screen_outer,
3434+ style,
3535+ ]}
3636+ {...props}
3737+ />
3838+ )
3939+}
4040+Screen = React.memo(Screen)
4141+export {Screen}
···1010import {msg, Trans} from '@lingui/macro'
1111import {useLingui} from '@lingui/react'
12121313+import {cleanError} from '#/lib/strings/errors'
1314import {logger} from '#/logger'
1415import {isWeb} from '#/platform/detection'
1516import {useModerationOpts} from '#/state/preferences/moderation-opts'
1617import {useGetPopularFeedsQuery} from '#/state/queries/feed'
1718import {usePreferencesQuery} from '#/state/queries/preferences'
1819import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
1919-import {cleanError} from 'lib/strings/errors'
2020import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
2121import {List} from '#/view/com/util/List'
2222-import {UserAvatar} from '#/view/com/util/UserAvatar'
2322import {
2423 FeedFeedLoadingPlaceholder,
2524 ProfileCardFeedLoadingPlaceholder,
2626-} from 'view/com/util/LoadingPlaceholder'
2525+} from '#/view/com/util/LoadingPlaceholder'
2626+import {UserAvatar} from '#/view/com/util/UserAvatar'
2727import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
2828import {Button} from '#/components/Button'
2929import * as FeedCard from '#/components/FeedCard'
···564564 [t, moderationOpts],
565565 )
566566567567+ // note: actually not a screen, instead it's nested within
568568+ // the search screen. so we don't need Layout.Screen
567569 return (
568570 <List
569571 data={items}
+3-2
src/view/screens/Search/Search.tsx
···6565import {SearchInput} from '#/components/forms/SearchInput'
6666import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
6767import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
6868+import * as Layout from '#/components/Layout'
68696970function Loader() {
7071 const pal = usePalette('default')
···852853 }, [setShowAutocomplete])
853854854855 return (
855855- <View style={isWeb ? null : {flex: 1}}>
856856+ <Layout.Screen testID="searchScreen">
856857 <CenteredView
857858 style={[
858859 a.p_md,
···957958 headerHeight={headerHeight}
958959 />
959960 </View>
960960- </View>
961961+ </Layout.Screen>
961962 )
962963}
963964
+3-2
src/view/screens/Settings/index.tsx
···5757import {useDialogControl} from '#/components/Dialog'
5858import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
5959import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
6060+import * as Layout from '#/components/Layout'
6061import {Email2FAToggle} from './Email2FAToggle'
6162import {ExportCarDialog} from './ExportCarDialog'
6263···286287 const {mutate: onPressDeleteChatDeclaration} = useDeleteActorDeclaration()
287288288289 return (
289289- <View style={s.hContentRegion} testID="settingsScreen">
290290+ <Layout.Screen testID="settingsScreen">
290291 <ExportCarDialog control={exportCarControl} />
291292 <BirthDateSettingsDialog control={birthdayControl} />
292293···919920 </View>
920921 <View style={s.footerSpacer} />
921922 </ScrollView>
922922- </View>
923923+ </Layout.Screen>
923924 )
924925}
925926
+10-5
src/view/screens/Storybook/index.tsx
···77import {ListContained} from '#/view/screens/Storybook/ListContained'
88import {atoms as a, ThemeProvider, useTheme} from '#/alf'
99import {Button, ButtonText} from '#/components/Button'
1010+import * as Layout from '#/components/Layout'
1011import {Admonitions} from './Admonitions'
1112import {Breakpoints} from './Breakpoints'
1213import {Buttons} from './Buttons'
···2122import {Typography} from './Typography'
22232324export function Storybook() {
2424- if (isWeb) return <StorybookInner />
2525-2625 return (
2727- <ScrollView>
2828- <StorybookInner />
2929- </ScrollView>
2626+ <Layout.Screen>
2727+ {isWeb ? (
2828+ <StorybookInner />
2929+ ) : (
3030+ <ScrollView>
3131+ <StorybookInner />
3232+ </ScrollView>
3333+ )}
3434+ </Layout.Screen>
3035 )
3136}
3237
+14-13
src/view/screens/Support.tsx
···11import React from 'react'
22-import {View} from 'react-native'
22+import {msg, Trans} from '@lingui/macro'
33+import {useLingui} from '@lingui/react'
34import {useFocusEffect} from '@react-navigation/native'
44-import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
55-import {ViewHeader} from '../com/util/ViewHeader'
66-import {Text} from 'view/com/util/text/Text'
77-import {TextLink} from 'view/com/util/Link'
88-import {CenteredView} from 'view/com/util/Views'
99-import {usePalette} from 'lib/hooks/usePalette'
1010-import {s} from 'lib/styles'
1111-import {HELP_DESK_URL} from 'lib/constants'
55+66+import {HELP_DESK_URL} from '#/lib/constants'
77+import {usePalette} from '#/lib/hooks/usePalette'
88+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
99+import {s} from '#/lib/styles'
1210import {useSetMinimalShellMode} from '#/state/shell'
1313-import {Trans, msg} from '@lingui/macro'
1414-import {useLingui} from '@lingui/react'
1111+import {TextLink} from '#/view/com/util/Link'
1212+import {Text} from '#/view/com/util/text/Text'
1313+import {ViewHeader} from '#/view/com/util/ViewHeader'
1414+import {CenteredView} from '#/view/com/util/Views'
1515+import * as Layout from '#/components/Layout'
15161617type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'>
1718export const SupportScreen = (_props: Props) => {
···2627 )
27282829 return (
2929- <View>
3030+ <Layout.Screen>
3031 <ViewHeader title={_(msg`Support`)} />
3132 <CenteredView>
3233 <Text type="title-xl" style={[pal.text, s.p20, s.pb5]}>
···4445 </Trans>
4546 </Text>
4647 </CenteredView>
4747- </View>
4848+ </Layout.Screen>
4849 )
4950}
+13-11
src/view/screens/TermsOfService.tsx
···11import React from 'react'
22import {View} from 'react-native'
33+import {msg, Trans} from '@lingui/macro'
44+import {useLingui} from '@lingui/react'
35import {useFocusEffect} from '@react-navigation/native'
44-import {Text} from 'view/com/util/text/Text'
55-import {TextLink} from 'view/com/util/Link'
66-import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
77-import {ViewHeader} from '../com/util/ViewHeader'
88-import {ScrollView} from 'view/com/util/Views'
99-import {usePalette} from 'lib/hooks/usePalette'
1010-import {s} from 'lib/styles'
66+77+import {usePalette} from '#/lib/hooks/usePalette'
88+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
99+import {s} from '#/lib/styles'
1110import {useSetMinimalShellMode} from '#/state/shell'
1212-import {Trans, msg} from '@lingui/macro'
1313-import {useLingui} from '@lingui/react'
1111+import {TextLink} from '#/view/com/util/Link'
1212+import {Text} from '#/view/com/util/text/Text'
1313+import {ScrollView} from '#/view/com/util/Views'
1414+import * as Layout from '#/components/Layout'
1515+import {ViewHeader} from '../com/util/ViewHeader'
14161517type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
1618export const TermsOfServiceScreen = (_props: Props) => {
···2527 )
26282729 return (
2828- <View>
3030+ <Layout.Screen>
2931 <ViewHeader title={_(msg`Terms of Service`)} />
3032 <ScrollView style={[s.hContentRegion, pal.view]}>
3133 <View style={[s.p20]}>
···4042 </View>
4143 <View style={s.footerSpacer} />
4244 </ScrollView>
4343- </View>
4545+ </Layout.Screen>
4446 )
4547}
+9-19
src/view/shell/index.tsx
···11import React from 'react'
22-import {
33- BackHandler,
44- DimensionValue,
55- StyleSheet,
66- useWindowDimensions,
77- View,
88-} from 'react-native'
22+import {BackHandler, StyleSheet, useWindowDimensions, View} from 'react-native'
93import {Drawer} from 'react-native-drawer-layout'
104import Animated from 'react-native-reanimated'
1111-import {useSafeAreaInsets} from 'react-native-safe-area-context'
125import * as NavigationBar from 'expo-navigation-bar'
136import {StatusBar} from 'expo-status-bar'
147import {useNavigation, useNavigationState} from '@react-navigation/native'
···3225import {Lightbox} from '#/view/com/lightbox/Lightbox'
3326import {ModalsContainer} from '#/view/com/modals/Modal'
3427import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
2828+import {atoms as a} from '#/alf'
3529import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
3630import {SigninDialog} from '#/components/dialogs/Signin'
3731import {Outlet as PortalOutlet} from '#/components/Portal'
···4640 const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
4741 const setIsDrawerOpen = useSetDrawerOpen()
4842 const winDim = useWindowDimensions()
4949- const safeAreaInsets = useSafeAreaInsets()
5050- const containerPadding = React.useMemo(
5151- () => ({height: '100%' as DimensionValue, paddingTop: safeAreaInsets.top}),
5252- [safeAreaInsets],
5353- )
4343+5444 const renderDrawerContent = React.useCallback(() => <DrawerContent />, [])
5545 const onOpenDrawer = React.useCallback(
5646 () => setIsDrawerOpen(true),
···6858 useNotificationsHandler()
69597060 React.useEffect(() => {
7171- let listener = {remove() {}}
7261 if (isAndroid) {
7373- listener = BackHandler.addEventListener('hardwareBackPress', () => {
6262+ const listener = BackHandler.addEventListener('hardwareBackPress', () => {
7463 return closeAnyActiveElement()
7564 })
7676- }
7777- return () => {
7878- listener.remove()
6565+6666+ return () => {
6767+ listener.remove()
6868+ }
7969 }
8070 }, [closeAnyActiveElement])
8171···1029210393 return (
10494 <>
105105- <Animated.View style={containerPadding}>
9595+ <Animated.View style={[a.h_full]}>
10696 <ErrorBoundary>
10797 <Drawer
10898 renderDrawerContent={renderDrawerContent}
+8-7
src/view/shell/index.web.tsx
···77import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
88import {useIntentHandler} from '#/lib/hooks/useIntentHandler'
99import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
1010+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
1011import {NavigationProp} from '#/lib/routes/types'
1111-import {colors, s} from '#/lib/styles'
1212+import {colors} from '#/lib/styles'
1213import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
1314import {useComposerKeyboardShortcut} from '#/state/shell/composer/useComposerKeyboardShortcut'
1415import {useCloseAllActiveElements} from '#/state/util'
1616+import {Lightbox} from '#/view/com/lightbox/Lightbox'
1717+import {ModalsContainer} from '#/view/com/modals/Modal'
1818+import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
1919+import {atoms as a} from '#/alf'
1520import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
1621import {SigninDialog} from '#/components/dialogs/Signin'
1722import {Outlet as PortalOutlet} from '#/components/Portal'
1818-import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
1919-import {FlatNavigator, RoutesContainer} from '../../Navigation'
2020-import {Lightbox} from '../com/lightbox/Lightbox'
2121-import {ModalsContainer} from '../com/modals/Modal'
2222-import {ErrorBoundary} from '../com/util/ErrorBoundary'
2323+import {FlatNavigator, RoutesContainer} from '#/Navigation'
2324import {Composer} from './Composer.web'
2425import {DrawerContent} from './Drawer'
2526···7879export const Shell: React.FC = function ShellImpl() {
7980 const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
8081 return (
8181- <View style={[s.hContentRegion, pageBg]}>
8282+ <View style={[a.util_screen_outer, pageBg]}>
8283 <RoutesContainer>
8384 <ShellInner />
8485 </RoutesContainer>