Skip to content
Merged
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
17 changes: 0 additions & 17 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1158,27 +1158,10 @@ const CONST = {
APPROVE: 'approve',
PAY: 'pay',
EXPORT_TO_ACCOUNTING: 'exportToAccounting',
REMOVE_HOLD: 'removeHold',
MARK_AS_CASH: 'markAsCash',
},
TRANSACTION_PRIMARY_ACTIONS: {
REMOVE_HOLD: 'removeHold',
REVIEW_DUPLICATES: 'reviewDuplicates',
MARK_AS_CASH: 'markAsCash',
},
REPORT_PREVIEW_ACTIONS: {
VIEW: 'view',
REVIEW: 'review',
SUBMIT: 'submit',
APPROVE: 'approve',
PAY: 'pay',
EXPORT_TO_ACCOUNTING: 'exportToAccounting',
},
TRANSACTION_SECONDARY_ACTIONS: {
HOLD: 'hold',
VIEW_DETAILS: 'viewDetails',
DELETE: 'delete',
},
ACTIONS: {
LIMIT: 50,
// OldDot Actions render getMessage from Web-Expensify/lib/Report/Action PHP files via getMessageOfOldDotReportAction in ReportActionsUtils.ts
Expand Down
19 changes: 11 additions & 8 deletions src/components/AvatarWithDisplayName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import ROUTES from '@src/ROUTES';
import type {Policy, Report} from '@src/types/onyx';
import type {Icon} from '@src/types/onyx/OnyxCommon';
import {getButtonRole} from './Button/utils';
import CaretWrapper from './CaretWrapper';
import DisplayNames from './DisplayNames';
import {FallbackAvatar} from './Icon/Expensicons';
import MultipleAvatars from './MultipleAvatars';
Expand Down Expand Up @@ -154,14 +155,16 @@ function AvatarWithDisplayName({policy, report, isAnonymous = false, size = CONS
</View>
</PressableWithoutFeedback>
<View style={[styles.flex1, styles.flexColumn]}>
<DisplayNames
fullTitle={title}
displayNamesWithTooltips={displayNamesWithTooltips}
tooltipEnabled
numberOfLines={1}
textStyles={[isAnonymous ? styles.headerAnonymousFooter : styles.headerText, styles.pre]}
shouldUseFullTitle={isMoneyRequestOrReport || isAnonymous}
/>
<CaretWrapper>
<DisplayNames
fullTitle={title}
displayNamesWithTooltips={displayNamesWithTooltips}
tooltipEnabled
numberOfLines={1}
textStyles={[isAnonymous ? styles.headerAnonymousFooter : styles.headerText, styles.pre]}
shouldUseFullTitle={isMoneyRequestOrReport || isAnonymous}
/>
</CaretWrapper>
{Object.keys(parentNavigationSubtitleData).length > 0 && (
<ParentNavigationSubtitle
parentNavigationSubtitleData={parentNavigationSubtitleData}
Expand Down
583 changes: 228 additions & 355 deletions src/components/MoneyReportHeader.tsx

Large diffs are not rendered by default.

198 changes: 47 additions & 151 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import {useRoute} from '@react-navigation/native';
import type {ReactNode} from 'react';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import React, {useCallback, useEffect} from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useTransactionViolations from '@hooks/useTransactionViolations';
import {deleteMoneyRequest, getNavigationUrlOnMoneyRequestDelete} from '@libs/actions/IOU';
import Navigation from '@libs/Navigation/Navigation';
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {getTransactionThreadPrimaryAction} from '@libs/ReportPrimaryActionUtils';
import {getSecondaryTransactionThreadActions} from '@libs/ReportSecondaryActionUtils';
import {changeMoneyRequestHoldStatus, navigateToDetailsPage} from '@libs/ReportUtils';
import {
checkIfShouldShowMarkAsCashButton,
hasPendingRTERViolation as hasPendingRTERViolationTransactionUtils,
hasReceipt,
isDuplicate as isDuplicateTransactionUtils,
Expand All @@ -37,10 +33,6 @@ import type IconAsset from '@src/types/utils/IconAsset';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import BrokenConnectionDescription from './BrokenConnectionDescription';
import Button from './Button';
import ButtonWithDropdownMenu from './ButtonWithDropdownMenu';
import type {DropdownOption} from './ButtonWithDropdownMenu/types';
import ConfirmModal from './ConfirmModal';
import DecisionModal from './DecisionModal';
import HeaderWithBackButton from './HeaderWithBackButton';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';
Expand Down Expand Up @@ -76,8 +68,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
);
const transactionViolations = useTransactionViolations(transaction?.transactionID);

const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const [downloadErrorModalVisible, setDownloadErrorModalVisible] = useState(false);
const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {initialValue: true});
const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA);
const isLoadingHoldUseExplained = isLoadingOnyxValue(dismissedHoldUseExplanationResult);
Expand All @@ -95,6 +85,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
const hasPendingRTERViolation = hasPendingRTERViolationTransactionUtils(transactionViolations);

const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(parentReport, policy, transactionViolations);
const shouldShowMarkAsCashButton = checkIfShouldShowMarkAsCashButton(hasPendingRTERViolation, shouldShowBrokenConnectionViolation, parentReport, policy);

const markAsCash = useCallback(() => {
markAsCashAction(transaction?.transactionID, reportID);
Expand Down Expand Up @@ -153,89 +144,12 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
Navigation.navigate(ROUTES.PROCESS_MONEY_REQUEST_HOLD.getRoute(Navigation.getReportRHPActiveRoute()));
}, [dismissedHoldUseExplanation, isLoadingHoldUseExplained, isOnHold]);

const primaryAction = useMemo(() => {
if (!report || !parentReport || !transaction) {
return '';
}
return getTransactionThreadPrimaryAction(report, parentReport, transaction, transactionViolations, policy);
}, [parentReport, policy, report, transaction, transactionViolations]);

const primaryActionImplementation = {
[CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REMOVE_HOLD]: (
<Button
success
text={translate('iou.unhold')}
onPress={() => {
changeMoneyRequestHoldStatus(parentReportAction);
}}
/>
),
[CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REVIEW_DUPLICATES]: (
<Button
success
text={translate('iou.reviewDuplicates')}
onPress={() => {
if (!reportID) {
return;
}
Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute()));
}}
/>
),
[CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.MARK_AS_CASH]: (
<Button
success
text={translate('iou.markAsCash')}
onPress={markAsCash}
/>
),
};

const secondaryActions = useMemo(() => {
if (!parentReport || !transaction) {
return [];
}
return getSecondaryTransactionThreadActions(parentReport, transaction);
}, [parentReport, transaction]);

const secondaryActionsImplementation: Record<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>, DropdownOption<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>>> = {
[CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD]: {
text: translate('iou.hold'),
icon: Expensicons.Stopwatch,
value: CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD,
onSelected: () => {
if (!parentReportAction) {
throw new Error('Parent action does not exist');
}

changeMoneyRequestHoldStatus(parentReportAction);
},
},
[CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]: {
value: CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS,
text: translate('iou.viewDetails'),
icon: Expensicons.Info,
onSelected: () => {
navigateToDetailsPage(report, Navigation.getReportRHPActiveRoute());
},
},
[CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.DELETE]: {
text: translate('common.delete'),
icon: Expensicons.Trashcan,
value: CONST.REPORT.SECONDARY_ACTIONS.DELETE,
onSelected: () => {
setIsDeleteModalVisible(true);
},
},
};

const applicableSecondaryActions = secondaryActions.map((action) => secondaryActionsImplementation[action]);

return (
<View style={[styles.pl0, styles.borderBottom]}>
<HeaderWithBackButton
shouldShowBorderBottom={false}
shouldShowReportAvatarWithDisplay
shouldEnableDetailPageNavigation
shouldShowPinButton={false}
report={
reportID
Expand All @@ -250,39 +164,53 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
shouldShowBackButton={shouldUseNarrowLayout}
shouldDisplaySearchRouter={shouldDisplaySearchRouter}
onBackButtonPress={onBackButtonPress}
shouldEnableDetailPageNavigation
>
{!shouldUseNarrowLayout && (
<View style={[styles.flexRow, styles.gap2]}>
{!!primaryAction && primaryActionImplementation[primaryAction]}
{!!applicableSecondaryActions.length && (
<ButtonWithDropdownMenu
success={false}
onPress={() => {}}
shouldAlwaysShowDropdownMenu
customText={translate('common.more')}
options={applicableSecondaryActions}
isSplitButton={false}
/>
)}
</View>
{shouldShowMarkAsCashButton && !shouldUseNarrowLayout && (
<Button
success
text={translate('iou.markAsCash')}
style={[styles.p0]}
onPress={markAsCash}
/>
)}
{isDuplicate && !shouldUseNarrowLayout && (
<Button
success
text={translate('iou.reviewDuplicates')}
style={[styles.p0, styles.ml2]}
onPress={() => {
if (!reportID) {
return;
}
Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute()));
}}
/>
)}
{shouldDisplayTransactionNavigation && <MoneyRequestReportTransactionsNavigation currentReportID={reportID} />}
</HeaderWithBackButton>
{shouldUseNarrowLayout && (
<View style={[styles.flexRow, styles.gap2, styles.pb3, styles.ph5, styles.w100, styles.alignItemsCenter, styles.justifyContentCenter]}>
{!!primaryAction && <View style={[styles.flexGrow4]}>{primaryActionImplementation[primaryAction]}</View>}
{!!applicableSecondaryActions.length && (
<ButtonWithDropdownMenu
success={false}
onPress={() => {}}
shouldAlwaysShowDropdownMenu
customText={translate('common.more')}
options={applicableSecondaryActions}
isSplitButton={false}
wrapperStyle={[!primaryAction && styles.flexGrow4]}
/>
)}
{shouldShowMarkAsCashButton && shouldUseNarrowLayout && (
<View style={[styles.ph5, styles.pb3]}>
<Button
success
text={translate('iou.markAsCash')}
style={[styles.w100, styles.pr0]}
onPress={markAsCash}
/>
</View>
)}
{isDuplicate && shouldUseNarrowLayout && (
<View style={[styles.ph5, styles.pb3]}>
<Button
success
text={translate('iou.reviewDuplicates')}
style={[styles.w100, styles.pr0]}
onPress={() => {
if (!reportID) {
return;
}
Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute()));
}}
/>
</View>
)}
{!!statusBarProps && (
Expand All @@ -294,38 +222,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
</View>
)}
<LoadingBar shouldShow={(isLoadingReportData && shouldUseNarrowLayout) ?? false} />
<DecisionModal
title={translate('common.downloadFailedTitle')}
prompt={translate('common.downloadFailedDescription')}
isSmallScreenWidth={isSmallScreenWidth}
onSecondOptionSubmit={() => setDownloadErrorModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
isVisible={downloadErrorModalVisible}
onClose={() => setDownloadErrorModalVisible(false)}
/>
<ConfirmModal
title={translate('iou.deleteExpense', {count: 1})}
isVisible={isDeleteModalVisible}
onConfirm={() => {
setIsDeleteModalVisible(false);
if (!parentReportAction || !transaction) {
throw new Error('Data missing');
}

deleteMoneyRequest(transaction?.transactionID, parentReportAction);

const goBackRoute = getNavigationUrlOnMoneyRequestDelete(transaction.transactionID, parentReportAction, true);
if (goBackRoute) {
Navigation.navigate(goBackRoute);
}
}}
onCancel={() => setIsDeleteModalVisible(false)}
prompt={translate('iou.deleteConfirmation', {count: 1})}
confirmText={translate('common.delete')}
cancelText={translate('common.cancel')}
danger
shouldEnableNewFocusManagement
/>
</View>
);
}
Expand Down
28 changes: 14 additions & 14 deletions src/components/ReportActionItem/ExportWithDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import ConfirmModal from '@components/ConfirmModal';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {savePreferredExportMethod as savePreferredExportMethodUtils} from '@libs/actions/Policy/Policy';
import {exportToIntegration, markAsManuallyExported} from '@libs/actions/Report';
import {hasIntegrationAutoSync as hasIntegrationAutoSyncUtils} from '@libs/PolicyUtils';
import {canBeExported as canBeExportedUtils, getIntegrationIcon, isExported as isExportedUtils} from '@libs/ReportUtils';
import * as PolicyActions from '@libs/actions/Policy/Policy';
import * as ReportActions from '@libs/actions/Report';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import type {ExportType} from '@pages/home/report/ReportDetailsExportPage';
import variables from '@styles/variables';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -51,10 +51,10 @@ function ExportWithDropdownMenu({
const [exportMethods] = useOnyx(ONYXKEYS.LAST_EXPORT_METHOD);
const [reportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`);

const iconToDisplay = getIntegrationIcon(connectionName);
const canBeExported = canBeExportedUtils(report);
const isExported = isExportedUtils(reportActions);
const hasIntegrationAutoSync = hasIntegrationAutoSyncUtils(policy, connectionName);
const iconToDisplay = ReportUtils.getIntegrationIcon(connectionName);
const canBeExported = ReportUtils.canBeExported(report);
const isExported = ReportUtils.isExported(reportActions);
const hasIntegrationAutoSync = PolicyUtils.hasIntegrationAutoSync(policy, connectionName);
const flattenedWrapperStyle = StyleSheet.flatten([styles.flex1, wrapperStyle]);

const dropdownOptions: Array<DropdownOption<ReportExportType>> = useMemo(() => {
Expand All @@ -74,7 +74,7 @@ function ExportWithDropdownMenu({
},
{
value: CONST.REPORT.EXPORT_OPTIONS.MARK_AS_EXPORTED,
text: translate('workspace.common.markAsEntered'),
text: translate('workspace.common.markAsExported'),
...optionTemplate,
},
];
Expand All @@ -93,17 +93,17 @@ function ExportWithDropdownMenu({
return;
}
if (modalStatus === CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION) {
exportToIntegration(reportID, connectionName);
ReportActions.exportToIntegration(reportID, connectionName);
} else if (modalStatus === CONST.REPORT.EXPORT_OPTIONS.MARK_AS_EXPORTED) {
markAsManuallyExported(reportID, connectionName);
ReportActions.markAsManuallyExported(reportID, connectionName);
}
}, [connectionName, modalStatus, reportID]);

const savePreferredExportMethod = (value: ReportExportType) => {
if (!report?.policyID) {
return;
}
savePreferredExportMethodUtils(report?.policyID, value);
PolicyActions.savePreferredExportMethod(report?.policyID, value);
};

return (
Expand All @@ -122,9 +122,9 @@ function ExportWithDropdownMenu({
return;
}
if (value === CONST.REPORT.EXPORT_OPTIONS.EXPORT_TO_INTEGRATION) {
exportToIntegration(reportID, connectionName);
ReportActions.exportToIntegration(reportID, connectionName);
} else if (value === CONST.REPORT.EXPORT_OPTIONS.MARK_AS_EXPORTED) {
markAsManuallyExported(reportID, connectionName);
ReportActions.markAsManuallyExported(reportID, connectionName);
}
}}
onOptionSelected={({value}) => savePreferredExportMethod(value)}
Expand Down
Loading