Skip to content

Commit

Permalink
Merge pull request Expensify#36934 from Expensify/nikki-one-transacti…
Browse files Browse the repository at this point in the history
…on-report-view

Add One Transaction Report View
  • Loading branch information
mountiny authored Mar 29, 2024
2 parents 1d191cd + 1aac6e0 commit c9b5901
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 51 deletions.
10 changes: 7 additions & 3 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ type MoneyRequestViewPropsWithoutTransaction = MoneyRequestViewOnyxPropsWithoutT

/** Whether we should display the horizontal rule below the component */
shouldShowHorizontalRule: boolean;

/** Whether we should display the animated banner above the component */
shouldShowAnimatedBackground: boolean;
};

type MoneyRequestViewProps = MoneyRequestViewTransactionOnyxProps & MoneyRequestViewPropsWithoutTransaction;
Expand All @@ -84,6 +87,7 @@ function MoneyRequestView({
policyTagList,
policy,
transactionViolations,
shouldShowAnimatedBackground,
}: MoneyRequestViewProps) {
const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -237,9 +241,9 @@ function MoneyRequestView({
);

return (
<View style={[StyleUtils.getReportWelcomeContainerStyle(isSmallScreenWidth)]}>
<AnimatedEmptyStateBackground />
<View style={[StyleUtils.getReportWelcomeTopMarginStyle(isSmallScreenWidth)]}>
<View style={[StyleUtils.getReportWelcomeContainerStyle(isSmallScreenWidth, true, shouldShowAnimatedBackground)]}>
{shouldShowAnimatedBackground && <AnimatedEmptyStateBackground />}
<View style={shouldShowAnimatedBackground && [StyleUtils.getReportWelcomeTopMarginStyle(isSmallScreenWidth, true)]}>
{/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */}
{(showMapAsImage || hasReceipt) && (
<OfflineWithFeedback
Expand Down
30 changes: 30 additions & 0 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,35 @@ function isTransactionThread(parentReportAction: OnyxEntry<ReportAction> | Empty
);
}

/**
* Returns the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions with a childReportID. Returns a reportID if there is exactly one transaction thread for the report, and null otherwise.
*/
function getOneTransactionThreadReportID(reportActions: OnyxEntry<ReportActions> | ReportAction[]): string | null {
const reportActionsArray = Object.values(reportActions ?? {});

if (!reportActionsArray.length) {
return null;
}

// Get all IOU report actions for the report.
const iouRequestTypes: Array<ValueOf<typeof CONST.IOU.REPORT_ACTION_TYPE>> = [CONST.IOU.REPORT_ACTION_TYPE.CREATE, CONST.IOU.REPORT_ACTION_TYPE.SPLIT, CONST.IOU.REPORT_ACTION_TYPE.PAY];
const iouRequestActions = reportActionsArray.filter(
(action) =>
action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU &&
(iouRequestTypes.includes(action.originalMessage.type) ?? []) &&
action.childReportID &&
action.originalMessage.IOUTransactionID,
);

// If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report
if (!iouRequestActions.length || iouRequestActions.length > 1) {
return null;
}

// Ensure we have a childReportID associated with the IOU report action
return iouRequestActions[0].childReportID ?? null;
}

/**
* Sort an array of reportActions by their created timestamp first, and reportActionID second
* This gives us a stable order even in the case of multiple reportActions created on the same millisecond
Expand Down Expand Up @@ -1030,6 +1059,7 @@ function getReportActionMessageText(reportAction: OnyxEntry<ReportAction> | Empt

export {
extractLinksFromMessageHtml,
getOneTransactionThreadReportID,
getAllReportActions,
getIOUReportIDFromReportActionPreview,
getLastClosedReportAction,
Expand Down
32 changes: 32 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,23 @@ function isMoneyRequestReport(reportOrID: OnyxEntry<Report> | EmptyObject | stri
return isIOUReport(report) || isExpenseReport(report);
}

/**
* Checks if a report has only one transaction associated with it
*/
function isOneTransactionReport(reportID: string): boolean {
const reportActions = reportActionsByReport?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`] ?? ([] as ReportAction[]);
return ReportActionsUtils.getOneTransactionThreadReportID(reportActions) !== null;
}

/**
* Checks if a report is a transaction thread associated with a report that has only one transaction
*/
function isOneTransactionThread(reportID: string, parentReportID: string): boolean {
const parentReportActions = reportActionsByReport?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`] ?? ([] as ReportAction[]);
const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(parentReportActions);
return reportID === transactionThreadReportID;
}

/**
* Should return true only for personal 1:1 report
*
Expand Down Expand Up @@ -1794,6 +1811,11 @@ function getIcons(
};
const isManager = currentUserAccountID === report?.managerID;

// For one transaction IOUs, display a simplified report icon
if (isOneTransactionReport(report?.reportID ?? '0')) {
return [ownerIcon];
}

return isManager ? [managerIcon, ownerIcon] : [ownerIcon, managerIcon];
}

Expand Down Expand Up @@ -4397,6 +4419,11 @@ function shouldReportBeInOptionList({
return false;
}

// If this is a transaction thread associated with a report that only has one transaction, omit it
if (isOneTransactionThread(report.reportID, report.parentReportID ?? '0')) {
return false;
}

// Include the currently viewed report. If we excluded the currently viewed report, then there
// would be no way to highlight it in the options list and it would be confusing to users because they lose
// a sense of context.
Expand Down Expand Up @@ -4867,6 +4894,10 @@ function shouldReportShowSubscript(report: OnyxEntry<Report>): boolean {
return true;
}

if (isExpenseReport(report) && isOneTransactionReport(report?.reportID ?? '')) {
return true;
}

if (isWorkspaceTaskReport(report)) {
return true;
}
Expand Down Expand Up @@ -5778,6 +5809,7 @@ export {
hasSingleParticipant,
getReportRecipientAccountIDs,
isOneOnOneChat,
isOneTransactionThread,
isPayer,
goBackToDetailsPage,
getTransactionReportName,
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ function ReportScreen({
isLoadingNewerReportActions={reportMetadata?.isLoadingNewerReportActions}
isLoadingOlderReportActions={reportMetadata?.isLoadingOlderReportActions}
isReadyForCommentLinking={!shouldShowSkeleton}
transactionThreadReportID={ReportActionsUtils.getOneTransactionThreadReportID(reportActions ?? [])}
/>
)}

Expand Down
56 changes: 50 additions & 6 deletions src/pages/home/report/ReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import SelectionScraper from '@libs/SelectionScraper';
import * as TransactionUtils from '@libs/TransactionUtils';
import {ReactionListContext} from '@pages/home/ReportScreenContext';
import * as BankAccounts from '@userActions/BankAccounts';
import * as EmojiPickerAction from '@userActions/EmojiPickerAction';
Expand Down Expand Up @@ -100,12 +101,22 @@ type ReportActionItemOnyxProps = {

/** The policy which the user has access to and which the report is tied to */
policy: OnyxEntry<OnyxTypes.Policy>;

/** Transaction associated with this report, if any */
transaction: OnyxEntry<OnyxTypes.Transaction>;
};

type ReportActionItemProps = {
/** Report for this action */
report: OnyxTypes.Report;

/** The transaction thread report associated with the report for this action, if any */
transactionThreadReport: OnyxEntry<OnyxTypes.Report>;

/** Array of report actions for the report for this action */
// eslint-disable-next-line react/no-unused-prop-types
reportActions: OnyxTypes.ReportAction[];

/** Report action belonging to the report's parent */
parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;

Expand Down Expand Up @@ -142,6 +153,7 @@ const isIOUReport = (actionObj: OnyxEntry<OnyxTypes.ReportAction>): actionObj is
function ReportActionItem({
action,
report,
transactionThreadReport,
linkedReportActionID,
displayAsGroup,
emojiReactions,
Expand All @@ -155,6 +167,7 @@ function ReportActionItem({
shouldHideThreadDividerLine = false,
shouldShowSubscriptAvatar = false,
policy,
transaction,
onPress = undefined,
}: ReportActionItemProps) {
const {translate} = useLocalize();
Expand All @@ -180,7 +193,7 @@ function ReportActionItem({
const originalReportID = ReportUtils.getOriginalReportID(report.reportID, action);
const originalReport = report.reportID === originalReportID ? report : ReportUtils.getReport(originalReportID);
const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID;

const transactionCurrency = TransactionUtils.getCurrency(transaction);
const reportScrollManager = useReportScrollManager();

const highlightedBackgroundColorIfNeeded = useMemo(
Expand Down Expand Up @@ -700,6 +713,7 @@ function ReportActionItem({
<MoneyRequestView
report={report}
shouldShowHorizontalRule={!shouldHideThreadDividerLine}
shouldShowAnimatedBackground
/>
</ShowContextMenuContext.Provider>
);
Expand Down Expand Up @@ -739,11 +753,30 @@ function ReportActionItem({
if (ReportUtils.isExpenseReport(report) || ReportUtils.isIOUReport(report)) {
return (
<OfflineWithFeedback pendingAction={action.pendingAction}>
<MoneyReportView
report={report}
policy={policy}
shouldShowHorizontalRule={!shouldHideThreadDividerLine}
/>
{transactionThreadReport && !isEmptyObject(transactionThreadReport) ? (
<>
{transactionCurrency !== report.currency && (
<MoneyReportView
report={report}
policy={policy}
shouldShowHorizontalRule={!shouldHideThreadDividerLine}
/>
)}
<ShowContextMenuContext.Provider value={contextValue}>
<MoneyRequestView
report={transactionThreadReport}
shouldShowHorizontalRule={!shouldHideThreadDividerLine}
shouldShowAnimatedBackground={transactionCurrency === report.currency}
/>
</ShowContextMenuContext.Provider>
</>
) : (
<MoneyReportView
report={report}
policy={policy}
shouldShowHorizontalRule={!shouldHideThreadDividerLine}
/>
)}
</OfflineWithFeedback>
);
}
Expand Down Expand Up @@ -899,6 +932,14 @@ export default withOnyx<ReportActionItemProps, ReportActionItemOnyxProps>({
userWallet: {
key: ONYXKEYS.USER_WALLET,
},
transaction: {
key: ({transactionThreadReport, reportActions}) => {
const parentReportActionID = isEmptyObject(transactionThreadReport) ? '0' : transactionThreadReport.parentReportActionID;
const action = reportActions?.find((reportAction) => reportAction.reportActionID === parentReportActionID);
const transactionID = (action as OnyxTypes.OriginalMessageIOU)?.originalMessage.IOUTransactionID ? (action as OnyxTypes.OriginalMessageIOU).originalMessage.IOUTransactionID : 0;
return `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
},
},
})(
memo(ReportActionItem, (prevProps, nextProps) => {
const prevParentReportAction = prevProps.parentReportAction;
Expand Down Expand Up @@ -930,6 +971,9 @@ export default withOnyx<ReportActionItemProps, ReportActionItemOnyxProps>({
prevProps.linkedReportActionID === nextProps.linkedReportActionID &&
lodashIsEqual(prevProps.report.fieldList, nextProps.report.fieldList) &&
lodashIsEqual(prevProps.policy, nextProps.policy) &&
lodashIsEqual(prevProps.transactionThreadReport, nextProps.transactionThreadReport) &&
lodashIsEqual(prevProps.reportActions, nextProps.reportActions) &&
lodashIsEqual(prevProps.transaction, nextProps.transaction) &&
lodashIsEqual(prevParentReportAction, nextParentReportAction)
);
}),
Expand Down
18 changes: 17 additions & 1 deletion src/pages/home/report/ReportActionItemParentAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,28 @@ type ReportActionItemParentActionProps = {
/** The current report is displayed */
report: OnyxEntry<OnyxTypes.Report>;

/** The transaction thread report associated with the current report, if any */
transactionThreadReport: OnyxEntry<OnyxTypes.Report>;

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

/** Report actions belonging to the report's parent */
parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;

/** Whether we should display "Replies" divider */
shouldDisplayReplyDivider: boolean;
};

function ReportActionItemParentAction({report, parentReportAction, index = 0, shouldHideThreadDividerLine = false, shouldDisplayReplyDivider}: ReportActionItemParentActionProps) {
function ReportActionItemParentAction({
report,
transactionThreadReport,
reportActions,
parentReportAction,
index = 0,
shouldHideThreadDividerLine = false,
shouldDisplayReplyDivider,
}: ReportActionItemParentActionProps) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {isSmallScreenWidth} = useWindowDimensions();
Expand Down Expand Up @@ -92,6 +106,8 @@ function ReportActionItemParentAction({report, parentReportAction, index = 0, sh
onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.parentReportID ?? '', ancestor.reportAction.reportActionID))}
parentReportAction={parentReportAction}
report={ancestor.report}
reportActions={reportActions}
transactionThreadReport={transactionThreadReport}
action={ancestor.reportAction}
displayAsGroup={false}
isMostRecentIOUReportAction={false}
Expand Down
12 changes: 12 additions & 0 deletions src/pages/home/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ type ReportActionsListProps = WithCurrentUserPersonalDetailsProps & {
/** The report currently being looked at */
report: OnyxTypes.Report;

/** The transaction thread report associated with the current report, if any */
transactionThreadReport: OnyxEntry<OnyxTypes.Report>;

/** Array of report actions for the current report */
reportActions: OnyxTypes.ReportAction[];

/** The report's parentReportAction */
parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;

Expand Down Expand Up @@ -125,6 +131,8 @@ const onScrollToIndexFailed = () => {};

function ReportActionsList({
report,
transactionThreadReport,
reportActions = [],
parentReportAction,
isLoadingInitialReportActions = false,
isLoadingOlderReportActions = false,
Expand Down Expand Up @@ -514,9 +522,11 @@ function ReportActionsList({
({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => (
<ReportActionsListItemRenderer
reportAction={reportAction}
reportActions={reportActions}
parentReportAction={parentReportAction}
index={index}
report={report}
transactionThreadReport={transactionThreadReport}
linkedReportActionID={linkedReportActionID}
displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedVisibleReportActions, index)}
mostRecentIOUReportActionID={mostRecentIOUReportActionID}
Expand All @@ -534,6 +544,8 @@ function ReportActionsList({
shouldHideThreadDividerLine,
shouldDisplayNewMarker,
parentReportAction,
reportActions,
transactionThreadReport,
],
);

Expand Down
Loading

0 comments on commit c9b5901

Please sign in to comment.