-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Mobile search-router is a RHP on native v2 #79486
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
54050e7
044b483
e7b0628
105e4d6
5fc3cbd
cabb844
5ca853e
ed3153f
fb7a93c
f8ebcf3
1ba1459
c5a089b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /** | ||
| * On native devices SearchRouter is served from SearchRouterPage, on web from SearchRouterModal. | ||
| */ | ||
| function SearchRouterModal() { | ||
| return null; | ||
| } | ||
|
|
||
| export default SearchRouterModal; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import React from 'react'; | ||
| import ScreenWrapper from '@components/ScreenWrapper'; | ||
| import SearchRouter from '@components/Search/SearchRouter/SearchRouter'; | ||
| import {useSearchRouterActions, useSearchRouterState} from '@components/Search/SearchRouter/SearchRouterContext'; | ||
|
|
||
| function SearchRouterPage() { | ||
| const {closeSearchRouter} = useSearchRouterActions(); | ||
| const {isSearchRouterDisplayed} = useSearchRouterState(); | ||
|
|
||
| return ( | ||
| <ScreenWrapper | ||
| testID="SearchRouterPage" | ||
| shouldEnableMaxHeight | ||
| enableEdgeToEdgeBottomSafeAreaPadding | ||
| includePaddingTop | ||
| includeSafeAreaPaddingBottom | ||
| > | ||
| <SearchRouter | ||
| onRouterClose={closeSearchRouter} | ||
| shouldHideInputCaret={false} | ||
| isSearchRouterDisplayed={isSearchRouterDisplayed} | ||
| /> | ||
| </ScreenWrapper> | ||
| ); | ||
| } | ||
|
|
||
| export default SearchRouterPage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import ROUTES from '@src/ROUTES'; | ||
|
|
||
| /** | ||
| * On native devices SearchRouter is served from SearchRouterPage, on web from SearchRouterModal. | ||
| */ | ||
| function SearchRouterPage() { | ||
| return Navigation.navigate(ROUTES.HOME); | ||
| } | ||
|
|
||
| export default SearchRouterPage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| import {findFocusedRoute} from '@react-navigation/native'; | ||
| import type {NavigationState} from '@react-navigation/routers'; | ||
| import type {OnyxCollection} from 'react-native-onyx'; | ||
| import type {SearchQueryItem} from '@components/SelectionListWithSections/Search/SearchQueryListItem'; | ||
| import {getPolicyNameWithFallback, sanitizeSearchValue} from '@libs/SearchQueryUtils'; | ||
| import type {ReportsSplitNavigatorParamList} from '@navigation/types'; | ||
| import CONST from '@src/CONST'; | ||
| import SCREENS from '@src/SCREENS'; | ||
| import type * as OnyxTypes from '@src/types/onyx'; | ||
|
|
||
| type ContextualReportData = { | ||
| contextualReportID: string | undefined; | ||
| isSearchRouterScreen: boolean; | ||
| }; | ||
|
|
||
| /** | ||
| * Extracts contextual report data from the navigation state. | ||
| * | ||
| * This function determines: | ||
| * 1. Whether the current screen is the SearchRouter modal | ||
| * 2. The reportID of the contextual report (if any) that was focused before opening the SearchRouter | ||
| * | ||
| * When the SearchRouter is open, it looks at the previous route in the stack to find the underlying | ||
| * report context. Otherwise it looks @ current screen for report context. | ||
| * This allows the search to provide contextual suggestions based on the report | ||
| * the user was viewing when they opened the search. | ||
| * | ||
| * @param state - The root navigation state from useRootNavigationState hook | ||
| * @returns Object containing contextualReportID (the report ID if on a report screen) and isSearchRouterScreen (whether SearchRouter is focused) | ||
| */ | ||
| function getContextualReportData(state: NavigationState | undefined): ContextualReportData { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use depend on
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about it but as far as I understand how it works it may produce false positives / or make logic more complex.
based on that I chose current solution |
||
| // Safe handling when navigation is not yet initialized | ||
| if (!state) { | ||
| return {contextualReportID: undefined, isSearchRouterScreen: false}; | ||
| } | ||
| let maybeReportRoute = findFocusedRoute(state); | ||
| const isSearchRouterScreen = maybeReportRoute?.name === SCREENS.RIGHT_MODAL.SEARCH_ROUTER; | ||
| if (isSearchRouterScreen) { | ||
| const stateWithoutLastRoute = { | ||
| ...state, | ||
| routes: state.routes.slice(0, -1), | ||
| index: state.index !== 0 ? state.index - 1 : 0, | ||
| }; | ||
| maybeReportRoute = findFocusedRoute(stateWithoutLastRoute); | ||
| } | ||
|
|
||
| if (maybeReportRoute?.name === SCREENS.REPORT || maybeReportRoute?.name === SCREENS.RIGHT_MODAL.EXPENSE_REPORT) { | ||
| // We're guaranteed that the type of params is of SCREENS.REPORT | ||
| return {contextualReportID: (maybeReportRoute?.params as ReportsSplitNavigatorParamList[typeof SCREENS.REPORT]).reportID, isSearchRouterScreen}; | ||
| } | ||
| return {contextualReportID: undefined, isSearchRouterScreen}; | ||
| } | ||
|
|
||
| function getContextualSearchAutocompleteKey(item: SearchQueryItem, policies: OnyxCollection<OnyxTypes.Policy>, reports?: OnyxCollection<OnyxTypes.Report>) { | ||
| if (item.roomType === CONST.SEARCH.DATA_TYPES.INVOICE) { | ||
| return `${CONST.SEARCH.SYNTAX_FILTER_KEYS.TO}:${item.searchQuery}`; | ||
| } | ||
| if (item.roomType === CONST.SEARCH.DATA_TYPES.CHAT) { | ||
| return `${CONST.SEARCH.SYNTAX_FILTER_KEYS.IN}:${item.searchQuery}`; | ||
| } | ||
| if (item.roomType === CONST.SEARCH.DATA_TYPES.EXPENSE) { | ||
| return `${CONST.SEARCH.SYNTAX_FILTER_KEYS.POLICY_ID}:${item.policyID ? getPolicyNameWithFallback(item.policyID, policies, reports) : ''}`; | ||
| } | ||
| } | ||
|
|
||
| function getContextualSearchQuery(item: SearchQueryItem, policies: OnyxCollection<OnyxTypes.Policy>, reports?: OnyxCollection<OnyxTypes.Report>) { | ||
| const baseQuery = `${CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.TYPE}:${item.roomType}`; | ||
| let additionalQuery = ''; | ||
|
|
||
| switch (item.roomType) { | ||
| case CONST.SEARCH.DATA_TYPES.EXPENSE: | ||
| additionalQuery += ` ${CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.POLICY_ID}:${sanitizeSearchValue(item.policyID ? getPolicyNameWithFallback(item.policyID, policies, reports) : '')}`; | ||
| break; | ||
| case CONST.SEARCH.DATA_TYPES.INVOICE: | ||
| additionalQuery += ` ${CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.POLICY_ID}:${item.policyID}`; | ||
| if (item.autocompleteID) { | ||
| additionalQuery += ` ${CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.TO}:${sanitizeSearchValue(item.searchQuery ?? '')}`; | ||
| } | ||
| break; | ||
| case CONST.SEARCH.DATA_TYPES.CHAT: | ||
| default: | ||
| additionalQuery = ` ${CONST.SEARCH.SEARCH_USER_FRIENDLY_KEYS.IN}:${sanitizeSearchValue(item.searchQuery ?? '')}`; | ||
| break; | ||
| } | ||
| return baseQuery + additionalQuery; | ||
| } | ||
|
|
||
| export {getContextualReportData, getContextualSearchAutocompleteKey, getContextualSearchQuery}; | ||
| export type {ContextualReportData}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import ROUTES from '@src/ROUTES'; | ||
|
|
||
| function openSearch() { | ||
| return Navigation.navigate(ROUTES.SEARCH_ROUTER); | ||
| } | ||
|
|
||
| function closeSearch() { | ||
| return Navigation.dismissModal(); | ||
mountiny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| export {openSearch, closeSearch}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| function openSearch(setSearchState: React.Dispatch<React.SetStateAction<boolean>>) { | ||
| return setSearchState(true); | ||
| } | ||
|
|
||
| function closeSearch(setSearchState: React.Dispatch<React.SetStateAction<boolean>>) { | ||
| return setSearchState(false); | ||
| } | ||
|
|
||
| export {openSearch, closeSearch}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Routing was not correct in this PR, which caused #80670