Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b329edc9e | |||
| 5608011974 | |||
| b1099d0169 | |||
| 8e4a1a9b06 | |||
| f54905454f | |||
| c21d83e85e | |||
| a32e6a9bb1 | |||
| f5047526ea | |||
| 74bc54fc65 |
+1
-1
@@ -68,7 +68,7 @@ export const atoms = {
|
||||
* Used for the outermost components on screens, to ensure that they can fill
|
||||
* the screen and extend beyond.
|
||||
*/
|
||||
// @ts-ignore - web only minHeight string
|
||||
// @ts-expect-error - web only minHeight string
|
||||
util_screen_outer: [
|
||||
web({
|
||||
minHeight: '100vh',
|
||||
|
||||
@@ -226,6 +226,7 @@ export function createInput(Component: typeof TextInput) {
|
||||
<>
|
||||
<Component
|
||||
accessibilityHint={undefined}
|
||||
hitSlop={HITSLOP_20}
|
||||
{...rest}
|
||||
accessibilityLabel={label}
|
||||
ref={refs}
|
||||
@@ -242,7 +243,6 @@ export function createInput(Component: typeof TextInput) {
|
||||
placeholder={placeholder || label}
|
||||
placeholderTextColor={t.palette.contrast_500}
|
||||
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
|
||||
hitSlop={HITSLOP_20}
|
||||
style={flattened}
|
||||
/>
|
||||
|
||||
|
||||
+175
-209
@@ -1,32 +1,34 @@
|
||||
import React from 'react'
|
||||
import React, {useEffect, useLayoutEffect} from 'react'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
ImageStyle,
|
||||
Pressable,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
TextInput,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {ScrollView as RNGHScrollView} from 'react-native-gesture-handler'
|
||||
import RNPickerSelect from 'react-native-picker-select'
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
LayoutAnimationConfig,
|
||||
LinearTransition,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated'
|
||||
import {AppBskyActorDefs, AppBskyFeedDefs, moderateProfile} from '@atproto/api'
|
||||
import {
|
||||
FontAwesomeIcon,
|
||||
FontAwesomeIconStyle,
|
||||
} from '@fortawesome/react-native-fontawesome'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {APP_LANGUAGES, LANGUAGES} from '#/lib/../locale/languages'
|
||||
import {createHitslop} from '#/lib/constants'
|
||||
import {createHitslop, HITSLOP_20} from '#/lib/constants'
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {MagnifyingGlassIcon} from '#/lib/icons'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {NavigationProp} from '#/lib/routes/types'
|
||||
@@ -47,7 +49,6 @@ import {useActorSearch} from '#/state/queries/actor-search'
|
||||
import {usePopularFeedsSearch} from '#/state/queries/feed'
|
||||
import {useSearchPostsQuery} from '#/state/queries/search-posts'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useSetDrawerOpen} from '#/state/shell'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {Pager} from '#/view/com/pager/Pager'
|
||||
import {TabBar} from '#/view/com/pager/TabBar'
|
||||
@@ -55,23 +56,18 @@ import {Post} from '#/view/com/post/Post'
|
||||
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 {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {Explore} from '#/view/screens/Search/Explore'
|
||||
import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
|
||||
import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
|
||||
import {
|
||||
atoms as a,
|
||||
tokens,
|
||||
useBreakpoints,
|
||||
useTheme as useThemeNew,
|
||||
web,
|
||||
} from '#/alf'
|
||||
import {atoms as a, native, tokens, useBreakpoints, useTheme, web} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as FeedCard from '#/components/FeedCard'
|
||||
import {SearchInput} from '#/components/forms/SearchInput'
|
||||
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
|
||||
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
function Loader() {
|
||||
return (
|
||||
@@ -84,13 +80,13 @@ function Loader() {
|
||||
}
|
||||
|
||||
function EmptyState({message, error}: {message: string; error?: string}) {
|
||||
const pal = usePalette('default')
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<Layout.Content>
|
||||
<View style={[a.p_xl]}>
|
||||
<View style={[pal.viewLight, {padding: 18, borderRadius: 8}]}>
|
||||
<Text style={[pal.text]}>{message}</Text>
|
||||
<View style={[t.atoms.bg_contrast_25, a.rounded_sm, a.p_lg]}>
|
||||
<Text style={[a.text_md]}>{message}</Text>
|
||||
|
||||
{error && (
|
||||
<>
|
||||
@@ -100,13 +96,13 @@ function EmptyState({message, error}: {message: string; error?: string}) {
|
||||
marginVertical: 12,
|
||||
height: 1,
|
||||
width: '100%',
|
||||
backgroundColor: pal.text.color,
|
||||
backgroundColor: t.atoms.text.color,
|
||||
opacity: 0.2,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Text style={[pal.textLight]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>Error:</Trans> {error}
|
||||
</Text>
|
||||
</>
|
||||
@@ -278,7 +274,7 @@ let SearchScreenFeedsResults = ({
|
||||
query: string
|
||||
active: boolean
|
||||
}): React.ReactNode => {
|
||||
const t = useThemeNew()
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
const {data: results, isFetched} = usePopularFeedsSearch({
|
||||
@@ -323,7 +319,7 @@ function SearchLanguageDropdown({
|
||||
value: string
|
||||
onChange(value: string): void
|
||||
}) {
|
||||
const t = useThemeNew()
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {appLanguage, contentLanguages} = useLanguagePrefs()
|
||||
|
||||
@@ -473,10 +469,10 @@ let SearchScreenInner = ({
|
||||
queryWithParams: string
|
||||
headerHeight: number
|
||||
}): React.ReactNode => {
|
||||
const pal = usePalette('default')
|
||||
const t = useTheme()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
const {hasSession} = useSession()
|
||||
const {isDesktop} = useWebMediaQueries()
|
||||
const {gtTablet} = useBreakpoints()
|
||||
const [activeTab, setActiveTab] = React.useState(0)
|
||||
const {_} = useLingui()
|
||||
|
||||
@@ -534,12 +530,7 @@ let SearchScreenInner = ({
|
||||
<Pager
|
||||
onPageSelected={onPageSelected}
|
||||
renderTabBar={props => (
|
||||
<Layout.Center
|
||||
style={[
|
||||
a.z_10,
|
||||
web([a.sticky]),
|
||||
{top: isWeb ? headerHeight : undefined},
|
||||
]}>
|
||||
<Layout.Center style={[a.z_10, web([a.sticky, {top: headerHeight}])]}>
|
||||
<TabBar items={sections.map(section => section.title)} {...props} />
|
||||
</Layout.Center>
|
||||
)}
|
||||
@@ -552,40 +543,30 @@ let SearchScreenInner = ({
|
||||
<Explore />
|
||||
) : (
|
||||
<Layout.Center>
|
||||
<View style={web({height: '100vh'})}>
|
||||
{isDesktop && (
|
||||
<Text
|
||||
type="title"
|
||||
<View style={a.flex_1}>
|
||||
{gtTablet && (
|
||||
<View
|
||||
style={[
|
||||
pal.text,
|
||||
pal.border,
|
||||
{
|
||||
display: 'flex',
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 18,
|
||||
fontWeight: '600',
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
a.px_lg,
|
||||
a.pt_sm,
|
||||
a.pb_lg,
|
||||
]}>
|
||||
<Trans>Search</Trans>
|
||||
</Text>
|
||||
<Text style={[a.text_2xl, a.font_heavy]}>
|
||||
<Trans>Search</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 30,
|
||||
gap: 15,
|
||||
}}>
|
||||
<View style={[a.align_center, a.justify_center, a.py_4xl, a.gap_lg]}>
|
||||
<MagnifyingGlassIcon
|
||||
strokeWidth={3}
|
||||
size={isDesktop ? 60 : 60}
|
||||
style={pal.textLight}
|
||||
size={60}
|
||||
style={t.atoms.text_contrast_medium as StyleProp<ViewStyle>}
|
||||
/>
|
||||
<Text type="xl" style={[pal.textLight, {paddingHorizontal: 18}]}>
|
||||
<Trans>Find posts and users on Bluesky</Trans>
|
||||
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
|
||||
<Trans>Find posts, users, and feeds on Bluesky</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -597,12 +578,11 @@ SearchScreenInner = React.memo(SearchScreenInner)
|
||||
export function SearchScreen(
|
||||
props: NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>,
|
||||
) {
|
||||
const t = useThemeNew()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
const textInput = React.useRef<TextInput>(null)
|
||||
const {_} = useLingui()
|
||||
const setDrawerOpen = useSetDrawerOpen()
|
||||
const setMinimalShellMode = useSetMinimalShellMode()
|
||||
|
||||
// Query terms
|
||||
@@ -621,11 +601,17 @@ export function SearchScreen(
|
||||
initialQuery: queryParam,
|
||||
})
|
||||
const showFilters = Boolean(queryWithParams && !showAutocomplete)
|
||||
/*
|
||||
* Arbitrary sizing, so guess and check, used for sticky header alignment and
|
||||
* sizing.
|
||||
*/
|
||||
const headerHeight = 60 + (showFilters ? 40 : 0)
|
||||
|
||||
// web only - measure header height for sticky positioning
|
||||
const [headerHeight, setHeaderHeight] = React.useState(0)
|
||||
const headerRef = React.useRef(null)
|
||||
useLayoutEffect(() => {
|
||||
if (isWeb) {
|
||||
if (!headerRef.current) return
|
||||
const measurement = (headerRef.current as Element).getBoundingClientRect()
|
||||
setHeaderHeight(measurement.height)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useFocusEffect(
|
||||
useNonReactiveCallback(() => {
|
||||
@@ -654,11 +640,6 @@ export function SearchScreen(
|
||||
loadSearchHistory()
|
||||
}, [])
|
||||
|
||||
const onPressMenu = React.useCallback(() => {
|
||||
textInput.current?.blur()
|
||||
setDrawerOpen(true)
|
||||
}, [setDrawerOpen])
|
||||
|
||||
const onPressClearQuery = React.useCallback(() => {
|
||||
scrollToTopWeb()
|
||||
setSearchText('')
|
||||
@@ -840,37 +821,79 @@ export function SearchScreen(
|
||||
}
|
||||
}, [setShowAutocomplete])
|
||||
|
||||
const cancelWidth = useSharedValue(70)
|
||||
const showCancelBtnAnimation = useSharedValue(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (showAutocomplete) {
|
||||
showCancelBtnAnimation.set(withTiming(1))
|
||||
} else {
|
||||
showCancelBtnAnimation.set(withTiming(0))
|
||||
}
|
||||
}, [showAutocomplete, showCancelBtnAnimation])
|
||||
|
||||
const animatedInputContainerStyle = useAnimatedStyle(() => ({
|
||||
marginRight: showCancelBtnAnimation.get() * cancelWidth.get(),
|
||||
}))
|
||||
|
||||
const animatedCancelBtnStyle = useAnimatedStyle(() => ({
|
||||
opacity: showCancelBtnAnimation.get(),
|
||||
right: cancelWidth.get() * -1,
|
||||
}))
|
||||
|
||||
return (
|
||||
<Layout.Screen testID="searchScreen">
|
||||
<View
|
||||
ref={headerRef}
|
||||
onLayout={evt => {
|
||||
if (isWeb) setHeaderHeight(evt.nativeEvent.layout.height)
|
||||
}}
|
||||
style={[
|
||||
a.relative,
|
||||
a.z_10,
|
||||
web({
|
||||
height: headerHeight,
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
}),
|
||||
]}>
|
||||
<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 && !showAutocomplete && (
|
||||
<Button
|
||||
testID="viewHeaderBackOrMenuBtn"
|
||||
onPress={onPressMenu}
|
||||
hitSlop={HITSLOP_10}
|
||||
label={_(msg`Menu`)}
|
||||
accessibilityHint={_(
|
||||
msg`Access navigation links and settings`,
|
||||
<Layout.Center style={t.atoms.bg}>
|
||||
<LayoutAnimationConfig skipEntering skipExiting>
|
||||
{!gtMobile && !showAutocomplete && (
|
||||
<Animated.View
|
||||
entering={native(FadeIn.delay(100).duration(200))}
|
||||
exiting={native(FadeOut.duration(200))}
|
||||
// HACK: shift up search input. we can't remove the top padding
|
||||
// on the search input because it messes up the layout animation
|
||||
// if we add it only when the header is hidden
|
||||
style={{marginBottom: tokens.space.sm * -1}}>
|
||||
<Layout.Header.Outer noBottomBorder>
|
||||
<Layout.Header.MenuButton />
|
||||
<Layout.Header.Content
|
||||
align={showFilters ? 'left' : 'platform'}>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Search</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
{showFilters ? (
|
||||
<View style={[{minWidth: 140}]}>
|
||||
<SearchLanguageDropdown
|
||||
value={params.lang}
|
||||
onChange={params.setLang}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<Layout.Header.Slot />
|
||||
)}
|
||||
size="large"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
shape="square">
|
||||
<ButtonIcon icon={Menu} size="lg" />
|
||||
</Button>
|
||||
)}
|
||||
<View style={[a.flex_1]}>
|
||||
</Layout.Header.Outer>
|
||||
</Animated.View>
|
||||
)}
|
||||
</LayoutAnimationConfig>
|
||||
<Animated.View
|
||||
style={[a.px_md, a.pt_sm, a.pb_sm, a.overflow_hidden]}
|
||||
layout={native(LinearTransition)}>
|
||||
<Animated.View
|
||||
style={[a.gap_sm, a.relative, animatedInputContainerStyle]}>
|
||||
<View style={[a.w_full]}>
|
||||
<SearchInput
|
||||
ref={textInput}
|
||||
value={searchText}
|
||||
@@ -878,9 +901,20 @@ export function SearchScreen(
|
||||
onChangeText={onChangeText}
|
||||
onClearText={onPressClearQuery}
|
||||
onSubmitEditing={onSubmit}
|
||||
placeholder={_(msg`Search for posts, users, or feeds`)}
|
||||
hitSlop={{...HITSLOP_20, top: 0}}
|
||||
/>
|
||||
</View>
|
||||
{showAutocomplete && (
|
||||
<Animated.View
|
||||
style={[
|
||||
a.absolute,
|
||||
{top: 0, bottom: 0},
|
||||
a.pl_xs,
|
||||
animatedCancelBtnStyle,
|
||||
!showAutocomplete && a.pointer_events_none,
|
||||
]}
|
||||
onLayout={evt => cancelWidth.set(evt.nativeEvent.layout.width)}
|
||||
aria-hidden={!showAutocomplete}>
|
||||
<Button
|
||||
label={_(msg`Cancel search`)}
|
||||
size="large"
|
||||
@@ -893,26 +927,26 @@ export function SearchScreen(
|
||||
<Trans>Cancel</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
</Animated.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}
|
||||
/>
|
||||
{showFilters && gtMobile && (
|
||||
<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>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
</Layout.Center>
|
||||
</View>
|
||||
|
||||
@@ -1026,17 +1060,17 @@ function SearchHistory({
|
||||
onRemoveItemClick: (item: string) => void
|
||||
onRemoveProfileClick: (profile: AppBskyActorDefs.ProfileViewBasic) => void
|
||||
}) {
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const pal = usePalette('default')
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<Layout.Content
|
||||
keyboardDismissMode="interactive"
|
||||
keyboardShouldPersistTaps="handled">
|
||||
<View style={styles.searchHistoryContainer}>
|
||||
<View style={[a.w_full, a.px_md]}>
|
||||
{(searchHistory.length > 0 || selectedProfiles.length > 0) && (
|
||||
<Text style={[pal.text, styles.searchHistoryTitle]}>
|
||||
<Text style={[a.text_md, a.font_bold, a.p_md]}>
|
||||
<Trans>Recent Searches</Trans>
|
||||
</Text>
|
||||
)}
|
||||
@@ -1044,7 +1078,7 @@ function SearchHistory({
|
||||
<View
|
||||
style={[
|
||||
styles.selectedProfilesContainer,
|
||||
isMobile && styles.selectedProfilesContainerMobile,
|
||||
!gtMobile && styles.selectedProfilesContainerMobile,
|
||||
]}>
|
||||
<RNGHScrollView
|
||||
keyboardShouldPersistTaps="handled"
|
||||
@@ -1052,7 +1086,7 @@ function SearchHistory({
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.flex_nowrap,
|
||||
{marginHorizontal: -tokens.space._2xl},
|
||||
{marginHorizontal: tokens.space._2xl * -1},
|
||||
]}
|
||||
contentContainerStyle={[a.px_2xl, a.border_0]}>
|
||||
{selectedProfiles.slice(0, 5).map((profile, index) => (
|
||||
@@ -1060,7 +1094,7 @@ function SearchHistory({
|
||||
key={index}
|
||||
style={[
|
||||
styles.profileItem,
|
||||
isMobile && styles.profileItemMobile,
|
||||
!gtMobile && styles.profileItemMobile,
|
||||
]}>
|
||||
<Link
|
||||
href={makeProfileLink(profile)}
|
||||
@@ -1068,15 +1102,15 @@ function SearchHistory({
|
||||
asAnchor
|
||||
anchorNoUnderline
|
||||
onBeforePress={() => onProfileClick(profile)}
|
||||
style={styles.profilePressable}>
|
||||
<Image
|
||||
source={{uri: profile.avatar}}
|
||||
style={styles.profileAvatar as StyleProp<ImageStyle>}
|
||||
accessibilityIgnoresInvertColors
|
||||
style={[a.align_center, a.w_full]}>
|
||||
<UserAvatar
|
||||
avatar={profile.avatar}
|
||||
type={profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
size={60}
|
||||
/>
|
||||
<Text
|
||||
emoji
|
||||
style={[pal.text, styles.profileName]}
|
||||
style={[a.text_xs, a.text_center, styles.profileName]}
|
||||
numberOfLines={1}>
|
||||
{sanitizeDisplayName(
|
||||
profile.displayName || profile.handle,
|
||||
@@ -1092,11 +1126,7 @@ function SearchHistory({
|
||||
onPress={() => onRemoveProfileClick(profile)}
|
||||
hitSlop={createHitslop(6)}
|
||||
style={styles.profileRemoveBtn}>
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
size={14}
|
||||
style={pal.textLight as FontAwesomeIconStyle}
|
||||
/>
|
||||
<XIcon size="xs" style={t.atoms.text_contrast_low} />
|
||||
</Pressable>
|
||||
</View>
|
||||
))}
|
||||
@@ -1104,34 +1134,25 @@ function SearchHistory({
|
||||
</View>
|
||||
)}
|
||||
{searchHistory.length > 0 && (
|
||||
<View style={styles.searchHistoryContent}>
|
||||
<View style={[a.pl_md, a.pr_xs, a.mt_md]}>
|
||||
{searchHistory.slice(0, 5).map((historyItem, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.mt_md,
|
||||
a.justify_center,
|
||||
a.justify_between,
|
||||
]}>
|
||||
<View key={index} style={[a.flex_row, a.align_center, a.mt_xs]}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
onPress={() => onItemClick(historyItem)}
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[a.flex_1, a.py_sm]}>
|
||||
<Text style={pal.text}>{historyItem}</Text>
|
||||
style={[a.flex_1, a.py_md]}>
|
||||
<Text style={[a.text_md]}>{historyItem}</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
<Button
|
||||
label={_(msg`Remove ${historyItem}`)}
|
||||
onPress={() => onRemoveItemClick(historyItem)}
|
||||
hitSlop={HITSLOP_10}
|
||||
style={[a.px_md, a.py_xs, a.justify_center]}>
|
||||
<FontAwesomeIcon
|
||||
icon="xmark"
|
||||
size={16}
|
||||
style={pal.textLight as FontAwesomeIconStyle}
|
||||
/>
|
||||
</Pressable>
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
shape="round">
|
||||
<ButtonIcon icon={XIcon} />
|
||||
</Button>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
@@ -1148,41 +1169,6 @@ function scrollToTopWeb() {
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerMenuBtn: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
borderRadius: 30,
|
||||
marginRight: 6,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
headerSearchContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
borderRadius: 30,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
},
|
||||
headerSearchIcon: {
|
||||
marginRight: 6,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
headerSearchInput: {
|
||||
flex: 1,
|
||||
fontSize: 17,
|
||||
minWidth: 0,
|
||||
},
|
||||
headerCancelBtn: {
|
||||
paddingLeft: 10,
|
||||
alignSelf: 'center',
|
||||
zIndex: -1,
|
||||
elevation: -1, // For Android
|
||||
},
|
||||
searchHistoryContainer: {
|
||||
width: '100%',
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
selectedProfilesContainer: {
|
||||
marginTop: 10,
|
||||
paddingHorizontal: 12,
|
||||
@@ -1199,20 +1185,9 @@ const styles = StyleSheet.create({
|
||||
profileItemMobile: {
|
||||
width: 70,
|
||||
},
|
||||
profilePressable: {
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
profileAvatar: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 45,
|
||||
},
|
||||
profileName: {
|
||||
width: 78,
|
||||
fontSize: 12,
|
||||
textAlign: 'center',
|
||||
marginTop: 5,
|
||||
marginTop: 6,
|
||||
},
|
||||
profileRemoveBtn: {
|
||||
position: 'absolute',
|
||||
@@ -1225,13 +1200,4 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
searchHistoryContent: {
|
||||
paddingHorizontal: 10,
|
||||
borderRadius: 8,
|
||||
},
|
||||
searchHistoryTitle: {
|
||||
fontWeight: '600',
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user