···1+import React from 'react'
2+import {View, ViewStyle} from 'react-native'
3+import {StyleProp} from 'react-native'
4+import {useSafeAreaInsets} from 'react-native-safe-area-context'
5+6+import {atoms as a} from '#/alf'
7+8+// Every screen should have a Layout component wrapping it.
9+// This component provides a default padding for the top of the screen.
10+// This allows certain screens to avoid the top padding if they want to.
11+//
12+// In a future PR I will add a unified header component to this file and
13+// things like a preconfigured scrollview.
14+15+/**
16+ * Every screen should have a Layout.Screen component wrapping it.
17+ * This component provides a default padding for the top of the screen
18+ * and height/minHeight
19+ */
20+let Screen = ({
21+ disableTopPadding,
22+ style,
23+ ...props
24+}: React.ComponentProps<typeof View> & {
25+ disableTopPadding?: boolean
26+ style?: StyleProp<ViewStyle>
27+}): React.ReactNode => {
28+ const {top} = useSafeAreaInsets()
29+ return (
30+ <View
31+ style={[
32+ {paddingTop: disableTopPadding ? 0 : top},
33+ a.util_screen_outer,
34+ style,
35+ ]}
36+ {...props}
37+ />
38+ )
39+}
40+Screen = React.memo(Screen)
41+export {Screen}
···56import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
7import {makeRecordUri} from '#/lib/strings/url-helpers'
08import {useSetMinimalShellMode} from '#/state/shell'
9-import {isWeb} from 'platform/detection'
10import {PostLikedBy as PostLikedByComponent} from '#/view/com/post-thread/PostLikedBy'
11import {ViewHeader} from '#/view/com/util/ViewHeader'
12-import {CenteredView} from 'view/com/util/Views'
13-import {atoms as a} from '#/alf'
14import {ListHeaderDesktop} from '#/components/Lists'
1516type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
···27 )
2829 return (
30- <CenteredView style={a.util_screen_outer} sideBorders={true}>
31- <ListHeaderDesktop title={_(msg`Liked By`)} />
32- <ViewHeader title={_(msg`Liked By`)} showBorder={!isWeb} />
33- <PostLikedByComponent uri={uri} />
34- </CenteredView>
0035 )
36}
···56import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
7import {makeRecordUri} from '#/lib/strings/url-helpers'
8+import {isWeb} from '#/platform/detection'
9import {useSetMinimalShellMode} from '#/state/shell'
010import {PostLikedBy as PostLikedByComponent} from '#/view/com/post-thread/PostLikedBy'
11import {ViewHeader} from '#/view/com/util/ViewHeader'
12+import {CenteredView} from '#/view/com/util/Views'
13+import * as Layout from '#/components/Layout'
14import {ListHeaderDesktop} from '#/components/Lists'
1516type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
···27 )
2829 return (
30+ <Layout.Screen>
31+ <CenteredView sideBorders={true}>
32+ <ListHeaderDesktop title={_(msg`Liked By`)} />
33+ <ViewHeader title={_(msg`Liked By`)} showBorder={!isWeb} />
34+ <PostLikedByComponent uri={uri} />
35+ </CenteredView>
36+ </Layout.Screen>
37 )
38}
+10-8
src/screens/Post/PostQuotes.tsx
···56import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
7import {makeRecordUri} from '#/lib/strings/url-helpers'
08import {useSetMinimalShellMode} from '#/state/shell'
9-import {isWeb} from 'platform/detection'
10import {PostQuotes as PostQuotesComponent} from '#/view/com/post-thread/PostQuotes'
11import {ViewHeader} from '#/view/com/util/ViewHeader'
12-import {CenteredView} from 'view/com/util/Views'
13-import {atoms as a} from '#/alf'
14import {ListHeaderDesktop} from '#/components/Lists'
1516type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostQuotes'>
···27 )
2829 return (
30- <CenteredView style={a.util_screen_outer} sideBorders={true}>
31- <ListHeaderDesktop title={_(msg`Quotes`)} />
32- <ViewHeader title={_(msg`Quotes`)} showBorder={!isWeb} />
33- <PostQuotesComponent uri={uri} />
34- </CenteredView>
0035 )
36}
···56import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
7import {makeRecordUri} from '#/lib/strings/url-helpers'
8+import {isWeb} from '#/platform/detection'
9import {useSetMinimalShellMode} from '#/state/shell'
010import {PostQuotes as PostQuotesComponent} from '#/view/com/post-thread/PostQuotes'
11import {ViewHeader} from '#/view/com/util/ViewHeader'
12+import {CenteredView} from '#/view/com/util/Views'
13+import * as Layout from '#/components/Layout'
14import {ListHeaderDesktop} from '#/components/Lists'
1516type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostQuotes'>
···27 )
2829 return (
30+ <Layout.Screen>
31+ <CenteredView sideBorders={true}>
32+ <ListHeaderDesktop title={_(msg`Quotes`)} />
33+ <ViewHeader title={_(msg`Quotes`)} showBorder={!isWeb} />
34+ <PostQuotesComponent uri={uri} />
35+ </CenteredView>
36+ </Layout.Screen>
37 )
38}
+10-8
src/screens/Post/PostRepostedBy.tsx
···56import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
7import {makeRecordUri} from '#/lib/strings/url-helpers'
08import {useSetMinimalShellMode} from '#/state/shell'
9-import {isWeb} from 'platform/detection'
10import {PostRepostedBy as PostRepostedByComponent} from '#/view/com/post-thread/PostRepostedBy'
11import {ViewHeader} from '#/view/com/util/ViewHeader'
12-import {CenteredView} from 'view/com/util/Views'
13-import {atoms as a} from '#/alf'
14import {ListHeaderDesktop} from '#/components/Lists'
1516type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
···27 )
2829 return (
30- <CenteredView style={a.util_screen_outer} sideBorders={true}>
31- <ListHeaderDesktop title={_(msg`Reposted By`)} />
32- <ViewHeader title={_(msg`Reposted By`)} showBorder={!isWeb} />
33- <PostRepostedByComponent uri={uri} />
34- </CenteredView>
0035 )
36}
···56import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
7import {makeRecordUri} from '#/lib/strings/url-helpers'
8+import {isWeb} from '#/platform/detection'
9import {useSetMinimalShellMode} from '#/state/shell'
010import {PostRepostedBy as PostRepostedByComponent} from '#/view/com/post-thread/PostRepostedBy'
11import {ViewHeader} from '#/view/com/util/ViewHeader'
12+import {CenteredView} from '#/view/com/util/Views'
13+import * as Layout from '#/components/Layout'
14import {ListHeaderDesktop} from '#/components/Lists'
1516type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
···27 )
2829 return (
30+ <Layout.Screen>
31+ <CenteredView sideBorders={true}>
32+ <ListHeaderDesktop title={_(msg`Reposted By`)} />
33+ <ViewHeader title={_(msg`Reposted By`)} showBorder={!isWeb} />
34+ <PostRepostedByComponent uri={uri} />
35+ </CenteredView>
36+ </Layout.Screen>
37 )
38}
+15-13
src/screens/Profile/KnownFollowers.tsx
···1import React from 'react'
2-import {View} from 'react-native'
3import {AppBskyActorDefs} from '@atproto/api'
4import {msg} from '@lingui/macro'
5import {useLingui} from '@lingui/react'
6import {useFocusEffect} from '@react-navigation/native'
7008import {cleanError} from '#/lib/strings/errors'
9import {logger} from '#/logger'
10import {useProfileKnownFollowersQuery} from '#/state/queries/known-followers'
11import {useResolveDidQuery} from '#/state/queries/resolve-uri'
12import {useSetMinimalShellMode} from '#/state/shell'
13-import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
14-import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
15import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
16import {List} from '#/view/com/util/List'
17import {ViewHeader} from '#/view/com/util/ViewHeader'
018import {
19 ListFooter,
20 ListHeaderDesktop,
···9293 if (followers.length < 1) {
94 return (
95- <ListMaybePlaceholder
96- isLoading={isDidLoading || isFollowersLoading}
97- isError={isError}
98- emptyType="results"
99- emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
100- errorMessage={cleanError(resolveError || error)}
101- onRetry={isError ? refetch : undefined}
102- />
00103 )
104 }
105106 return (
107- <View style={{flex: 1}}>
108 <ViewHeader title={_(msg`Followers you know`)} />
109 <List
110 data={followers}
···129 initialNumToRender={initialNumToRender}
130 windowSize={11}
131 />
132- </View>
133 )
134}
···1import React from 'react'
02import {AppBskyActorDefs} from '@atproto/api'
3import {msg} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5import {useFocusEffect} from '@react-navigation/native'
67+import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
8+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
9import {cleanError} from '#/lib/strings/errors'
10import {logger} from '#/logger'
11import {useProfileKnownFollowersQuery} from '#/state/queries/known-followers'
12import {useResolveDidQuery} from '#/state/queries/resolve-uri'
13import {useSetMinimalShellMode} from '#/state/shell'
0014import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
15import {List} from '#/view/com/util/List'
16import {ViewHeader} from '#/view/com/util/ViewHeader'
17+import * as Layout from '#/components/Layout'
18import {
19 ListFooter,
20 ListHeaderDesktop,
···9293 if (followers.length < 1) {
94 return (
95+ <Layout.Screen>
96+ <ListMaybePlaceholder
97+ isLoading={isDidLoading || isFollowersLoading}
98+ isError={isError}
99+ emptyType="results"
100+ emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
101+ errorMessage={cleanError(resolveError || error)}
102+ onRetry={isError ? refetch : undefined}
103+ />
104+ </Layout.Screen>
105 )
106 }
107108 return (
109+ <Layout.Screen>
110 <ViewHeader title={_(msg`Followers you know`)} />
111 <List
112 data={followers}
···131 initialNumToRender={initialNumToRender}
132 windowSize={11}
133 />
134+ </Layout.Screen>
135 )
136}
+3-3
src/screens/Profile/ProfileLabelerLikedBy.tsx
···1import React from 'react'
2-import {View} from 'react-native'
3import {msg} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5import {useFocusEffect} from '@react-navigation/native'
···8import {makeRecordUri} from '#/lib/strings/url-helpers'
9import {useSetMinimalShellMode} from '#/state/shell'
10import {ViewHeader} from '#/view/com/util/ViewHeader'
011import {LikedByList} from '#/components/LikedByList'
1213export function ProfileLabelerLikedByScreen({
···25 )
2627 return (
28- <View style={{flex: 1}}>
29 <ViewHeader title={_(msg`Liked By`)} />
30 <LikedByList uri={uri} />
31- </View>
32 )
33}
···1import React from 'react'
02import {msg} from '@lingui/macro'
3import {useLingui} from '@lingui/react'
4import {useFocusEffect} from '@react-navigation/native'
···7import {makeRecordUri} from '#/lib/strings/url-helpers'
8import {useSetMinimalShellMode} from '#/state/shell'
9import {ViewHeader} from '#/view/com/util/ViewHeader'
10+import * as Layout from '#/components/Layout'
11import {LikedByList} from '#/components/LikedByList'
1213export function ProfileLabelerLikedByScreen({
···25 )
2627 return (
28+ <Layout.Screen>
29 <ViewHeader title={_(msg`Liked By`)} />
30 <LikedByList uri={uri} />
31+ </Layout.Screen>
32 )
33}
+3-3
src/screens/Settings/AppearanceSettings.tsx
···1011import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
12import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
13-import {s} from '#/lib/styles'
14import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
15import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
16import {ScrollView} from '#/view/com/util/Views'
···21import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone'
22import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/TextSize'
23import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase'
024import {Text} from '#/components/Typography'
2526type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'>
···7677 return (
78 <LayoutAnimationConfig skipExiting skipEntering>
79- <View testID="preferencesThreadsScreen" style={s.hContentRegion}>
80 <ScrollView
81 // @ts-ignore web only -prf
82 dataSet={{'stable-gutters': 1}}
···180 </View>
181 </View>
182 </ScrollView>
183- </View>
184 </LayoutAnimationConfig>
185 )
186}
···1011import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
12import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
013import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
14import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
15import {ScrollView} from '#/view/com/util/Views'
···20import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone'
21import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/TextSize'
22import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase'
23+import * as Layout from '#/components/Layout'
24import {Text} from '#/components/Typography'
2526type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'>
···7677 return (
78 <LayoutAnimationConfig skipExiting skipEntering>
79+ <Layout.Screen testID="preferencesThreadsScreen">
80 <ScrollView
81 // @ts-ignore web only -prf
82 dataSet={{'stable-gutters': 1}}
···180 </View>
181 </View>
182 </ScrollView>
183+ </Layout.Screen>
184 </LayoutAnimationConfig>
185 )
186}
+19-8
src/screens/StarterPack/StarterPackScreen.tsx
···53import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
54import {Pencil_Stroke2_Corner0_Rounded as Pencil} from '#/components/icons/Pencil'
55import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
056import {ListMaybePlaceholder} from '#/components/Lists'
57import {Loader} from '#/components/Loader'
58import * as Menu from '#/components/Menu'
···76>
7778export function StarterPackScreen({route}: StarterPackScreeProps) {
79- return <StarterPackScreenInner routeParams={route.params} />
000080}
8182export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
···9192 if (isLoading || isError || !resolvedStarterPack) {
93 return (
94- <ListMaybePlaceholder
95- isLoading={isLoading}
96- isError={isError}
97- errorMessage={_(msg`That starter pack could not be found.`)}
98- emptyMessage={_(msg`That starter pack could not be found.`)}
99- />
00100 )
101 }
102- return <StarterPackScreenInner routeParams={resolvedStarterPack} />
0000103}
104105export function StarterPackScreenInner({
···53import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
54import {Pencil_Stroke2_Corner0_Rounded as Pencil} from '#/components/icons/Pencil'
55import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
56+import * as Layout from '#/components/Layout'
57import {ListMaybePlaceholder} from '#/components/Lists'
58import {Loader} from '#/components/Loader'
59import * as Menu from '#/components/Menu'
···77>
7879export function StarterPackScreen({route}: StarterPackScreeProps) {
80+ return (
81+ <Layout.Screen>
82+ <StarterPackScreenInner routeParams={route.params} />
83+ </Layout.Screen>
84+ )
85}
8687export function StarterPackScreenShort({route}: StarterPackScreenShortProps) {
···9697 if (isLoading || isError || !resolvedStarterPack) {
98 return (
99+ <Layout.Screen>
100+ <ListMaybePlaceholder
101+ isLoading={isLoading}
102+ isError={isError}
103+ errorMessage={_(msg`That starter pack could not be found.`)}
104+ emptyMessage={_(msg`That starter pack could not be found.`)}
105+ />
106+ </Layout.Screen>
107 )
108 }
109+ return (
110+ <Layout.Screen>
111+ <StarterPackScreenInner routeParams={resolvedStarterPack} />
112+ </Layout.Screen>
113+ )
114}
115116export function StarterPackScreenInner({
+45-38
src/screens/StarterPack/Wizard/index.tsx
···19import {useFocusEffect, useNavigation} from '@react-navigation/native'
20import {NativeStackScreenProps} from '@react-navigation/native-stack'
2122-import {logger} from '#/logger'
23-import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from 'lib/constants'
24-import {createSanitizedDisplayName} from 'lib/moderation/create-sanitized-display-name'
25-import {CommonNavigatorParams, NavigationProp} from 'lib/routes/types'
26-import {logEvent} from 'lib/statsig/statsig'
27-import {sanitizeDisplayName} from 'lib/strings/display-names'
28-import {sanitizeHandle} from 'lib/strings/handles'
29-import {enforceLen} from 'lib/strings/helpers'
30import {
31 getStarterPackOgCard,
32 parseStarterPackUri,
33-} from 'lib/strings/starter-pack'
34-import {isAndroid, isNative, isWeb} from 'platform/detection'
35-import {useModerationOpts} from 'state/preferences/moderation-opts'
36-import {useAllListMembersQuery} from 'state/queries/list-members'
37-import {useProfileQuery} from 'state/queries/profile'
038import {
39 useCreateStarterPackMutation,
40 useEditStarterPackMutation,
41 useStarterPackQuery,
42-} from 'state/queries/starter-packs'
43-import {useSession} from 'state/session'
44-import {useSetMinimalShellMode} from 'state/shell'
45import * as Toast from '#/view/com/util/Toast'
46-import {UserAvatar} from 'view/com/util/UserAvatar'
47-import {CenteredView} from 'view/com/util/Views'
48import {useWizardState, WizardStep} from '#/screens/StarterPack/Wizard/State'
49import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails'
50import {StepFeeds} from '#/screens/StarterPack/Wizard/StepFeeds'
···52import {atoms as a, useTheme} from '#/alf'
53import {Button, ButtonText} from '#/components/Button'
54import {useDialogControl} from '#/components/Dialog'
055import {ListMaybePlaceholder} from '#/components/Lists'
56import {Loader} from '#/components/Loader'
57import {WizardEditListDialog} from '#/components/StarterPack/Wizard/WizardEditListDialog'
···9798 if (!isReady) {
99 return (
100- <ListMaybePlaceholder
101- isLoading={
102- isLoadingStarterPack || isLoadingProfiles || isLoadingProfile
103- }
104- isError={isErrorStarterPack || isErrorProfiles || isErrorProfile}
105- errorMessage={_(msg`That starter pack could not be found.`)}
106- />
00107 )
108 } else if (isEdit && starterPack?.creator.did !== currentAccount?.did) {
109 return (
110- <ListMaybePlaceholder
111- isLoading={false}
112- isError={true}
113- errorMessage={_(msg`That starter pack could not be found.`)}
114- />
00115 )
116 }
117118 return (
119- <Provider starterPack={starterPack} listItems={listItems}>
120- <WizardInner
121- currentStarterPack={starterPack}
122- currentListItems={listItems}
123- profile={profile}
124- moderationOpts={moderationOpts}
125- />
126- </Provider>
00127 )
128}
129
···19import {useFocusEffect, useNavigation} from '@react-navigation/native'
20import {NativeStackScreenProps} from '@react-navigation/native-stack'
2122+import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
23+import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
24+import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
25+import {logEvent} from '#/lib/statsig/statsig'
26+import {sanitizeDisplayName} from '#/lib/strings/display-names'
27+import {sanitizeHandle} from '#/lib/strings/handles'
28+import {enforceLen} from '#/lib/strings/helpers'
029import {
30 getStarterPackOgCard,
31 parseStarterPackUri,
32+} from '#/lib/strings/starter-pack'
33+import {logger} from '#/logger'
34+import {isAndroid, isNative, isWeb} from '#/platform/detection'
35+import {useModerationOpts} from '#/state/preferences/moderation-opts'
36+import {useAllListMembersQuery} from '#/state/queries/list-members'
37+import {useProfileQuery} from '#/state/queries/profile'
38import {
39 useCreateStarterPackMutation,
40 useEditStarterPackMutation,
41 useStarterPackQuery,
42+} from '#/state/queries/starter-packs'
43+import {useSession} from '#/state/session'
44+import {useSetMinimalShellMode} from '#/state/shell'
45import * as Toast from '#/view/com/util/Toast'
46+import {UserAvatar} from '#/view/com/util/UserAvatar'
47+import {CenteredView} from '#/view/com/util/Views'
48import {useWizardState, WizardStep} from '#/screens/StarterPack/Wizard/State'
49import {StepDetails} from '#/screens/StarterPack/Wizard/StepDetails'
50import {StepFeeds} from '#/screens/StarterPack/Wizard/StepFeeds'
···52import {atoms as a, useTheme} from '#/alf'
53import {Button, ButtonText} from '#/components/Button'
54import {useDialogControl} from '#/components/Dialog'
55+import * as Layout from '#/components/Layout'
56import {ListMaybePlaceholder} from '#/components/Lists'
57import {Loader} from '#/components/Loader'
58import {WizardEditListDialog} from '#/components/StarterPack/Wizard/WizardEditListDialog'
···9899 if (!isReady) {
100 return (
101+ <Layout.Screen>
102+ <ListMaybePlaceholder
103+ isLoading={
104+ isLoadingStarterPack || isLoadingProfiles || isLoadingProfile
105+ }
106+ isError={isErrorStarterPack || isErrorProfiles || isErrorProfile}
107+ errorMessage={_(msg`That starter pack could not be found.`)}
108+ />
109+ </Layout.Screen>
110 )
111 } else if (isEdit && starterPack?.creator.did !== currentAccount?.did) {
112 return (
113+ <Layout.Screen>
114+ <ListMaybePlaceholder
115+ isLoading={false}
116+ isError={true}
117+ errorMessage={_(msg`That starter pack could not be found.`)}
118+ />
119+ </Layout.Screen>
120 )
121 }
122123 return (
124+ <Layout.Screen>
125+ <Provider starterPack={starterPack} listItems={listItems}>
126+ <WizardInner
127+ currentStarterPack={starterPack}
128+ currentListItems={listItems}
129+ profile={profile}
130+ moderationOpts={moderationOpts}
131+ />
132+ </Provider>
133+ </Layout.Screen>
134 )
135}
136
···10import {msg, Trans} from '@lingui/macro'
11import {useLingui} from '@lingui/react'
12013import {logger} from '#/logger'
14import {isWeb} from '#/platform/detection'
15import {useModerationOpts} from '#/state/preferences/moderation-opts'
16import {useGetPopularFeedsQuery} from '#/state/queries/feed'
17import {usePreferencesQuery} from '#/state/queries/preferences'
18import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
19-import {cleanError} from 'lib/strings/errors'
20import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
21import {List} from '#/view/com/util/List'
22-import {UserAvatar} from '#/view/com/util/UserAvatar'
23import {
24 FeedFeedLoadingPlaceholder,
25 ProfileCardFeedLoadingPlaceholder,
26-} from 'view/com/util/LoadingPlaceholder'
027import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
28import {Button} from '#/components/Button'
29import * as FeedCard from '#/components/FeedCard'
···564 [t, moderationOpts],
565 )
56600567 return (
568 <List
569 data={items}
···10import {msg, Trans} from '@lingui/macro'
11import {useLingui} from '@lingui/react'
1213+import {cleanError} from '#/lib/strings/errors'
14import {logger} from '#/logger'
15import {isWeb} from '#/platform/detection'
16import {useModerationOpts} from '#/state/preferences/moderation-opts'
17import {useGetPopularFeedsQuery} from '#/state/queries/feed'
18import {usePreferencesQuery} from '#/state/queries/preferences'
19import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows'
020import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
21import {List} from '#/view/com/util/List'
022import {
23 FeedFeedLoadingPlaceholder,
24 ProfileCardFeedLoadingPlaceholder,
25+} from '#/view/com/util/LoadingPlaceholder'
26+import {UserAvatar} from '#/view/com/util/UserAvatar'
27import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
28import {Button} from '#/components/Button'
29import * as FeedCard from '#/components/FeedCard'
···564 [t, moderationOpts],
565 )
566567+ // note: actually not a screen, instead it's nested within
568+ // the search screen. so we don't need Layout.Screen
569 return (
570 <List
571 data={items}
+3-2
src/view/screens/Search/Search.tsx
···65import {SearchInput} from '#/components/forms/SearchInput'
66import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
67import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
06869function Loader() {
70 const pal = usePalette('default')
···852 }, [setShowAutocomplete])
853854 return (
855- <View style={isWeb ? null : {flex: 1}}>
856 <CenteredView
857 style={[
858 a.p_md,
···957 headerHeight={headerHeight}
958 />
959 </View>
960- </View>
961 )
962}
963
···65import {SearchInput} from '#/components/forms/SearchInput'
66import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
67import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
68+import * as Layout from '#/components/Layout'
6970function Loader() {
71 const pal = usePalette('default')
···853 }, [setShowAutocomplete])
854855 return (
856+ <Layout.Screen testID="searchScreen">
857 <CenteredView
858 style={[
859 a.p_md,
···958 headerHeight={headerHeight}
959 />
960 </View>
961+ </Layout.Screen>
962 )
963}
964
···57import {useDialogControl} from '#/components/Dialog'
58import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
59import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
60+import * as Layout from '#/components/Layout'
61import {Email2FAToggle} from './Email2FAToggle'
62import {ExportCarDialog} from './ExportCarDialog'
63···287 const {mutate: onPressDeleteChatDeclaration} = useDeleteActorDeclaration()
288289 return (
290+ <Layout.Screen testID="settingsScreen">
291 <ExportCarDialog control={exportCarControl} />
292 <BirthDateSettingsDialog control={birthdayControl} />
293···920 </View>
921 <View style={s.footerSpacer} />
922 </ScrollView>
923+ </Layout.Screen>
924 )
925}
926
+10-5
src/view/screens/Storybook/index.tsx
···7import {ListContained} from '#/view/screens/Storybook/ListContained'
8import {atoms as a, ThemeProvider, useTheme} from '#/alf'
9import {Button, ButtonText} from '#/components/Button'
010import {Admonitions} from './Admonitions'
11import {Breakpoints} from './Breakpoints'
12import {Buttons} from './Buttons'
···21import {Typography} from './Typography'
2223export function Storybook() {
24- if (isWeb) return <StorybookInner />
25-26 return (
27- <ScrollView>
28- <StorybookInner />
29- </ScrollView>
00000030 )
31}
32
···7import {ListContained} from '#/view/screens/Storybook/ListContained'
8import {atoms as a, ThemeProvider, useTheme} from '#/alf'
9import {Button, ButtonText} from '#/components/Button'
10+import * as Layout from '#/components/Layout'
11import {Admonitions} from './Admonitions'
12import {Breakpoints} from './Breakpoints'
13import {Buttons} from './Buttons'
···22import {Typography} from './Typography'
2324export function Storybook() {
0025 return (
26+ <Layout.Screen>
27+ {isWeb ? (
28+ <StorybookInner />
29+ ) : (
30+ <ScrollView>
31+ <StorybookInner />
32+ </ScrollView>
33+ )}
34+ </Layout.Screen>
35 )
36}
37
+14-13
src/view/screens/Support.tsx
···1import React from 'react'
2-import {View} from 'react-native'
03import {useFocusEffect} from '@react-navigation/native'
4-import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
5-import {ViewHeader} from '../com/util/ViewHeader'
6-import {Text} from 'view/com/util/text/Text'
7-import {TextLink} from 'view/com/util/Link'
8-import {CenteredView} from 'view/com/util/Views'
9-import {usePalette} from 'lib/hooks/usePalette'
10-import {s} from 'lib/styles'
11-import {HELP_DESK_URL} from 'lib/constants'
12import {useSetMinimalShellMode} from '#/state/shell'
13-import {Trans, msg} from '@lingui/macro'
14-import {useLingui} from '@lingui/react'
0001516type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'>
17export const SupportScreen = (_props: Props) => {
···26 )
2728 return (
29- <View>
30 <ViewHeader title={_(msg`Support`)} />
31 <CenteredView>
32 <Text type="title-xl" style={[pal.text, s.p20, s.pb5]}>
···44 </Trans>
45 </Text>
46 </CenteredView>
47- </View>
48 )
49}
···1import React from 'react'
2+import {msg, Trans} from '@lingui/macro'
3+import {useLingui} from '@lingui/react'
4import {useFocusEffect} from '@react-navigation/native'
5+6+import {HELP_DESK_URL} from '#/lib/constants'
7+import {usePalette} from '#/lib/hooks/usePalette'
8+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
9+import {s} from '#/lib/styles'
00010import {useSetMinimalShellMode} from '#/state/shell'
11+import {TextLink} from '#/view/com/util/Link'
12+import {Text} from '#/view/com/util/text/Text'
13+import {ViewHeader} from '#/view/com/util/ViewHeader'
14+import {CenteredView} from '#/view/com/util/Views'
15+import * as Layout from '#/components/Layout'
1617type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'>
18export const SupportScreen = (_props: Props) => {
···27 )
2829 return (
30+ <Layout.Screen>
31 <ViewHeader title={_(msg`Support`)} />
32 <CenteredView>
33 <Text type="title-xl" style={[pal.text, s.p20, s.pb5]}>
···45 </Trans>
46 </Text>
47 </CenteredView>
48+ </Layout.Screen>
49 )
50}
+13-11
src/view/screens/TermsOfService.tsx
···1import React from 'react'
2import {View} from 'react-native'
003import {useFocusEffect} from '@react-navigation/native'
4-import {Text} from 'view/com/util/text/Text'
5-import {TextLink} from 'view/com/util/Link'
6-import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
7-import {ViewHeader} from '../com/util/ViewHeader'
8-import {ScrollView} from 'view/com/util/Views'
9-import {usePalette} from 'lib/hooks/usePalette'
10-import {s} from 'lib/styles'
11import {useSetMinimalShellMode} from '#/state/shell'
12-import {Trans, msg} from '@lingui/macro'
13-import {useLingui} from '@lingui/react'
0001415type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
16export const TermsOfServiceScreen = (_props: Props) => {
···25 )
2627 return (
28- <View>
29 <ViewHeader title={_(msg`Terms of Service`)} />
30 <ScrollView style={[s.hContentRegion, pal.view]}>
31 <View style={[s.p20]}>
···40 </View>
41 <View style={s.footerSpacer} />
42 </ScrollView>
43- </View>
44 )
45}
···1import React from 'react'
2import {View} from 'react-native'
3+import {msg, Trans} from '@lingui/macro'
4+import {useLingui} from '@lingui/react'
5import {useFocusEffect} from '@react-navigation/native'
6+7+import {usePalette} from '#/lib/hooks/usePalette'
8+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
9+import {s} from '#/lib/styles'
00010import {useSetMinimalShellMode} from '#/state/shell'
11+import {TextLink} from '#/view/com/util/Link'
12+import {Text} from '#/view/com/util/text/Text'
13+import {ScrollView} from '#/view/com/util/Views'
14+import * as Layout from '#/components/Layout'
15+import {ViewHeader} from '../com/util/ViewHeader'
1617type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'>
18export const TermsOfServiceScreen = (_props: Props) => {
···27 )
2829 return (
30+ <Layout.Screen>
31 <ViewHeader title={_(msg`Terms of Service`)} />
32 <ScrollView style={[s.hContentRegion, pal.view]}>
33 <View style={[s.p20]}>
···42 </View>
43 <View style={s.footerSpacer} />
44 </ScrollView>
45+ </Layout.Screen>
46 )
47}
+9-19
src/view/shell/index.tsx
···1import React from 'react'
2-import {
3- BackHandler,
4- DimensionValue,
5- StyleSheet,
6- useWindowDimensions,
7- View,
8-} from 'react-native'
9import {Drawer} from 'react-native-drawer-layout'
10import Animated from 'react-native-reanimated'
11-import {useSafeAreaInsets} from 'react-native-safe-area-context'
12import * as NavigationBar from 'expo-navigation-bar'
13import {StatusBar} from 'expo-status-bar'
14import {useNavigation, useNavigationState} from '@react-navigation/native'
···32import {Lightbox} from '#/view/com/lightbox/Lightbox'
33import {ModalsContainer} from '#/view/com/modals/Modal'
34import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
035import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
36import {SigninDialog} from '#/components/dialogs/Signin'
37import {Outlet as PortalOutlet} from '#/components/Portal'
···46 const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
47 const setIsDrawerOpen = useSetDrawerOpen()
48 const winDim = useWindowDimensions()
49- const safeAreaInsets = useSafeAreaInsets()
50- const containerPadding = React.useMemo(
51- () => ({height: '100%' as DimensionValue, paddingTop: safeAreaInsets.top}),
52- [safeAreaInsets],
53- )
54 const renderDrawerContent = React.useCallback(() => <DrawerContent />, [])
55 const onOpenDrawer = React.useCallback(
56 () => setIsDrawerOpen(true),
···68 useNotificationsHandler()
6970 React.useEffect(() => {
71- let listener = {remove() {}}
72 if (isAndroid) {
73- listener = BackHandler.addEventListener('hardwareBackPress', () => {
74 return closeAnyActiveElement()
75 })
76- }
77- return () => {
78- listener.remove()
079 }
80 }, [closeAnyActiveElement])
81···102103 return (
104 <>
105- <Animated.View style={containerPadding}>
106 <ErrorBoundary>
107 <Drawer
108 renderDrawerContent={renderDrawerContent}
···1import React from 'react'
2+import {BackHandler, StyleSheet, useWindowDimensions, View} from 'react-native'
0000003import {Drawer} from 'react-native-drawer-layout'
4import Animated from 'react-native-reanimated'
05import * as NavigationBar from 'expo-navigation-bar'
6import {StatusBar} from 'expo-status-bar'
7import {useNavigation, useNavigationState} from '@react-navigation/native'
···25import {Lightbox} from '#/view/com/lightbox/Lightbox'
26import {ModalsContainer} from '#/view/com/modals/Modal'
27import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
28+import {atoms as a} from '#/alf'
29import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
30import {SigninDialog} from '#/components/dialogs/Signin'
31import {Outlet as PortalOutlet} from '#/components/Portal'
···40 const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
41 const setIsDrawerOpen = useSetDrawerOpen()
42 const winDim = useWindowDimensions()
43+000044 const renderDrawerContent = React.useCallback(() => <DrawerContent />, [])
45 const onOpenDrawer = React.useCallback(
46 () => setIsDrawerOpen(true),
···58 useNotificationsHandler()
5960 React.useEffect(() => {
061 if (isAndroid) {
62+ const listener = BackHandler.addEventListener('hardwareBackPress', () => {
63 return closeAnyActiveElement()
64 })
65+66+ return () => {
67+ listener.remove()
68+ }
69 }
70 }, [closeAnyActiveElement])
71···9293 return (
94 <>
95+ <Animated.View style={[a.h_full]}>
96 <ErrorBoundary>
97 <Drawer
98 renderDrawerContent={renderDrawerContent}
+8-7
src/view/shell/index.web.tsx
···7import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
8import {useIntentHandler} from '#/lib/hooks/useIntentHandler'
9import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
010import {NavigationProp} from '#/lib/routes/types'
11-import {colors, s} from '#/lib/styles'
12import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
13import {useComposerKeyboardShortcut} from '#/state/shell/composer/useComposerKeyboardShortcut'
14import {useCloseAllActiveElements} from '#/state/util'
000015import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
16import {SigninDialog} from '#/components/dialogs/Signin'
17import {Outlet as PortalOutlet} from '#/components/Portal'
18-import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
19-import {FlatNavigator, RoutesContainer} from '../../Navigation'
20-import {Lightbox} from '../com/lightbox/Lightbox'
21-import {ModalsContainer} from '../com/modals/Modal'
22-import {ErrorBoundary} from '../com/util/ErrorBoundary'
23import {Composer} from './Composer.web'
24import {DrawerContent} from './Drawer'
25···78export const Shell: React.FC = function ShellImpl() {
79 const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
80 return (
81- <View style={[s.hContentRegion, pageBg]}>
82 <RoutesContainer>
83 <ShellInner />
84 </RoutesContainer>
···7import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
8import {useIntentHandler} from '#/lib/hooks/useIntentHandler'
9import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
10+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
11import {NavigationProp} from '#/lib/routes/types'
12+import {colors} from '#/lib/styles'
13import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
14import {useComposerKeyboardShortcut} from '#/state/shell/composer/useComposerKeyboardShortcut'
15import {useCloseAllActiveElements} from '#/state/util'
16+import {Lightbox} from '#/view/com/lightbox/Lightbox'
17+import {ModalsContainer} from '#/view/com/modals/Modal'
18+import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
19+import {atoms as a} from '#/alf'
20import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
21import {SigninDialog} from '#/components/dialogs/Signin'
22import {Outlet as PortalOutlet} from '#/components/Portal'
23+import {FlatNavigator, RoutesContainer} from '#/Navigation'
000024import {Composer} from './Composer.web'
25import {DrawerContent} from './Drawer'
26···79export const Shell: React.FC = function ShellImpl() {
80 const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
81 return (
82+ <View style={[a.util_screen_outer, pageBg]}>
83 <RoutesContainer>
84 <ShellInner />
85 </RoutesContainer>