[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
+45 -83
View File
@@ -24,14 +24,13 @@ import {useSetMinimalShellMode} from '#/state/shell'
import {useComposerControls} from '#/state/shell/composer'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {FAB} from '#/view/com/util/fab/FAB'
import {TextLink} from '#/view/com/util/Link'
import {List, ListMethods} from '#/view/com/util/List'
import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {Text} from '#/view/com/util/text/Text'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
import {atoms as a, useTheme} from '#/alf'
import {ButtonIcon} from '#/components/Button'
import {Divider} from '#/components/Divider'
import * as FeedCard from '#/components/FeedCard'
import {SearchInput} from '#/components/forms/SearchInput'
@@ -40,7 +39,9 @@ import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
import {ListMagnifyingGlass_Stroke2_Corner0_Rounded} from '#/components/icons/ListMagnifyingGlass'
import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkle'
import {SettingsGear2_Stroke2_Corner0_Rounded as Gear} from '#/components/icons/SettingsGear2'
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
import * as ListCard from '#/components/ListCard'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Feeds'>
@@ -102,7 +103,7 @@ type FlatlistSlice =
export function FeedsScreen(_props: Props) {
const pal = usePalette('default')
const {openComposer} = useComposerControls()
const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
const {isMobile} = useWebMediaQueries()
const [query, setQuery] = React.useState('')
const [isPTR, setIsPTR] = React.useState(false)
const {
@@ -374,22 +375,6 @@ export function FeedsScreen(_props: Props) {
isUserSearching,
])
const renderHeaderBtn = React.useCallback(() => {
return (
<View style={styles.headerBtnGroup}>
<TextLink
testID="editFeedsBtn"
type="lg-medium"
href="/settings/saved-feeds"
accessibilityLabel={_(msg`Edit My Feeds`)}
accessibilityHint=""
text={_(msg`Edit`)}
style={[pal.link, a.pr_xs]}
/>
</View>
)
}, [pal, _])
const searchBarIndex = items.findIndex(
item => item.type === 'popularFeedsHeader',
)
@@ -430,36 +415,7 @@ export function FeedsScreen(_props: Props) {
</View>
)
} else if (item.type === 'savedFeedsHeader') {
return (
<>
{!isMobile && (
<View
style={[
pal.view,
styles.header,
pal.border,
{
borderBottomWidth: 1,
},
]}>
<Text type="title-lg" style={[pal.text, s.bold]}>
<Trans>Feeds</Trans>
</Text>
<View style={styles.headerBtnGroup}>
<TextLink
type="lg"
href="/settings/saved-feeds"
accessibilityLabel={_(msg`Edit My Feeds`)}
accessibilityHint=""
text={_(msg`Edit`)}
style={[pal.link]}
/>
</View>
</View>
)}
<FeedsSavedHeader />
</>
)
return <FeedsSavedHeader />
} else if (item.type === 'savedFeedNoResults') {
return (
<View
@@ -530,13 +486,8 @@ export function FeedsScreen(_props: Props) {
return null
},
[
isMobile,
pal.view,
pal.border,
pal.text,
pal.textLight,
pal.link,
_,
query,
onChangeQuery,
onPressCancelSearch,
@@ -547,31 +498,45 @@ export function FeedsScreen(_props: Props) {
return (
<Layout.Screen testID="FeedsScreen">
{isMobile && (
<ViewHeader
title={_(msg`Feeds`)}
renderButton={renderHeaderBtn}
showBorder
/>
)}
<Layout.Center>
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>
<Trans>Feeds</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Layout.Header.Slot>
<Link
testID="editFeedsBtn"
to="/settings/saved-feeds"
label={_(msg`Edit My Feeds`)}
size="small"
variant="ghost"
color="secondary"
shape="round"
style={[a.justify_center, {right: -3}]}>
<ButtonIcon icon={Gear} size="lg" />
</Link>
</Layout.Header.Slot>
</Layout.Header.Outer>
<List
ref={listRef}
style={[!isTabletOrDesktop && s.flex1, styles.list]}
data={items}
keyExtractor={item => item.key}
contentContainerStyle={styles.contentContainer}
renderItem={renderItem}
refreshing={isPTR}
onRefresh={isUserSearching ? undefined : onPullToRefresh}
initialNumToRender={10}
onEndReached={onEndReached}
// @ts-ignore our .web version only -prf
desktopFixedHeight
scrollIndicatorInsets={{right: 1}}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"
/>
<List
ref={listRef}
data={items}
keyExtractor={item => item.key}
contentContainerStyle={styles.contentContainer}
renderItem={renderItem}
refreshing={isPTR}
onRefresh={isUserSearching ? undefined : onPullToRefresh}
initialNumToRender={10}
onEndReached={onEndReached}
desktopFixedHeight
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"
sideBorders={false}
/>
</Layout.Center>
{hasSession && (
<FAB
@@ -728,7 +693,7 @@ function FeedsSavedHeader() {
}>
<IconCircle icon={ListSparkle_Stroke2_Corner0_Rounded} size="lg" />
<View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.flex_1, a.text_2xl, a.font_bold, t.atoms.text]}>
<Text style={[a.flex_1, a.text_2xl, a.font_heavy, t.atoms.text]}>
<Trans>My Feeds</Trans>
</Text>
<Text style={[t.atoms.text_contrast_high]}>
@@ -754,7 +719,7 @@ function FeedsAboutHeader() {
size="lg"
/>
<View style={[a.flex_1, a.gap_sm]}>
<Text style={[a.flex_1, a.text_2xl, a.font_bold, t.atoms.text]}>
<Text style={[a.flex_1, a.text_2xl, a.font_heavy, t.atoms.text]}>
<Trans>Discover New Feeds</Trans>
</Text>
<Text style={[t.atoms.text_contrast_high]}>
@@ -769,9 +734,6 @@ function FeedsAboutHeader() {
}
const styles = StyleSheet.create({
list: {
height: '100%',
},
contentContainer: {
paddingBottom: 100,
},
+26 -46
View File
@@ -1,33 +1,26 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import {AtUri} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {useEmail} from '#/lib/hooks/useEmail'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {NavigationProp} from '#/lib/routes/types'
import {s} from '#/lib/styles'
import {useModalControls} from '#/state/modals'
import {useSetMinimalShellMode} from '#/state/shell'
import {MyLists} from '#/view/com/lists/MyLists'
import {Button} from '#/view/com/util/forms/Button'
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
import {Text} from '#/view/com/util/text/Text'
import {atoms as a} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Lists'>
export function ListsScreen({}: Props) {
const {_} = useLingui()
const pal = usePalette('default')
const setMinimalShellMode = useSetMinimalShellMode()
const {isMobile} = useWebMediaQueries()
const navigation = useNavigation<NavigationProp>()
const {openModal} = useModalControls()
const {needsEmailVerification} = useEmail()
@@ -62,43 +55,30 @@ export function ListsScreen({}: Props) {
return (
<Layout.Screen testID="listsScreen">
<SimpleViewHeader
showBackButton={isMobile}
style={[
pal.border,
isMobile
? {borderBottomWidth: StyleSheet.hairlineWidth}
: {
borderLeftWidth: StyleSheet.hairlineWidth,
borderRightWidth: StyleSheet.hairlineWidth,
},
]}>
<View style={{flex: 1}}>
<Text type="title-lg" style={[pal.text, {fontWeight: '600'}]}>
<Trans>User Lists</Trans>
</Text>
<Text style={pal.textLight}>
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content align="left">
<Layout.Header.TitleText>
<Trans>Lists</Trans>
</Layout.Header.TitleText>
<Layout.Header.SubtitleText>
<Trans>Public, shareable lists which can drive feeds.</Trans>
</Text>
</View>
<View style={[{marginLeft: 18}, isMobile && {marginLeft: 12}]}>
<Button
testID="newUserListBtn"
type="default"
onPress={onPressNewList}
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 8,
}}>
<FontAwesomeIcon icon="plus" color={pal.colors.text} />
<Text type="button" style={pal.text}>
<Trans context="action">New</Trans>
</Text>
</Button>
</View>
</SimpleViewHeader>
<MyLists filter="curate" style={s.flexGrow1} />
</Layout.Header.SubtitleText>
</Layout.Header.Content>
<Button
label={_(msg`New list`)}
testID="newUserListBtn"
color="secondary"
variant="solid"
size="small"
onPress={onPressNewList}>
<ButtonIcon icon={PlusIcon} />
<ButtonText>
<Trans context="action">New</Trans>
</ButtonText>
</Button>
</Layout.Header.Outer>
<MyLists filter="curate" style={a.flex_grow} />
<VerifyEmailDialog
reasonText={_(
msg`Before creating a list, you must first verify your email.`,
+6 -20
View File
@@ -23,7 +23,6 @@ import {ProfileCard} from '#/view/com/profile/ProfileCard'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {Text} from '#/view/com/util/text/Text'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps<
@@ -97,14 +96,7 @@ export function ModerationBlockedAccounts({}: Props) {
)
return (
<Layout.Screen testID="blockedAccountsScreen">
<CenteredView
style={[
styles.container,
isTabletOrDesktop && styles.containerDesktop,
pal.view,
pal.border,
]}
testID="blockedAccountsScreen">
<Layout.Center>
<ViewHeader title={_(msg`Blocked Accounts`)} showOnDesktop />
<Text
type="sm"
@@ -112,6 +104,9 @@ export function ModerationBlockedAccounts({}: Props) {
styles.description,
pal.text,
isTabletOrDesktop && styles.descriptionDesktop,
{
marginTop: 20,
},
]}>
<Trans>
Blocked accounts cannot reply in your threads, mention you, or
@@ -120,7 +115,7 @@ export function ModerationBlockedAccounts({}: Props) {
</Trans>
</Text>
{isEmpty ? (
<View style={[pal.border, !isTabletOrDesktop && styles.flex1]}>
<View style={[pal.border]}>
{isError ? (
<ErrorScreen
title="Oops!"
@@ -166,21 +161,12 @@ export function ModerationBlockedAccounts({}: Props) {
desktopFixedHeight
/>
)}
</CenteredView>
</Layout.Center>
</Layout.Screen>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingBottom: 100,
},
containerDesktop: {
borderLeftWidth: 1,
borderRightWidth: 1,
paddingBottom: 0,
},
title: {
textAlign: 'center',
marginTop: 12,
+25 -39
View File
@@ -1,33 +1,26 @@
import React from 'react'
import {View} from 'react-native'
import {AtUri} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {useEmail} from '#/lib/hooks/useEmail'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {NavigationProp} from '#/lib/routes/types'
import {s} from '#/lib/styles'
import {useModalControls} from '#/state/modals'
import {useSetMinimalShellMode} from '#/state/shell'
import {MyLists} from '#/view/com/lists/MyLists'
import {Button} from '#/view/com/util/forms/Button'
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
import {Text} from '#/view/com/util/text/Text'
import {atoms as a} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {useDialogControl} from '#/components/Dialog'
import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog'
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ModerationModlists'>
export function ModerationModlistsScreen({}: Props) {
const {_} = useLingui()
const pal = usePalette('default')
const setMinimalShellMode = useSetMinimalShellMode()
const {isMobile} = useWebMediaQueries()
const navigation = useNavigation<NavigationProp>()
const {openModal} = useModalControls()
const {needsEmailVerification} = useEmail()
@@ -62,39 +55,32 @@ export function ModerationModlistsScreen({}: Props) {
return (
<Layout.Screen testID="moderationModlistsScreen">
<SimpleViewHeader
showBackButton={isMobile}
style={
!isMobile && [pal.border, {borderLeftWidth: 1, borderRightWidth: 1}]
}>
<View style={{flex: 1}}>
<Text type="title-lg" style={[pal.text, {fontWeight: '600'}]}>
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content align="left">
<Layout.Header.TitleText>
<Trans>Moderation Lists</Trans>
</Text>
<Text style={pal.textLight}>
</Layout.Header.TitleText>
<Layout.Header.SubtitleText>
<Trans>
Public, shareable lists of users to mute or block in bulk.
</Trans>
</Text>
</View>
<View style={[{marginLeft: 18}, isMobile && {marginLeft: 12}]}>
<Button
testID="newModListBtn"
type="default"
onPress={onPressNewList}
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 8,
}}>
<FontAwesomeIcon icon="plus" color={pal.colors.text} />
<Text type="button" style={pal.text}>
<Trans>New</Trans>
</Text>
</Button>
</View>
</SimpleViewHeader>
<MyLists filter="mod" style={s.flexGrow1} />
</Layout.Header.SubtitleText>
</Layout.Header.Content>
<Button
label={_(msg`New list`)}
testID="newModListBtn"
color="secondary"
variant="solid"
size="small"
onPress={onPressNewList}>
<ButtonIcon icon={PlusIcon} />
<ButtonText>
<Trans context="action">New</Trans>
</ButtonText>
</Button>
</Layout.Header.Outer>
<MyLists filter="mod" style={a.flex_grow} />
<VerifyEmailDialog
reasonText={_(
msg`Before creating a list, you must first verify your email.`,
+7 -21
View File
@@ -23,7 +23,6 @@ import {ProfileCard} from '#/view/com/profile/ProfileCard'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {Text} from '#/view/com/util/text/Text'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps<
@@ -97,21 +96,17 @@ export function ModerationMutedAccounts({}: Props) {
)
return (
<Layout.Screen testID="mutedAccountsScreen">
<CenteredView
style={[
styles.container,
isTabletOrDesktop && styles.containerDesktop,
pal.view,
pal.border,
]}
testID="mutedAccountsScreen">
<ViewHeader title={_(msg`Muted Accounts`)} showOnDesktop />
<ViewHeader title={_(msg`Muted Accounts`)} showOnDesktop />
<Layout.Center>
<Text
type="sm"
style={[
styles.description,
pal.text,
isTabletOrDesktop && styles.descriptionDesktop,
{
marginTop: 20,
},
]}>
<Trans>
Muted accounts have their posts removed from your feed and from your
@@ -119,7 +114,7 @@ export function ModerationMutedAccounts({}: Props) {
</Trans>
</Text>
{isEmpty ? (
<View style={[pal.border, !isTabletOrDesktop && styles.flex1]}>
<View style={[pal.border]}>
{isError ? (
<ErrorScreen
title="Oops!"
@@ -165,21 +160,12 @@ export function ModerationMutedAccounts({}: Props) {
desktopFixedHeight
/>
)}
</CenteredView>
</Layout.Center>
</Layout.Screen>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingBottom: 100,
},
containerDesktop: {
borderLeftWidth: 1,
borderRightWidth: 1,
paddingBottom: 0,
},
title: {
textAlign: 'center',
marginTop: 12,
+65 -113
View File
@@ -1,4 +1,4 @@
import React, {useCallback} from 'react'
import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -6,7 +6,6 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {ComposeIcon2} from '#/lib/icons'
import {
NativeStackScreenProps,
@@ -14,7 +13,7 @@ import {
} from '#/lib/routes/types'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {isNative, isWeb} from '#/platform/detection'
import {emitSoftReset, listenSoftReset} from '#/state/events'
import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
import {
@@ -29,28 +28,25 @@ import {FAB} from '#/view/com/util/fab/FAB'
import {ListMethods} from '#/view/com/util/List'
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {MainScrollProvider} from '#/view/com/util/MainScrollProvider'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {SettingsGear2_Stroke2_Corner0_Rounded as SettingsIcon} from '#/components/icons/SettingsGear2'
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
type Props = NativeStackScreenProps<
NotificationsTabNavigatorParams,
'Notifications'
>
export function NotificationsScreen({route: {params}}: Props) {
const t = useTheme()
const {gtTablet} = useBreakpoints()
const {_} = useLingui()
const setMinimalShellMode = useSetMinimalShellMode()
const [isScrolledDown, setIsScrolledDown] = React.useState(false)
const [isLoadingLatest, setIsLoadingLatest] = React.useState(false)
const scrollElRef = React.useRef<ListMethods>(null)
const t = useTheme()
const {isDesktop} = useWebMediaQueries()
const queryClient = useQueryClient()
const unreadNotifs = useUnreadNotifications()
const unreadApi = useUnreadNotificationsApi()
@@ -110,121 +106,77 @@ export function NotificationsScreen({route: {params}}: Props) {
return listenSoftReset(onPressLoadLatest)
}, [onPressLoadLatest, isScreenFocused])
const renderButton = useCallback(() => {
return (
<Link
to="/notifications/settings"
label={_(msg`Notification settings`)}
size="small"
variant="ghost"
color="secondary"
shape="square"
style={[a.justify_center]}>
<SettingsIcon size="md" style={t.atoms.text_contrast_medium} />
</Link>
)
}, [_, t])
const ListHeaderComponent = React.useCallback(() => {
if (isDesktop) {
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,
]}>
return (
<Layout.Screen testID="notificationsScreen">
<Layout.Header.Outer>
<Layout.Header.MenuButton />
<Layout.Header.Content>
<Button
label={_(msg`Notifications`)}
accessibilityHint={_(msg`Refresh notifications`)}
onPress={emitSoftReset}>
{({hovered, pressed}) => (
<Text
style={[
a.text_2xl,
a.font_bold,
(hovered || pressed) && a.underline,
]}>
onPress={emitSoftReset}
style={[a.justify_start]}>
{({hovered}) => (
<Layout.Header.TitleText
style={[a.w_full, hovered && a.underline]}>
<Trans>Notifications</Trans>
{hasNew && (
{isWeb && gtTablet && hasNew && (
<View
style={{
left: 4,
top: -8,
backgroundColor: t.palette.primary_500,
width: 8,
height: 8,
borderRadius: 4,
}}
style={[
a.rounded_full,
{
width: 8,
height: 8,
bottom: 3,
left: 6,
backgroundColor: t.palette.primary_500,
},
]}
/>
)}
</Text>
</Layout.Header.TitleText>
)}
</Button>
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
{isLoadingLatest ? <Loader size="md" /> : <></>}
{renderButton()}
</View>
</View>
)
}
return <></>
}, [isDesktop, t, hasNew, renderButton, _, isLoadingLatest])
</Layout.Header.Content>
<Layout.Header.Slot>
<Link
to="/notifications/settings"
label={_(msg`Notification settings`)}
size="small"
variant="ghost"
color="secondary"
shape="round"
style={[a.justify_center]}>
<ButtonIcon
icon={isLoadingLatest ? Loader : SettingsIcon}
size="lg"
/>
</Link>
</Layout.Header.Slot>
</Layout.Header.Outer>
const renderHeaderSpinner = React.useCallback(() => {
return (
<View
style={[
{width: 30, height: 20},
a.flex_row,
a.align_center,
a.justify_end,
a.gap_md,
]}>
{isLoadingLatest ? <Loader width={20} /> : <></>}
{renderButton()}
</View>
)
}, [renderButton, isLoadingLatest])
return (
<Layout.Screen testID="notificationsScreen">
<CenteredView style={[a.flex_1, {paddingTop: 2}]} sideBorders={true}>
<ViewHeader
title={_(msg`Notifications`)}
canGoBack={false}
showBorder={true}
renderButton={renderHeaderSpinner}
<MainScrollProvider>
<Feed
onScrolledDownChange={setIsScrolledDown}
scrollElRef={scrollElRef}
overridePriorityNotifications={params?.show === 'all'}
/>
<MainScrollProvider>
<Feed
onScrolledDownChange={setIsScrolledDown}
scrollElRef={scrollElRef}
ListHeaderComponent={ListHeaderComponent}
overridePriorityNotifications={params?.show === 'all'}
/>
</MainScrollProvider>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onPressLoadLatest}
label={_(msg`Load new notifications`)}
showIndicator={hasNew}
/>
)}
<FAB
testID="composeFAB"
onPress={() => openComposer({})}
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
accessibilityRole="button"
accessibilityLabel={_(msg`New post`)}
accessibilityHint=""
</MainScrollProvider>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onPressLoadLatest}
label={_(msg`Load new notifications`)}
showIndicator={hasNew}
/>
</CenteredView>
)}
<FAB
testID="composeFAB"
onPress={() => openComposer({})}
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
accessibilityRole="button"
accessibilityLabel={_(msg`New post`)}
accessibilityHint=""
/>
</Layout.Screen>
)
}
+1 -5
View File
@@ -1,12 +1,10 @@
import React from 'react'
import {View} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {makeRecordUri} from '#/lib/strings/url-helpers'
import {useSetMinimalShellMode} from '#/state/shell'
import {PostThread as PostThreadComponent} from '#/view/com/post-thread/PostThread'
import {atoms as a} from '#/alf'
import * as Layout from '#/components/Layout'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostThread'>
@@ -24,9 +22,7 @@ export function PostThreadScreen({route}: Props) {
return (
<Layout.Screen testID="postThreadScreen">
<View style={a.flex_1}>
<PostThreadComponent uri={uri} />
</View>
<PostThreadComponent uri={uri} />
</Layout.Screen>
)
}
+2 -4
View File
@@ -40,11 +40,9 @@ import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {FAB} from '#/view/com/util/fab/FAB'
import {ListRef} from '#/view/com/util/List'
import {CenteredView} from '#/view/com/util/Views'
import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header'
import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
import {web} from '#/alf'
import * as Layout from '#/components/Layout'
import {ScreenHider} from '#/components/moderation/ScreenHider'
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
@@ -116,9 +114,9 @@ function ProfileScreenInner({route}: Props) {
// Most pushes will happen here, since we will have only placeholder data
if (isLoadingDid || isLoadingProfile || starterPacksQuery.isLoading) {
return (
<CenteredView sideBorders style={web({height: '100vh'})}>
<Layout.Content>
<ProfileHeaderLoading />
</CenteredView>
</Layout.Content>
)
}
if (resolveError || profileError) {
+4 -5
View File
@@ -49,7 +49,6 @@ import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {LoadingScreen} from '#/view/com/util/LoadingScreen'
import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useTheme} from '#/alf'
import {Button as NewButton, ButtonText} from '#/components/Button'
import {useRichText} from '#/components/hooks/useRichText'
@@ -98,7 +97,7 @@ export function ProfileFeedScreen(props: Props) {
if (error) {
return (
<Layout.Screen testID="profileFeedScreenError">
<CenteredView>
<Layout.Content>
<View style={[pal.view, pal.border, styles.notFoundContainer]}>
<Text type="title-lg" style={[pal.text, s.mb10]}>
<Trans>Could not load feed</Trans>
@@ -120,7 +119,7 @@ export function ProfileFeedScreen(props: Props) {
</Button>
</View>
</View>
</CenteredView>
</Layout.Content>
</Layout.Screen>
)
}
@@ -394,7 +393,7 @@ export function ProfileFeedScreenInner({
])
return (
<View style={s.hContentRegion}>
<>
<ReportDialog
control={reportDialogControl}
params={{
@@ -434,7 +433,7 @@ export function ProfileFeedScreenInner({
accessibilityHint=""
/>
)}
</View>
</>
)
}
-2
View File
@@ -10,7 +10,6 @@ import {ProfileFollowers as ProfileFollowersComponent} from '#/view/com/profile/
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, 'ProfileFollowers'>
export const ProfileFollowersScreen = ({route}: Props) => {
@@ -27,7 +26,6 @@ export const ProfileFollowersScreen = ({route}: Props) => {
return (
<Layout.Screen testID="profileFollowersScreen">
<CenteredView sideBorders={true}>
<ListHeaderDesktop title={_(msg`Followers`)} />
<ViewHeader title={_(msg`Followers`)} showBorder={!isWeb} />
<ProfileFollowersComponent name={name} />
</CenteredView>
-2
View File
@@ -10,7 +10,6 @@ import {ProfileFollows as ProfileFollowsComponent} from '#/view/com/profile/Prof
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, 'ProfileFollows'>
export const ProfileFollowsScreen = ({route}: Props) => {
@@ -27,7 +26,6 @@ export const ProfileFollowsScreen = ({route}: Props) => {
return (
<Layout.Screen testID="profileFollowsScreen">
<CenteredView sideBorders={true}>
<ListHeaderDesktop title={_(msg`Following`)} />
<ViewHeader title={_(msg`Following`)} showBorder={!isWeb} />
<ProfileFollowsComponent name={name} />
</CenteredView>
+4 -6
View File
@@ -69,7 +69,6 @@ import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
import {LoadingScreen} from '#/view/com/util/LoadingScreen'
import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
import {CenteredView} from '#/view/com/util/Views'
import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen'
import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
@@ -107,20 +106,20 @@ function ProfileListScreenInner(props: Props) {
if (resolveError) {
return (
<CenteredView>
<Layout.Content>
<ErrorScreen
error={_(
msg`We're sorry, but we were unable to resolve this list. If this persists, please contact the list creator, @${handleOrDid}.`,
)}
/>
</CenteredView>
</Layout.Content>
)
}
if (listError) {
return (
<CenteredView>
<Layout.Content>
<ErrorScreen error={cleanError(listError)} />
</CenteredView>
</Layout.Content>
)
}
@@ -1010,7 +1009,6 @@ function ErrorScreen({error}: {error: string}) {
pal.view,
pal.border,
{
marginTop: 10,
paddingHorizontal: 18,
paddingVertical: 14,
borderTopWidth: StyleSheet.hairlineWidth,
+117 -127
View File
@@ -25,13 +25,12 @@ import {FeedSourceCard} from '#/view/com/feeds/FeedSourceCard'
import {TextLink} from '#/view/com/util/Link'
import {Text} from '#/view/com/util/text/Text'
import * as Toast from '#/view/com/util/Toast'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
import {FloppyDisk_Stroke2_Corner0_Rounded as SaveIcon} from '#/components/icons/FloppyDisk'
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
@@ -51,7 +50,7 @@ function SavedFeedsInner({
}) {
const pal = usePalette('default')
const {_} = useLingui()
const {isMobile, isTabletOrDesktop, isDesktop} = useWebMediaQueries()
const {isMobile, isDesktop} = useWebMediaQueries()
const setMinimalShellMode = useSetMinimalShellMode()
const {mutateAsync: overwriteSavedFeeds, isPending: isOverwritePending} =
useOverwriteSavedFeedsMutation()
@@ -88,136 +87,128 @@ function SavedFeedsInner({
}
}, [_, overwriteSavedFeeds, currentFeeds, navigation])
const renderHeaderBtn = React.useCallback(() => {
return (
<Button
size="small"
variant={hasUnsavedChanges ? 'solid' : 'solid'}
color={hasUnsavedChanges ? 'primary' : 'secondary'}
onPress={onSaveChanges}
label={_(msg`Save changes`)}
disabled={isOverwritePending || !hasUnsavedChanges}
style={[isDesktop && a.mt_sm]}
testID="saveChangesBtn">
<ButtonText style={[isDesktop && a.text_md]}>
{isDesktop ? <Trans>Save changes</Trans> : <Trans>Save</Trans>}
</ButtonText>
{isOverwritePending && <ButtonIcon icon={Loader} />}
</Button>
)
}, [_, isDesktop, onSaveChanges, hasUnsavedChanges, isOverwritePending])
return (
<Layout.Screen>
<CenteredView
style={[a.util_screen_outer]}
sideBorders={isTabletOrDesktop}>
<ViewHeader
title={_(msg`Edit My Feeds`)}
showOnDesktop
showBorder
renderButton={renderHeaderBtn}
/>
<ScrollView style={[a.flex_1]} contentContainerStyle={[a.border_0]}>
{noSavedFeedsOfAnyType && (
<View style={[pal.border, a.border_b]}>
<NoSavedFeedsOfAnyType />
<Layout.Header.Outer>
<Layout.Header.BackButton />
<Layout.Header.Content align="left">
<Layout.Header.TitleText>
<Trans>Feeds</Trans>
</Layout.Header.TitleText>
</Layout.Header.Content>
<Button
testID="saveChangesBtn"
size="small"
variant={hasUnsavedChanges ? 'solid' : 'solid'}
color={hasUnsavedChanges ? 'primary' : 'secondary'}
onPress={onSaveChanges}
label={_(msg`Save changes`)}
disabled={isOverwritePending || !hasUnsavedChanges}>
<ButtonIcon icon={isOverwritePending ? Loader : SaveIcon} />
<ButtonText>
{isDesktop ? <Trans>Save changes</Trans> : <Trans>Save</Trans>}
</ButtonText>
</Button>
</Layout.Header.Outer>
<Layout.Content>
{noSavedFeedsOfAnyType && (
<View style={[pal.border, a.border_b]}>
<NoSavedFeedsOfAnyType />
</View>
)}
<View style={[pal.text, pal.border, styles.title]}>
<Text type="title" style={pal.text}>
<Trans>Pinned Feeds</Trans>
</Text>
</View>
{preferences ? (
!pinnedFeeds.length ? (
<View
style={[
pal.border,
isMobile && s.flex1,
pal.viewLight,
styles.empty,
]}>
<Text type="lg" style={[pal.text]}>
<Trans>You don't have any pinned feeds.</Trans>
</Text>
</View>
)}
<View style={[pal.text, pal.border, styles.title]}>
<Text type="title" style={pal.text}>
<Trans>Pinned Feeds</Trans>
</Text>
</View>
{preferences ? (
!pinnedFeeds.length ? (
<View
style={[
pal.border,
isMobile && s.flex1,
pal.viewLight,
styles.empty,
]}>
<Text type="lg" style={[pal.text]}>
<Trans>You don't have any pinned feeds.</Trans>
</Text>
</View>
) : (
pinnedFeeds.map(f => (
<ListItem
key={f.id}
feed={f}
isPinned
currentFeeds={currentFeeds}
setCurrentFeeds={setCurrentFeeds}
preferences={preferences}
/>
))
)
) : (
<ActivityIndicator style={{marginTop: 20}} />
)}
pinnedFeeds.map(f => (
<ListItem
key={f.id}
feed={f}
isPinned
currentFeeds={currentFeeds}
setCurrentFeeds={setCurrentFeeds}
preferences={preferences}
/>
))
)
) : (
<ActivityIndicator style={{marginTop: 20}} />
)}
{noFollowingFeed && (
<View style={[pal.border, a.border_b]}>
<NoFollowingFeed />
{noFollowingFeed && (
<View style={[pal.border, a.border_b]}>
<NoFollowingFeed />
</View>
)}
<View style={[pal.text, pal.border, styles.title]}>
<Text type="title" style={pal.text}>
<Trans>Saved Feeds</Trans>
</Text>
</View>
{preferences ? (
!unpinnedFeeds.length ? (
<View
style={[
pal.border,
isMobile && s.flex1,
pal.viewLight,
styles.empty,
]}>
<Text type="lg" style={[pal.text]}>
<Trans>You don't have any saved feeds.</Trans>
</Text>
</View>
)}
<View style={[pal.text, pal.border, styles.title]}>
<Text type="title" style={pal.text}>
<Trans>Saved Feeds</Trans>
</Text>
</View>
{preferences ? (
!unpinnedFeeds.length ? (
<View
style={[
pal.border,
isMobile && s.flex1,
pal.viewLight,
styles.empty,
]}>
<Text type="lg" style={[pal.text]}>
<Trans>You don't have any saved feeds.</Trans>
</Text>
</View>
) : (
unpinnedFeeds.map(f => (
<ListItem
key={f.id}
feed={f}
isPinned={false}
currentFeeds={currentFeeds}
setCurrentFeeds={setCurrentFeeds}
preferences={preferences}
/>
))
)
) : (
<ActivityIndicator style={{marginTop: 20}} />
)}
unpinnedFeeds.map(f => (
<ListItem
key={f.id}
feed={f}
isPinned={false}
currentFeeds={currentFeeds}
setCurrentFeeds={setCurrentFeeds}
preferences={preferences}
/>
))
)
) : (
<ActivityIndicator style={{marginTop: 20}} />
)}
<View style={styles.footerText}>
<Text type="sm" style={pal.textLight}>
<Trans>
Feeds are custom algorithms that users build with a little
coding expertise.{' '}
<TextLink
type="sm"
style={pal.link}
href="https://github.com/bluesky-social/feed-generator"
text={_(msg`See this guide`)}
/>{' '}
for more information.
</Trans>
</Text>
</View>
<View style={{height: 100}} />
</ScrollView>
</CenteredView>
<View style={styles.footerText}>
<Text type="sm" style={pal.textLight}>
<Trans>
Feeds are custom algorithms that users build with a little coding
expertise.{' '}
<TextLink
type="sm"
style={pal.link}
href="https://github.com/bluesky-social/feed-generator"
text={_(msg`See this guide`)}
/>{' '}
for more information.
</Trans>
</Text>
</View>
</Layout.Content>
</Layout.Screen>
)
}
@@ -456,7 +447,6 @@ const styles = StyleSheet.create({
},
footerText: {
paddingHorizontal: 26,
paddingTop: 22,
paddingBottom: 100,
paddingVertical: 22,
},
})
+105 -131
View File
@@ -55,7 +55,6 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {Link} from '#/view/com/util/Link'
import {List} from '#/view/com/util/List'
import {Text} from '#/view/com/util/text/Text'
import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {Explore} from '#/view/screens/Search/Explore'
import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
@@ -68,63 +67,46 @@ import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import * as Layout from '#/components/Layout'
function Loader() {
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
return (
<CenteredView
style={[
// @ts-ignore web only -prf
{
padding: 18,
height: isWeb ? '100vh' : undefined,
},
pal.border,
]}
sideBorders={!isMobile}>
<ActivityIndicator />
</CenteredView>
<Layout.Content>
<View style={[a.py_xl]}>
<ActivityIndicator />
</View>
</Layout.Content>
)
}
function EmptyState({message, error}: {message: string; error?: string}) {
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
return (
<CenteredView
sideBorders={!isMobile}
style={[
pal.border,
// @ts-ignore web only -prf
{
padding: 18,
height: isWeb ? '100vh' : undefined,
},
]}>
<View style={[pal.viewLight, {padding: 18, borderRadius: 8}]}>
<Text style={[pal.text]}>{message}</Text>
<Layout.Content>
<View style={[a.p_xl]}>
<View style={[pal.viewLight, {padding: 18, borderRadius: 8}]}>
<Text style={[pal.text]}>{message}</Text>
{error && (
<>
<View
style={[
{
marginVertical: 12,
height: 1,
width: '100%',
backgroundColor: pal.text.color,
opacity: 0.2,
},
]}
/>
{error && (
<>
<View
style={[
{
marginVertical: 12,
height: 1,
width: '100%',
backgroundColor: pal.text.color,
opacity: 0.2,
},
]}
/>
<Text style={[pal.textLight]}>
<Trans>Error:</Trans> {error}
</Text>
</>
)}
<Text style={[pal.textLight]}>
<Trans>Error:</Trans> {error}
</Text>
</>
)}
</View>
</View>
</CenteredView>
</Layout.Content>
)
}
@@ -224,7 +206,7 @@ let SearchScreenPostResults = ({
if (item.type === 'post') {
return <Post post={item.post} />
} else {
return <Loader />
return null
}
}}
keyExtractor={item => item.key}
@@ -550,19 +532,13 @@ let SearchScreenInner = ({
<Pager
onPageSelected={onPageSelected}
renderTabBar={props => (
<CenteredView
sideBorders
<Layout.Center
style={[
pal.border,
pal.view,
web({
position: isWeb ? 'sticky' : '',
zIndex: 1,
}),
web([a.sticky, a.z_10]),
{top: isWeb ? headerHeight : undefined},
]}>
<TabBar items={sections.map(section => section.title)} {...props} />
</CenteredView>
</Layout.Center>
)}
initialPage={0}>
{sections.map((section, i) => (
@@ -572,7 +548,7 @@ let SearchScreenInner = ({
) : hasSession ? (
<Explore />
) : (
<CenteredView sideBorders style={pal.border}>
<Layout.Center>
<View
// @ts-ignore web only -esb
style={{
@@ -614,7 +590,7 @@ let SearchScreenInner = ({
</Text>
</View>
</View>
</CenteredView>
</Layout.Center>
)
}
SearchScreenInner = React.memo(SearchScreenInner)
@@ -650,7 +626,7 @@ export function SearchScreen(
* Arbitrary sizing, so guess and check, used for sticky header alignment and
* sizing.
*/
const headerHeight = 64 + (showFilters ? 40 : 0)
const headerHeight = 60 + (showFilters ? 40 : 0)
useFocusEffect(
useNonReactiveCallback(() => {
@@ -861,73 +837,79 @@ export function SearchScreen(
return (
<Layout.Screen testID="searchScreen">
<CenteredView
<View
style={[
a.p_md,
a.pb_sm,
a.gap_sm,
t.atoms.bg,
web({
height: headerHeight,
position: 'sticky',
top: 0,
zIndex: 1,
}),
]}
sideBorders={gtMobile}>
<View style={[a.flex_row, a.gap_sm]}>
{!gtMobile && (
<Button
testID="viewHeaderBackOrMenuBtn"
onPress={onPressMenu}
hitSlop={HITSLOP_10}
label={_(msg`Menu`)}
accessibilityHint={_(msg`Access navigation links and settings`)}
size="large"
variant="solid"
color="secondary"
shape="square">
<ButtonIcon icon={Menu} size="lg" />
</Button>
)}
<View style={[a.flex_1]}>
<SearchInput
ref={textInput}
value={searchText}
onFocus={onSearchInputFocus}
onChangeText={onChangeText}
onClearText={onPressClearQuery}
onSubmitEditing={onSubmit}
/>
</View>
{showAutocomplete && (
<Button
label={_(msg`Cancel search`)}
size="large"
variant="ghost"
color="secondary"
style={[a.px_sm]}
onPress={onPressCancelSearch}
hitSlop={HITSLOP_10}>
<ButtonText>
<Trans>Cancel</Trans>
</ButtonText>
</Button>
)}
</View>
{showFilters && (
<View
style={[a.flex_row, a.align_center, a.justify_between, a.gap_sm]}>
<View style={[{width: 140}]}>
<SearchLanguageDropdown
value={params.lang}
onChange={params.setLang}
/>
]}>
<Layout.Center>
<View style={[a.p_md, a.pb_sm, a.gap_sm, t.atoms.bg]}>
<View style={[a.flex_row, a.gap_sm]}>
{!gtMobile && (
<Button
testID="viewHeaderBackOrMenuBtn"
onPress={onPressMenu}
hitSlop={HITSLOP_10}
label={_(msg`Menu`)}
accessibilityHint={_(
msg`Access navigation links and settings`,
)}
size="large"
variant="solid"
color="secondary"
shape="square">
<ButtonIcon icon={Menu} size="lg" />
</Button>
)}
<View style={[a.flex_1]}>
<SearchInput
ref={textInput}
value={searchText}
onFocus={onSearchInputFocus}
onChangeText={onChangeText}
onClearText={onPressClearQuery}
onSubmitEditing={onSubmit}
/>
</View>
{showAutocomplete && (
<Button
label={_(msg`Cancel search`)}
size="large"
variant="ghost"
color="secondary"
style={[a.px_sm]}
onPress={onPressCancelSearch}
hitSlop={HITSLOP_10}>
<ButtonText>
<Trans>Cancel</Trans>
</ButtonText>
</Button>
)}
</View>
{showFilters && (
<View
style={[
a.flex_row,
a.align_center,
a.justify_between,
a.gap_sm,
]}>
<View style={[{width: 140}]}>
<SearchLanguageDropdown
value={params.lang}
onChange={params.setLang}
/>
</View>
</View>
)}
</View>
)}
</CenteredView>
</Layout.Center>
</View>
<View
style={{
@@ -992,10 +974,7 @@ let AutocompleteResults = ({
!moderationOpts ? (
<Loader />
) : (
<ScrollView
style={{height: '100%'}}
// @ts-ignore web only -prf
dataSet={{stableGutters: '1'}}
<Layout.Content
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag">
<SearchLinkCard
@@ -1020,7 +999,7 @@ let AutocompleteResults = ({
/>
))}
<View style={{height: 200}} />
</ScrollView>
</Layout.Content>
)}
</>
)
@@ -1042,17 +1021,12 @@ function SearchHistory({
onRemoveItemClick: (item: string) => void
onRemoveProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void
}) {
const {isTabletOrDesktop, isMobile} = useWebMediaQueries()
const {isMobile} = useWebMediaQueries()
const pal = usePalette('default')
const {_} = useLingui()
return (
<CenteredView
sideBorders={isTabletOrDesktop}
// @ts-ignore web only -prf
style={{
height: isWeb ? '100vh' : undefined,
}}>
<Layout.Content>
<View style={styles.searchHistoryContainer}>
{(searchHistory.length > 0 || selectedProfiles.length > 0) && (
<Text style={[pal.text, styles.searchHistoryTitle]}>
@@ -1152,7 +1126,7 @@ function SearchHistory({
</View>
)}
</View>
</CenteredView>
</Layout.Content>
)
}