Bluesky app fork with some witchin' additions 💫

Auto-select search results tab (#9159)

When the user clicks the search button next to "Discover New Feeds" or "Suggested Accounts" on the Explore page, we'll auto-select the respective search results tab (feeds or users).

authored by

Alex Benzer and committed by
GitHub
b38edc52 d68fc78e

+80 -18
+4 -4
src/lib/routes/types.ts
··· 67 67 InterestsSettings: undefined 68 68 AboutSettings: undefined 69 69 AppIconSettings: undefined 70 - Search: {q?: string} 70 + Search: {q?: string; tab?: 'user' | 'profile' | 'feed'} 71 71 Hashtag: {tag: string; author?: string} 72 72 Topic: {topic: string} 73 73 MessagesConversation: {conversation: string; embed?: string; accept?: true} ··· 102 102 } 103 103 104 104 export type SearchTabNavigatorParams = CommonNavigatorParams & { 105 - Search: {q?: string} 105 + Search: {q?: string; tab?: 'user' | 'profile' | 'feed'} 106 106 } 107 107 108 108 export type NotificationsTabNavigatorParams = CommonNavigatorParams & { ··· 119 119 120 120 export type FlatNavigatorParams = CommonNavigatorParams & { 121 121 Home: undefined 122 - Search: {q?: string} 122 + Search: {q?: string; tab?: 'user' | 'profile' | 'feed'} 123 123 Feeds: undefined 124 124 Notifications: undefined 125 125 Messages: {pushToConversation?: string; animation?: 'push' | 'pop'} ··· 129 129 HomeTab: undefined 130 130 Home: undefined 131 131 SearchTab: undefined 132 - Search: {q?: string} 132 + Search: {q?: string; tab?: 'user' | 'profile' | 'feed'} 133 133 Feeds: undefined 134 134 NotificationsTab: undefined 135 135 Notifications: undefined
+12 -2
src/screens/Search/Explore.tsx
··· 726 726 <ModuleHeader.SearchButton 727 727 {...item.searchButton} 728 728 onPress={() => 729 - focusSearchInput(item.searchButton?.tab || 'user') 729 + focusSearchInput( 730 + (item.searchButton?.tab || 'user') as 731 + | 'user' 732 + | 'profile' 733 + | 'feed', 734 + ) 730 735 } 731 736 /> 732 737 )} ··· 743 748 <ModuleHeader.SearchButton 744 749 {...item.searchButton} 745 750 onPress={() => 746 - focusSearchInput(item.searchButton?.tab || 'user') 751 + focusSearchInput( 752 + (item.searchButton?.tab || 'user') as 753 + | 'user' 754 + | 'profile' 755 + | 'feed', 756 + ) 747 757 } 748 758 /> 749 759 )}
+3 -1
src/screens/Search/SearchResults.tsx
··· 30 30 activeTab, 31 31 onPageSelected, 32 32 headerHeight, 33 + initialPage = 0, 33 34 }: { 34 35 query: string 35 36 queryWithParams: string 36 37 activeTab: number 37 38 onPageSelected: (page: number) => void 38 39 headerHeight: number 40 + initialPage?: number 39 41 }): React.ReactNode => { 40 42 const {_} = useLingui() 41 43 ··· 89 91 <TabBar items={sections.map(section => section.title)} {...props} /> 90 92 </Layout.Center> 91 93 )} 92 - initialPage={0}> 94 + initialPage={initialPage}> 93 95 {sections.map((section, i) => ( 94 96 <View key={i}>{section.component}</View> 95 97 ))}
+61 -11
src/screens/Search/Shell.tsx
··· 190 190 setShowAutocomplete(false) 191 191 if (isWeb) { 192 192 // Empty params resets the URL to be /search rather than /search?q= 193 - 194 - const {q: _q, ...parameters} = (route.params ?? {}) as { 193 + // Also clear the tab parameter 194 + const { 195 + q: _q, 196 + tab: _tab, 197 + ...parameters 198 + } = (route.params ?? {}) as { 195 199 [key: string]: string 196 200 } 197 201 // @ts-expect-error route is not typesafe 198 202 navigation.replace(route.name, parameters) 199 203 } else { 200 204 setSearchText('') 201 - navigation.setParams({q: ''}) 205 + navigation.setParams({q: '', tab: undefined}) 202 206 } 203 207 }, [setShowAutocomplete, setSearchText, navigation, route.params, route.name]) 204 208 ··· 236 240 const onSoftReset = useCallback(() => { 237 241 if (isWeb) { 238 242 // Empty params resets the URL to be /search rather than /search?q= 239 - 240 - const {q: _q, ...parameters} = (route.params ?? {}) as { 243 + // Also clear the tab parameter when soft resetting 244 + const { 245 + q: _q, 246 + tab: _tab, 247 + ...parameters 248 + } = (route.params ?? {}) as { 241 249 [key: string]: string 242 250 } 243 251 // @ts-expect-error route is not typesafe 244 252 navigation.replace(route.name, parameters) 245 253 } else { 246 254 setSearchText('') 247 - navigation.setParams({q: ''}) 255 + navigation.setParams({q: '', tab: undefined}) 248 256 textInput.current?.focus() 249 257 } 250 258 }, [navigation, route]) ··· 268 276 } 269 277 }, [setShowAutocomplete]) 270 278 271 - const focusSearchInput = useCallback(() => { 272 - textInput.current?.focus() 273 - }, []) 279 + const focusSearchInput = useCallback( 280 + (tab?: 'user' | 'profile' | 'feed') => { 281 + textInput.current?.focus() 282 + 283 + // If a tab is specified, set the tab parameter 284 + if (tab) { 285 + if (isWeb) { 286 + navigation.setParams({...route.params, tab}) 287 + } else { 288 + navigation.setParams({tab}) 289 + } 290 + } 291 + }, 292 + [navigation, route], 293 + ) 274 294 275 295 const showHeader = !gtMobile || navButton !== 'menu' 276 296 ··· 421 441 query: string 422 442 queryWithParams: string 423 443 headerHeight: number 424 - focusSearchInput: () => void 444 + focusSearchInput: (tab?: 'user' | 'profile' | 'feed') => void 425 445 }): React.ReactNode => { 426 446 const t = useTheme() 427 447 const setMinimalShellMode = useSetMinimalShellMode() 428 448 const {hasSession} = useSession() 429 449 const {gtTablet} = useBreakpoints() 430 - const [activeTab, setActiveTab] = useState(0) 450 + const route = useRoute() 451 + 452 + // Get tab parameter from route params 453 + const tabParam = ( 454 + route.params as {q?: string; tab?: 'user' | 'profile' | 'feed'} 455 + )?.tab 456 + 457 + // Map tab parameter to tab index 458 + const getInitialTabIndex = useCallback(() => { 459 + if (!tabParam) return 0 460 + switch (tabParam) { 461 + case 'user': 462 + case 'profile': 463 + return 2 // People tab 464 + case 'feed': 465 + return 3 // Feeds tab 466 + default: 467 + return 0 468 + } 469 + }, [tabParam]) 470 + 471 + const [activeTab, setActiveTab] = useState(getInitialTabIndex()) 472 + 473 + // Update activeTab when tabParam changes 474 + useLayoutEffect(() => { 475 + const newTabIndex = getInitialTabIndex() 476 + if (newTabIndex !== activeTab) { 477 + setActiveTab(newTabIndex) 478 + } 479 + }, [tabParam, activeTab, getInitialTabIndex]) 431 480 432 481 const onPageSelected = useCallback( 433 482 (index: number) => { ··· 444 493 activeTab={activeTab} 445 494 headerHeight={headerHeight} 446 495 onPageSelected={onPageSelected} 496 + initialPage={activeTab} 447 497 /> 448 498 ) : hasSession ? ( 449 499 <Explore focusSearchInput={focusSearchInput} headerHeight={headerHeight} />