Skip to content

Commit

Permalink
Merge pull request #41769 from software-mansion-labs/search-v1-p2/pol…
Browse files Browse the repository at this point in the history
…icy-filter

[Search v1] Implement policy filter
  • Loading branch information
luacmartins authored May 14, 2024
2 parents 58cbe04 + 36edddc commit 1280049
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 71 deletions.
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4767,6 +4767,8 @@ const CONST = {
DISTANCE: 'distance',
},

SEARCH_BOTTOM_TAB_URL: '/Search_Bottom_Tab',

SEARCH_DATA_TYPES: {
TRANSACTION: 'transaction',
},
Expand Down
10 changes: 6 additions & 4 deletions src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,25 @@ import TableListItemSkeleton from './Skeletons/TableListItemSkeleton';

type SearchProps = {
query: string;
policyIDs?: string;
};

function Search({query}: SearchProps) {
function Search({query, policyIDs}: SearchProps) {
const {isOffline} = useNetwork();
const styles = useThemeStyles();
useCustomBackHandler();

const hash = SearchUtils.getQueryHash(query);
const hash = SearchUtils.getQueryHash(query, policyIDs);
const [searchResults, searchResultsMeta] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`);

useEffect(() => {
if (isOffline) {
return;
}

SearchActions.search(query);
}, [query, isOffline]);
SearchActions.search(hash, query, policyIDs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hash, isOffline]);

const isLoading = (!isOffline && isLoadingOnyxValue(searchResultsMeta)) || searchResults?.data === undefined;
const shouldShowEmptyState = !isLoading && isEmptyObject(searchResults?.data);
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/Search.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
type SearchParams = {
query: string;
policyIDs?: string;
hash: number;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function TopBar({policy, session, breadcrumbLabel, shouldDisplaySearch = true}:
return (
<View style={styles.w100}>
<View
style={[styles.flexRow, styles.gap4, styles.mh3, styles.mv5, styles.alignItemsCenter, {justifyContent: 'space-between'}]}
style={[styles.flexRow, styles.gap4, styles.mh3, styles.mv5, styles.alignItemsCenter, styles.justifyContentBetween]}
dataSet={{dragArea: true}}
>
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter, styles.ml2]}>
Expand Down
10 changes: 7 additions & 3 deletions src/libs/Navigation/linkingConfig/customGetPathFromState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import {getPathFromState} from '@react-navigation/native';
import _ from 'lodash';
import getPolicyIDFromState from '@libs/Navigation/getPolicyIDFromState';
import getTopmostBottomTabRoute from '@libs/Navigation/getTopmostBottomTabRoute';
import type {RootStackParamList, State} from '@libs/Navigation/types';
import type {BottomTabName, RootStackParamList, State} from '@libs/Navigation/types';
import SCREENS from '@src/SCREENS';

// The policy ID parameter should be included in the URL when any of these pages is opened in the bottom tab.
const SCREENS_WITH_POLICY_ID_IN_URL: BottomTabName[] = [SCREENS.HOME, SCREENS.SEARCH.BOTTOM_TAB] as const;

const removePolicyIDParamFromState = (state: State<RootStackParamList>) => {
const stateCopy = _.cloneDeep(state);
const bottomTabRoute = getTopmostBottomTabRoute(stateCopy);
Expand All @@ -19,8 +22,9 @@ const customGetPathFromState: typeof getPathFromState = (state, options) => {
const stateWithoutPolicyID = removePolicyIDParamFromState(state as State<RootStackParamList>);
const path = getPathFromState(stateWithoutPolicyID, options);
const policyIDFromState = getPolicyIDFromState(state as State<RootStackParamList>);
const isHomeOpened = getTopmostBottomTabRoute(state as State<RootStackParamList>)?.name === SCREENS.HOME;
return `${policyIDFromState && isHomeOpened ? `/w/${policyIDFromState}` : ''}${path}`;
const topmostBottomTabRouteName = getTopmostBottomTabRoute(state as State<RootStackParamList>)?.name;
const shouldAddPolicyID = !!topmostBottomTabRouteName && SCREENS_WITH_POLICY_ID_IN_URL.includes(topmostBottomTabRouteName);
return `${policyIDFromState && shouldAddPolicyID ? `/w/${policyIDFromState}` : ''}${path}`;
};

export default customGetPathFromState;
89 changes: 35 additions & 54 deletions src/libs/Navigation/switchPolicyID.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {getActionFromState} from '@react-navigation/core';
import type {NavigationAction, NavigationContainerRef, NavigationState, PartialState} from '@react-navigation/native';
import {getPathFromState} from '@react-navigation/native';
import type {ValueOf, Writable} from 'type-fest';
import type {Writable} from 'type-fest';
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
import type {Route} from '@src/ROUTES';
import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import getStateFromPath from './getStateFromPath';
import getTopmostCentralPaneRoute from './getTopmostCentralPaneRoute';
Expand Down Expand Up @@ -35,14 +36,8 @@ function getActionForBottomTabNavigator(action: StackNavigationAction, state: Na
let payloadParams = params?.params as Record<string, string | undefined>;
let screen = params.screen;

// Case when the user is on the AllSettingsScreen and selects the specific workspace. The user is redirected then to the specific workspace settings.
if (screen === SCREENS.ALL_SETTINGS && policyID) {
screen = SCREENS.WORKSPACE.INITIAL;
}

// Alternative case when the user is on the specific workspace settings screen and selects "All" workspace.
else if (!policyID && screen === SCREENS.WORKSPACE.INITIAL) {
screen = SCREENS.ALL_SETTINGS;
if (screen === SCREENS.SEARCH.CENTRAL_PANE) {
screen = SCREENS.SEARCH.BOTTOM_TAB;
}

if (!payloadParams) {
Expand All @@ -65,7 +60,6 @@ export default function switchPolicyID(navigation: NavigationContainerRef<RootSt
if (!navigation) {
throw new Error("Couldn't find a navigation object. Is your component inside a screen in a navigator?");
}

let root: NavigationRoot = navigation;
let current: NavigationRoot | undefined;

Expand All @@ -76,7 +70,14 @@ export default function switchPolicyID(navigation: NavigationContainerRef<RootSt
}

const rootState = navigation.getRootState() as NavigationState<RootStackParamList>;
const newPath = route ?? getPathFromState({routes: rootState.routes} as State, linkingConfig.config);
let newPath = route ?? getPathFromState({routes: rootState.routes} as State, linkingConfig.config);

// Currently, the search page displayed in the bottom tab has the same URL as the page in the central pane, so we need to redirect to the correct search route.
// Here's the configuration: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx
const isOpeningSearchFromBottomTab = newPath.startsWith(CONST.SEARCH_BOTTOM_TAB_URL);
if (isOpeningSearchFromBottomTab) {
newPath = ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL);
}
const stateFromPath = getStateFromPath(newPath as Route) as PartialState<NavigationState<RootStackParamList>>;
const action: StackNavigationAction = getActionFromState(stateFromPath, linkingConfig.config);

Expand All @@ -93,51 +94,31 @@ export default function switchPolicyID(navigation: NavigationContainerRef<RootSt
return;
}

// The correct route for SearchPage is located in the CentralPane
const shouldAddToCentralPane = !getIsNarrowLayout() || isOpeningSearchFromBottomTab;

// If the layout is wide we need to push matching central pane route to the stack.
if (!getIsNarrowLayout()) {
// Case when the user selects "All" workspace from the specific workspace settings
if (checkIfActionPayloadNameIsEqual(actionForBottomTabNavigator, SCREENS.ALL_SETTINGS) && !policyID) {
root.dispatch({
type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
payload: {
name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR,
params: {
screen: SCREENS.SETTINGS.WORKSPACES,
params: undefined,
},
},
});
} else {
const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState);
const screen = topmostCentralPaneRoute?.name;
const params: CentralPaneRouteParams = {...topmostCentralPaneRoute?.params};
const isWorkspaceScreen = screen && Object.values(SCREENS.WORKSPACE).includes(screen as ValueOf<typeof SCREENS.WORKSPACE>);

// Only workspace settings screens have to store the policyID in the params.
// In other case, the policyID is read from the BottomTab params.
if (!isWorkspaceScreen) {
delete params.policyID;
} else {
params.policyID = policyID;
}

// If the user is on the home page and changes the current workspace, then should be displayed a report from the selected workspace.
// To achieve that, it's necessary to navigate without the reportID param.
if (checkIfActionPayloadNameIsEqual(actionForBottomTabNavigator, SCREENS.HOME)) {
delete params.reportID;
}

root.dispatch({
type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
payload: {
name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR,
params: {
screen,
params,
},
},
});
if (shouldAddToCentralPane) {
const topmostCentralPaneRoute = getTopmostCentralPaneRoute(rootState);
const screen = topmostCentralPaneRoute?.name;
const params: CentralPaneRouteParams = {...topmostCentralPaneRoute?.params};

// If the user is on the home page and changes the current workspace, then should be displayed a report from the selected workspace.
// To achieve that, it's necessary to navigate without the reportID param.
if (checkIfActionPayloadNameIsEqual(actionForBottomTabNavigator, SCREENS.HOME)) {
delete params.reportID;
}

root.dispatch({
type: CONST.NAVIGATION.ACTION_TYPE.PUSH,
payload: {
name: NAVIGATORS.CENTRAL_PANE_NAVIGATOR,
params: {
screen,
params,
},
},
});
} else {
// If the layout is small we need to pop everything from the central pane so the bottom tab navigator is visible.
root.dispatch({
Expand Down
5 changes: 3 additions & 2 deletions src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ function getSections<K extends keyof SearchTypeToItemMap>(data: OnyxTypes.Search
return searchTypeToItemMap[type].getSections(data) as ReturnType<SearchTypeToItemMap[K]['getSections']>;
}

function getQueryHash(query: string): number {
return UserUtils.hashText(query, 2 ** 32);
function getQueryHash(query: string, policyID?: string): number {
const textToHash = [query, policyID].filter(Boolean).join('_');
return UserUtils.hashText(textToHash, 2 ** 32);
}

export {getListItem, getQueryHash, getSections, getShouldShowColumn, getShouldShowMerchant, getSearchType};
6 changes: 2 additions & 4 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import * as API from '@libs/API';
import {READ_COMMANDS} from '@libs/API/types';
import * as SearchUtils from '@libs/SearchUtils';

function search(query: string) {
const hash = SearchUtils.getQueryHash(query);
API.read(READ_COMMANDS.SEARCH, {query, hash});
function search(hash: number, query: string, policyIDs?: string) {
API.read(READ_COMMANDS.SEARCH, {hash, query, policyIDs});
}

export {
Expand Down
16 changes: 15 additions & 1 deletion src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Illustrations from '@components/Icon/Illustrations';
import ScreenWrapper from '@components/ScreenWrapper';
import Search from '@components/Search';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useLocalize from '@hooks/useLocalize';
import useWindowDimensions from '@hooks/useWindowDimensions';
import Navigation from '@libs/Navigation/Navigation';
import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
import CONST from '@src/CONST';
Expand All @@ -18,6 +20,7 @@ type SearchPageProps = StackScreenProps<CentralPaneNavigatorParamList, typeof SC

function SearchPage({route}: SearchPageProps) {
const {translate} = useLocalize();
const {isSmallScreenWidth} = useWindowDimensions();
const currentQuery = route?.params && 'query' in route.params ? route?.params?.query : '';
const query = currentQuery as SearchQuery;
const isValidQuery = Object.values(CONST.TAB_SEARCH).includes(query);
Expand All @@ -28,6 +31,14 @@ function SearchPage({route}: SearchPageProps) {

const handleOnBackButtonPress = () => Navigation.goBack(ROUTES.SEARCH.getRoute(CONST.TAB_SEARCH.ALL));

const {activeWorkspaceID} = useActiveWorkspace();

// On small screens this page is not displayed, the configuration is in the file: src/libs/Navigation/AppNavigator/createCustomStackNavigator/index.tsx
// To avoid calling hooks in the Search component when this page isn't visible, we return null here.
if (isSmallScreenWidth) {
return null;
}

return (
<ScreenWrapper testID={Search.displayName}>
<FullPageNotFoundView
Expand All @@ -41,7 +52,10 @@ function SearchPage({route}: SearchPageProps) {
icon={headerContent[query]?.icon}
shouldShowBackButton={false}
/>
<Search query={query} />
<Search
policyIDs={activeWorkspaceID}
query={query}
/>
</FullPageNotFoundView>
</ScreenWrapper>
);
Expand Down
11 changes: 9 additions & 2 deletions src/pages/Search/SearchPageBottomTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView
import ScreenWrapper from '@components/ScreenWrapper';
import Search from '@components/Search';
import useActiveRoute from '@hooks/useActiveRoute';
import useActiveWorkspace from '@hooks/useActiveWorkspace';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
Expand All @@ -18,7 +19,7 @@ function SearchPageBottomTab() {
const {isSmallScreenWidth} = useWindowDimensions();
const activeRoute = useActiveRoute();
const styles = useThemeStyles();

const {activeWorkspaceID} = useActiveWorkspace();
const currentQuery = activeRoute?.params && 'query' in activeRoute.params ? activeRoute?.params?.query : '';
const query = currentQuery as SearchQuery;
const isValidQuery = Object.values(CONST.TAB_SEARCH).includes(query);
Expand All @@ -36,11 +37,17 @@ function SearchPageBottomTab() {
shouldShowLink={false}
>
<TopBar
activeWorkspaceID={activeWorkspaceID}
breadcrumbLabel={translate('common.search')}
shouldDisplaySearch={false}
/>
<SearchFilters query={query} />
{isSmallScreenWidth && <Search query={query} />}
{isSmallScreenWidth && (
<Search
policyIDs={activeWorkspaceID}
query={query}
/>
)}
</FullPageNotFoundView>
</ScreenWrapper>
);
Expand Down

0 comments on commit 1280049

Please sign in to comment.