Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,16 @@ const ROUTES = {
return `search/view/${reportID}` as const;
},
},
TRANSACTION_HOLD_REASON_RHP: 'search/hold',

TRANSACTION_HOLD_REASON_RHP: {
route: 'search/hold/:transactionID?/:reportID?',
getRoute: (transactionID?: string, reportID?: string) => {
if (transactionID && reportID) {
return `search/hold/${transactionID}/${reportID}` as const;
}
return `search/hold` 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',
Expand Down
5 changes: 3 additions & 2 deletions src/components/PromotedActionsBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type PromotedActionsType = Record<BasePromotedActions, (report: OnyxReport) => P
reportID?: string;
isDelegateAccessRestricted: boolean;
setIsNoDelegateAccessMenuVisible: (isVisible: boolean) => void;
currentSearchHash: number;
}) => PromotedAction;
};

Expand Down Expand Up @@ -76,7 +77,7 @@ const PromotedActions = {
}
},
}),
hold: ({isTextHold, reportAction, reportID, isDelegateAccessRestricted, setIsNoDelegateAccessMenuVisible}) => ({
hold: ({isTextHold, reportAction, reportID, isDelegateAccessRestricted, setIsNoDelegateAccessMenuVisible, currentSearchHash}) => ({
key: CONST.PROMOTED_ACTIONS.HOLD,
icon: Expensicons.Stopwatch,
text: Localize.translateLocal(`iou.${isTextHold ? 'hold' : 'unhold'}`),
Expand All @@ -97,7 +98,7 @@ const PromotedActions = {
return;
}

ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(targetedReportID));
ReportUtils.changeMoneyRequestHoldStatus(reportAction, ROUTES.SEARCH_REPORT.getRoute(targetedReportID), currentSearchHash);
},
}),
} satisfies PromotedActionsType;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ function SearchPageHeader({queryJSON, hash, onSelectDeleteOption, setOfflineModa
return;
}

Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP);
Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP.getRoute());
},
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,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,
[SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: ROUTES.TRANSACTION_HOLD_REASON_RHP.route,
},
},
[SCREENS.RIGHT_MODAL.SEARCH_ADVANCED_FILTERS]: {
Expand Down
11 changes: 10 additions & 1 deletion src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import type IconAsset from '@src/types/utils/IconAsset';
import * as IOU from './actions/IOU';
import * as PolicyActions from './actions/Policy/Policy';
import * as store from './actions/ReimbursementAccount/store';
import * as SearchActions from './actions/Search';
import * as SessionUtils from './actions/Session';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
Expand Down Expand Up @@ -3134,7 +3135,7 @@ function canHoldUnholdReportAction(reportAction: OnyxInputOrEntry<ReportAction>)
return {canHoldRequest, canUnholdRequest};
}

const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry<ReportAction>, backTo?: string): void => {
const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry<ReportAction>, backTo?: string, currentSearchHash?: number): void => {
if (!ReportActionsUtils.isMoneyRequestAction(reportAction)) {
return;
}
Expand All @@ -3151,8 +3152,16 @@ const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry<ReportAction>, bac
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${moneyRequestReport.policyID}`] ?? null;

if (isOnHold) {
if (currentSearchHash) {
SearchActions.unholdMoneyRequestOnSearch(currentSearchHash, [transactionID], reportAction?.childReportID);
return;
}
IOU.unholdRequest(transactionID, reportAction.childReportID ?? '');
} else {
if (currentSearchHash) {
Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP.getRoute(transactionID, reportAction?.childReportID));
return;
}
const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams());
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Navigation.navigate(ROUTES.MONEY_REQUEST_HOLD_REASON.getRoute(policy?.type ?? CONST.POLICY.TYPE.PERSONAL, transactionID, reportAction.childReportID ?? '', backTo || activeRoute));
Expand Down
79 changes: 75 additions & 4 deletions src/libs/actions/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import * as API from '@libs/API';
import type {ExportSearchItemsToCSVParams} from '@libs/API/parameters';
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import * as ApiUtils from '@libs/ApiUtils';
import DateUtils from '@libs/DateUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import fileDownload from '@libs/fileDownload';
import enhanceParameters from '@libs/Network/enhanceParameters';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import FILTER_KEYS from '@src/types/form/SearchAdvancedFiltersForm';
import type {ReportAction} from '@src/types/onyx';
import type {SearchTransaction} from '@src/types/onyx/SearchResults';
import * as Report from './Report';

Expand Down Expand Up @@ -91,16 +95,83 @@ function createTransactionThread(hash: number, transactionID: string, reportID:
Onyx.merge(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, onyxUpdate);
}

function holdMoneyRequestOnSearch(hash: number, transactionIDList: string[], comment: string) {
function holdMoneyRequestOnSearch(hash: number, transactionIDList: string[], comment: string, reportID?: string) {
const currentTime = DateUtils.getDBTime();
const {optimisticData, finallyData} = getOnyxLoadingData(hash);
const createdReportAction = ReportUtils.buildOptimisticHoldReportAction(currentTime);
const createdReportActionComment = ReportUtils.buildOptimisticHoldReportActionComment(comment, DateUtils.addMillisecondsFromDateTime(currentTime, 1));
const successData = finallyData;
const failureData = finallyData;
if (reportID) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDList[0]}`,
value: {
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
comment: {
hold: CONST.POLICY.ID_FAKE,
},
},
});
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[createdReportAction.reportActionID]: createdReportAction as ReportAction,
[createdReportActionComment.reportActionID]: createdReportActionComment as ReportAction,
},
});
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDList[0]}`,
value: {
pendingAction: null,
comment: {
hold: null,
},
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericHoldExpenseFailureMessage'),
},
});
}

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

function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
function unholdMoneyRequestOnSearch(hash: number, transactionIDList: string[], reportID?: string) {
const {optimisticData, finallyData} = getOnyxLoadingData(hash);
const createdReportAction = ReportUtils.buildOptimisticUnHoldReportAction();
const successData = finallyData;
const failureData = finallyData;
if (reportID) {
optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDList[0]}`,
value: {
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
comment: {
hold: null,
},
},
});

optimisticData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`,
value: {
[createdReportAction.reportActionID]: createdReportAction as ReportAction,
},
});
failureData.push({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionIDList[0]}`,
value: {
pendingAction: null,
errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericUnholdExpenseFailureMessage'),
},
});
}

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

function deleteMoneyRequestOnSearch(hash: number, transactionIDList: string[]) {
Expand Down
15 changes: 14 additions & 1 deletion src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import PromotedActionsBar, {PromotedActions} from '@components/PromotedActionsBa
import RoomHeaderAvatars from '@components/RoomHeaderAvatars';
import ScreenWrapper from '@components/ScreenWrapper';
import ScrollView from '@components/ScrollView';
import {useSearchContext} from '@components/Search/SearchContext';
import Text from '@components/Text';
import useDelegateUserDetails from '@hooks/useDelegateUserDetails';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -85,6 +86,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const styles = useThemeStyles();
const {currentSearchHash} = useSearchContext();

// The app would crash due to subscribing to the entire report collection if parentReportID is an empty string. So we should have a fallback ID here.
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
Expand Down Expand Up @@ -560,6 +562,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
reportID: transactionThreadReportID ? report.reportID : moneyRequestAction?.childReportID ?? '-1',
isDelegateAccessRestricted,
setIsNoDelegateAccessMenuVisible,
currentSearchHash,
}),
);
}
Expand All @@ -571,7 +574,17 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
result.push(PromotedActions.share(report));

return result;
}, [report, moneyRequestAction, canJoin, isExpenseReport, shouldShowHoldAction, canHoldUnholdReportAction.canHoldRequest, transactionThreadReportID, isDelegateAccessRestricted]);
}, [
report,
moneyRequestAction,
canJoin,
isExpenseReport,
shouldShowHoldAction,
canHoldUnholdReportAction.canHoldRequest,
transactionThreadReportID,
isDelegateAccessRestricted,
currentSearchHash,
]);

const nameSectionExpenseIOU = (
<View style={[styles.reportDetailsRoomInfo, styles.mw100]}>
Expand Down
8 changes: 6 additions & 2 deletions src/pages/Search/SearchHoldReasonPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import INPUT_IDS from '@src/types/form/MoneyRequestHoldReasonForm';
type SearchHoldReasonPageRouteParams = {
/** Link to previous page */
backTo: Route;

transactionID?: string;

reportID?: string;
};

type SearchHoldReasonPageProps = {
Expand All @@ -26,11 +30,11 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) {
const {translate} = useLocalize();

const {currentSearchHash, selectedTransactions} = useSearchContext();
const {backTo = ''} = route.params ?? {};
const {backTo = '', transactionID = '', reportID = ''} = route.params ?? {};

const selectedTransactionIDs = Object.keys(selectedTransactions);
const onSubmit = (values: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_HOLD_FORM>) => {
SearchActions.holdMoneyRequestOnSearch(currentSearchHash, selectedTransactionIDs, values.comment);
SearchActions.holdMoneyRequestOnSearch(currentSearchHash, transactionID ? [transactionID] : selectedTransactionIDs, values.comment, reportID);
Navigation.goBack();
};

Expand Down