Skip to content
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

[Search v1] Add handling of actions and improve Search list items #41725

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a245b25
Add handling of actions to TransactionListItem
Kicu May 7, 2024
619cd3c
Extract ActionCell to a separate component
Kicu Jun 19, 2024
1584ced
Implement action buttons for transactions and reports in Search
Kicu Jun 21, 2024
424ad95
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jun 24, 2024
dbb2748
Implement small changes in Actions after review
Kicu Jun 24, 2024
b573e74
refactor Consts for Search
Kicu Jun 25, 2024
6633b0d
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jun 25, 2024
54303a1
Update ActionCell component to correctly handle hold and unhold actions
Kicu Jun 25, 2024
b38a8e4
add small fixes to search utils
Kicu Jun 27, 2024
57f39fb
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jun 27, 2024
5e3cb31
Add Search hold reason RHP page and handle hold/unhold actions
Kicu Jun 28, 2024
0f870a4
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jun 28, 2024
979bd17
Cleanup Search ActionCell and actions
Kicu Jun 28, 2024
7c3e57e
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jul 3, 2024
7f68d12
Update Search Item Actions after merging bulk actions
Kicu Jul 3, 2024
ef948e3
Cleanup Search params old command leftovers
Kicu Jul 3, 2024
435205f
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jul 4, 2024
28b8c3c
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jul 5, 2024
87c2866
Add SearchContext and use it for storing current search hash
Kicu Jul 8, 2024
9891377
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jul 8, 2024
f2d2544
Add small style updates to transaction items
Kicu Jul 8, 2024
0484b99
Add small style fixes to search
Kicu Jul 9, 2024
ad49412
Merge branch 'main' into search/kicu/39890-action-buttons
Kicu Jul 9, 2024
fbf83af
Fix Search transaction list item highlight colors
Kicu Jul 9, 2024
a224fe2
Fix margin on ReportListItem
Kicu Jul 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4999,6 +4999,9 @@ const CONST = {
DONE: 'done',
PAID: 'paid',
VIEW: 'view',
REVIEW: 'review',
HOLD: 'hold',
UNHOLD: 'unhold',
},
TRANSACTION_TYPE: {
CASH: 'cash',
Expand Down
5 changes: 5 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ const ROUTES = {
getRoute: (query: string, reportID: string) => `search/${query}/view/${reportID}` as const,
},

TRANSACTION_HOLD_REASON_RHP: {
route: '/search/:query/hold/:transactionID/:searchHash',
getRoute: (query: string, transactionID: string, searchHash: number) => `search/${query}/hold/${transactionID}/${searchHash}` as const,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking more about this, why do we need the hash in the URL? The hash is derived from the query which is already in the URL. Can we just remove the hash entirely?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also was thinking about this. Currently hash is generated like this: getQueryHash(query, policyIDs, sortBy, sortOrder) so it requires 4 params. I decided it makes more sense to add 1 hash param to this /hold route, than to add all 4 Search query params.

It is messy I know :/

},

// This is a utility route used to go to the user's concierge chat, or the sign-in page if the user's not authenticated
CONCIERGE: 'concierge',
FLAG_COMMENT: {
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const SCREENS = {
SEARCH: {
CENTRAL_PANE: 'Search_Central_Pane',
REPORT_RHP: 'Search_Report_RHP',
TRANSACTION_HOLD_REASON_RHP: 'Search_Transaction_Hold_Reason_RHP',
BOTTOM_TAB: 'Search_Bottom_Tab',
},
SETTINGS: {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) {

const ListItem = SearchUtils.getListItem(type);

const data = SearchUtils.getSections(searchResults?.data ?? {}, searchResults?.search ?? {}, type);
const searchContext = {searchHash: hash};
const data = SearchUtils.getSections(searchResults?.data ?? {}, searchResults?.search ?? {}, type, searchContext);
const sortedData = SearchUtils.getSortedSections(type, data, sortBy, sortOrder);

const onSortPress = (column: SearchColumnType, order: SortOrder) => {
Expand Down
57 changes: 49 additions & 8 deletions src/components/SelectionList/Search/ActionCell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useCallback} from 'react';
import {View} from 'react-native';
import Badge from '@components/Badge';
import Button from '@components/Button';
Expand All @@ -7,28 +7,57 @@ import useLocalize from '@hooks/useLocalize';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@navigation/Navigation';
import variables from '@styles/variables';
import * as SearchActions from '@userActions/Search';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ROUTES from '@src/ROUTES';
import type {SearchTransactionAction} from '@src/types/onyx/SearchResults';

const actionTranslationsMap: Record<SearchTransactionAction, TranslationPaths> = {
view: 'common.view',
review: 'common.review',
done: 'common.done',
paid: 'iou.settledExpensify',
hold: 'iou.hold',
unhold: 'iou.unhold',
};
Kicu marked this conversation as resolved.
Show resolved Hide resolved

type ActionCellProps = {
onButtonPress: () => void;
action?: string;
action?: SearchTransactionAction;
transactionID?: string;
searchHash: number;
isLargeScreenWidth?: boolean;
isSelected?: boolean;
goToItem: () => void;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'd prefer not to rename onButtonPress to goToItem here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?


function ActionCell({onButtonPress, action = CONST.SEARCH.ACTION_TYPES.VIEW, isLargeScreenWidth = true, isSelected = false}: ActionCellProps) {
function ActionCell({action = CONST.SEARCH.ACTION_TYPES.VIEW, transactionID, searchHash, isLargeScreenWidth = true, isSelected = false, goToItem}: ActionCellProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();

const onButtonPress = useCallback(() => {
if (!transactionID) {
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
return;
}

if (action === CONST.SEARCH.ACTION_TYPES.HOLD) {
Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP.getRoute(CONST.SEARCH.TAB.ALL, transactionID, searchHash));
} else if (action === CONST.SEARCH.ACTION_TYPES.UNHOLD) {
SearchActions.unholdMoneyRequestOnSearch(searchHash, [transactionID]);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of passing the hash as an argument here, we could just compute it from the route within unholdMoneyRequestOnSearch itself. Would that remove the need for passing the hash around?

}, [action, searchHash, transactionID]);

const text = translate(actionTranslationsMap[action]);

if (action === CONST.SEARCH.ACTION_TYPES.PAID || action === CONST.SEARCH.ACTION_TYPES.DONE) {
const buttonTextKey = action === CONST.SEARCH.ACTION_TYPES.PAID ? 'iou.settledExpensify' : 'common.done';
return (
<View style={[StyleUtils.getHeight(variables.h28), styles.justifyContentCenter]}>
<Badge
text={translate(buttonTextKey)}
text={text}
icon={Expensicons.Checkmark}
badgeStyles={[
styles.ml0,
Expand All @@ -47,9 +76,21 @@ function ActionCell({onButtonPress, action = CONST.SEARCH.ACTION_TYPES.VIEW, isL
);
}

if (action === CONST.SEARCH.ACTION_TYPES.VIEW || action === CONST.SEARCH.ACTION_TYPES.REVIEW) {
return (
<Button
text={text}
onPress={goToItem}
small
pressOnEnter
style={[styles.w100]}
/>
);
}

return (
<Button
text={translate('common.view')}
text={text}
onPress={onButtonPress}
small
pressOnEnter
Expand Down
22 changes: 17 additions & 5 deletions src/components/SelectionList/Search/ExpenseItemHeaderNarrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import variables from '@styles/variables';
import type {SearchAccountDetails} from '@src/types/onyx/SearchResults';
import type {SearchAccountDetails, SearchTransactionAction} from '@src/types/onyx/SearchResults';
import ActionCell from './ActionCell';
import UserInfoCell from './UserInfoCell';

Expand All @@ -15,11 +15,22 @@ type ExpenseItemHeaderNarrowProps = {
participantTo: SearchAccountDetails;
participantFromDisplayName: string;
participantToDisplayName: string;
action?: SearchTransactionAction;
transactionID?: string;
searchHash: number;
onButtonPress: () => void;
action?: string;
};

function ExpenseItemHeaderNarrow({participantFrom, participantFromDisplayName, participantTo, participantToDisplayName, onButtonPress, action}: ExpenseItemHeaderNarrowProps) {
function ExpenseItemHeaderNarrow({
participantFrom,
participantFromDisplayName,
participantTo,
participantToDisplayName,
action,
transactionID,
searchHash,
onButtonPress,
}: ExpenseItemHeaderNarrowProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const theme = useTheme();
Expand Down Expand Up @@ -48,9 +59,10 @@ function ExpenseItemHeaderNarrow({participantFrom, participantFromDisplayName, p
</View>
<View style={[StyleUtils.getWidthStyle(variables.w80)]}>
<ActionCell
onButtonPress={onButtonPress}
action={action}
isLargeScreenWidth={false}
transactionID={transactionID}
searchHash={searchHash}
goToItem={onButtonPress}
/>
</View>
</View>
Expand Down
8 changes: 6 additions & 2 deletions src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ function ReportListItem<TItem extends ListItem>({
);
}

// every child item comes from the same search hash, so it doesn't matter which one we use, we just need the hash
const {searchHash} = reportItem.transactions[0];

return (
<BaseListItem
item={item}
Expand Down Expand Up @@ -142,6 +145,7 @@ function ReportListItem<TItem extends ListItem>({
participantTo={participantTo}
participantToDisplayName={participantToDisplayName}
action={reportItem.action}
searchHash={searchHash}
onButtonPress={handleOnButtonPress}
/>
)}
Expand Down Expand Up @@ -177,9 +181,9 @@ function ReportListItem<TItem extends ListItem>({
<View style={StyleUtils.getSearchTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.TYPE)} />
<View style={StyleUtils.getSearchTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.ACTION)}>
<ActionCell
isLargeScreenWidth={isLargeScreenWidth}
onButtonPress={handleOnButtonPress}
action={reportItem.action}
searchHash={searchHash}
goToItem={handleOnButtonPress}
isSelected={item.isSelected}
/>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,10 @@ function TransactionListItemRow({
participantFromDisplayName={item.formattedFrom}
participantTo={item.to}
participantToDisplayName={item.formattedTo}
onButtonPress={onButtonPress}
action={item.action}
transactionID={item.transactionID}
searchHash={item.searchHash}
onButtonPress={onButtonPress}
/>
)}

Expand Down Expand Up @@ -384,9 +386,11 @@ function TransactionListItemRow({
</View>
<View style={[StyleUtils.getSearchTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.ACTION)]}>
<ActionCell
onButtonPress={onButtonPress}
action={item.action}
transactionID={item.transactionID}
searchHash={item.searchHash}
isSelected={isButtonSelected}
goToItem={onButtonPress}
/>
</View>
</View>
Expand Down
3 changes: 3 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ type TransactionListItemType = ListItem &
*/
shouldShowYear: boolean;

/** Hash of search that was used to retrieve this item, needed for performing Actions on it */
searchHash: number;

/** Key used internally by React */
keyForList: string;
};
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export default {
wallet: 'Wallet',
preferences: 'Preferences',
view: 'View',
review: 'Review',
not: 'Not',
signIn: 'Sign in',
signInWithGoogle: 'Sign in with Google',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export default {
wallet: 'Billetera',
preferences: 'Preferencias',
view: 'Ver',
review: 'Revisar',
not: 'No',
privacyPolicy: 'la Política de Privacidad de Expensify',
addCardTermsOfService: 'Términos de Servicio',
Expand Down
7 changes: 7 additions & 0 deletions src/libs/API/parameters/HoldRequestOnSearchParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type HoldRequestOnSearchParams = {
searchHash: number;
transactionIDList: string[];
comment?: string;
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
};

export default HoldRequestOnSearchParams;
6 changes: 6 additions & 0 deletions src/libs/API/parameters/UnholdRequestOnSearchParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type UnholdRequestOnSearchParams = {
searchHash: number;
transactionIDList: string[];
};

export default UnholdRequestOnSearchParams;
2 changes: 2 additions & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,5 @@ export type {default as UpdateNetSuiteSubsidiaryParams} from './UpdateNetSuiteSu
export type {default as OpenPolicyExpensifyCardsPageParams} from './OpenPolicyExpensifyCardsPageParams';
export type {default as RequestExpensifyCardLimitIncreaseParams} from './RequestExpensifyCardLimitIncreaseParams';
export type {default as UpdateNetSuiteGenericTypeParams} from './UpdateNetSuiteGenericTypeParams';
export type {default as HoldRequestOnSearchParams} from './HoldRequestOnSearchParams';
export type {default as UnholdRequestOnSearchParams} from './UnholdRequestOnSearchParams';
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ const TransactionDuplicateStackNavigator = createModalStackNavigator<Transaction

const SearchReportModalStackNavigator = createModalStackNavigator<SearchReportParamList>({
[SCREENS.SEARCH.REPORT_RHP]: () => require<ReactComponentModule>('../../../../pages/home/ReportScreen').default,
[SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: () => require<ReactComponentModule>('../../../../pages/Search/SearchHoldReasonPage').default,
});

const RestrictedActionModalStackNavigator = createModalStackNavigator<SearchReportParamList>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const CENTRAL_PANE_TO_RHP_MAPPING: Partial<Record<CentralPaneName, string[]>> =
[SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS],
[SCREENS.SETTINGS.SAVE_THE_WORLD]: [SCREENS.I_KNOW_A_TEACHER, SCREENS.INTRO_SCHOOL_PRINCIPAL, SCREENS.I_AM_A_TEACHER],
[SCREENS.SETTINGS.TROUBLESHOOT]: [SCREENS.SETTINGS.CONSOLE],
[SCREENS.SEARCH.CENTRAL_PANE]: [SCREENS.SEARCH.REPORT_RHP],
[SCREENS.SEARCH.CENTRAL_PANE]: [SCREENS.SEARCH.REPORT_RHP, SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP],
[SCREENS.SETTINGS.SUBSCRIPTION.ROOT]: [
SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD,
SCREENS.SETTINGS.SUBSCRIPTION.SIZE,
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.RIGHT_MODAL.SEARCH_REPORT]: {
screens: {
[SCREENS.SEARCH.REPORT_RHP]: ROUTES.SEARCH_REPORT.route,
[SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: ROUTES.TRANSACTION_HOLD_REASON_RHP.route,
},
},
[SCREENS.RIGHT_MODAL.RESTRICTED_ACTION]: {
Expand Down
14 changes: 10 additions & 4 deletions src/libs/SearchUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import * as UserUtils from './UserUtils';

type SortOrder = ValueOf<typeof CONST.SEARCH.SORT_ORDER>;
type SearchColumnType = ValueOf<typeof CONST.SEARCH.TABLE_COLUMNS>;
type SearchDataContext = {
searchHash: number;
};

const columnNamesToSortingProperty = {
[CONST.SEARCH.TABLE_COLUMNS.TO]: 'formattedTo' as const,
Expand Down Expand Up @@ -117,7 +120,7 @@ function shouldShowYear(data: TransactionListItemType[] | ReportListItemType[] |
return false;
}

function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']): TransactionListItemType[] {
function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], context: SearchDataContext): TransactionListItemType[] {
const shouldShowMerchant = getShouldShowMerchant(data);

const doesDataContainAPastYearTransaction = shouldShowYear(data);
Expand All @@ -143,6 +146,7 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata
formattedMerchant,
date,
shouldShowMerchant,
searchHash: context.searchHash,
Kicu marked this conversation as resolved.
Show resolved Hide resolved
shouldShowCategory: metadata?.columnsToShow?.shouldShowCategoryColumn,
shouldShowTag: metadata?.columnsToShow?.shouldShowTagColumn,
shouldShowTax: metadata?.columnsToShow?.shouldShowTaxColumn,
Expand All @@ -152,7 +156,7 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata
});
}

function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']): ReportListItemType[] {
function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], context: SearchDataContext): ReportListItemType[] {
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
const shouldShowMerchant = getShouldShowMerchant(data);

const doesDataContainAPastYearTransaction = shouldShowYear(data);
Expand Down Expand Up @@ -190,6 +194,7 @@ function getReportSections(data: OnyxTypes.SearchResults['data'], metadata: Onyx
formattedMerchant,
date,
shouldShowMerchant,
searchHash: context.searchHash,
shouldShowCategory: metadata?.columnsToShow?.shouldShowCategoryColumn,
shouldShowTag: metadata?.columnsToShow?.shouldShowTagColumn,
shouldShowTax: metadata?.columnsToShow?.shouldShowTaxColumn,
Expand Down Expand Up @@ -229,8 +234,9 @@ function getSections<K extends keyof SearchTypeToItemMap>(
data: OnyxTypes.SearchResults['data'],
metadata: OnyxTypes.SearchResults['search'],
type: K,
context: SearchDataContext,
): ReturnType<SearchTypeToItemMap[K]['getSections']> {
return searchTypeToItemMap[type].getSections(data, metadata) as ReturnType<SearchTypeToItemMap[K]['getSections']>;
return searchTypeToItemMap[type].getSections(data, metadata, context) as ReturnType<SearchTypeToItemMap[K]['getSections']>;
}

function getSortedSections<K extends keyof SearchTypeToItemMap>(
Expand Down Expand Up @@ -284,4 +290,4 @@ function getSearchParams() {
}

export {getListItem, getQueryHash, getSections, getSortedSections, getShouldShowMerchant, getSearchType, getSearchParams, shouldShowYear, isReportListItemType, isTransactionListItemType};
export type {SearchColumnType, SortOrder};
export type {SearchColumnType, SortOrder, SearchDataContext};
3 changes: 2 additions & 1 deletion src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,18 @@ function createTransactionThread(hash: number, transactionID: string, reportID:
},
},
};

Onyx.merge(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, onyxUpdate);
}

function holdMoneyRequestOnSearch(hash: number, transactionIDList: string[], comment: string) {
const {optimisticData, finallyData} = getOnyxLoadingData(hash);

API.write(WRITE_COMMANDS.HOLD_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList, comment}, {optimisticData, finallyData});
}

function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
const {optimisticData, finallyData} = getOnyxLoadingData(hash);

API.write(WRITE_COMMANDS.UNHOLD_MONEY_REQUEST_ON_SEARCH, {hash, transactionIDList}, {optimisticData, finallyData});
}

Expand Down
Loading
Loading