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
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import MoneyRequestReportTransactionList from './MoneyRequestReportTransactionList';
import MoneyRequestViewReportFields from './MoneyRequestViewReportFields';
import SearchMoneyRequestReportEmptyState from './SearchMoneyRequestReportEmptyState';

/**
Expand All @@ -54,6 +55,9 @@ type MoneyRequestReportListProps = {
/** The report */
report: OnyxTypes.Report;

/** Policy that the report belongs to */
policy: OnyxEntry<OnyxTypes.Policy>;

/** Array of report actions for this report */
reportActions?: OnyxTypes.ReportAction[];

Expand All @@ -74,7 +78,7 @@ function getParentReportAction(parentReportActions: OnyxEntry<OnyxTypes.ReportAc
return parentReportActions[parentReportActionID];
}

function MoneyRequestReportActionsList({report, reportActions = [], transactions = [], hasNewerActions, hasOlderActions}: MoneyRequestReportListProps) {
function MoneyRequestReportActionsList({report, policy, reportActions = [], transactions = [], hasNewerActions, hasOlderActions}: MoneyRequestReportListProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {preferredLocale} = useLocalize();
Expand All @@ -85,14 +89,15 @@ function MoneyRequestReportActionsList({report, reportActions = [], transactions

const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {
canEvict: false,
canBeMissing: true,
selector: (parentReportActions) => getParentReportAction(parentReportActions, report?.parentReportActionID),
});

const mostRecentIOUReportActionID = useMemo(() => getMostRecentIOURequestActionID(reportActions), [reportActions]);
const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], false);
const firstVisibleReportActionID = useMemo(() => getFirstVisibleReportActionID(reportActions, isOffline), [reportActions, isOffline]);
const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID ?? CONST.DEFAULT_NUMBER_ID}`);
const [currentUserAccountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID});
const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true});
const [currentUserAccountID] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false, selector: (session) => session?.accountID});

const canPerformWriteAction = canUserPerformWriteAction(report);
const [isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible] = useState(false);
Expand Down Expand Up @@ -399,8 +404,15 @@ function MoneyRequestReportActionsList({report, reportActions = [], transactions
isActive={isFloatingMessageCounterVisible}
onClick={scrollToBottomAndMarkReportAsRead}
/>

{isEmpty(visibleReportActions) && isEmpty(transactions) ? (
<SearchMoneyRequestReportEmptyState />
<>
<MoneyRequestViewReportFields
Comment thread
borys3kk marked this conversation as resolved.
report={report}
policy={policy}
/>
<SearchMoneyRequestReportEmptyState />
</>
) : (
<FlatList
initialNumToRender={INITIAL_NUM_TO_RENDER}
Expand All @@ -415,12 +427,18 @@ function MoneyRequestReportActionsList({report, reportActions = [], transactions
onStartReached={onStartReached}
onStartReachedThreshold={0.75}
ListHeaderComponent={
<MoneyRequestReportTransactionList
report={report}
transactions={transactions}
reportActions={reportActions}
hasComments={reportHasComments}
/>
<>
<MoneyRequestViewReportFields
report={report}
policy={policy}
/>
<MoneyRequestReportTransactionList
report={report}
transactions={transactions}
reportActions={reportActions}
hasComments={reportHasComments}
/>
</>
}
keyboardShouldPersistTaps="handled"
onScroll={trackVerticalScrolling}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ function MoneyRequestReportTransactionList({report, transactions, reportActions,
const pressableStyle = [styles.overflowHidden];

const listHorizontalPadding = styles.ph5;

return !isEmpty(transactions) ? (
<>
{!displayNarrowVersion && (
Expand Down Expand Up @@ -178,12 +177,12 @@ function MoneyRequestReportTransactionList({report, transactions, reportActions,
{sortedTransactions.map((transaction) => {
return (
<PressableWithFeedback
key={transaction.transactionID}
onPress={(e) => {
if (isMouseDownOnInput) {
e?.stopPropagation();
return;
}

navigateToTransaction(transaction);
}}
accessibilityLabel={translate('iou.viewDetails')}
Expand All @@ -194,7 +193,6 @@ function MoneyRequestReportTransactionList({report, transactions, reportActions,
id={transaction.transactionID}
style={[pressableStyle]}
onMouseLeave={handleMouseLeave}
key={transaction.transactionID}
>
<TransactionItemRow
transactionItem={transaction}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe

const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {
canEvict: false,
selector: (parentReportActions) => getParentReportAction(parentReportActions, report?.parentReportActionID),
canBeMissing: true,
selector: (parentReportActions) => getParentReportAction(parentReportActions, report?.parentReportActionID),
});

const lastReportAction = [...reportActions, parentReportAction].find((action) => canEditReportAction(action) && !isMoneyRequestAction(action));
Expand Down Expand Up @@ -202,6 +202,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
) : (
<MoneyRequestReportActionsList
report={report}
policy={policy}
transactions={transactions}
reportActions={reportActions}
hasOlderActions={hasOlderActions}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import {Str} from 'expensify-common';
import React, {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import useThemeStyles from '@hooks/useThemeStyles';
import {clearReportFieldKeyErrors} from '@libs/actions/Report';
import Navigation from '@libs/Navigation/Navigation';
import {
getAvailableReportFields,
getFieldViolation,
getFieldViolationTranslation,
getReportFieldKey,
isInvoiceReport as isInvoiceReportUtils,
isPaidGroupPolicyExpenseReport as isPaidGroupPolicyExpenseReportUtils,
isReportFieldDisabled,
isReportFieldOfTypeTitle,
} from '@libs/ReportUtils';
import type {ThemeStyles} from '@styles/index';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {Policy, PolicyReportField, Report, ReportViolationName} from '@src/types/onyx';
import type {PendingAction} from '@src/types/onyx/OnyxCommon';

type MoneyRequestViewReportFieldsProps = {
/** The report currently being looked at */
report: OnyxEntry<Report>;

/** Policy that the report belongs to */
policy: OnyxEntry<Policy>;

/** Indicates whether the IOU report is a combined report */
isCombinedReport?: boolean;

/** Indicates whether we have any pending actions from parent component */
pendingAction?: PendingAction;
Comment thread
borys3kk marked this conversation as resolved.
};

type EnrichedPolicyReportField = {
fieldValue: string;
isFieldDisabled: boolean;
fieldKey: string;
violation: ReportViolationName | undefined;
violationTranslation: string;
} & PolicyReportField;

function ReportFieldView(reportField: EnrichedPolicyReportField, report: OnyxEntry<Report>, styles: ThemeStyles, pendingAction?: PendingAction) {
return (
<OfflineWithFeedback
// Need to return undefined when we have pendingAction to avoid the duplicate pending action
pendingAction={pendingAction ? undefined : report?.pendingFields?.[reportField.fieldKey as keyof typeof report.pendingFields]}
errorRowStyles={styles.ph5}
key={`menuItem-${reportField.fieldKey}`}
onClose={() => clearReportFieldKeyErrors(report?.reportID, reportField.fieldKey)}
>
<MenuItemWithTopDescription
description={Str.UCFirst(reportField.name)}
title={reportField.fieldValue}
onPress={() => {
Navigation.navigate(ROUTES.EDIT_REPORT_FIELD_REQUEST.getRoute(report?.reportID, report?.policyID, reportField.fieldID, Navigation.getActiveRoute()));
}}
shouldShowRightIcon
disabled={reportField.isFieldDisabled}
Comment on lines +64 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

caused this minor bug: #62035

wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
shouldGreyOutWhenDisabled={false}
numberOfLinesTitle={0}
interactive={!reportField.isFieldDisabled}
shouldStackHorizontally={false}
onSecondaryInteraction={() => {}}
titleWithTooltips={[]}
brickRoadIndicator={reportField.violation ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
errorText={reportField.violationTranslation}
/>
</OfflineWithFeedback>
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
}

function MoneyRequestViewReportFields({report, policy, isCombinedReport = false, pendingAction}: MoneyRequestViewReportFieldsProps) {
const styles = useThemeStyles();

const [violations] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_VIOLATIONS}${report?.reportID}`, {canBeMissing: true});

const shouldHideSingleReportField = (reportField: PolicyReportField) => {
const fieldValue = reportField.value ?? reportField.defaultValue;
const hasEnableOption = reportField.type !== CONST.REPORT_FIELD_TYPES.LIST || reportField.disabledOptions.some((option) => !option);

return isReportFieldOfTypeTitle(reportField) || (!fieldValue && !hasEnableOption);
};

const sortedPolicyReportFields = useMemo<EnrichedPolicyReportField[]>((): EnrichedPolicyReportField[] => {
const fields = getAvailableReportFields(report, Object.values(policy?.fieldList ?? {}));
return fields
.filter((field) => field.target === report?.type)
Comment thread
borys3kk marked this conversation as resolved.
.filter((reportField) => !shouldHideSingleReportField(reportField))
.sort(({orderWeight: firstOrderWeight}, {orderWeight: secondOrderWeight}) => firstOrderWeight - secondOrderWeight)
.map((field): EnrichedPolicyReportField => {
const fieldValue = field.value ?? field.defaultValue;
const isFieldDisabled = isReportFieldDisabled(report, field, policy);
const fieldKey = getReportFieldKey(field.fieldID);

const violation = getFieldViolation(violations, field);
const violationTranslation = getFieldViolationTranslation(field, violation);

return {
...field,
fieldValue,
isFieldDisabled,
fieldKey,
violation,
violationTranslation,
};
});
}, [policy, report, violations]);

const enabledReportFields = sortedPolicyReportFields.filter((reportField) => !isReportFieldDisabled(report, reportField, policy));
const isOnlyTitleFieldEnabled = enabledReportFields.length === 1 && isReportFieldOfTypeTitle(enabledReportFields.at(0));
const isPaidGroupPolicyExpenseReport = isPaidGroupPolicyExpenseReportUtils(report);
const isInvoiceReport = isInvoiceReportUtils(report);
Comment thread
borys3kk marked this conversation as resolved.

const shouldDisplayReportFields = (isPaidGroupPolicyExpenseReport || isInvoiceReport) && policy?.areReportFieldsEnabled && (!isOnlyTitleFieldEnabled || !isCombinedReport);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if in general it would be cleaner to refactor this method to do early returns so we can stop the execution immediately we know the report fields should not be displayed, for example if policy?.areReportFieldsEnabled is false


return (
shouldDisplayReportFields &&
sortedPolicyReportFields.map((reportField) => {
return ReportFieldView(reportField, report, styles, pendingAction);
})
);
}
MoneyRequestViewReportFields.displayName = 'MoneyRequestViewReportFields';

export default MoneyRequestViewReportFields;