Add search button to header on feeds screen (#2848)

* add search bar to header

* add button on web
zio/stable
Hailey 2024-02-12 13:47:48 -08:00 committed by GitHub
parent ba7463cadf
commit b936da1c0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 127 additions and 73 deletions

View File

@ -26,65 +26,80 @@ interface Props {
onSubmitQuery: () => void onSubmitQuery: () => void
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
} }
export function SearchInput({
query,
setIsInputFocused,
onChangeQuery,
onPressCancelSearch,
onSubmitQuery,
style,
}: Props) {
const theme = useTheme()
const pal = usePalette('default')
const {_} = useLingui()
const textInput = React.useRef<TextInput>(null)
const onPressCancelSearchInner = React.useCallback(() => { export interface SearchInputRef {
onPressCancelSearch() focus?: () => void
textInput.current?.blur()
}, [onPressCancelSearch, textInput])
return (
<View style={[pal.viewLight, styles.container, style]}>
<MagnifyingGlassIcon style={[pal.icon, styles.icon]} size={21} />
<TextInput
testID="searchTextInput"
ref={textInput}
placeholder={_(msg`Search`)}
placeholderTextColor={pal.colors.textLight}
selectTextOnFocus
returnKeyType="search"
value={query}
style={[pal.text, styles.input]}
keyboardAppearance={theme.colorScheme}
onFocus={() => setIsInputFocused?.(true)}
onBlur={() => setIsInputFocused?.(false)}
onChangeText={onChangeQuery}
onSubmitEditing={onSubmitQuery}
accessibilityRole="search"
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
autoCorrect={false}
autoCapitalize="none"
/>
{query ? (
<TouchableOpacity
onPress={onPressCancelSearchInner}
accessibilityRole="button"
accessibilityLabel={_(msg`Clear search query`)}
accessibilityHint=""
hitSlop={HITSLOP_10}>
<FontAwesomeIcon
icon="xmark"
size={16}
style={pal.textLight as FontAwesomeIconStyle}
/>
</TouchableOpacity>
) : undefined}
</View>
)
} }
export const SearchInput = React.forwardRef<SearchInputRef, Props>(
function SearchInput(
{
query,
setIsInputFocused,
onChangeQuery,
onPressCancelSearch,
onSubmitQuery,
style,
},
ref,
) {
const theme = useTheme()
const pal = usePalette('default')
const {_} = useLingui()
const textInput = React.useRef<TextInput>(null)
const onPressCancelSearchInner = React.useCallback(() => {
onPressCancelSearch()
textInput.current?.blur()
}, [onPressCancelSearch, textInput])
React.useImperativeHandle(ref, () => ({
focus: () => textInput.current?.focus(),
blur: () => textInput.current?.blur(),
}))
return (
<View style={[pal.viewLight, styles.container, style]}>
<MagnifyingGlassIcon style={[pal.icon, styles.icon]} size={21} />
<TextInput
testID="searchTextInput"
ref={textInput}
placeholder={_(msg`Search`)}
placeholderTextColor={pal.colors.textLight}
selectTextOnFocus
returnKeyType="search"
value={query}
style={[pal.text, styles.input]}
keyboardAppearance={theme.colorScheme}
onFocus={() => setIsInputFocused?.(true)}
onBlur={() => setIsInputFocused?.(false)}
onChangeText={onChangeQuery}
onSubmitEditing={onSubmitQuery}
accessibilityRole="search"
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
autoCorrect={false}
autoCapitalize="none"
/>
{query ? (
<TouchableOpacity
onPress={onPressCancelSearchInner}
accessibilityRole="button"
accessibilityLabel={_(msg`Clear search query`)}
accessibilityHint=""
hitSlop={HITSLOP_10}>
<FontAwesomeIcon
icon="xmark"
size={16}
style={pal.textLight as FontAwesomeIconStyle}
/>
</TouchableOpacity>
) : undefined}
</View>
)
},
)
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,

View File

@ -1,5 +1,11 @@
import React from 'react' import React from 'react'
import {ActivityIndicator, StyleSheet, View, type FlatList} from 'react-native' import {
ActivityIndicator,
StyleSheet,
View,
type FlatList,
Pressable,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
import {ViewHeader} from 'view/com/util/ViewHeader' import {ViewHeader} from 'view/com/util/ViewHeader'
@ -8,9 +14,9 @@ import {Link} from 'view/com/util/Link'
import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, FeedsTabNavigatorParams} from 'lib/routes/types'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {ComposeIcon2, CogIcon} from 'lib/icons' import {ComposeIcon2, CogIcon, MagnifyingGlassIcon2} from 'lib/icons'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {SearchInput} from 'view/com/util/forms/SearchInput' import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput'
import {UserAvatar} from 'view/com/util/UserAvatar' import {UserAvatar} from 'view/com/util/UserAvatar'
import { import {
LoadingPlaceholder, LoadingPlaceholder,
@ -36,6 +42,7 @@ import {cleanError} from 'lib/strings/errors'
import {useComposerControls} from '#/state/shell/composer' import {useComposerControls} from '#/state/shell/composer'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {isNative} from '#/platform/detection' import {isNative} from '#/platform/detection'
import {HITSLOP_10} from 'lib/constants'
type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'> type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
@ -121,6 +128,7 @@ export function FeedsScreen(_props: Props) {
} = useSearchPopularFeedsMutation() } = useSearchPopularFeedsMutation()
const {hasSession} = useSession() const {hasSession} = useSession()
const listRef = React.useRef<FlatList>(null) const listRef = React.useRef<FlatList>(null)
const searchInputRef = React.useRef<SearchInputRef>(null)
/** /**
* A search query is present. We may not have search results yet. * A search query is present. We may not have search results yet.
@ -330,14 +338,26 @@ export function FeedsScreen(_props: Props) {
const renderHeaderBtn = React.useCallback(() => { const renderHeaderBtn = React.useCallback(() => {
return ( return (
<Link <View style={styles.headerBtnGroup}>
href="/settings/saved-feeds" <Pressable
hitSlop={10} accessibilityRole="button"
accessibilityRole="button" hitSlop={HITSLOP_10}
accessibilityLabel={_(msg`Edit Saved Feeds`)} onPress={searchInputRef.current?.focus}>
accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}> <MagnifyingGlassIcon2
<CogIcon size={22} strokeWidth={2} style={pal.textLight} /> size={22}
</Link> strokeWidth={2}
style={pal.textLight}
/>
</Pressable>
<Link
href="/settings/saved-feeds"
hitSlop={10}
accessibilityRole="button"
accessibilityLabel={_(msg`Edit Saved Feeds`)}
accessibilityHint={_(msg`Opens screen to edit Saved Feeds`)}>
<CogIcon size={22} strokeWidth={2} style={pal.textLight} />
</Link>
</View>
) )
}, [pal, _]) }, [pal, _])
@ -398,12 +418,24 @@ export function FeedsScreen(_props: Props) {
<Text type="title-lg" style={[pal.text, s.bold]}> <Text type="title-lg" style={[pal.text, s.bold]}>
<Trans>My Feeds</Trans> <Trans>My Feeds</Trans>
</Text> </Text>
<Link <View style={styles.headerBtnGroup}>
href="/settings/saved-feeds" <Pressable
accessibilityLabel={_(msg`Edit My Feeds`)} accessibilityRole="button"
accessibilityHint=""> hitSlop={HITSLOP_10}
<CogIcon strokeWidth={1.5} style={pal.icon} size={28} /> onPress={searchInputRef.current?.focus}>
</Link> <MagnifyingGlassIcon2
size={22}
strokeWidth={2}
style={pal.icon}
/>
</Pressable>
<Link
href="/settings/saved-feeds"
accessibilityLabel={_(msg`Edit My Feeds`)}
accessibilityHint="">
<CogIcon strokeWidth={1.5} style={pal.icon} size={28} />
</Link>
</View>
</View> </View>
) )
} }
@ -443,6 +475,7 @@ export function FeedsScreen(_props: Props) {
{!isMobile && ( {!isMobile && (
<SearchInput <SearchInput
ref={searchInputRef}
query={query} query={query}
onChangeQuery={onChangeQuery} onChangeQuery={onChangeQuery}
onPressCancelSearch={onPressCancelSearch} onPressCancelSearch={onPressCancelSearch}
@ -456,6 +489,7 @@ export function FeedsScreen(_props: Props) {
{isMobile && ( {isMobile && (
<View style={{paddingHorizontal: 8, paddingBottom: 10}}> <View style={{paddingHorizontal: 8, paddingBottom: 10}}>
<SearchInput <SearchInput
ref={searchInputRef}
query={query} query={query}
onChangeQuery={onChangeQuery} onChangeQuery={onChangeQuery}
onPressCancelSearch={onPressCancelSearch} onPressCancelSearch={onPressCancelSearch}
@ -663,4 +697,9 @@ const styles = StyleSheet.create({
paddingHorizontal: 4, paddingHorizontal: 4,
paddingVertical: 2, paddingVertical: 2,
}, },
headerBtnGroup: {
flexDirection: 'row',
gap: 15,
alignItems: 'center',
},
}) })