Skip to content
6 changes: 6 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,12 @@ const CONST = {
ADD_UNREPORTED_EXPENSE: 'addUnreportedExpense',
TRACK_DISTANCE_EXPENSE: 'trackDistanceExpense',
},
ACTION_BADGE: {
SUBMIT: 'submit',
APPROVE: 'approve',
PAY: 'pay',
FIX: 'fix',
},
ACTIONS: {
LIMIT: 50,
// OldDot Actions render getMessage from Web-Expensify/lib/Report/Action PHP files via getMessageOfOldDotReportAction in ReportActionsUtils.ts
Expand Down
2 changes: 1 addition & 1 deletion src/components/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function Badge({
accessible={false}
>
{!!icon && (
<View style={[styles.mr2, iconStyles]}>
<View style={[styles.mr1, iconStyles]}>
<Icon
width={iconSize}
height={iconSize}
Expand Down
87 changes: 62 additions & 25 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {useMemo, useRef, useState} from 'react';
import type {GestureResponderEvent, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
import Badge from '@components/Badge';
import DisplayNames from '@components/DisplayNames';
import Hoverable from '@components/Hoverable';
import Icon from '@components/Icon';
Expand All @@ -12,6 +13,7 @@ import Text from '@components/Text';
import Tooltip from '@components/Tooltip';
import EducationalTooltip from '@components/Tooltip/EducationalTooltip';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useEnvironment from '@hooks/useEnvironment';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand Down Expand Up @@ -55,6 +57,7 @@ function OptionRowLHN({
testID,
conciergeReportID,
}: OptionRowLHNProps) {
const {isProduction} = useEnvironment();
const theme = useTheme();
const styles = useThemeStyles();
const popoverAnchor = useRef<View>(null);
Expand Down Expand Up @@ -158,6 +161,13 @@ function OptionRowLHN({
}

const brickRoadIndicator = optionItem.brickRoadIndicator;
const actionBadgeText = !isProduction && optionItem.actionBadge ? translate(`common.actionBadge.${optionItem.actionBadge}`) : '';
let accessibilityLabelForBadge = '';
if (brickRoadIndicator) {
accessibilityLabelForBadge = `. ${translate('common.yourReviewIsRequired')}, ${actionBadgeText}`;
} else if (optionItem.isPinned) {
accessibilityLabelForBadge = `. ${translate('common.pinned')}`;
}
const textStyle = isOptionFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText;
const textUnreadStyle = shouldUseBoldText(optionItem) ? [textStyle, styles.sidebarLinkTextBold] : [textStyle];
const displayNameStyle = [styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, textUnreadStyle, styles.flexShrink0, style];
Expand Down Expand Up @@ -293,7 +303,7 @@ function OptionRowLHN({
(hovered || isContextMenuActive) && !isOptionFocused ? styles.sidebarLinkHover : null,
]}
role={CONST.ROLE.BUTTON}
accessibilityLabel={`${translate('accessibilityHints.navigatesToChat')} ${optionItem.text}. ${optionItem.isUnread ? `${translate('common.unread')}.` : ''} ${optionItem.alternateText}${brickRoadIndicator ? `. ${translate('common.yourReviewIsRequired')}` : ''}`}
accessibilityLabel={`${translate('accessibilityHints.navigatesToChat')} ${optionItem.text}. ${optionItem.isUnread ? `${translate('common.unread')}.` : ''} ${optionItem.alternateText}${accessibilityLabelForBadge}`}
onLayout={onLayout}
needsOffscreenAlphaCompositing={(optionItem?.icons?.length ?? 0) >= 2}
sentryLabel={CONST.SENTRY_LABEL.LHN.OPTION_ROW}
Expand Down Expand Up @@ -376,25 +386,42 @@ function OptionRowLHN({
) : null}
{brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR && (
<View style={[styles.alignItemsCenter, styles.justifyContentCenter]}>
<Icon
testID="RBR Icon"
src={expensifyIcons.DotIndicator}
fill={theme.danger}
/>
{actionBadgeText ? (
<Badge
text={actionBadgeText}
error
isCondensed
isStrong
/>
) : (
<Icon
testID="RBR Icon"
src={expensifyIcons.DotIndicator}
fill={theme.danger}
/>
)}
</View>
)}
</View>
</View>
<View style={[styles.flexRow, styles.alignItemsCenter]}>
{brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO && (
<View style={styles.ml2}>
<Icon
testID="GBR Icon"
src={expensifyIcons.DotIndicator}
fill={theme.success}
{brickRoadIndicator === CONST.BRICK_ROAD_INDICATOR_STATUS.INFO &&
(actionBadgeText ? (
<Badge
text={actionBadgeText}
success
isCondensed
isStrong
/>
</View>
)}
) : (
<View style={styles.ml2}>
<Icon
testID="GBR Icon"
src={expensifyIcons.DotIndicator}
fill={theme.success}
/>
</View>
))}
{hasDraftComment && !!optionItem.isAllowedToComment && (
<View
style={styles.ml2}
Expand All @@ -407,18 +434,28 @@ function OptionRowLHN({
/>
</View>
)}
{!brickRoadIndicator && !!optionItem.isPinned && (
<View
style={styles.ml2}
accessibilityLabel={translate('sidebarScreen.chatPinned')}
>
<Icon
testID="Pin Icon"
fill={theme.icon}
src={expensifyIcons.Pin}
{!brickRoadIndicator &&
!!optionItem.isPinned &&
(isProduction ? (
<View
style={styles.ml2}
accessibilityLabel={translate('sidebarScreen.chatPinned')}
>
<Icon
testID="Pin Icon"
fill={theme.icon}
src={expensifyIcons.Pin}
/>
</View>
) : (
<Badge
icon={expensifyIcons.Pin}
text={translate('common.pinned')}
badgeStyles={isOptionFocused && styles.badgeDefaultActive}
isCondensed
isStrong
/>
</View>
)}
))}
</View>
</PressableWithSecondaryInteraction>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/LHNOptionsList/OptionRowLHNData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ function OptionRowLHNData({
}, [
fullReport,
reportAttributes?.brickRoadStatus,
reportAttributes?.actionBadge,
reportAttributes?.reportName,
areReportErrorsEqual,
oneTransactionThreadReport,
Expand Down
6 changes: 6 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'Startseite',
inbox: 'Posteingang',
yourReviewIsRequired: 'Ihre Überprüfung ist erforderlich',
actionBadge: {
submit: 'Senden',
approve: 'Genehmigen',
pay: 'Bezahlen',
fix: 'Beheben',
},
success: 'Erfolgreich',
group: 'Gruppe',
profile: 'Profil',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ const translations = {
home: 'Home',
inbox: 'Inbox',
yourReviewIsRequired: 'Your review is required',
actionBadge: {
submit: 'Submit',
approve: 'Approve',
pay: 'Pay',
fix: 'Fix',
},
// @context Used in confirmation or result messages indicating that an action completed successfully, not the abstract noun “success.”
success: 'Success',
group: 'Group',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ const translations: TranslationDeepObject<typeof en> = {
workspaces: 'Espacios de trabajo',
inbox: 'Recibidos',
yourReviewIsRequired: 'Se requiere tu revisión',
actionBadge: {
submit: 'Enviar',
approve: 'Aprobar',
pay: 'Pagar',
fix: 'Corregir',
},
home: 'Inicio',
group: 'Grupo',
profile: 'Perfil',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'Accueil',
inbox: 'Boîte de réception',
yourReviewIsRequired: 'Votre révision est requise',
actionBadge: {
submit: 'Soumettre',
approve: 'Approuver',
pay: 'Payer',
fix: 'Corriger',
},
success: 'Réussi',
group: 'Groupe',
profile: 'Profil',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'Home',
inbox: 'Posta in arrivo',
yourReviewIsRequired: 'È richiesta la tua revisione',
actionBadge: {
submit: 'Invia',
approve: 'Approva',
pay: 'Paga',
fix: 'Correggi',
},
success: 'Operazione riuscita',
group: 'Gruppo',
profile: 'Profilo',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'ホーム',
inbox: '受信トレイ',
yourReviewIsRequired: '確認が必要です',
actionBadge: {
submit: '送信',
approve: '承認する',
pay: '支払う',
fix: '修正',
},
success: '成功しました',
group: 'グループ',
profile: 'プロフィール',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'Home',
inbox: 'Inbox',
yourReviewIsRequired: 'Uw beoordeling is vereist',
actionBadge: {
submit: 'Verzenden',
approve: 'Goedkeuren',
pay: 'Betalen',
fix: 'Oplossen',
},
success: 'Gelukt',
group: 'Groep',
profile: 'Profiel',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'Strona główna',
inbox: 'Skrzynka odbiorcza',
yourReviewIsRequired: 'Wymagana jest Twoja weryfikacja',
actionBadge: {
submit: 'Wyślij',
approve: 'Zatwierdź',
pay: 'Zapłać',
fix: 'Napraw',
},
success: 'Sukces',
group: 'Grupa',
profile: 'Profil',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: 'Início',
inbox: 'Caixa de entrada',
yourReviewIsRequired: 'Sua revisão é necessária',
actionBadge: {
submit: 'Enviar',
approve: 'Aprovar',
pay: 'Pagar',
fix: 'Corrigir',
},
success: 'Concluído',
group: 'Grupo',
profile: 'Perfil',
Expand Down
6 changes: 6 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const translations: TranslationDeepObject<typeof en> = {
home: '主页',
inbox: '收件箱',
yourReviewIsRequired: '需要您的审核',
actionBadge: {
submit: '提交',
approve: '批准',
pay: '支付',
fix: '修复',
},
success: '成功',
group: '群组',
profile: '个人资料',
Expand Down
13 changes: 9 additions & 4 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
canIOUBePaid,
canSubmitReport,
createDraftTransaction,
getIOUReportActionToApproveOrPay,
getIOUReportActionWithBadge,
setMoneyRequestParticipants,
setMoneyRequestParticipantsFromReport,
setMoneyRequestReportID,
Expand Down Expand Up @@ -831,6 +831,7 @@
alternateText?: string;
allReportErrors?: Errors;
brickRoadIndicator?: ValueOf<typeof CONST.BRICK_ROAD_INDICATOR_STATUS> | '' | null;
actionBadge?: ValueOf<typeof CONST.REPORT.ACTION_BADGE>;
tooltipText?: string | null;
alternateTextMaxLines?: number;
boldStyle?: boolean;
Expand Down Expand Up @@ -1034,7 +1035,7 @@
};

let conciergeReportIDOnyxConnect: OnyxEntry<string>;
Onyx.connect({

Check warning on line 1038 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.CONCIERGE_REPORT_ID,
callback: (value) => {
conciergeReportIDOnyxConnect = value;
Expand All @@ -1042,7 +1043,7 @@
});

const defaultAvatarBuildingIconTestID = 'SvgDefaultAvatarBuilding Icon';
Onyx.connect({

Check warning on line 1046 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
// When signed out, val is undefined
Expand All @@ -1060,7 +1061,7 @@
let allPersonalDetails: OnyxEntry<PersonalDetailsList>;
let allPersonalDetailLogins: string[];
let currentUserPersonalDetails: OnyxEntry<PersonalDetails>;
Onyx.connect({

Check warning on line 1064 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
if (currentUserAccountID) {
Expand All @@ -1072,7 +1073,7 @@
});

let allReportsDraft: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 1076 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_DRAFT,
waitForCollectionCallback: true,
callback: (value) => (allReportsDraft = value),
Expand All @@ -1080,7 +1081,7 @@

let allPolicies: OnyxCollection<Policy>;
let policiesArray: Policy[] = [];
Onyx.connect({

Check warning on line 1084 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1090,7 +1091,7 @@
});

let allPolicyDrafts: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 1094 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_DRAFTS,
waitForCollectionCallback: true,
callback: (value) => (allPolicyDrafts = value),
Expand All @@ -1098,7 +1099,7 @@

let allReports: OnyxCollection<Report>;
let reportsByPolicyID: ReportByPolicyMap;
Onyx.connect({

Check warning on line 1102 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -1134,14 +1135,14 @@
});

let betaConfiguration: OnyxEntry<BetaConfiguration> = {};
Onyx.connect({

Check warning on line 1138 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.BETA_CONFIGURATION,
callback: (value) => (betaConfiguration = value ?? {}),
});

let allTransactions: OnyxCollection<Transaction> = {};
let reportsTransactions: Record<string, Transaction[]> = {};
Onyx.connect({

Check warning on line 1145 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.TRANSACTION,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -1167,7 +1168,7 @@
});

let allReportActions: OnyxCollection<ReportActions>;
Onyx.connect({

Check warning on line 1171 in src/libs/ReportUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
waitForCollectionCallback: true,
callback: (actions) => {
Expand Down Expand Up @@ -4244,6 +4245,7 @@
type ReasonAndReportActionThatRequiresAttention = {
reason: ValueOf<typeof CONST.REQUIRES_ATTENTION_REASONS>;
reportAction?: OnyxEntry<ReportAction>;
actionBadge?: ValueOf<typeof CONST.REPORT.ACTION_BADGE>;
};

/**
Expand Down Expand Up @@ -4322,7 +4324,7 @@
// This will be fixed as part of https://github.com/Expensify/Expensify/issues/507850
// eslint-disable-next-line @typescript-eslint/no-deprecated
const invoiceReceiverPolicy = invoiceReceiverPolicyID ? getPolicy(invoiceReceiverPolicyID) : undefined;
const iouReportActionToApproveOrPay = getIOUReportActionToApproveOrPay(optionOrReport, undefined, optionReportMetadata, invoiceReceiverPolicy);
const {reportAction: iouReportActionToApproveOrPay, actionBadge} = getIOUReportActionWithBadge(optionOrReport, undefined, optionReportMetadata, invoiceReceiverPolicy);
const iouReportID = getIOUReportIDFromReportActionPreview(iouReportActionToApproveOrPay);
const transactions = getReportTransactions(iouReportID);
const hasOnlyPendingTransactions = transactions.length > 0 && transactions.every((t) => isExpensifyCardTransaction(t) && isPending(t));
Expand All @@ -4338,6 +4340,7 @@
return {
reason: CONST.REQUIRES_ATTENTION_REASONS.HAS_CHILD_REPORT_AWAITING_ACTION,
reportAction: iouReportActionToApproveOrPay,
actionBadge,
};
}

Expand Down Expand Up @@ -12706,6 +12709,7 @@
reportActions?: OnyxCollection<ReportActions>;
transactionViolations: OnyxCollection<TransactionViolation[]>;
isReportArchived: boolean;
actionBadge?: ValueOf<typeof CONST.REPORT.ACTION_BADGE>;
}) {
const reportActionsList = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.reportID}`];
const parentReportActionsList = reportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`];
Expand All @@ -12715,7 +12719,7 @@
const hasErrors = Object.entries(reportErrors ?? {}).length > 0;
const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActionsList);
const parentReportAction = report?.parentReportActionID ? parentReportActionsList?.[report.parentReportActionID] : undefined;
const requiresAttention = requiresAttentionFromCurrentUser(report, parentReportAction, isReportArchived);
const {reason, actionBadge} = getReasonAndReportActionThatRequiresAttention(report, parentReportAction, isReportArchived) ?? {};

return {
hasViolationsToDisplayInLHN,
Expand All @@ -12724,7 +12728,8 @@
hasErrors,
oneTransactionThreadReportID,
parentReportAction,
requiresAttention,
requiresAttention: !!reason,
actionBadge,
};
}

Expand Down
1 change: 1 addition & 0 deletions src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ function getOptionData({
result.shouldShowSubscript = rawShouldShowSubscript && !threadSuppression && !taskSuppression;
result.pendingAction = report.pendingFields?.addWorkspaceRoom ?? report.pendingFields?.createChat;
result.brickRoadIndicator = reportAttributes?.brickRoadStatus;
result.actionBadge = reportAttributes?.actionBadge;
result.ownerAccountID = report.ownerAccountID;
result.managerID = report.managerID;
result.reportID = report.reportID;
Expand Down
Loading
Loading