Skip to content

Commit

Permalink
Merge pull request Expensify#45151 from software-mansion-labs/@cdOut/…
Browse files Browse the repository at this point in the history
…cleanup-hold-unhold

[Held requests] Clean up the hold/unhold logic
  • Loading branch information
robertjchen authored Aug 10, 2024
2 parents e6546d6 + 03e5b25 commit 93dc1c9
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 19 deletions.
17 changes: 10 additions & 7 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2965,27 +2965,30 @@ function canHoldUnholdReportAction(reportAction: OnyxInputOrEntry<ReportAction>)
const transactionID = moneyRequestReport ? ReportActionsUtils.getOriginalMessage(reportAction)?.IOUTransactionID : 0;
const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] ?? ({} as Transaction);

const parentReport = getReportOrDraftReport(String(moneyRequestReport.parentReportID));
const parentReportAction = ReportActionsUtils.getParentReportAction(moneyRequestReport);

const isRequestIOU = parentReport?.type === 'iou';
const isRequestHoldCreator = isHoldCreator(transaction, moneyRequestReport?.reportID) && isRequestIOU;
const isRequestIOU = isIOUReport(moneyRequestReport);
const isHoldActionCreator = isHoldCreator(transaction, reportAction.childReportID ?? '-1');

const isTrackExpenseMoneyReport = isTrackExpenseReport(moneyRequestReport);
const isActionOwner =
typeof parentReportAction?.actorAccountID === 'number' &&
typeof currentUserPersonalDetails?.accountID === 'number' &&
parentReportAction.actorAccountID === currentUserPersonalDetails?.accountID;
const isApprover = isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && currentUserPersonalDetails?.accountID === moneyRequestReport?.managerID;
const isAdmin = isPolicyAdmin(moneyRequestReport.policyID ?? '-1', allPolicies);
const isOnHold = TransactionUtils.isOnHold(transaction);
const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
const isClosed = isClosedReport(moneyRequestReport);

const canModifyStatus = !isTrackExpenseMoneyReport && (isPolicyAdmin || isActionOwner || isApprover);
const canModifyStatus = !isTrackExpenseMoneyReport && (isAdmin || isActionOwner || isApprover);
const canModifyUnholdStatus = !isTrackExpenseMoneyReport && (isAdmin || (isActionOwner && isHoldActionCreator) || isApprover);
const isDeletedParentAction = isEmptyObject(parentReportAction) || ReportActionsUtils.isDeletedAction(parentReportAction);

const canHoldOrUnholdRequest = !isRequestSettled && !isApproved && !isDeletedParentAction;
const canHoldRequest = canHoldOrUnholdRequest && !isOnHold && (isRequestHoldCreator || (!isRequestIOU && canModifyStatus)) && !isScanning && !!transaction?.reimbursable;
const canHoldOrUnholdRequest = !isRequestSettled && !isApproved && !isDeletedParentAction && !isClosed;
const canHoldRequest = canHoldOrUnholdRequest && !isOnHold && (isRequestIOU || canModifyStatus) && !isScanning && !!transaction?.reimbursable;
const canUnholdRequest =
!!(canHoldOrUnholdRequest && isOnHold && !TransactionUtils.isDuplicate(transaction.transactionID, true) && (isRequestHoldCreator || (!isRequestIOU && canModifyStatus))) &&
!!(canHoldOrUnholdRequest && isOnHold && !TransactionUtils.isDuplicate(transaction.transactionID, true) && (isRequestIOU ? isHoldActionCreator : canModifyUnholdStatus)) &&
!!transaction?.reimbursable;

return {canHoldRequest, canUnholdRequest};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useEnvironment from '@hooks/useEnvironment';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
Expand Down Expand Up @@ -141,13 +142,64 @@ function BaseReportActionContextMenu({

const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`);

const childReport = ReportUtils.getReport(reportAction?.childReportID ?? '-1');
const parentReportAction = ReportActionsUtils.getReportAction(childReport?.parentReportID ?? '', childReport?.parentReportActionID ?? '');
const {reportActions: paginatedReportActions} = usePaginatedReportActions(childReport?.reportID ?? '-1');

const transactionThreadReportID = useMemo(
() => ReportActionsUtils.getOneTransactionThreadReportID(childReport?.reportID ?? '-1', paginatedReportActions ?? [], isOffline),
[childReport?.reportID, paginatedReportActions, isOffline],
);

const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`);

const isMoneyRequestReport = useMemo(() => ReportUtils.isMoneyRequestReport(childReport), [childReport]);
const isInvoiceReport = useMemo(() => ReportUtils.isInvoiceReport(childReport), [childReport]);

const requestParentReportAction = useMemo(() => {
if (isMoneyRequestReport || isInvoiceReport) {
if (!paginatedReportActions || !transactionThreadReport?.parentReportActionID) {
return undefined;
}
return paginatedReportActions.find((action) => action.reportActionID === transactionThreadReport.parentReportActionID);
}
return parentReportAction;
}, [parentReportAction, isMoneyRequestReport, isInvoiceReport, paginatedReportActions, transactionThreadReport?.parentReportActionID]);

const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : parentReportAction;

const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.parentReportID ?? '-1'}`);
const parentReport = ReportUtils.getReport(childReport?.parentReportID ?? '-1');

const isMoneyRequest = useMemo(() => ReportUtils.isMoneyRequest(childReport), [childReport]);
const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(childReport);
const isSingleTransactionView = isMoneyRequest || isTrackExpenseReport;
const isMoneyRequestOrReport = isMoneyRequestReport || isInvoiceReport || isSingleTransactionView;

const areHoldRequirementsMet = isMoneyRequestOrReport && !ReportUtils.isArchivedRoom(transactionThreadReportID ? childReport : parentReport, parentReportNameValuePairs);

const originalReportID = useMemo(() => ReportUtils.getOriginalReportID(reportID, reportAction), [reportID, reportAction]);

const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen);
let filteredContextMenuActions = ContextMenuActions.filter(
(contextAction) =>
!disabledActions.includes(contextAction) &&
contextAction.shouldShow(type, reportAction, isArchivedRoom, betas, anchor, isChronosReport, reportID, isPinnedChat, isUnreadChat, !!isOffline, isMini, isProduction),
contextAction.shouldShow(
type,
reportAction,
isArchivedRoom,
betas,
anchor,
isChronosReport,
reportID,
isPinnedChat,
isUnreadChat,
!!isOffline,
isMini,
isProduction,
moneyRequestAction,
areHoldRequirementsMet,
),
);

if (isMini) {
Expand Down Expand Up @@ -254,6 +306,7 @@ function BaseReportActionContextMenu({
interceptAnonymousUser,
openOverflowMenu,
setIsEmojiPickerActive,
moneyRequestAction,
};

if ('renderContent' in contextAction) {
Expand Down
53 changes: 42 additions & 11 deletions src/pages/home/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type ShouldShow = (
isOffline: boolean,
isMini: boolean,
isProduction: boolean,
moneyRequestAction: ReportAction | undefined,
areHoldRequirementsMet: boolean,
) => boolean;

type ContextMenuActionPayload = {
Expand All @@ -84,6 +86,7 @@ type ContextMenuActionPayload = {
event?: GestureResponderEvent | MouseEvent | KeyboardEvent;
setIsEmojiPickerActive?: (state: boolean) => void;
anchorRef?: MutableRefObject<View | null>;
moneyRequestAction: ReportAction | undefined;
};

type OnPress = (closePopover: boolean, payload: ContextMenuActionPayload, selection?: string, reportID?: string, draftMessage?: string) => void;
Expand Down Expand Up @@ -262,35 +265,63 @@ const ContextMenuActions: ContextMenuAction[] = [
},
{
isAnonymousAction: false,
textTranslateKey: 'iou.unholdExpense',
textTranslateKey: 'iou.unhold',
icon: Expensicons.Stopwatch,
shouldShow: (type, reportAction) =>
type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && ReportUtils.canHoldUnholdReportAction(reportAction).canUnholdRequest,
onPress: (closePopover, {reportAction}) => {
shouldShow: (
type,
reportAction,
isArchivedRoom,
betas,
anchor,
isChronosReport,
reportID,
isPinnedChat,
isUnreadChat,
isOffline,
isMini,
isProduction,
moneyRequestAction,
areHoldRequirementsMet,
) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canUnholdRequest,
onPress: (closePopover, {moneyRequestAction}) => {
if (closePopover) {
hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(reportAction));
hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction));
return;
}

// No popover to hide, call changeMoneyRequestHoldStatus immediately
ReportUtils.changeMoneyRequestHoldStatus(reportAction);
ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction);
},
getDescription: () => {},
},
{
isAnonymousAction: false,
textTranslateKey: 'iou.hold',
icon: Expensicons.Stopwatch,
shouldShow: (type, reportAction) =>
type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && ReportUtils.canEditReportAction(reportAction) && ReportUtils.canHoldUnholdReportAction(reportAction).canHoldRequest,
onPress: (closePopover, {reportAction}) => {
shouldShow: (
type,
reportAction,
isArchivedRoom,
betas,
anchor,
isChronosReport,
reportID,
isPinnedChat,
isUnreadChat,
isOffline,
isMini,
isProduction,
moneyRequestAction,
areHoldRequirementsMet,
) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && areHoldRequirementsMet && ReportUtils.canHoldUnholdReportAction(moneyRequestAction).canHoldRequest,
onPress: (closePopover, {moneyRequestAction}) => {
if (closePopover) {
hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(reportAction));
hideContextMenu(false, () => ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction));
return;
}

// No popover to hide, call changeMoneyRequestHoldStatus immediately
ReportUtils.changeMoneyRequestHoldStatus(reportAction);
ReportUtils.changeMoneyRequestHoldStatus(moneyRequestAction);
},
getDescription: () => {},
},
Expand Down

0 comments on commit 93dc1c9

Please sign in to comment.