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

Add One Transaction Report View #36934

Merged
merged 51 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
53ada16
report action utils functions
NikkiWines Feb 7, 2024
df1b56c
report utils functions for one transaction report
NikkiWines Feb 7, 2024
b841b6a
hide background for money requests on a single transaction report
NikkiWines Feb 7, 2024
7ec2e5c
update css style for money request
NikkiWines Feb 7, 2024
353b740
update report actions items to retrieve transaction thread report det…
NikkiWines Feb 9, 2024
42adcea
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Feb 9, 2024
66f498b
use undefined instead of {}
NikkiWines Feb 9, 2024
de66987
check prevProps
NikkiWines Feb 9, 2024
74a42f6
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Feb 20, 2024
1bf4c04
simplify transactionThreadReportActions display
NikkiWines Feb 20, 2024
6153bab
minor style and lint updates
NikkiWines Feb 21, 2024
d436ffe
use simplified report icons when applicable
NikkiWines Feb 21, 2024
b722725
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Feb 22, 2024
b13fc4b
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Feb 26, 2024
452d012
add transactionThreadReportID to report structure for onyx and props
NikkiWines Feb 27, 2024
cac6d81
simplify transactionThreadReportID logic to use value returned in the…
NikkiWines Feb 27, 2024
6a08ba5
minor style
NikkiWines Feb 27, 2024
1674735
don't show report if transaction thread and expense report have the s…
NikkiWines Feb 27, 2024
f3f5e52
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Feb 27, 2024
90bc361
update default value for transactionThreadReportActions key
NikkiWines Feb 28, 2024
0f21504
update utils files to no longer reference report.transactionThreadRep…
NikkiWines Mar 6, 2024
4bb0313
use general report and reportActions onyx keys instead of relying on …
NikkiWines Mar 6, 2024
35d0264
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 6, 2024
c941d23
update getOneTransactionReportID to take in only reportActions
NikkiWines Mar 7, 2024
2a02be8
fix some malformed logic
NikkiWines Mar 7, 2024
b5ae144
ensure we don't show outdated UI due to removed IOU requests
NikkiWines Mar 7, 2024
f6cfcb3
use transaction currency instead of checking between transactionThrea…
NikkiWines Mar 7, 2024
0140912
merge main and address conflicts from ts migrations for ReportActionItem
NikkiWines Mar 8, 2024
db05afc
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 8, 2024
c274090
style and ts updates
NikkiWines Mar 8, 2024
618b5ac
minor style
NikkiWines Mar 8, 2024
31cf018
adjust currency and filtering combined reportActions logic
NikkiWines Mar 9, 2024
6b2d28a
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 11, 2024
1b27a33
minor adjustments and stylistic changes
NikkiWines Mar 11, 2024
d36bc98
ts fixes
NikkiWines Mar 11, 2024
686a2dc
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 11, 2024
7994917
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 14, 2024
1003a1f
get transactionThreadReportActions in ReportActionsView instead of Re…
NikkiWines Mar 15, 2024
de600c8
pass transactionThreadReport and reportActions for report to reportAc…
NikkiWines Mar 15, 2024
034b93f
lint / style
NikkiWines Mar 15, 2024
716023c
supress incorrect linter error
NikkiWines Mar 20, 2024
3b88172
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 20, 2024
45c7fc1
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 21, 2024
f9f3607
update reportActionsView to account for getting newer and older repor…
NikkiWines Mar 22, 2024
e3be337
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 26, 2024
1090c5b
type resolution
NikkiWines Mar 26, 2024
a4926d3
updates to one-transaction utils functions
NikkiWines Mar 28, 2024
7dd2384
use reportActionsByReport, and some minor stylistic updates
NikkiWines Mar 28, 2024
fec3aa3
pass transactionThreadReportID from ReportScreen
NikkiWines Mar 28, 2024
5438f69
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 28, 2024
1aac6e0
Merge branch 'main' of github.com:Expensify/App into nikki-one-transa…
NikkiWines Mar 28, 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
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 @@ -235,9 +239,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
43 changes: 43 additions & 0 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,32 @@ function isTransactionThread(parentReportAction: OnyxEntry<ReportAction>): boole
);
}

function getOneTransactionThreadReportID(reportActions: OnyxEntry<ReportActions>): string {
const reportActionsArray = Object.values(reportActions ?? {});

if (!reportActionsArray.length) {
return '0';
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
}

// 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];
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this single transaction view specific only for expense reports? I feel like we should treat the IOU reports the same and in that case we are missing the case of Send Money flow and pay IOU action type https://github.com/Expensify/Auth/blob/488096eac43b22e5a9d4a8355704105b6dfd2972/auth/command/SendMoney.cpp#L173

ie it would be a test case of Send money to another user and then if you visit the iou report, its only one transaction so it should render as the single transaction view, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not specific to just expense reports, no. I was using the iou types from getMostRecentIOURequestActionID() here but can update this to include pay 👍

const iouRequestActions = reportActionsArray.filter(
(action) =>
action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU &&
(iouRequestTypes.includes(action.originalMessage.type) ?? []) &&
action.childReportID &&
action.originalMessage.IOUTransactionID,
tgolen marked this conversation as resolved.
Show resolved Hide resolved
);

// If we don't have any IOU request actions, or we have more than one IOU request actions, this isn't a oneTransaction report and we don't want to return the transactionThreadReportActions
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
if (!iouRequestActions.length || iouRequestActions.length > 1) {
return '0';
}

// Ensure we have a childReportID associated with the IOU report action
return String(iouRequestActions[0].childReportID);
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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 @@ -532,6 +558,21 @@ function getSortedReportActionsForDisplay(reportActions: ReportActions | ReportA
return getSortedReportActions(baseURLAdjustedReportActions, true, shouldMarkTheFirstItemAsNewest);
}

/**
* This method returns a combined array of report actions from a parent report and child transaction thread report that
* are ready for display in the ReportActionView.
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
*/
function getCombinedReportActionsForDisplay(reportActions: ReportAction[], transactionThreadReportActions: ReportAction[]): ReportAction[] {
// Filter out the created action from the transaction thread report actions, since we already have the parent report's created action
const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED);

// Sort the combined list of parent report actions and transaction thread report actions
const sortedReportActions = getSortedReportActions([...reportActions, ...filteredTransactionThreadReportActions], true);

// Filter out "created" IOU report actions because we don't want to show any preview actions for one transaction reports
return sortedReportActions.filter((action) => ((action as OriginalMessageIOU).originalMessage?.type ?? '') !== CONST.IOU.REPORT_ACTION_TYPE.CREATE);
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB: I would expect this filtering to happen before the sort function as you did in for the created action. Not really a blocker but having a filter, sort and filter feels a bit odd to me instead of filter, filter sort :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We intentionally don't filter the combined reportActions and filteredTransactionThreadReport actions first because we only want to remove the created actions from the transaction thread report actions here.

We could do it this way if you find that clearer:

// Filter out the created action from the transaction thread report actions, since we already have the parent report's created action in `reportActions`
const filteredTransactionThreadReportActions = transactionThreadReportActions?.filter((action) => action.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED);

// Filter out "created" IOU report actions because we don't want to show any preview actions for one transaction reports    
const filteredReportActions = [...reportActions, ...filteredTransactionThreadReportActions].filter((action) => ((action as OriginalMessageIOU).originalMessage?.type ?? '') !== CONST.IOU.REPORT_ACTION_TYPE.CREATE);

return  getSortedReportActions(filteredReportActions, true);

}

/**
* In some cases, there can be multiple closed report actions in a chat report.
* This method returns the last closed report action so we can always show the correct archived report reason.
Expand Down Expand Up @@ -922,6 +963,7 @@ function getReportActionMessageText(reportAction: OnyxEntry<ReportAction> | Empt

export {
extractLinksFromMessageHtml,
getOneTransactionThreadReportID,
getAllReportActions,
getIOUReportIDFromReportActionPreview,
getLastClosedReportAction,
Expand All @@ -939,6 +981,7 @@ export {
getReportPreviewAction,
getSortedReportActions,
getSortedReportActionsForDisplay,
getCombinedReportActionsForDisplay,
isConsecutiveActionMadeByPreviousActor,
isCreatedAction,
isCreatedTaskReportAction,
Expand Down
33 changes: 33 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,23 @@ function isMoneyRequestReport(reportOrID: OnyxEntry<Report> | string): boolean {
return isIOUReport(report) || isExpenseReport(report);
}

/**
* Checks if a report has only one transaction associated with it
*/
function isOneTransactionReport(reportID: string): boolean {
const reportActions = ReportActionsUtils.getAllReportActions(reportID);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm adding getAllReportActions() to the list of bad methods in #27262. Can you please remove the uses of it and use the local reportActionsByReport collection instead?

Copy link
Contributor Author

@NikkiWines NikkiWines Mar 27, 2024

Choose a reason for hiding this comment

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

Yep, I actually mentioned below here about possibly following up in a separate PR to handle this case but I'll look at using reportActionsByReport here and in isOneTransactionThread. Using reportActionsByReport works too though!

return ReportActionsUtils.getOneTransactionThreadReportID(reportActions) !== '0';
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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 = ReportActionsUtils.getAllReportActions(parentReportID);
const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(parentReportActions);
return reportID === transactionThreadReportID;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tgolen we discussed previously about how using methods like getAllReportActions isn't the best practice. However, I thought it would be cleaner to implement it like this for now, and make a separate PR to update all the instances of ReportUtils.getIcons(), ReportUtils. shouldReportBeInOptionList(), and ReportUtils.shouldReportShowSubscript() (which use these methods) to send across the appropriate reportActions instead of just reportIDs.

Lemme know if you disagree and I can add those changes in this PR instead.


/**
* Should return true only for personal 1:1 report
*
Expand Down Expand Up @@ -1683,6 +1700,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 @@ -4101,6 +4123,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 @@ -4553,6 +4580,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 @@ -5337,6 +5368,8 @@ export {
hasSingleParticipant,
getReportRecipientAccountIDs,
isOneOnOneChat,
isOneTransactionReport,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this method being exported? It doesn't look like it is used anywhere outside of this file (and this is one of those anti-patterns we discussed). In fact, would you mind adding this method to https://github.com/Expensify/App/blob/main/tests/actions/EnforceActionExportRestrictions.ts so that it doesn't get exported and used somewhere else?

Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, now I'm also worried that any of the methods in this file that use isOneTransactionReport() are being exported and used improperly.

isOneTransactionThread,
isPayer,
goBackToDetailsPage,
getTransactionReportName,
Expand Down
30 changes: 26 additions & 4 deletions src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@
/** The report metadata loading states */
reportMetadata: reportMetadataPropTypes,

/** All the report actions stored in Onyx */
allReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)),

/** All the report actions for this report */
reportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)),
reportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)),
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved

/** The report's parentReportAction */
parentReportAction: PropTypes.shape(reportActionPropTypes),
Expand Down Expand Up @@ -103,7 +106,9 @@

const defaultProps = {
isSidebarLoaded: false,
reportActions: {},
reportActions: [],
allReportActions: {},
transactionThreadReportActions: [],
parentReportAction: {},
report: {},
reportMetadata: {
Expand Down Expand Up @@ -142,6 +147,7 @@
route,
report: reportProp,
reportMetadata,
allReportActions,
reportActions,
parentReportAction,
accountManagerReportID,
Expand Down Expand Up @@ -263,7 +269,6 @@
const {reportPendingAction, reportErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report);
const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}];
const isEmptyChat = useMemo(() => _.isEmpty(reportActions), [reportActions]);
// There are no reportActions at all to display and we are still in the process of loading the next set of actions.
const isLoadingInitialReportActions = _.isEmpty(reportActions) && reportMetadata.isLoadingInitialReportActions;
const isOptimisticDelete = lodashGet(report, 'statusNum') === CONST.REPORT.STATUS_NUM.CLOSED;
const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas);
Expand All @@ -280,6 +285,15 @@
const policy = policies[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`] || {};
const isTopMostReportId = currentReportID === getReportID(route);
const didSubscribeToReportLeavingEvents = useRef(false);
const transactionThreadReportID = ReportActionsUtils.getOneTransactionThreadReportID(allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]);
const transactionThreadReportActions = useMemo(() => {
if (transactionThreadReportID === '0') {
return [];
}

const reportActions = allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`] ?? [];

Check failure on line 294 in src/pages/home/ReportScreen.js

View workflow job for this annotation

GitHub Actions / Run ESLint

'reportActions' is already declared in the upper scope on line 151 column 5

Check failure on line 294 in src/pages/home/ReportScreen.js

View workflow job for this annotation

GitHub Actions / Run ESLint

ES2020 nullish coalescing operators are forbidden
return ReportActionsUtils.getSortedReportActionsForDisplay(reportActions);
}, [allReportActions, transactionThreadReportID]);

useEffect(() => {
if (!report.reportID || shouldHideReport) {
Expand Down Expand Up @@ -559,7 +573,11 @@
>
{isReportReadyForDisplay && !isLoadingInitialReportActions && !isLoading && (
<ReportActionsView
reportActions={reportActions}
reportActions={
_.isEmpty(transactionThreadReportActions)
? reportActions
: ReportActionsUtils.getCombinedReportActionsForDisplay(reportActions, transactionThreadReportActions)
}
report={report}
parentReportAction={parentReportAction}
isLoadingInitialReportActions={reportMetadata.isLoadingInitialReportActions}
Expand Down Expand Up @@ -608,6 +626,9 @@
isSidebarLoaded: {
key: ONYXKEYS.IS_SIDEBAR_LOADED,
},
allReportActions: {
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
},
NikkiWines marked this conversation as resolved.
Show resolved Hide resolved
reportActions: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`,
canEvict: false,
Expand Down Expand Up @@ -664,6 +685,7 @@
ReportScreen,
(prevProps, nextProps) =>
prevProps.isSidebarLoaded === nextProps.isSidebarLoaded &&
_.isEqual(prevProps.allReportActions, nextProps.allReportActions) &&
_.isEqual(prevProps.reportActions, nextProps.reportActions) &&
_.isEqual(prevProps.reportMetadata, nextProps.reportMetadata) &&
prevProps.isComposerFullSize === nextProps.isComposerFullSize &&
Expand Down
Loading
Loading