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 all 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
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import OnyxProvider from './components/OnyxProvider';
import PopoverContextProvider from './components/PopoverProvider';
import SafeArea from './components/SafeArea';
import ScrollOffsetContextProvider from './components/ScrollOffsetContextProvider';
import {SearchContextProvider} from './components/Search/SearchContext';
import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider';
import ThemeProvider from './components/ThemeProvider';
import ThemeStylesProvider from './components/ThemeStylesProvider';
Expand Down Expand Up @@ -91,6 +92,7 @@ function App({url}: AppProps) {
VolumeContextProvider,
VideoPopoverMenuContextProvider,
KeyboardProvider,
SearchContextProvider,
]}
>
<CustomStatusBarAndBackground />
Expand Down
3 changes: 3 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5062,6 +5062,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',
getRoute: (query: string, transactionID: string) => `search/${query}/hold/${transactionID}` as const,
},

// 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
7 changes: 6 additions & 1 deletion src/components/ReceiptImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ type ReceiptImageProps = (
/** The size of the fallback icon */
fallbackIconSize?: number;

/** The colod of the fallback icon */
/** The color of the fallback icon */
fallbackIconColor?: string;

/** The background color of fallback icon */
fallbackIconBackground?: string;
};

function ReceiptImage({
Expand All @@ -93,6 +96,7 @@ function ReceiptImage({
fallbackIconSize,
shouldUseInitialObjectPosition = false,
fallbackIconColor,
fallbackIconBackground,
}: ReceiptImageProps) {
const styles = useThemeStyles();

Expand Down Expand Up @@ -129,6 +133,7 @@ function ReceiptImage({
fallbackIcon={fallbackIcon}
fallbackIconSize={fallbackIconSize}
fallbackIconColor={fallbackIconColor}
fallbackIconBackground={fallbackIconBackground}
objectPosition={shouldUseInitialObjectPosition ? CONST.IMAGE_OBJECT_POSITION.INITIAL : CONST.IMAGE_OBJECT_POSITION.TOP}
/>
);
Expand Down
58 changes: 58 additions & 0 deletions src/components/Search/SearchContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, {useCallback, useContext, useMemo, useState} from 'react';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type {SearchContext} from './types';

const defaultSearchContext = {
currentSearchHash: -1,
selectedTransactionIDs: [],
setCurrentSearchHash: () => {},
setSelectedTransactionIds: () => {},
};

const Context = React.createContext<SearchContext>(defaultSearchContext);

function SearchContextProvider({children}: ChildrenProps) {
const [searchContextData, setSearchContextData] = useState<Pick<SearchContext, 'currentSearchHash' | 'selectedTransactionIDs'>>({
currentSearchHash: defaultSearchContext.currentSearchHash,
selectedTransactionIDs: defaultSearchContext.selectedTransactionIDs,
});

const setCurrentSearchHash = useCallback(
(searchHash: number) => {
setSearchContextData({
...searchContextData,
currentSearchHash: searchHash,
});
},
[searchContextData],
);

const setSelectedTransactionIds = useCallback(
(selectedTransactionIDs: string[]) => {
setSearchContextData({
...searchContextData,
selectedTransactionIDs,
});
},
[searchContextData],
);

const searchContext = useMemo<SearchContext>(
() => ({
...searchContextData,
setCurrentSearchHash,
setSelectedTransactionIds,
}),
[searchContextData, setCurrentSearchHash, setSelectedTransactionIds],
);

return <Context.Provider value={searchContext}>{children}</Context.Provider>;
}

function useSearchContext() {
return useContext(Context);
}

SearchContextProvider.displayName = 'SearchContextProvider';

export {SearchContextProvider, useSearchContext};
5 changes: 4 additions & 1 deletion src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import * as SearchActions from '@libs/actions/Search';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import Log from '@libs/Log';
import * as ReportUtils from '@libs/ReportUtils';
import type {SearchColumnType, SortOrder} from '@libs/SearchUtils';
import * as SearchUtils from '@libs/SearchUtils';
import Navigation from '@navigation/Navigation';
import type {AuthScreensParamList} from '@navigation/types';
Expand All @@ -25,8 +24,10 @@ import ROUTES from '@src/ROUTES';
import type SearchResults from '@src/types/onyx/SearchResults';
import type {SearchDataTypes, SearchQuery} from '@src/types/onyx/SearchResults';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import {useSearchContext} from './SearchContext';
import SearchListWithHeader from './SearchListWithHeader';
import SearchPageHeader from './SearchPageHeader';
import type {SearchColumnType, SortOrder} from './types';

type SearchProps = {
query: SearchQuery;
Expand All @@ -47,6 +48,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) {
const {isLargeScreenWidth} = useWindowDimensions();
const navigation = useNavigation<StackNavigationProp<AuthScreensParamList>>();
const lastSearchResultsRef = useRef<OnyxEntry<SearchResults>>();
const {setCurrentSearchHash} = useSearchContext();

const getItemHeight = useCallback(
(item: TransactionListItemType | ReportListItemType) => {
Expand Down Expand Up @@ -83,6 +85,7 @@ function Search({query, policyIDs, sortBy, sortOrder}: SearchProps) {
return;
}

setCurrentSearchHash(hash);
SearchActions.search({hash, query, policyIDs, offset: 0, sortBy, sortOrder});
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [hash, isOffline]);
Expand Down
16 changes: 14 additions & 2 deletions src/components/Search/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type {ValueOf} from 'react-native-gesture-handler/lib/typescript/typeUtils';
import type CONST from '@src/CONST';

/** Model of the selected transaction */
type SelectedTransactionInfo = {
/** Whether the transaction is selected */
Expand All @@ -13,5 +16,14 @@ type SelectedTransactionInfo = {
/** Model of selected results */
type SelectedTransactions = Record<string, SelectedTransactionInfo>;

// eslint-disable-next-line import/prefer-default-export
export type {SelectedTransactionInfo, SelectedTransactions};
type SortOrder = ValueOf<typeof CONST.SEARCH.SORT_ORDER>;
type SearchColumnType = ValueOf<typeof CONST.SEARCH.TABLE_COLUMNS>;

type SearchContext = {
currentSearchHash: number;
selectedTransactionIDs: string[];
setCurrentSearchHash: (hash: number) => void;
setSelectedTransactionIds: (selectedTransactionIds: string[]) => void;
};

export type {SelectedTransactionInfo, SelectedTransactions, SearchColumnType, SortOrder, SearchContext};
67 changes: 57 additions & 10 deletions src/components/SelectionList/Search/ActionCell.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,78 @@
import React from 'react';
import React, {useCallback} from 'react';
import {View} from 'react-native';
import Badge from '@components/Badge';
import Button from '@components/Button';
import * as Expensicons from '@components/Icon/Expensicons';
import {useSearchContext} from '@components/Search/SearchContext';
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;
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, isLargeScreenWidth = true, isSelected = false, goToItem}: ActionCellProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();

const {currentSearchHash} = useSearchContext();

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));
} else if (action === CONST.SEARCH.ACTION_TYPES.UNHOLD) {
SearchActions.unholdMoneyRequestOnSearch(currentSearchHash, [transactionID]);
}
}, [action, currentSearchHash, transactionID]);

if (!isLargeScreenWidth) {
return null;
}

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,
styles.ph2,
styles.gap1,
isLargeScreenWidth ? styles.alignSelfCenter : styles.alignSelfEnd,
StyleUtils.getBorderColorStyle(theme.border),
StyleUtils.getHeight(variables.h20),
StyleUtils.getMinimumHeight(variables.h20),
isSelected ? StyleUtils.getBorderColorStyle(theme.buttonHoveredBG) : StyleUtils.getBorderColorStyle(theme.border),
]}
textStyles={StyleUtils.getFontSizeStyle(variables.fontSizeExtraSmall)}
iconStyles={styles.mr0}
Expand All @@ -50,14 +82,29 @@ function ActionCell({onButtonPress, action = CONST.SEARCH.ACTION_TYPES.VIEW, isL
);
}

const buttonInnerStyles = isSelected ? styles.buttonDefaultHovered : {};

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

return (
<Button
text={translate('common.view')}
text={text}
onPress={onButtonPress}
small
pressOnEnter
style={[styles.w100]}
innerStyles={isSelected ? styles.buttonDefaultHovered : {}}
innerStyles={buttonInnerStyles}
/>
);
}
Expand Down
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,12 @@ type ExpenseItemHeaderNarrowProps = {
participantTo: SearchAccountDetails;
participantFromDisplayName: string;
participantToDisplayName: string;
action?: SearchTransactionAction;
transactionID?: string;
onButtonPress: () => void;
action?: string;
};

function ExpenseItemHeaderNarrow({participantFrom, participantFromDisplayName, participantTo, participantToDisplayName, onButtonPress, action}: ExpenseItemHeaderNarrowProps) {
function ExpenseItemHeaderNarrow({participantFrom, participantFromDisplayName, participantTo, participantToDisplayName, action, transactionID, onButtonPress}: ExpenseItemHeaderNarrowProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const theme = useTheme();
Expand Down Expand Up @@ -48,9 +49,9 @@ function ExpenseItemHeaderNarrow({participantFrom, participantFromDisplayName, p
</View>
<View style={[StyleUtils.getWidthStyle(variables.w80)]}>
<ActionCell
onButtonPress={onButtonPress}
action={action}
isLargeScreenWidth={false}
transactionID={transactionID}
goToItem={onButtonPress}
/>
</View>
</View>
Expand Down
5 changes: 2 additions & 3 deletions src/components/SelectionList/Search/ReportListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function ReportListItem<TItem extends ListItem>({
onButtonPress={handleOnButtonPress}
/>
)}
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter, styles.gap3, isLargeScreenWidth && styles.mr4]}>
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter, styles.gap3]}>
<View style={[styles.flexRow, styles.flex1, styles.alignItemsCenter, styles.justifyContentBetween]}>
<View style={[styles.flexRow, styles.alignItemsCenter, styles.flex2]}>
{canSelectMultiple && (
Expand Down Expand Up @@ -177,9 +177,8 @@ 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}
goToItem={handleOnButtonPress}
isSelected={item.isSelected}
/>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function TransactionListItem<TItem extends ListItem>({

const {isLargeScreenWidth} = useWindowDimensions();

const listItemPressableStyle = [styles.selectionListPressableItemWrapper, styles.pv3, item.isSelected && styles.activeComponentBG, isFocused && styles.sidebarLinkActive];
const listItemPressableStyle = [styles.selectionListPressableItemWrapper, styles.pv3, styles.ph3, item.isSelected && styles.activeComponentBG, isFocused && styles.sidebarLinkActive];

const listItemWrapperStyle = [
styles.flex1,
Expand Down
Loading
Loading