Skip to content

Commit bca3953

Browse files
authored
Merge pull request Expensify#85192 from aimane-chnaif/feature-84764-2
feat: deep link to report preview or message that caused GBR/RBR
2 parents 9bf2704 + cfa0814 commit bca3953

13 files changed

Lines changed: 523 additions & 595 deletions

File tree

src/components/Badge.tsx

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ function Badge({
9292
[styles.defaultBadge, styles.condensedBadge, styles.alignSelfCenter, styles.ml2, StyleUtils, success, error, environment, badgeStyles, isCondensed, isStrong],
9393
);
9494

95+
if (!text && !icon) {
96+
return null;
97+
}
98+
9599
return (
96100
<Wrapper
97101
style={pressable ? wrapperStyles : wrapperStyles({focused: false, hovered: false, isDisabled: false, isScreenReaderActive: false, pressed: false})}
@@ -102,7 +106,7 @@ function Badge({
102106
accessible={false}
103107
>
104108
{!!icon && (
105-
<View style={[styles.mr1, iconStyles]}>
109+
<View style={[!!text && styles.mr1, iconStyles]}>
106110
<Icon
107111
width={iconSize}
108112
height={iconSize}
@@ -111,22 +115,24 @@ function Badge({
111115
/>
112116
</View>
113117
)}
114-
<Text
115-
style={[
116-
styles.badgeText,
117-
styles.textStrong,
118-
isCondensed && styles.condensedBadgeText,
119-
!isStrong && !success && !error && styles.badgeDefaultText,
120-
!isStrong && success && styles.badgeSuccessText,
121-
!isStrong && error && styles.badgeDangerText,
122-
isStrong && (success || error) && styles.badgeStrongText,
123-
textStyles,
124-
isDeleted ? styles.offlineFeedbackDeleted : {},
125-
]}
126-
numberOfLines={1}
127-
>
128-
{text}
129-
</Text>
118+
{!!text && (
119+
<Text
120+
style={[
121+
styles.badgeText,
122+
styles.textStrong,
123+
isCondensed && styles.condensedBadgeText,
124+
!isStrong && !success && !error && styles.badgeDefaultText,
125+
!isStrong && success && styles.badgeSuccessText,
126+
!isStrong && error && styles.badgeDangerText,
127+
isStrong && (success || error) && styles.badgeStrongText,
128+
textStyles,
129+
isDeleted ? styles.offlineFeedbackDeleted : {},
130+
]}
131+
numberOfLines={1}
132+
>
133+
{text}
134+
</Text>
135+
)}
130136
</Wrapper>
131137
);
132138
}

src/components/LHNOptionsList/OptionRowLHN.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ function OptionRowLHN({
450450
) : (
451451
<Badge
452452
icon={expensifyIcons.Pin}
453-
text={translate('common.pinned')}
453+
text=""
454454
badgeStyles={isOptionFocused && styles.badgeDefaultActive}
455455
isCondensed
456456
isStrong

src/components/LHNOptionsList/OptionRowLHNData.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ function OptionRowLHNData({
114114
fullReport,
115115
reportAttributes?.brickRoadStatus,
116116
reportAttributes?.actionBadge,
117+
reportAttributes?.actionTargetReportActionID,
117118
reportAttributes?.reportName,
118119
areReportErrorsEqual,
119120
oneTransactionThreadReport,

src/libs/OptionsListUtils/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ import {
130130
getReportSubtitlePrefix,
131131
getReportTransactions,
132132
getUnreportedTransactionMessage,
133+
getViolatingReportIDForRBRInLHN,
133134
hasIOUWaitingOnCurrentUserBankAccount,
134135
isArchivedNonExpenseReport,
135136
isChatThread,
@@ -151,7 +152,6 @@ import {
151152
isPolicyExpenseChat as reportUtilsIsPolicyExpenseChat,
152153
isSelfDM as reportUtilsIsSelfDM,
153154
isTaskReport as reportUtilsIsTaskReport,
154-
shouldDisplayViolationsRBRInLHN,
155155
shouldReportBeInOptionList,
156156
} from '@libs/ReportUtils';
157157
import StringUtils from '@libs/StringUtils';
@@ -2106,7 +2106,7 @@ function isValidReport(option: SearchOption<Report>, policy: OnyxEntry<Policy>,
21062106
isTimeRequest = false,
21072107
} = config;
21082108
const topmostReportId = Navigation.getTopmostReportId();
2109-
const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(option.item, transactionViolations);
2109+
const doesReportHaveViolations = !!getViolatingReportIDForRBRInLHN(option.item, transactionViolations);
21102110

21112111
const shouldBeInOptionList = shouldReportBeInOptionList({
21122112
report: option.item,

src/libs/ReportUtils.ts

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ type OptionData = {
846846
allReportErrors?: Errors;
847847
brickRoadIndicator?: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | '' | null;
848848
actionBadge?: ValueOf<typeof CONST.REPORT.ACTION_BADGE>;
849+
actionTargetReportActionID?: string;
849850
tooltipText?: string | null;
850851
alternateTextMaxLines?: number;
851852
boldStyle?: boolean;
@@ -9100,67 +9101,51 @@ function shouldHideReport(
91009101
}
91019102

91029103
/**
9103-
* Should we display a RBR on the LHN on this report due to violations?
9104+
* Returns the reportID of the first child expense report that has violations under the same policy,
9105+
* or undefined if none found. Used to find the REPORT_PREVIEW action to deep-link to.
91049106
*/
9105-
function shouldDisplayViolationsRBRInLHN(report: OnyxEntry<Report>, transactionViolations: OnyxCollection<TransactionViolation[]>): boolean {
9107+
function getViolatingReportIDForRBRInLHN(report: OnyxEntry<Report>, transactionViolations: OnyxCollection<TransactionViolation[]>): string | null {
91069108
// We only show the RBR in the highest level, which is the expense chat
91079109
if (!report || !isPolicyExpenseChat(report)) {
9108-
return false;
9110+
return null;
91099111
}
91109112

91119113
// We only show the RBR to the submitter
91129114
if (!isCurrentUserSubmitter(report)) {
9113-
return false;
9115+
return null;
91149116
}
91159117
if (!report.policyID || !reportsByPolicyID) {
9116-
return false;
9118+
return null;
91179119
}
91189120

91199121
// If any report has a violation, then it should have a RBR
91209122
const potentialReports = Object.values(reportsByPolicyID[report.policyID] ?? {}) ?? [];
9121-
return potentialReports.some((potentialReport) => {
9122-
if (!potentialReport) {
9123-
return false;
9124-
}
9125-
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${potentialReport.policyID}`];
9126-
const transactions = getReportTransactions(potentialReport.reportID);
9123+
const violatingReport = potentialReports
9124+
// eslint-disable-next-line rulesdir/prefer-locale-compare-from-context
9125+
.sort((a, b) => (a?.created ?? '').localeCompare(b?.created ?? ''))
9126+
.find((potentialReport) => {
9127+
if (!potentialReport) {
9128+
return false;
9129+
}
9130+
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${potentialReport.policyID}`];
9131+
const transactions = getReportTransactions(potentialReport.reportID);
91279132

9128-
// Allow both open and processing reports to show RBR for violations
9129-
if (!isOpenOrProcessingReport(potentialReport)) {
9130-
return false;
9131-
}
9133+
// Allow both open and processing reports to show RBR for violations
9134+
if (!isOpenOrProcessingReport(potentialReport)) {
9135+
return false;
9136+
}
91329137

9133-
return (
9134-
!isInvoiceReport(potentialReport) &&
9135-
ViolationsUtils.hasVisibleViolationsForUser(
9136-
potentialReport,
9137-
transactionViolations,
9138-
currentUserEmail ?? '',
9139-
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
9140-
policy,
9141-
transactions,
9142-
) &&
9143-
(hasViolations(
9144-
potentialReport.reportID,
9145-
transactionViolations,
9146-
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
9147-
currentUserEmail ?? '',
9148-
true,
9149-
transactions,
9150-
potentialReport,
9151-
policy,
9152-
) ||
9153-
hasWarningTypeViolations(
9154-
potentialReport.reportID,
9138+
return (
9139+
!isInvoiceReport(potentialReport) &&
9140+
ViolationsUtils.hasVisibleViolationsForUser(
9141+
potentialReport,
91559142
transactionViolations,
9156-
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
91579143
currentUserEmail ?? '',
9158-
true,
9159-
transactions,
9160-
potentialReport,
9144+
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
91619145
policy,
9162-
) ||
9163-
hasNoticeTypeViolations(
9146+
transactions,
9147+
) &&
9148+
(hasViolations(
91649149
potentialReport.reportID,
91659150
transactionViolations,
91669151
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
@@ -9169,9 +9154,30 @@ function shouldDisplayViolationsRBRInLHN(report: OnyxEntry<Report>, transactionV
91699154
transactions,
91709155
potentialReport,
91719156
policy,
9172-
))
9173-
);
9174-
});
9157+
) ||
9158+
hasWarningTypeViolations(
9159+
potentialReport.reportID,
9160+
transactionViolations,
9161+
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
9162+
currentUserEmail ?? '',
9163+
true,
9164+
transactions,
9165+
potentialReport,
9166+
policy,
9167+
) ||
9168+
hasNoticeTypeViolations(
9169+
potentialReport.reportID,
9170+
transactionViolations,
9171+
currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID,
9172+
currentUserEmail ?? '',
9173+
true,
9174+
transactions,
9175+
potentialReport,
9176+
policy,
9177+
))
9178+
);
9179+
});
9180+
return violatingReport ? violatingReport.reportID : null;
91759181
}
91769182

91779183
/**
@@ -9402,7 +9408,7 @@ function hasReportErrorsOtherThanFailedReceipt(
94029408
let doesTransactionThreadReportHasViolations = false;
94039409
if (oneTransactionThreadReportID) {
94049410
const transactionReport = getReport(oneTransactionThreadReportID, allReports);
9405-
doesTransactionThreadReportHasViolations = !!transactionReport && shouldDisplayViolationsRBRInLHN(transactionReport, transactionViolations);
9411+
doesTransactionThreadReportHasViolations = !!transactionReport && !!getViolatingReportIDForRBRInLHN(transactionReport, transactionViolations);
94069412
}
94079413
return (
94089414
doesTransactionThreadReportHasViolations ||
@@ -12750,16 +12756,17 @@ function generateReportAttributes({
1275012756
transactionViolations: OnyxCollection<TransactionViolation[]>;
1275112757
isReportArchived: boolean;
1275212758
actionBadge?: ValueOf<typeof CONST.REPORT.ACTION_BADGE>;
12759+
actionTargetReportActionID?: string;
1275312760
}) {
1275412761
const reportActionsList = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`];
1275512762
const parentReportActionsList = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`];
12756-
const hasViolationsToDisplayInLHN = shouldDisplayViolationsRBRInLHN(report, transactionViolations);
12763+
const hasViolationsToDisplayInLHN = !!getViolatingReportIDForRBRInLHN(report, transactionViolations);
1275712764
const hasAnyTypeOfViolations = hasViolationsToDisplayInLHN;
1275812765
const reportErrors = getAllReportErrors(report, reportActionsList, isReportArchived);
1275912766
const hasErrors = Object.entries(reportErrors ?? {}).length > 0;
1276012767
const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActionsList);
1276112768
const parentReportAction = report?.parentReportActionID ? parentReportActionsList?.[report.parentReportActionID] : undefined;
12762-
const {reason, actionBadge} = getReasonAndReportActionThatRequiresAttention(report, parentReportAction, isReportArchived) ?? {};
12769+
const {reason, actionBadge, reportAction} = getReasonAndReportActionThatRequiresAttention(report, parentReportAction, isReportArchived) ?? {};
1276312770

1276412771
return {
1276512772
hasViolationsToDisplayInLHN,
@@ -12770,6 +12777,7 @@ function generateReportAttributes({
1277012777
parentReportAction,
1277112778
requiresAttention: !!reason,
1277212779
actionBadge,
12780+
actionTargetReportActionID: reportAction?.reportActionID,
1277312781
};
1277412782
}
1277512783

@@ -13282,6 +13290,7 @@ export {
1328213290
getDisplayedReportID,
1328313291
getTransactionsWithReceipts,
1328413292
getUserDetailTooltipText,
13293+
getViolatingReportIDForRBRInLHN,
1328513294
getWhisperDisplayNames,
1328613295
getWorkspaceChats,
1328713296
getWorkspaceIcon,
@@ -13422,7 +13431,6 @@ export {
1342213431
shouldDisableRename,
1342313432
shouldDisableThread,
1342413433
shouldDisplayThreadReplies,
13425-
shouldDisplayViolationsRBRInLHN,
1342613434
shouldReportBeInOptionList,
1342713435
shouldReportShowSubscript,
1342813436
shouldShowFlagComment,

src/libs/SidebarUtils.ts

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import {
5656
getIntegrationSyncFailedMessage,
5757
getInvoiceCompanyNameUpdateMessage,
5858
getInvoiceCompanyWebsiteUpdateMessage,
59+
getIOUReportIDFromReportActionPreview,
5960
getLastVisibleMessage,
6061
getMessageOfOldDotReportAction,
6162
getOriginalMessage,
@@ -151,6 +152,7 @@ import {
151152
getReportParticipantsTitle,
152153
getReportSubtitlePrefix,
153154
getUnreportedTransactionMessage,
155+
getViolatingReportIDForRBRInLHN,
154156
getWorkspaceNameUpdatedMessage,
155157
hasReportErrorsOtherThanFailedReceipt,
156158
isAdminRoom,
@@ -182,7 +184,6 @@ import {
182184
isUnread,
183185
isUnreadWithMention,
184186
isWorkspaceTaskReport,
185-
shouldDisplayViolationsRBRInLHN,
186187
shouldReportBeInOptionList,
187188
shouldReportShowSubscript,
188189
} from './ReportUtils';
@@ -246,7 +247,7 @@ function shouldDisplayReportInLHN(
246247

247248
// Get report metadata and status
248249
const parentReportAction = getReportAction(report?.parentReportID, report?.parentReportActionID);
249-
const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(report, transactionViolations);
250+
const doesReportHaveViolations = !!getViolatingReportIDForRBRInLHN(report, transactionViolations);
250251
const isHidden = isHiddenForCurrentUser(report);
251252
const isFocused = report.reportID === currentReportId;
252253
const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`];
@@ -627,20 +628,23 @@ function getReasonAndReportActionThatHasRedBrickRoad(
627628
transactionViolations?: OnyxCollection<TransactionViolation[]>,
628629
isReportArchived = false,
629630
): ReasonAndReportActionThatHasRedBrickRoad | null {
630-
const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions, isReportArchived);
631-
const errors = reportErrors;
632-
const hasErrors = Object.keys(errors).length !== 0;
633-
634631
if (isReportArchived) {
635632
return null;
636633
}
637634

638-
if (shouldDisplayViolationsRBRInLHN(report, transactionViolations)) {
635+
const violatingReportID = getViolatingReportIDForRBRInLHN(report, transactionViolations);
636+
if (violatingReportID) {
637+
const reportPreviewAction = Object.values(reportActions ?? {}).find((action) => getIOUReportIDFromReportActionPreview(action) === violatingReportID);
639638
return {
640639
reason: CONST.RBR_REASONS.HAS_TRANSACTION_THREAD_VIOLATIONS,
640+
reportAction: reportPreviewAction,
641641
};
642642
}
643643

644+
const {reportAction} = getAllReportActionsErrorsAndReportActionThatRequiresAttention(report, reportActions, isReportArchived);
645+
const errors = reportErrors;
646+
const hasErrors = Object.keys(errors).length !== 0;
647+
644648
if (hasErrors) {
645649
return {
646650
reason: CONST.RBR_REASONS.HAS_ERRORS,
@@ -657,19 +661,6 @@ function getReasonAndReportActionThatHasRedBrickRoad(
657661
return getReceiptUploadErrorReason(report, chatReport, reportActions, transactions);
658662
}
659663

660-
function shouldShowRedBrickRoad(
661-
report: Report,
662-
chatReport: OnyxEntry<Report>,
663-
reportActions: OnyxEntry<ReportActions>,
664-
hasViolations: boolean,
665-
reportErrors: Errors,
666-
transactions: OnyxCollection<Transaction>,
667-
transactionViolations?: OnyxCollection<TransactionViolation[]>,
668-
isReportArchived = false,
669-
) {
670-
return !!getReasonAndReportActionThatHasRedBrickRoad(report, chatReport, reportActions, hasViolations, reportErrors, transactions, transactionViolations, isReportArchived);
671-
}
672-
673664
/**
674665
* Gets all the data necessary for rendering an OptionRowLHN component
675666
*/
@@ -792,6 +783,7 @@ function getOptionData({
792783
result.pendingAction = report.pendingFields?.addWorkspaceRoom ?? report.pendingFields?.createChat;
793784
result.brickRoadIndicator = reportAttributes?.brickRoadStatus;
794785
result.actionBadge = reportAttributes?.actionBadge;
786+
result.actionTargetReportActionID = reportAttributes?.actionTargetReportActionID;
795787
result.ownerAccountID = report.ownerAccountID;
796788
result.managerID = report.managerID;
797789
result.reportID = report.reportID;
@@ -1360,7 +1352,6 @@ export default {
13601352
combineReportCategories,
13611353
getWelcomeMessage,
13621354
getReasonAndReportActionThatHasRedBrickRoad,
1363-
shouldShowRedBrickRoad,
13641355
getReportsToDisplayInLHN,
13651356
updateReportsToDisplayInLHN,
13661357
shouldDisplayReportInLHN,

0 commit comments

Comments
 (0)