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

[Details Revamp] Remove the Three Dot Menu on Affected Reports #44025

Merged
merged 28 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
53a9399
remove three dot menu from affected pages
cdOut Jun 19, 2024
27c716e
clean up unused imports and vars
cdOut Jun 19, 2024
cd4f695
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jun 26, 2024
e6b67c5
fix prettier
cdOut Jun 26, 2024
0ba0fc0
move the Book a call action into report details
cdOut Jul 1, 2024
14ea14c
remove Book a call from HeaderView
cdOut Jul 1, 2024
55711a5
fix HeaderView prettier
cdOut Jul 1, 2024
207327b
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jul 1, 2024
93f1f4c
remove code from MoneyReportHeader
cdOut Jul 1, 2024
d1ff414
fix lint errors
cdOut Jul 1, 2024
af0d2a0
fix prettier
cdOut Jul 1, 2024
09f7f33
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jul 2, 2024
46e7f6d
move Book a call from ReportDeailsPage into ProfilePage
cdOut Jul 2, 2024
13c1462
fix prettier
cdOut Jul 2, 2024
96d5a64
remove import issues and fix lint
cdOut Jul 2, 2024
2528eac
revert hold menu removal from MoneyRequestHeader
cdOut Jul 2, 2024
8516678
fix prettier issues
cdOut Jul 2, 2024
9c5c52f
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jul 2, 2024
5e5684e
remove unused const definition for BOOK_A_CALL
cdOut Jul 3, 2024
d59bd98
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jul 3, 2024
6a2ea04
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jul 3, 2024
c1abbb7
add isClosedReport check for holding expenses
cdOut Jul 3, 2024
2f10c65
fix hold promoted action not showing in specific cases
cdOut Jul 3, 2024
02a3470
fix lint errors
cdOut Jul 3, 2024
81e0783
Revert "fix lint errors"
cdOut Jul 4, 2024
b4a1a40
Revert "fix hold promoted action not showing in specific cases"
cdOut Jul 4, 2024
767937c
Revert "add isClosedReport check for holding expenses"
cdOut Jul 5, 2024
2a7a161
Merge branch 'main' into @cdOut/three-dot-removal
cdOut Jul 5, 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
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2283,6 +2283,7 @@ const CONST = {
PRIVATE_NOTES: 'privateNotes',
DELETE: 'delete',
MARK_AS_INCOMPLETE: 'markAsIncomplete',
BOOK_A_CALL: 'bookACall',
grgia marked this conversation as resolved.
Show resolved Hide resolved
},
EDIT_REQUEST_FIELD: {
AMOUNT: 'amount',
Expand Down
70 changes: 1 addition & 69 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CurrencyUtils from '@libs/CurrencyUtils';
import * as HeaderUtils from '@libs/HeaderUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
Expand All @@ -22,7 +21,6 @@ import ROUTES from '@src/ROUTES';
import type {Route} from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import Button from './Button';
Expand Down Expand Up @@ -86,27 +84,20 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const [shouldShowHoldMenu, setShouldShowHoldMenu] = useState(false);
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const {isSmallScreenWidth, windowWidth} = useWindowDimensions();
const {isSmallScreenWidth} = useWindowDimensions();
const {reimbursableSpend} = ReportUtils.getMoneyRequestSpendBreakdown(moneyRequestReport);
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
const isApproved = ReportUtils.isReportApproved(moneyRequestReport);
const isOnHold = TransactionUtils.isOnHold(transaction);
const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
const isDeletedParentAction = !!requestParentReportAction && ReportActionsUtils.isDeletedAction(requestParentReportAction);
const canHoldOrUnholdRequest = !isEmptyObject(transaction) && !isSettled && !isApproved && !isDeletedParentAction;

// Only the requestor can delete the request, admins can only edit it.
const isActionOwner =
typeof requestParentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && requestParentReportAction.actorAccountID === session?.accountID;
const canDeleteRequest = isActionOwner && ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) && !isDeletedParentAction;
const isPolicyAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && session?.accountID === moneyRequestReport?.managerID;
const [isHoldMenuVisible, setIsHoldMenuVisible] = useState(false);
const [paymentType, setPaymentType] = useState<PaymentMethodType>();
const [requestType, setRequestType] = useState<ActionHandledType>();
const canAllowSettlement = ReportUtils.hasUpdatedTotal(moneyRequestReport, policy);
const policyType = policy?.type;
const isPayer = ReportUtils.isPayer(session, moneyRequestReport);
const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false);

Expand Down Expand Up @@ -197,22 +188,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
TransactionActions.markAsCash(iouTransactionID, reportID);
}, [requestParentReportAction, transactionThreadReport?.reportID]);

const changeMoneyRequestStatus = () => {
if (!transactionThreadReport) {
return;
}
const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(requestParentReportAction)
? ReportActionsUtils.getOriginalMessage(requestParentReportAction)?.IOUTransactionID ?? '-1'
: '-1';

if (isOnHold) {
IOU.unholdRequest(iouTransactionID, transactionThreadReport.reportID);
} else {
const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams());
Navigation.navigate(ROUTES.MONEY_REQUEST_HOLD_REASON.getRoute(policy?.type ?? CONST.POLICY.TYPE.PERSONAL, iouTransactionID, transactionThreadReport.reportID, activeRoute));
}
};

const getStatusIcon: (src: IconAsset) => React.ReactNode = (src) => (
<Icon
src={src}
Expand Down Expand Up @@ -242,29 +217,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
[chatReport?.isOwnPolicyExpenseChat, policy?.harvesting?.enabled],
);

const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(moneyRequestReport)];
if (canHoldOrUnholdRequest) {
const isRequestIOU = ReportUtils.isIOUReport(chatReport);
const isHoldCreator = ReportUtils.isHoldCreator(transaction, moneyRequestReport?.reportID) && isRequestIOU;
const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(moneyRequestReport);
const canModifyStatus = !isTrackExpenseReport && (isPolicyAdmin || isActionOwner || isApprover);
const isInvoiceReport = ReportUtils.isInvoiceReport(moneyRequestReport);
if (isOnHold && (isHoldCreator || (!isRequestIOU && canModifyStatus)) && !isInvoiceReport) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
text: translate('iou.unholdExpense'),
onSelected: () => changeMoneyRequestStatus(),
});
}
if (!isOnHold && (isRequestIOU || canModifyStatus) && !isScanning && !isInvoiceReport) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
text: translate('iou.hold'),
onSelected: () => changeMoneyRequestStatus(),
});
}
}

useEffect(() => {
if (isLoadingHoldUseExplained) {
return;
Expand All @@ -290,23 +242,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
IOU.dismissHoldUseExplanation();
};

if (isPayer && isSettled && ReportUtils.isExpenseReport(moneyRequestReport)) {
threeDotsMenuItems.push({
icon: Expensicons.Trashcan,
text: translate('iou.cancelPayment'),
onSelected: () => setIsConfirmModalVisible(true),
});
}

// If the report supports adding transactions to it, then it also supports deleting transactions from it.
if (canDeleteRequest && !isEmptyObject(transactionThreadReport)) {
threeDotsMenuItems.push({
icon: Expensicons.Trashcan,
text: translate('reportActionContextMenu.deleteAction', {action: requestParentReportAction}),
onSelected: () => setIsDeleteRequestModalVisible(true),
});
}

useEffect(() => {
if (canDeleteRequest) {
return;
Expand All @@ -327,9 +262,6 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
onBackButtonPress={onBackButtonPress}
// Shows border if no buttons or banners are showing below the header
shouldShowBorderBottom={!isMoreContentShown}
shouldShowThreeDotsButton
threeDotsMenuItems={threeDotsMenuItems}
threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(windowWidth)}
>
{shouldShowSettlementButton && !shouldUseNarrowLayout && (
<View style={styles.pv2}>
Expand Down
100 changes: 2 additions & 98 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import type {ReactNode} from 'react';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import React, {useCallback, useEffect, useState} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as HeaderUtils from '@libs/HeaderUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import variables from '@styles/variables';
import * as IOU from '@userActions/IOU';
import * as TransactionActions from '@userActions/Transaction';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Route} from '@src/ROUTES';
import type {Policy, Report, ReportAction} from '@src/types/onyx';
import type IconAsset from '@src/types/utils/IconAsset';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import Button from './Button';
import ConfirmModal from './ConfirmModal';
import HeaderWithBackButton from './HeaderWithBackButton';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
Expand Down Expand Up @@ -56,67 +52,28 @@ function MoneyRequestHeader({report, parentReportAction, policy, shouldUseNarrow
}`,
);
const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true});
const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult);
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const [shouldShowHoldMenu, setShouldShowHoldMenu] = useState(false);
const isSelfDMTrackExpenseReport = ReportUtils.isTrackExpenseReport(report) && ReportUtils.isSelfDM(parentReport);
const moneyRequestReport = !isSelfDMTrackExpenseReport ? parentReport : undefined;
const isSettled = ReportUtils.isSettled(moneyRequestReport?.reportID);
const isApproved = ReportUtils.isReportApproved(moneyRequestReport);
const isDraft = ReportUtils.isOpenExpenseReport(moneyRequestReport);
const isOnHold = TransactionUtils.isOnHold(transaction);
const isDuplicate = TransactionUtils.isDuplicate(transaction?.transactionID ?? '');
const {isSmallScreenWidth, windowWidth} = useWindowDimensions();
const {isSmallScreenWidth} = useWindowDimensions();

const navigateBackToAfterDelete = useRef<Route>();

// Only the requestor can take delete the request, admins can only edit it.
const isActionOwner = typeof parentReportAction?.actorAccountID === 'number' && typeof session?.accountID === 'number' && parentReportAction.actorAccountID === session?.accountID;
const isPolicyAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
const isApprover = ReportUtils.isMoneyRequestReport(moneyRequestReport) && moneyRequestReport?.managerID !== null && session?.accountID === moneyRequestReport?.managerID;
const hasAllPendingRTERViolations = TransactionUtils.allHavePendingRTERViolation([transaction?.transactionID ?? '-1']);
const shouldShowMarkAsCashButton = isDraft && hasAllPendingRTERViolations;
const deleteTransaction = useCallback(() => {
if (parentReportAction) {
const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(parentReportAction) ? ReportActionsUtils.getOriginalMessage(parentReportAction)?.IOUTransactionID ?? '-1' : '-1';
if (ReportActionsUtils.isTrackExpenseAction(parentReportAction)) {
navigateBackToAfterDelete.current = IOU.deleteTrackExpense(parentReport?.reportID ?? '-1', iouTransactionID, parentReportAction, true);
} else {
navigateBackToAfterDelete.current = IOU.deleteMoneyRequest(iouTransactionID, parentReportAction, true);
}
}

setIsDeleteModalVisible(false);
}, [parentReport?.reportID, parentReportAction, setIsDeleteModalVisible]);

const markAsCash = useCallback(() => {
TransactionActions.markAsCash(transaction?.transactionID ?? '-1', report.reportID);
}, [report.reportID, transaction?.transactionID]);

const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);

const isDeletedParentAction = ReportActionsUtils.isDeletedAction(parentReportAction);
const canHoldOrUnholdRequest = !isSettled && !isApproved && !isDeletedParentAction && !ReportUtils.isArchivedRoom(parentReport);

// If the report supports adding transactions to it, then it also supports deleting transactions from it.
const canDeleteRequest = isActionOwner && (ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) || isSelfDMTrackExpenseReport) && !isDeletedParentAction;

const changeMoneyRequestStatus = () => {
const iouTransactionID = ReportActionsUtils.isMoneyRequestAction(parentReportAction) ? ReportActionsUtils.getOriginalMessage(parentReportAction)?.IOUTransactionID ?? '-1' : '-1';

if (isOnHold) {
IOU.unholdRequest(iouTransactionID, report?.reportID);
} else {
const activeRoute = encodeURIComponent(Navigation.getActiveRouteWithoutParams());
Navigation.navigate(ROUTES.MONEY_REQUEST_HOLD_REASON.getRoute(policy?.type ?? CONST.POLICY.TYPE.PERSONAL, iouTransactionID, report?.reportID, activeRoute));
}
};

const getStatusIcon: (src: IconAsset) => ReactNode = (src) => (
<Icon
src={src}
Expand Down Expand Up @@ -144,36 +101,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, shouldUseNarrow

const statusBarProps = getStatusBarProps();

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

setIsDeleteModalVisible(false);
}, [canDeleteRequest]);

const threeDotsMenuItems = [HeaderUtils.getPinMenuItem(report)];
if (canHoldOrUnholdRequest) {
const isRequestIOU = parentReport?.type === 'iou';
const isHoldCreator = ReportUtils.isHoldCreator(transaction, report?.reportID) && isRequestIOU;
const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(report);
const canModifyStatus = !isTrackExpenseReport && (isPolicyAdmin || isActionOwner || isApprover);
if (isOnHold && !isDuplicate && (isHoldCreator || (!isRequestIOU && canModifyStatus))) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
text: translate('iou.unholdExpense'),
onSelected: () => changeMoneyRequestStatus(),
});
}
if (!isOnHold && (isRequestIOU || canModifyStatus) && !isScanning) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
text: translate('iou.hold'),
onSelected: () => changeMoneyRequestStatus(),
});
}
}

useEffect(() => {
if (isLoadingHoldUseExplained) {
return;
Expand All @@ -199,14 +126,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, shouldUseNarrow
IOU.dismissHoldUseExplanation();
};

if (canDeleteRequest) {
threeDotsMenuItems.push({
icon: Expensicons.Trashcan,
text: translate('reportActionContextMenu.deleteAction', {action: parentReportAction}),
onSelected: () => setIsDeleteModalVisible(true),
});
}

return (
<>
<View style={[styles.pl0]}>
Expand All @@ -215,9 +134,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, shouldUseNarrow
shouldShowReportAvatarWithDisplay
shouldEnableDetailPageNavigation
shouldShowPinButton={false}
shouldShowThreeDotsButton
threeDotsMenuItems={threeDotsMenuItems}
threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(windowWidth)}
report={{
...report,
ownerAccountID: parentReport?.ownerAccountID,
Expand Down Expand Up @@ -281,18 +197,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, shouldUseNarrow
</View>
)}
</View>
<ConfirmModal
title={translate('iou.deleteExpense')}
isVisible={isDeleteModalVisible}
onConfirm={deleteTransaction}
onCancel={() => setIsDeleteModalVisible(false)}
onModalHide={() => ReportUtils.navigateBackAfterDeleteTransaction(navigateBackToAfterDelete.current)}
prompt={translate('iou.deleteConfirmation')}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
shouldEnableNewFocusManagement
/>
{isSmallScreenWidth && shouldShowHoldMenu && (
<ProcessMoneyRequestHoldMenu
Copy link
Member

Choose a reason for hiding this comment

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

Why did we remove this menu? It shows information about held transactions when users open one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, I've readded it.

onClose={handleHoldRequestClose}
Expand Down
16 changes: 16 additions & 0 deletions src/pages/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as ReportUtils from '@libs/ReportUtils';
import * as UserUtils from '@libs/UserUtils';
import * as ValidationUtils from '@libs/ValidationUtils';
import type {ProfileNavigatorParamList} from '@navigation/types';
import * as LinkActions from '@userActions/Link';
import * as PersonalDetailsActions from '@userActions/PersonalDetails';
import * as ReportActions from '@userActions/Report';
import * as SessionActions from '@userActions/Session';
Expand Down Expand Up @@ -79,6 +80,9 @@ function ProfilePage({route}: ProfilePageProps) {
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_METADATA);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [guideCalendarLink] = useOnyx(ONYXKEYS.ACCOUNT, {
selector: (account) => account?.guideCalendarLink,
});

const reportKey = useMemo(() => {
const accountID = Number(route.params?.accountID ?? -1);
Expand Down Expand Up @@ -175,6 +179,8 @@ function ProfilePage({route}: ProfilePageProps) {
return result;
}, [accountID, isCurrentUser, loginParams, report]);

const isConcierge = ReportUtils.isConciergeChatReport(report);

return (
<ScreenWrapper testID={ProfilePage.displayName}>
<FullPageNotFoundView shouldShow={shouldShowBlockingView}>
Expand Down Expand Up @@ -276,6 +282,16 @@ function ProfilePage({route}: ProfilePageProps) {
brickRoadIndicator={ReportActions.hasErrorInPrivateNotes(report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
/>
)}
{isConcierge && guideCalendarLink && (
<MenuItem
title={translate('videoChatButtonAndMenu.tooltip')}
icon={Expensicons.Phone}
isAnonymousAction={false}
onPress={SessionActions.checkIfActionIsAllowed(() => {
LinkActions.openExternalLink(guideCalendarLink);
})}
/>
)}
</ScrollView>
{!hasAvatar && isLoading && <FullScreenLoadingIndicator style={styles.flex1} />}
</View>
Expand Down
Loading
Loading