about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-02-12 13:47:48 -0800
committerGitHub <noreply@github.com>2024-02-12 13:47:48 -0800
commitb936da1c0fa7b8a49e84a1b36ae064db9f8c0e47 (patch)
treede9fba4a47fb505a5dfb754bc1dd469701cfcece /src
parentba7463cadf15bd5420e1a8cc46952bde2c81cad9 (diff)
downloadvoidsky-b936da1c0fa7b8a49e84a1b36ae064db9f8c0e47.tar.zst
Add search button to header on feeds screen (#2848)
* add search bar to header

* add button on web
Diffstat (limited to 'src')
-rw-r--r--src/view/com/util/forms/SearchInput.tsx125
-rw-r--r--src/view/screens/Feeds.tsx73
2 files changed, 126 insertions, 72 deletions
diff --git a/src/view/com/util/forms/SearchInput.tsx b/src/view/com/util/forms/SearchInput.tsx
index a78d23c9b..5a21d8fdd 100644
--- a/src/view/com/util/forms/SearchInput.tsx
+++ b/src/view/com/util/forms/SearchInput.tsx
@@ -26,64 +26,79 @@ interface Props {
   onSubmitQuery: () => void
   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(() => {
-    onPressCancelSearch()
-    textInput.current?.blur()
-  }, [onPressCancelSearch, textInput])
+export interface SearchInputRef {
+  focus?: () => void
+}
+
+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])
 
-  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`)}
+    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=""
-          hitSlop={HITSLOP_10}>
-          <FontAwesomeIcon
-            icon="xmark"
-            size={16}
-            style={pal.textLight as FontAwesomeIconStyle}
-          />
-        </TouchableOpacity>
-      ) : undefined}
-    </View>
-  )
-}
+          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({
   container: {
diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx
index 6651084bd..7216fd109 100644
--- a/src/view/screens/Feeds.tsx
+++ b/src/view/screens/Feeds.tsx
@@ -1,5 +1,11 @@
 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 {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
 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 {usePalette} from 'lib/hooks/usePalette'
 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 {SearchInput} from 'view/com/util/forms/SearchInput'
+import {SearchInput, SearchInputRef} from 'view/com/util/forms/SearchInput'
 import {UserAvatar} from 'view/com/util/UserAvatar'
 import {
   LoadingPlaceholder,
@@ -36,6 +42,7 @@ import {cleanError} from 'lib/strings/errors'
 import {useComposerControls} from '#/state/shell/composer'
 import {useSession} from '#/state/session'
 import {isNative} from '#/platform/detection'
+import {HITSLOP_10} from 'lib/constants'
 
 type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
 
@@ -121,6 +128,7 @@ export function FeedsScreen(_props: Props) {
   } = useSearchPopularFeedsMutation()
   const {hasSession} = useSession()
   const listRef = React.useRef<FlatList>(null)
+  const searchInputRef = React.useRef<SearchInputRef>(null)
 
   /**
    * 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(() => {
     return (
-      <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 style={styles.headerBtnGroup}>
+        <Pressable
+          accessibilityRole="button"
+          hitSlop={HITSLOP_10}
+          onPress={searchInputRef.current?.focus}>
+          <MagnifyingGlassIcon2
+            size={22}
+            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, _])
 
@@ -398,12 +418,24 @@ export function FeedsScreen(_props: Props) {
               <Text type="title-lg" style={[pal.text, s.bold]}>
                 <Trans>My Feeds</Trans>
               </Text>
-              <Link
-                href="/settings/saved-feeds"
-                accessibilityLabel={_(msg`Edit My Feeds`)}
-                accessibilityHint="">
-                <CogIcon strokeWidth={1.5} style={pal.icon} size={28} />
-              </Link>
+              <View style={styles.headerBtnGroup}>
+                <Pressable
+                  accessibilityRole="button"
+                  hitSlop={HITSLOP_10}
+                  onPress={searchInputRef.current?.focus}>
+                  <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>
           )
         }
@@ -443,6 +475,7 @@ export function FeedsScreen(_props: Props) {
 
               {!isMobile && (
                 <SearchInput
+                  ref={searchInputRef}
                   query={query}
                   onChangeQuery={onChangeQuery}
                   onPressCancelSearch={onPressCancelSearch}
@@ -456,6 +489,7 @@ export function FeedsScreen(_props: Props) {
             {isMobile && (
               <View style={{paddingHorizontal: 8, paddingBottom: 10}}>
                 <SearchInput
+                  ref={searchInputRef}
                   query={query}
                   onChangeQuery={onChangeQuery}
                   onPressCancelSearch={onPressCancelSearch}
@@ -663,4 +697,9 @@ const styles = StyleSheet.create({
     paddingHorizontal: 4,
     paddingVertical: 2,
   },
+  headerBtnGroup: {
+    flexDirection: 'row',
+    gap: 15,
+    alignItems: 'center',
+  },
 })