[Layout] Base (#6907)

* Add common gutter styles as hook

* Add computed scrollbar gutter CSS vars

* Add new layout components

* Replace layout components in settings screens

* Remove old back button

* Invert web border logic for easier migration

* Clean up Slot API

* Port over FF handling of scrollbar offset

* Trade boilerplate for ease of use

* Limit to one line

* Allow two lines, fix wrapping

* Fix alignment

* sticky headers

* set max with on header and center

* [Layout] Notifications Header (#6910)

* Replace notifications screen header

* fix cropped indicator

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

* Replace Hashtag header (#6928)

* [Layout] ChatList header (#6929)

* Replace ChatList header

* update chat settings as well

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

* Add web borders to Chat settings

* Remove unused var

* Move ChatList header outside center

* Replace empty chat layout

* fix breakpoints

* [Layout] Scrollbar gutters (#6908)

* Fix sidebar alignment

* Make sure scrollbars don't hide

* Gift left nav more space

* Use stable one-edge, update logic in RightNav

* Ope

* Increase width

* Reset

* Add transform to sidebars

* Remove bg in sidebars

* Handle shifts in layout components

* Replace scroll-removal handling

* Make react-remove-scroll an explicit dep

* Remove unused script

* use correct scroll insets (#6950)

* [Layout] Feeds headers (#6913)

* Replace ViewHeader internals, duplicate old ViewHeader

* Replace Feeds header

* Replace SavedFeeds header

* Visual alignment

* Uglier but clear

* Use old ViewHeader for SavedFeeds

* use Layout.Center instead of Layout.Content

* use left-aligned header for feed edit

* delete unused old view header

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

* [Layout] Every other screen (#6953)

* attempt to fix double borders on every other screen

* delete ListHeaderDesktop

* delete `SimpleViewHeader` and fix screens (#6956)

* Make Layout.Center not full height

* Refactor List to use Layout.Center, remove built-in borders

* Fix Home screen

* Refactor PagerWithHeader to use Layout components

* Replace components in ProfileFeed and ProfileList

* Borders on Profile

* Search screen replacements

* use new header for profile subpage header (#6958)

* Search AutocompleteResults

* use new header for starter pack wizard (#6957)

* Fix post thread

* Enable borders by default

* Moderation muted and blocked accounts

* Fix scrollbar offset on Labeler

* Remove ScrollView from Moderation

* Remove ScrollView from Deactivated

* Remove ScrollView from onboarding

* Remove ScrollView from SignupQueued

* Mark deprecations

* fix lint

* Fix double borders on profile load

* Remove unneeded CenteredView from noty Feed

* Remove double Center layout on Notifications screen

* Remove double Center layout on ChatList screen

* Handle scrollbar offset in chat

* Use new atom for other scrollbar offsets

* Remove borders from old views

* Better doc

* Remove temp migration prop

* Fix new atom usage on native

* Clean up Hashtag screen

* Layout docs

* Clarify usage in Pager

* Handle nested offset contexts

* Clean up Layout

* fix feeds page

* asymmetric header on native (#6969)

* Reusable header const

* Fix up home header

* Add back button to convo

* Add hitslop to header buttons

* Comment

* Better handling on native for new atom

* Format

* Fix nested flatlist on mod screens

* Use react-remove-scroll-bar directly

* Fix notification count overflow on web

* Clarify doc

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
Eric Bailey
2024-12-05 18:59:26 -06:00
committed by GitHub
parent 8467dfd452
commit 143e2c802d
89 changed files with 1725 additions and 2052 deletions
+7 -14
View File
@@ -17,13 +17,13 @@ import {
} from '#/state/session'
import {useSetMinimalShellMode} from '#/state/shell'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {ScrollView} from '#/view/com/util/Views'
import {Logo} from '#/view/icons/Logo'
import {atoms as a, useTheme} from '#/alf'
import {AccountList} from '#/components/AccountList'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {Divider} from '#/components/Divider'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
@@ -104,24 +104,17 @@ export function Deactivated() {
}, [_, agent, setPending, setError, queryClient])
return (
<View style={[a.util_screen_outer, a.flex_1, t.atoms.bg]}>
<ScrollView
style={[
a.h_full,
a.w_full,
<View style={[a.util_screen_outer, a.flex_1]}>
<Layout.Content
contentContainerStyle={[
a.px_2xl,
{
paddingTop: isWeb ? 64 : insets.top + 16,
paddingBottom: isWeb ? 64 : insets.bottom,
},
]}
contentContainerStyle={[
a.w_full,
a.flex_row,
a.justify_center,
{borderWidth: 0},
]}>
<View style={[a.w_full, {maxWidth: COL_WIDTH}]}>
<View
style={[a.w_full, {marginHorizontal: 'auto', maxWidth: COL_WIDTH}]}>
<View style={[a.w_full, a.justify_center, a.align_center, a.pb_5xl]}>
<Logo width={40} />
</View>
@@ -218,7 +211,7 @@ export function Deactivated() {
</>
)}
</View>
</ScrollView>
</Layout.Content>
</View>
)
}
+30 -41
View File
@@ -1,5 +1,5 @@
import React from 'react'
import {ListRenderItemInfo, Pressable, View} from 'react-native'
import {ListRenderItemInfo, View} from 'react-native'
import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -13,16 +13,15 @@ import {shareUrl} from '#/lib/sharing'
import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles'
import {enforceLen} from '#/lib/strings/helpers'
import {isNative, isWeb} from '#/platform/detection'
import {useSearchPostsQuery} from '#/state/queries/search-posts'
import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
import {Pager} from '#/view/com/pager/Pager'
import {TabBar} from '#/view/com/pager/TabBar'
import {Post} from '#/view/com/post/Post'
import {List} from '#/view/com/util/List'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
import {atoms as a, web} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import * as Layout from '#/components/Layout'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
@@ -110,46 +109,36 @@ export default function HashtagScreen({
return (
<Layout.Screen>
<CenteredView sideBorders={true}>
<ViewHeader
showOnDesktop
title={headerTitle}
subtitle={author ? _(msg`From @${sanitizedAuthor}`) : undefined}
canGoBack
renderButton={
isNative
? () => (
<Pressable
accessibilityRole="button"
onPress={onShare}
hitSlop={HITSLOP_10}>
<ArrowOutOfBox_Stroke2_Corner0_Rounded
size="lg"
onPress={onShare}
/>
</Pressable>
)
: undefined
}
/>
</CenteredView>
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>{headerTitle}</Layout.Header.TitleText>
{author && (
<Layout.Header.SubtitleText>
{_(msg`From @${sanitizedAuthor}`)}
</Layout.Header.SubtitleText>
)}
</Layout.Header.Content>
<Layout.Header.Slot>
<Button
label={_(msg`Share`)}
size="small"
variant="ghost"
color="primary"
shape="round"
onPress={onShare}
hitSlop={HITSLOP_10}
style={[{right: -3}]}>
<ButtonIcon icon={Share} size="md" />
</Button>
</Layout.Header.Slot>
</Layout.Header.Outer>
<Pager
onPageSelected={onPageSelected}
renderTabBar={props => (
<CenteredView
sideBorders={true}
// @ts-ignore web only
style={
isWeb
? {
position: isWeb ? 'sticky' : '',
top: 0,
zIndex: 1,
}
: undefined
}>
<Layout.Center style={web([a.sticky, a.z_10, {top: 0}])}>
<TabBar items={sections.map(section => section.title)} {...props} />
</CenteredView>
</Layout.Center>
)}
initialPage={0}>
{sections.map((section, i) => (
+55 -105
View File
@@ -16,8 +16,6 @@ import {MESSAGE_SCREEN_POLL_INTERVAL} from '#/state/messages/convo/const'
import {useMessagesEventBus} from '#/state/messages/events'
import {useListConvosQuery} from '#/state/queries/messages/list-converations'
import {List} from '#/view/com/util/List'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {DialogControlProps, useDialogControl} from '#/components/Dialog'
@@ -49,7 +47,6 @@ export function MessagesScreen({navigation, route}: Props) {
const {_} = useLingui()
const t = useTheme()
const newChatControl = useDialogControl()
const {gtMobile} = useBreakpoints()
const pushToConversation = route.params?.pushToConversation
// Whenever we have `pushToConversation` set, it means we pressed a notification for a chat without being on
@@ -81,21 +78,6 @@ export function MessagesScreen({navigation, route}: Props) {
}, [messagesBus, isActive]),
)
const renderButton = useCallback(() => {
return (
<Link
to="/messages/settings"
label={_(msg`Chat settings`)}
size="small"
variant="ghost"
color="secondary"
shape="square"
style={[a.justify_center]}>
<SettingsSlider size="md" style={[t.atoms.text_contrast_medium]} />
</Link>
)
}, [_, t])
const initialNumToRender = useInitialNumToRender({minItemHeight: 80})
const [isPTRing, setIsPTRing] = useState(false)
@@ -144,28 +126,11 @@ export function MessagesScreen({navigation, route}: Props) {
[navigation],
)
const onNavigateToSettings = useCallback(() => {
navigation.navigate('MessagesSettings')
}, [navigation])
if (conversations.length < 1) {
return (
<Layout.Screen>
<CenteredView sideBorders={gtMobile} style={[a.h_full_vh]}>
{gtMobile ? (
<DesktopHeader
newChatControl={newChatControl}
onNavigateToSettings={onNavigateToSettings}
/>
) : (
<ViewHeader
title={_(msg`Messages`)}
renderButton={renderButton}
showBorder
canGoBack={false}
/>
)}
<Header newChatControl={newChatControl} />
<Layout.Center>
{isLoading ? (
<View style={[a.align_center, a.pt_3xl, web({paddingTop: '10vh'})]}>
<Loader size="xl" />
@@ -227,7 +192,7 @@ export function MessagesScreen({navigation, route}: Props) {
)}
</>
)}
</CenteredView>
</Layout.Center>
{!isLoading && !isError && (
<NewChat onNewChat={onNewChat} control={newChatControl} />
@@ -238,14 +203,7 @@ export function MessagesScreen({navigation, route}: Props) {
return (
<Layout.Screen testID="messagesScreen">
{!gtMobile && (
<ViewHeader
title={_(msg`Messages`)}
renderButton={renderButton}
showBorder
canGoBack={false}
/>
)}
<Header newChatControl={newChatControl} />
<NewChat onNewChat={onNewChat} control={newChatControl} />
<List
data={conversations}
@@ -254,12 +212,6 @@ export function MessagesScreen({navigation, route}: Props) {
refreshing={isPTRing}
onRefresh={onRefresh}
onEndReached={onEndReached}
ListHeaderComponent={
<DesktopHeader
newChatControl={newChatControl}
onNavigateToSettings={onNavigateToSettings}
/>
}
ListFooterComponent={
<ListFooter
isFetchingNextPage={isFetchingNextPage}
@@ -276,67 +228,65 @@ export function MessagesScreen({navigation, route}: Props) {
windowSize={11}
// @ts-ignore our .web version only -sfn
desktopFixedHeight
sideBorders={false}
/>
</Layout.Screen>
)
}
function DesktopHeader({
newChatControl,
onNavigateToSettings,
}: {
newChatControl: DialogControlProps
onNavigateToSettings: () => void
}) {
const t = useTheme()
function Header({newChatControl}: {newChatControl: DialogControlProps}) {
const {_} = useLingui()
const {gtMobile, gtTablet} = useBreakpoints()
const {gtMobile} = useBreakpoints()
if (!gtMobile) {
return null
}
const settingsLink = (
<Link
to="/messages/settings"
label={_(msg`Chat settings`)}
size="small"
variant="ghost"
color="secondary"
shape="square"
style={[a.justify_center]}>
<ButtonIcon icon={SettingsSlider} size="md" />
</Link>
)
return (
<View
style={[
t.atoms.bg,
a.flex_row,
a.align_center,
a.justify_between,
a.gap_lg,
a.px_lg,
a.pr_md,
a.py_sm,
a.border_b,
t.atoms.border_contrast_low,
]}>
<Text style={[a.text_2xl, a.font_bold]}>
<Trans>Messages</Trans>
</Text>
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
<Button
label={_(msg`Message settings`)}
color="secondary"
size="small"
variant="ghost"
shape="square"
onPress={onNavigateToSettings}>
<SettingsSlider size="md" style={[t.atoms.text_contrast_medium]} />
</Button>
{gtTablet && (
<Button
label={_(msg`New chat`)}
color="primary"
size="small"
variant="solid"
onPress={newChatControl.open}>
<ButtonIcon icon={Plus} position="left" />
<ButtonText>
<Trans>New chat</Trans>
</ButtonText>
</Button>
)}
</View>
</View>
<Layout.Header.Outer>
{gtMobile ? (
<>
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Messages</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
{settingsLink}
<Button
label={_(msg`New chat`)}
color="primary"
size="small"
variant="solid"
onPress={newChatControl.open}>
<ButtonIcon icon={Plus} position="left" />
<ButtonText>
<Trans>New chat</Trans>
</ButtonText>
</Button>
</View>
</>
) : (
<>
<Layout.Header.MenuButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Messages</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot>{settingsLink}</Layout.Header.Slot>
</>
)}
</Layout.Header.Outer>
)
}
+4 -5
View File
@@ -17,7 +17,6 @@ import {useCurrentConvoId} from '#/state/messages/current-convo-id'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useProfileQuery} from '#/state/queries/profile'
import {useSetMinimalShellMode} from '#/state/shell'
import {CenteredView} from '#/view/com/util/Views'
import {MessagesList} from '#/screens/Messages/components/MessagesList'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
@@ -97,7 +96,7 @@ function Inner() {
if (convoState.status === ConvoStatus.Error) {
return (
<CenteredView style={[a.flex_1]} sideBorders>
<Layout.Center style={[a.flex_1]}>
<MessagesListHeader />
<Error
title={_(msg`Something went wrong`)}
@@ -105,12 +104,12 @@ function Inner() {
onRetry={() => convoState.error.retry()}
sideBorders={false}
/>
</CenteredView>
</Layout.Center>
)
}
return (
<CenteredView style={[a.flex_1]} sideBorders>
<Layout.Center style={[a.flex_1]}>
{!readyToShow && <MessagesListHeader />}
<View style={[a.flex_1]}>
{moderationOpts && recipient ? (
@@ -140,7 +139,7 @@ function Inner() {
</View>
)}
</View>
</CenteredView>
</Layout.Center>
)
}
+11 -5
View File
@@ -10,8 +10,6 @@ import {useUpdateActorDeclaration} from '#/state/queries/messages/actor-declarat
import {useProfileQuery} from '#/state/queries/profile'
import {useSession} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {ScrollView} from '#/view/com/util/Views'
import {atoms as a} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Divider} from '#/components/Divider'
@@ -57,8 +55,16 @@ export function MessagesSettingsScreen({}: Props) {
return (
<Layout.Screen testID="messagesSettingsScreen">
<ScrollView stickyHeaderIndices={[0]}>
<ViewHeader title={_(msg`Chat Settings`)} showOnDesktop showBorder />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Chat Settings</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<View style={[a.p_lg, a.gap_md]}>
<Text style={[a.text_lg, a.font_bold]}>
<Trans>Allow new messages from</Trans>
@@ -142,7 +148,7 @@ export function MessagesSettingsScreen({}: Props) {
</>
)}
</View>
</ScrollView>
</Layout.Content>
</Layout.Screen>
)
}
+13 -34
View File
@@ -1,6 +1,5 @@
import React from 'react'
import {Fragment, useCallback} from 'react'
import {Linking, View} from 'react-native'
import {useSafeAreaFrame} from 'react-native-safe-area-context'
import {LABELS} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -19,8 +18,6 @@ import {
import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities'
import {useSetMinimalShellMode} from '#/state/shell'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme, ViewStyleProp} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
@@ -37,6 +34,7 @@ import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Perso
import * as LabelingService from '#/components/LabelingServiceCard'
import * as Layout from '#/components/Layout'
import {InlineLinkText, Link} from '#/components/Link'
import {ListMaybePlaceholder} from '#/components/Lists'
import {Loader} from '#/components/Loader'
import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
import {Text} from '#/components/Typography'
@@ -75,35 +73,22 @@ function ErrorState({error}: {error: string}) {
export function ModerationScreen(
_props: NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>,
) {
const t = useTheme()
const {_} = useLingui()
const {
isLoading: isPreferencesLoading,
error: preferencesError,
data: preferences,
} = usePreferencesQuery()
const {gtMobile} = useBreakpoints()
const {height} = useSafeAreaFrame()
const isLoading = isPreferencesLoading
const error = preferencesError
return (
<Layout.Screen testID="moderationScreen">
<CenteredView
testID="moderationScreen"
style={[
t.atoms.border_contrast_low,
t.atoms.bg,
{minHeight: height},
...(gtMobile ? [a.border_l, a.border_r] : []),
]}>
<ViewHeader title={_(msg`Moderation`)} showOnDesktop />
<ViewHeader title={_(msg`Moderation`)} showOnDesktop />
<Layout.Content>
{isLoading ? (
<View style={[a.w_full, a.align_center, a.pt_2xl]}>
<Loader size="xl" fill={t.atoms.text.color} />
</View>
<ListMaybePlaceholder isLoading={true} sideBorders={false} />
) : error || !preferences ? (
<ErrorState
error={
@@ -114,7 +99,7 @@ export function ModerationScreen(
) : (
<ModerationScreenInner preferences={preferences} />
)}
</CenteredView>
</Layout.Content>
</Layout.Screen>
)
}
@@ -169,7 +154,7 @@ export function ModerationScreenInner({
} = useMyLabelersQuery()
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
}, [setMinimalShellMode]),
)
@@ -183,7 +168,7 @@ export function ModerationScreenInner({
const ageNotSet = !preferences.userAge
const isUnderage = (preferences.userAge || 0) < 18
const onToggleAdultContentEnabled = React.useCallback(
const onToggleAdultContentEnabled = useCallback(
async (selected: boolean) => {
try {
await setAdultContentPref({
@@ -201,13 +186,7 @@ export function ModerationScreenInner({
const disabledOnIOS = isIOS && !adultContentEnabled
return (
<ScrollView
contentContainerStyle={[
a.border_0,
a.pt_2xl,
a.px_lg,
gtMobile && a.px_2xl,
]}>
<View style={[a.pt_2xl, a.px_lg, gtMobile && a.px_2xl]}>
<Text
style={[a.text_md, a.font_bold, a.pb_md, t.atoms.text_contrast_high]}>
<Trans>Moderation tools</Trans>
@@ -420,7 +399,7 @@ export function ModerationScreenInner({
<View style={[a.rounded_sm, t.atoms.bg_contrast_25]}>
{labelers.map((labeler, i) => {
return (
<React.Fragment key={labeler.creator.did}>
<Fragment key={labeler.creator.did}>
{i !== 0 && <Divider />}
<LabelingService.Link labeler={labeler}>
{state => (
@@ -457,12 +436,12 @@ export function ModerationScreenInner({
</LabelingService.Outer>
)}
</LabelingService.Link>
</React.Fragment>
</Fragment>
)
})}
</View>
)}
<View style={{height: 200}} />
</ScrollView>
<View style={{height: 150}} />
</View>
)
}
+2 -4
View File
@@ -1,13 +1,11 @@
import React from 'react'
import {View} from 'react-native'
import Animated from 'react-native-reanimated'
import {ScrollView, View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {isWeb} from '#/platform/detection'
import {useOnboardingDispatch} from '#/state/shell'
import {ScrollView} from '#/view/com/util/Views'
import {Context} from '#/screens/Onboarding/state'
import {
atoms as a,
@@ -36,7 +34,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
const {gtMobile} = useBreakpoints()
const onboardDispatch = useOnboardingDispatch()
const {state, dispatch} = React.useContext(Context)
const scrollview = React.useRef<Animated.ScrollView>(null)
const scrollview = React.useRef<ScrollView>(null)
const prevActiveStep = React.useRef<string>(state.activeStep)
React.useEffect(() => {
+2 -8
View File
@@ -5,13 +5,10 @@ import {useFocusEffect} from '@react-navigation/native'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {makeRecordUri} from '#/lib/strings/url-helpers'
import {isWeb} from '#/platform/detection'
import {useSetMinimalShellMode} from '#/state/shell'
import {PostLikedBy as PostLikedByComponent} from '#/view/com/post-thread/PostLikedBy'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
export const PostLikedByScreen = ({route}: Props) => {
@@ -28,11 +25,8 @@ export const PostLikedByScreen = ({route}: Props) => {
return (
<Layout.Screen>
<CenteredView sideBorders={true}>
<ListHeaderDesktop title={_(msg`Liked By`)} />
<ViewHeader title={_(msg`Liked By`)} showBorder={!isWeb} />
<PostLikedByComponent uri={uri} />
</CenteredView>
<ViewHeader title={_(msg`Liked By`)} />
<PostLikedByComponent uri={uri} />
</Layout.Screen>
)
}
-2
View File
@@ -11,7 +11,6 @@ import {PostQuotes as PostQuotesComponent} from '#/view/com/post-thread/PostQuot
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostQuotes'>
export const PostQuotesScreen = ({route}: Props) => {
@@ -29,7 +28,6 @@ export const PostQuotesScreen = ({route}: Props) => {
return (
<Layout.Screen>
<CenteredView sideBorders={true}>
<ListHeaderDesktop title={_(msg`Quotes`)} />
<ViewHeader title={_(msg`Quotes`)} showBorder={!isWeb} />
<PostQuotesComponent uri={uri} />
</CenteredView>
-2
View File
@@ -11,7 +11,6 @@ import {PostRepostedBy as PostRepostedByComponent} from '#/view/com/post-thread/
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
import {ListHeaderDesktop} from '#/components/Lists'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
export const PostRepostedByScreen = ({route}: Props) => {
@@ -29,7 +28,6 @@ export const PostRepostedByScreen = ({route}: Props) => {
return (
<Layout.Screen>
<CenteredView sideBorders={true}>
<ListHeaderDesktop title={_(msg`Reposted By`)} />
<ViewHeader title={_(msg`Reposted By`)} showBorder={!isWeb} />
<PostRepostedByComponent uri={uri} />
</CenteredView>
+19 -10
View File
@@ -15,14 +15,22 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {List} from '#/view/com/util/List'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import * as Layout from '#/components/Layout'
import {
ListFooter,
ListHeaderDesktop,
ListMaybePlaceholder,
} from '#/components/Lists'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
function renderItem({item}: {item: AppBskyActorDefs.ProfileViewBasic}) {
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
function renderItem({
item,
index,
}: {
item: AppBskyActorDefs.ProfileViewBasic
index: number
}) {
return (
<ProfileCardWithFollowBtn
key={item.did}
profile={item}
noBorder={index === 0}
/>
)
}
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
@@ -93,6 +101,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
if (followers.length < 1) {
return (
<Layout.Screen>
<ViewHeader title={_(msg`Followers you know`)} />
<ListMaybePlaceholder
isLoading={isDidLoading || isFollowersLoading}
isError={isError}
@@ -100,6 +109,8 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
errorMessage={cleanError(resolveError || error)}
onRetry={isError ? refetch : undefined}
topBorder={false}
sideBorders={false}
/>
</Layout.Screen>
)
@@ -116,9 +127,6 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
onRefresh={onRefresh}
onEndReached={onEndReached}
onEndReachedThreshold={4}
ListHeaderComponent={
<ListHeaderDesktop title={_(msg`Followers you know`)} />
}
ListFooterComponent={
<ListFooter
isFetchingNextPage={isFetchingNextPage}
@@ -130,6 +138,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
desktopFixedHeight
initialNumToRender={initialNumToRender}
windowSize={11}
sideBorders={false}
/>
</Layout.Screen>
)
+4 -3
View File
@@ -15,10 +15,11 @@ import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
import {useScrollHandlers} from '#/lib/ScrollContext'
import {isNative} from '#/platform/detection'
import {ListRef} from '#/view/com/util/List'
import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {ScrollView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
import {Text} from '#/components/Typography'
@@ -75,7 +76,7 @@ export const ProfileLabelsSection = React.forwardRef<
}, [isFocused, scrollElRef, setScrollViewTag])
return (
<CenteredView style={{flex: 1, minHeight}} sideBorders>
<Layout.Center style={{flex: 1, minHeight}}>
{isLabelerLoading ? (
<View style={[a.w_full, a.align_center]}>
<Loader size="xl" />
@@ -95,7 +96,7 @@ export const ProfileLabelsSection = React.forwardRef<
headerHeight={headerHeight}
/>
)}
</CenteredView>
</Layout.Center>
)
})
+9 -1
View File
@@ -21,7 +21,15 @@ export function AboutSettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`About`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>About</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.LinkItem
@@ -39,7 +39,15 @@ export function AccessibilitySettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`Accessibility`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Accessibility</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Group contentContainerStyle={[a.gap_sm]}>
+9 -1
View File
@@ -38,7 +38,15 @@ export function AccountSettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`Account`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Account</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Item>
+10 -2
View File
@@ -1,7 +1,7 @@
import React from 'react'
import {Alert, View} from 'react-native'
import {Image} from 'expo-image'
import {msg} from '@lingui/macro'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import * as AppIcon from '@mozzius/expo-dynamic-app-icon'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
@@ -20,7 +20,15 @@ export function AppIconSettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_('App Icon')} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>App Icon</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content
contentContainerStyle={[a.py_2xl, a.px_xl, {paddingBottom: 100}]}>
<Text style={[a.text_lg, a.font_heavy]}>Defaults</Text>
+9 -1
View File
@@ -44,7 +44,15 @@ export function AppPasswordsScreen({}: Props) {
return (
<Layout.Screen testID="AppPasswordsScreen">
<Layout.Header title={_(msg`App Passwords`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>App Passwords</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
{error ? (
<ErrorScreen
+9 -1
View File
@@ -79,7 +79,15 @@ export function AppearanceSettingsScreen({}: Props) {
return (
<LayoutAnimationConfig skipExiting skipEntering>
<Layout.Screen testID="preferencesThreadsScreen">
<Layout.Header title={_(msg`Appearance`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Appearance</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<AppearanceToggleButtonGroup
@@ -32,7 +32,15 @@ export function ContentAndMediaSettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`Content and Media`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Content & Media</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.LinkItem
@@ -1,7 +1,6 @@
import {Fragment} from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/macro'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {
@@ -23,10 +22,17 @@ type Props = NativeStackScreenProps<
'PreferencesExternalEmbeds'
>
export function ExternalMediaPreferencesScreen({}: Props) {
const {_} = useLingui()
return (
<Layout.Screen testID="externalMediaPreferencesScreen">
<Layout.Header title={_(msg`External Media Preferences`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>External Media Preferences</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Item>
@@ -46,7 +46,15 @@ export function FollowingFeedPreferencesScreen({}: Props) {
return (
<Layout.Screen testID="followingFeedPreferencesScreen">
<Layout.Header title={_(msg`Following Feed Preferences`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Following Feed Preferences</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Item>
+9 -1
View File
@@ -64,7 +64,15 @@ export function LanguageSettingsScreen({}: Props) {
return (
<Layout.Screen testID="PreferencesLanguagesScreen">
<Layout.Header title={_(msg`Languages`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Languages</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Group iconInset={false}>
@@ -33,7 +33,15 @@ export function NotificationSettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`Notification Settings`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Notification Settings</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
{isQueryError ? (
<Error
@@ -29,7 +29,15 @@ export function PrivacyAndSecuritySettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`Privacy and Security`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Privacy and Security</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Item>
+9 -1
View File
@@ -73,7 +73,15 @@ export function SettingsScreen({}: Props) {
return (
<Layout.Screen>
<Layout.Header title={_(msg`Settings`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Settings</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<View
+9 -1
View File
@@ -38,7 +38,15 @@ export function ThreadPreferencesScreen({}: Props) {
return (
<Layout.Screen testID="threadPreferencesScreen">
<Layout.Header title={_(msg`Thread Preferences`)} />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Thread Preferences</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Layout.Content>
<SettingsList.Container>
<SettingsList.Group>
+1 -2
View File
@@ -1,5 +1,5 @@
import React from 'react'
import {Modal, View} from 'react-native'
import {Modal, ScrollView, View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {StatusBar} from 'expo-status-bar'
import {msg, plural, Trans} from '@lingui/macro'
@@ -9,7 +9,6 @@ import {logger} from '#/logger'
import {isIOS, isWeb} from '#/platform/detection'
import {isSignupQueued, useAgent, useSessionApi} from '#/state/session'
import {useOnboardingDispatch} from '#/state/shell'
import {ScrollView} from '#/view/com/util/Views'
import {Logo} from '#/view/icons/Logo'
import {atoms as a, native, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
+29 -52
View File
@@ -1,5 +1,5 @@
import React from 'react'
import {Keyboard, TouchableOpacity, View} from 'react-native'
import {Keyboard, View} from 'react-native'
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {Image} from 'expo-image'
@@ -10,13 +10,12 @@ import {
ModerationOpts,
} from '@atproto/api'
import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
import {STARTER_PACK_MAX_SIZE} from '#/lib/constants'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
@@ -29,7 +28,7 @@ import {
parseStarterPackUri,
} from '#/lib/strings/starter-pack'
import {logger} from '#/logger'
import {isAndroid, isNative, isWeb} from '#/platform/detection'
import {isNative} from '#/platform/detection'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useAllListMembersQuery} from '#/state/queries/list-members'
import {useProfileQuery} from '#/state/queries/profile'
@@ -147,7 +146,6 @@ function WizardInner({
}) {
const navigation = useNavigation<NavigationProp>()
const {_} = useLingui()
const t = useTheme()
const setMinimalShellMode = useSetMinimalShellMode()
const [state, dispatch] = useWizardState()
const {currentAccount} = useSession()
@@ -283,45 +281,24 @@ function WizardInner({
return (
<CenteredView style={[a.flex_1]} sideBorders>
<View
style={[
a.flex_row,
a.pb_sm,
a.px_md,
a.border_b,
t.atoms.border_contrast_medium,
a.gap_sm,
a.justify_between,
a.align_center,
isAndroid && a.pt_sm,
isWeb && [a.py_md],
]}>
<View style={[{width: 65}]}>
<TouchableOpacity
testID="viewHeaderDrawerBtn"
hitSlop={HITSLOP_10}
accessibilityRole="button"
accessibilityLabel={_(msg`Back`)}
accessibilityHint={_(msg`Go back to the previous step`)}
onPress={() => {
if (state.currentStep === 'Details') {
navigation.pop()
} else {
dispatch({type: 'Back'})
}
}}>
<FontAwesomeIcon
size={18}
icon="angle-left"
color={t.atoms.text.color}
/>
</TouchableOpacity>
</View>
<Text style={[a.flex_1, a.font_bold, a.text_lg, a.text_center]}>
{currUiStrings.header}
</Text>
<View style={[{width: 65}]} />
</View>
<Layout.Header.Outer>
<Layout.Header.BackButton
label={_(msg`Back`)}
accessibilityHint={_(msg`Go back to the previous step`)}
onPress={evt => {
if (state.currentStep !== 'Details') {
evt.preventDefault()
dispatch({type: 'Back'})
}
}}
/>
<Layout.Header.Content>
<Layout.Header.TitleText>
{currUiStrings.header}
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot />
</Layout.Header.Outer>
<Container>
{state.currentStep === 'Details' ? (
@@ -463,17 +440,17 @@ function Footer({
<Trans>
<Text style={[a.font_bold, textStyles]}>You</Text> and
<Text> </Text>
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[1] /* [0] is self, skip it */)}{' '}
</Text>
are included in your starter pack
</Trans>
) : items.length > 2 ? (
<Trans context="profiles">
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[1] /* [0] is self, skip it */)},{' '}
</Text>
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[2])},{' '}
</Text>
and{' '}
@@ -504,29 +481,29 @@ function Footer({
{
items.length === 1 ? (
<Trans>
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[0])}
</Text>{' '}
is included in your starter pack
</Trans>
) : items.length === 2 ? (
<Trans>
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[0])}
</Text>{' '}
and
<Text> </Text>
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[1])}{' '}
</Text>
are included in your starter pack
</Trans>
) : items.length > 2 ? (
<Trans context="feeds">
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[0])},{' '}
</Text>
<Text style={[a.font_bold, textStyles]}>
<Text style={[a.font_bold, textStyles]} emoji>
{getName(items[1])},{' '}
</Text>
and{' '}