about summary refs log tree commit diff
path: root/src/view
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-02-14 23:22:32 +0000
committerGitHub <noreply@github.com>2025-02-14 23:22:32 +0000
commit1e3b7feccfb59c70a97ed51b26bee8c6ea33348f (patch)
tree7f6c6979317cf3634b734dd06ccf68dfc2ec817f /src/view
parentab4be7a9e144cbb658385976f21394a036043cb8 (diff)
downloadvoidsky-1e3b7feccfb59c70a97ed51b26bee8c6ea33348f.tar.zst
Screen for searching user's posts (#7622)
* search user's posts screen

* custom placeholder copy if self

* navigate to /profile/:handle

* add name to title

* show header on desktop
Diffstat (limited to 'src/view')
-rw-r--r--src/view/com/profile/ProfileMenu.tsx17
-rw-r--r--src/view/screens/Search/Search.tsx71
2 files changed, 73 insertions, 15 deletions
diff --git a/src/view/com/profile/ProfileMenu.tsx b/src/view/com/profile/ProfileMenu.tsx
index 770d17f48..102b34922 100644
--- a/src/view/com/profile/ProfileMenu.tsx
+++ b/src/view/com/profile/ProfileMenu.tsx
@@ -2,10 +2,12 @@ import React, {memo} from 'react'
 import {AppBskyActorDefs} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {useNavigation} from '@react-navigation/native'
 import {useQueryClient} from '@tanstack/react-query'
 
 import {HITSLOP_20} from '#/lib/constants'
 import {makeProfileLink} from '#/lib/routes/links'
+import {NavigationProp} from '#/lib/routes/types'
 import {shareText, shareUrl} from '#/lib/sharing'
 import {toShareUrl} from '#/lib/strings/url-helpers'
 import {logger} from '#/logger'
@@ -26,6 +28,7 @@ import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons
 import {DotGrid_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
 import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag'
 import {ListSparkle_Stroke2_Corner0_Rounded as List} from '#/components/icons/ListSparkle'
+import {MagnifyingGlass2_Stroke2_Corner0_Rounded as SearchIcon} from '#/components/icons/MagnifyingGlass2'
 import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
 import {PeopleRemove2_Stroke2_Corner0_Rounded as UserMinus} from '#/components/icons/PeopleRemove2'
 import {
@@ -48,6 +51,7 @@ let ProfileMenu = ({
   const {openModal} = useModalControls()
   const reportDialogControl = useReportDialogControl()
   const queryClient = useQueryClient()
+  const navigation = useNavigation<NavigationProp>()
   const isSelf = currentAccount?.did === profile.did
   const isFollowing = profile.viewer?.following
   const isBlocked = profile.viewer?.blocking || profile.viewer?.blockedBy
@@ -177,6 +181,10 @@ let ProfileMenu = ({
     shareText(profile.did)
   }, [profile.did])
 
+  const onPressSearch = React.useCallback(() => {
+    navigation.navigate('ProfileSearch', {name: profile.handle})
+  }, [navigation, profile.handle])
+
   return (
     <EventStopper onKeyDown={false}>
       <Menu.Root>
@@ -215,6 +223,15 @@ let ProfileMenu = ({
               </Menu.ItemText>
               <Menu.ItemIcon icon={Share} />
             </Menu.Item>
+            <Menu.Item
+              testID="profileHeaderDropdownSearchBtn"
+              label={_(msg`Search Posts`)}
+              onPress={onPressSearch}>
+              <Menu.ItemText>
+                <Trans>Search Posts</Trans>
+              </Menu.ItemText>
+              <Menu.ItemIcon icon={SearchIcon} />
+            </Menu.Item>
           </Menu.Group>
 
           {hasSession && (
diff --git a/src/view/screens/Search/Search.tsx b/src/view/screens/Search/Search.tsx
index f16b4fff2..83503a706 100644
--- a/src/view/screens/Search/Search.tsx
+++ b/src/view/screens/Search/Search.tsx
@@ -17,7 +17,7 @@ import {
 } from '@fortawesome/react-native-fontawesome'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
-import {useFocusEffect, useNavigation} from '@react-navigation/native'
+import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'
 
 import {APP_LANGUAGES, LANGUAGES} from '#/lib/../locale/languages'
 import {createHitslop, HITSLOP_20} from '#/lib/constants'
@@ -55,7 +55,7 @@ import {List} from '#/view/com/util/List'
 import {Text} from '#/view/com/util/text/Text'
 import {Explore} from '#/view/screens/Search/Explore'
 import {SearchLinkCard, SearchProfileCard} from '#/view/shell/desktop/Search'
-import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
+import {makeSearchQuery, Params, parseSearchQuery} from '#/screens/Search/utils'
 import {
   atoms as a,
   native,
@@ -419,7 +419,13 @@ function SearchLanguageDropdown({
   )
 }
 
-function useQueryManager({initialQuery}: {initialQuery: string}) {
+function useQueryManager({
+  initialQuery,
+  fixedParams,
+}: {
+  initialQuery: string
+  fixedParams?: Params
+}) {
   const {query, params: initialParams} = React.useMemo(() => {
     return parseSearchQuery(initialQuery || '')
   }, [initialQuery])
@@ -438,8 +444,9 @@ function useQueryManager({initialQuery}: {initialQuery: string}) {
       ...initialParams,
       // managed stuff
       lang,
+      ...fixedParams,
     }),
-    [lang, initialParams],
+    [lang, initialParams, fixedParams],
   )
   const handlers = React.useMemo(
     () => ({
@@ -588,16 +595,34 @@ SearchScreenInner = React.memo(SearchScreenInner)
 export function SearchScreen(
   props: NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>,
 ) {
+  const queryParam = props.route?.params?.q ?? ''
+
+  return <SearchScreenShell queryParam={queryParam} testID="searchScreen" />
+}
+
+export function SearchScreenShell({
+  queryParam,
+  testID,
+  fixedParams,
+  navButton = 'menu',
+  inputPlaceholder,
+}: {
+  queryParam: string
+  testID: string
+  fixedParams?: Params
+  navButton?: 'back' | 'menu'
+  inputPlaceholder?: string
+}) {
   const t = useTheme()
   const {gtMobile} = useBreakpoints()
   const navigation = useNavigation<NavigationProp>()
+  const route = useRoute()
   const textInput = React.useRef<TextInput>(null)
   const {_} = useLingui()
   const setMinimalShellMode = useSetMinimalShellMode()
   const {currentAccount} = useSession()
 
   // Query terms
-  const queryParam = props.route?.params?.q ?? ''
   const [searchText, setSearchText] = React.useState<string>(queryParam)
   const {data: autocompleteData, isFetching: isAutocompleteFetching} =
     useActorAutocompleteQuery(searchText, true)
@@ -656,6 +681,7 @@ export function SearchScreen(
 
   const {params, query, queryWithParams} = useQueryManager({
     initialQuery: queryParam,
+    fixedParams,
   })
   const showFilters = Boolean(queryWithParams && !showAutocomplete)
 
@@ -696,13 +722,14 @@ export function SearchScreen(
       updateSearchHistory(item)
 
       if (isWeb) {
-        navigation.push('Search', {q: item})
+        // @ts-expect-error route is not typesafe
+        navigation.push(route.name, {...route.params, q: item})
       } else {
         textInput.current?.blur()
         navigation.setParams({q: item})
       }
     },
-    [updateSearchHistory, navigation],
+    [updateSearchHistory, navigation, route],
   )
 
   const onPressCancelSearch = React.useCallback(() => {
@@ -751,13 +778,18 @@ export function SearchScreen(
   const onSoftReset = React.useCallback(() => {
     if (isWeb) {
       // Empty params resets the URL to be /search rather than /search?q=
-      navigation.replace('Search', {})
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      const {q: _q, ...parameters} = (route.params ?? {}) as {
+        [key: string]: string
+      }
+      // @ts-expect-error route is not typesafe
+      navigation.replace(route.name, parameters)
     } else {
       setSearchText('')
       navigation.setParams({q: ''})
       textInput.current?.focus()
     }
-  }, [navigation])
+  }, [navigation, route])
 
   useFocusEffect(
     React.useCallback(() => {
@@ -778,8 +810,10 @@ export function SearchScreen(
     }
   }, [setShowAutocomplete])
 
+  const showHeader = !gtMobile || navButton !== 'menu'
+
   return (
-    <Layout.Screen testID="searchScreen">
+    <Layout.Screen testID={testID}>
       <View
         ref={headerRef}
         onLayout={evt => {
@@ -794,14 +828,18 @@ export function SearchScreen(
           }),
         ]}>
         <Layout.Center style={t.atoms.bg}>
-          {!gtMobile && (
+          {showHeader && (
             <View
               // 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.xs * -1}}>
               <Layout.Header.Outer noBottomBorder>
-                <Layout.Header.MenuButton />
+                {navButton === 'menu' ? (
+                  <Layout.Header.MenuButton />
+                ) : (
+                  <Layout.Header.BackButton />
+                )}
                 <Layout.Header.Content align="left">
                   <Layout.Header.TitleText>
                     <Trans>Search</Trans>
@@ -829,7 +867,10 @@ export function SearchScreen(
                     onChangeText={onChangeText}
                     onClearText={onPressClearQuery}
                     onSubmitEditing={onSubmit}
-                    placeholder={_(msg`Search for posts, users, or feeds`)}
+                    placeholder={
+                      inputPlaceholder ??
+                      _(msg`Search for posts, users, or feeds`)
+                    }
                     hitSlop={{...HITSLOP_20, top: 0}}
                   />
                 </View>
@@ -849,7 +890,7 @@ export function SearchScreen(
                 )}
               </View>
 
-              {showFilters && gtMobile && (
+              {showFilters && !showHeader && (
                 <View
                   style={[
                     a.flex_row,
@@ -870,7 +911,7 @@ export function SearchScreen(
 
       <View
         style={{
-          display: showAutocomplete ? 'flex' : 'none',
+          display: showAutocomplete && !fixedParams ? 'flex' : 'none',
           flex: 1,
         }}>
         {searchText.length > 0 ? (