[HOLD] feat: Replace GBR/RBR dots with action badges in Inbox LHN#80810
[HOLD] feat: Replace GBR/RBR dots with action badges in Inbox LHN#80810
Conversation
|
@aimane-chnaif Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
| const [movedToReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(lastAction, CONST.REPORT.MOVE_TYPE.TO)}`, {canBeMissing: true}); | ||
|
|
||
| // Only fetch bankAccountList for expense reports to avoid unnecessary data fetching | ||
| const isExpenseReport = isExpenseReportUtils(fullReport); |
There was a problem hiding this comment.
❌ PERF-11 (docs)
The bankAccountList is fetched unconditionally via useOnyx for all reports in the LHN, even though it's only needed for expense reports. This causes unnecessary re-renders whenever bank account data changes, even for chat rooms and other non-expense reports.
Suggested fix: Only fetch bankAccountList conditionally when needed:
// Only fetch bankAccountList for expense reports to avoid unnecessary data fetching
const isExpenseReport = isExpenseReportUtils(fullReport);
const [bankAccountList = getEmptyObject<BankAccountList>()] = useOnyx(
ONYXKEYS.BANK_ACCOUNT_LIST,
{canBeMissing: true, allowStaleData: !isExpenseReport}
);Or use a conditional selector:
const [bankAccountList = getEmptyObject<BankAccountList>()] = useOnyx(
ONYXKEYS.BANK_ACCOUNT_LIST,
{
canBeMissing: true,
selector: isExpenseReport ? undefined : () => ({}),
}
);Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
There was a problem hiding this comment.
Good catch on the performance issue! I've prepared a fix that adds a conditional selector to the useOnyx call. For non-expense reports, the selector returns an empty object, preventing unnecessary re-renders when bank account data changes. See my main comment on this PR for the full diff.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b2119a43a6
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/CONST/index.ts
Outdated
| ACTION_BADGE_VERBS: { | ||
| SUBMIT: 'Submit', | ||
| APPROVE: 'Approve', | ||
| PAY: 'Pay', | ||
| EXPORT: 'Export', |
There was a problem hiding this comment.
These are hard-coded English strings that are rendered directly in ActionBadge (the verb is displayed without translate). That means the new LHN badges will stay in English for all non-English locales, which is a user-facing regression. Consider storing translation keys here (or reusing existing keys) and passing the result of translate(...) into the badge so the labels are localized.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
These need to be translated strings
There was a problem hiding this comment.
@tgolen Good catch! I've prepared a fix that updates ACTION_BADGE_VERBS to store translation keys instead of hardcoded English strings. The keys now reference existing translations: SUBMIT: 'common.submit', APPROVE: 'search.bulkActions.approve', PAY: 'iou.pay', EXPORT: 'common.export'. See my main comment on this PR for the full diff.
| // Only fetch bankAccountList for expense reports to avoid unnecessary data fetching | ||
| const isExpenseReport = isExpenseReportUtils(fullReport); | ||
| const [bankAccountList = getEmptyObject<BankAccountList>()] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST, {canBeMissing: true}); |
There was a problem hiding this comment.
Avoid subscribing every LHN row to BANK_ACCOUNT_LIST
useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST) runs for every LHN row, even when the report isn’t an expense report. Because useOnyx subscribes to the key, any bank-account update will re-render all rows (including non-expense ones), which can cause unnecessary list churn on large inboxes. If the badge only needs bank account data for expense reports, consider moving this subscription higher or isolating it to the rows that actually need it.
Useful? React with 👍 / 👎.
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
Interesting, why a new badge component and not reuse the existing component we have? |
src/CONST/index.ts
Outdated
| ACTION_BADGE_VERBS: { | ||
| SUBMIT: 'Submit', | ||
| APPROVE: 'Approve', | ||
| PAY: 'Pay', | ||
| EXPORT: 'Export', |
There was a problem hiding this comment.
These need to be translated strings
|
@tgolen will you be pushing the changes? I am not sure when Melvin will address review feedback 😄 |
I bet Melvin doesn't super know how design systems work 😂 Should we/can we run a build here? I'm pretty curious to see what Melv came up with. |
I'm honestly not sure how to do this. I am trying to use Melvin to build this, but I don't know if this is actually working or not -- I don't have a dev environment to test this. I don't know what it looks like. So this is all just a MelvinBot experiment. I'm eager for @AndrewGable or someone to tell me what I should be doing here , but I'm certainly not the person to merge this. Should I be engaging a contributor to test this? |
|
Asking about next steps in #melvinbot-ai here: https://expensify.slack.com/archives/C0ABNPDS20L/p1769722204303029?thread_ts=1769648596.189009&cid=C0ABNPDS20L |
|
@MelvinBot please take a look at your PR and address feedback and questions |
|
Ah hang tight, we are working on getting Melvin to work on fixing what he did here cc @thienlnam |
|
@MelvinBot Can you address the review comments? |
|
🤖 Addressing Review Comments I've analyzed the review feedback and prepared fixes for all the issues raised. However, I don't have push access to this branch. Here are the changes that need to be applied: Fix 1: Localize Action Badge LabelsIssue: The action badge verbs are hardcoded English strings that won't display correctly in non-English locales. Solution: Change Changes to
|
|
🤖 Analysis of Failing Checks Prettier Check FailureThe Prettier check is failing on Fix: Put the style array on a single line: - <View
- style={[
- styles.actionBadgeDot,
- {backgroundColor: badgeColor},
- ]}
- />
+ <View style={[styles.actionBadgeDot, {backgroundColor: badgeColor}]} />Other Failing Checks
I don't have push access to apply the fix directly. Could someone with push access apply this change to |
|
@MelvinBot Try again to push those changes |
|
🤖 Review Comments Addressed (commit a4f3783) I've pushed fixes for the review feedback: 1. Localized Action Badge Labels (@tgolen's comment)
2. Optimized
|
|
@MelvinBot Address the failing prettier check and typescript check |
This change replaces the green/red dot indicators in the Left Hand Navigation with action badges that display a verb (Submit, Approve, Pay, Export) to describe the required action for expense reports. - Add ActionBadge component for rendering action badges - Add getReportActionBadge function to determine the appropriate action - Add ACTION_BADGE_VERBS constants for action labels - Update OptionRowLHN to conditionally render ActionBadge instead of dots - Add styles for action badge dot and text Co-authored-by: David Barrett <quinthar@users.noreply.github.com>
|
🤖 CI Check Analysis I've analyzed the failing checks on this PR: 1. ✅ Build Failure - Unrelated to PR changesThe Android build failure is not caused by this PR. The error is a pre-existing infrastructure issue: This is a dependency resolution issue with Evidence: Files changed in this PR:
None of these affect Android build configuration or native dependencies. 2. ❌ CLA Check - Requires action from PR authorThe CLA check failure requires the PR author to sign the Contributor License Agreement. This cannot be fixed by code changes. 3. ❌ PR Author Checklist - AdministrativeThis is an administrative check that requires the PR author to complete the checklist. Summary: The code-related checks (TypeScript, ESLint, Prettier, Tests) are all passing. The build failure is a flaky/infrastructure issue that should resolve on retry or is being addressed separately. |
|
🤖 PR Check Analysis I've analyzed the failing checks on this PR. Here's the breakdown: Failing Checks
AnalysisBuild Failure (Unrelated to PR)The Android hybrid build is failing due to an infrastructure/dependency issue with This is not related to the changes in this PR. The PR only modifies:
The Skia library configuration issue is a pre-existing infrastructure problem. CLA & ChecklistThese are standard PR housekeeping items that need to be addressed by the PR author/committers:
Passing Checks ✅
Recommendation: The code changes in this PR are passing all code quality checks. The failing checks are unrelated to the PR changes. |
|
Ya, Melvin seems to have failed his first test. I'll work with @dannymcclain on updating the design. Thanks for following along with this experiment! |
|
🤖 Build Failure Analysis The failing Build ErrorThe error is in Why This Is UnrelatedThis PR only modifies these TypeScript/React files:
The Android Gradle build error is in Other Failing Checks
RecommendationThis appears to be an infrastructure or dependency-related issue. Try:
|
|
Love the new design Danny came up with here!!
@dannymcclain can you add the full specs for the button sizes and such so we can update the PR to do this? |
| const [movedToReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(lastAction, CONST.REPORT.MOVE_TYPE.TO)}`, {canBeMissing: true}); | ||
|
|
||
| // Only fetch bankAccountList for expense reports to avoid unnecessary data fetching and re-renders | ||
| const isExpenseReport = isExpenseReportUtils(fullReport); |
There was a problem hiding this comment.
@MelvinBot we're showing GBR/RBR in chat report, not expense report.
As fullReport where you're trying to show badge is chat report, isExpenseReport will be always false.
|
🤖 Analysis of Failing Checks SummaryI've analyzed the failing checks on this PR. Here's what I found: Failing Checks
Build Failure AnalysisThe Android build failure is NOT related to this PR's changes. The error is: This is an infrastructure issue with the Evidence this is unrelated:
Other Failing Checks
Recommended Actions
The code changes in this PR (action badge localization and performance optimization) are correct and should not cause the Android build to fail. |
| ); | ||
| } | ||
|
|
||
| ActionBadge.displayName = 'ActionBadge'; |
There was a problem hiding this comment.
Don't add displayName if not necessary https://github.com/Expensify/App/blob/main/contributingGuides/STYLE.md#function-component-style
I wonder if Malving is checking the STYLE.md during development. It should be easy to self-check
|
🤖 Analysis of Failing Checks All code-related checks pass ✅:
The failing checks are unrelated to this PR's code changes: 1.
|
Specs are as follows:
For the inbox, I have them set to a fixed-width of Ideally we want this size of button to have all the same attributes and props as our other sizes (variants for Success, Danger, and regular; ability to add icons to either the left, right, or both; same colors and states as all the other sizes, etc.) cc @Expensify/design (will add this size to the component library soon) |
|
Thanks all for the great work on this. Let's pause for a moment as I raise for discussion here: https://expensify.slack.com/archives/C07HPDRELLD/p1770260453595379?thread_ts=1741658389.133729&cid=C07HPDRELLD (it's been a long time since this was originally proposed, so let's make sure we're still on board). Thanks! |
|
🤖 PR Check Failure Analysis I've analyzed the failing checks on this PR: 1. Build (Debug, true) - ❌ Unrelated to PRThe build failure is in the This is a third-party dependency/infrastructure issue unrelated to the code changes in this PR. The PR only modifies TypeScript/React components:
None of these files affect the Android Gradle build configuration. 2. Checklist - ❌ Action Required by PR AuthorThe checklist check is failing because there are 21 unchecked items in the PR author checklist. This requires the PR author to review and check off the applicable items in the PR description. 3. CLA - ❌ Contributor AgreementThis is a Contributor License Agreement check that needs to be handled by the contributor. Summary:
No code fixes are needed from this PR to address these failures. |
NEW SPECS 👀We're going to update all our badges throughout the app, so to keep things in harmony, I have some updated specs based on that issue that will help us achieve this:
The Specs
Not too different than what I posted above, but just want to make sure we keep everything tidy! |
JmillsExpensify
left a comment
There was a problem hiding this comment.
Part of an existing product initiative / discussion.
|
LGTM 👍 |
|
Yup, same 👍 |
|
|
|
Lines 8161 to 8169 in 4e4263f Here's mapping: 🟢 cases: 🔴 cases: |
|
I suggest we leave everything else just a 🟢 for now -- we can always go back and update them later if we choose to. |
|
@MelvinBot close this PR. New PR is ready |
|
Closed as requested by |







Strategy:
To expand into the "blue ocean" of untapped customers, we need a tool that is not only more powerful than currently available, but also lower cost. The key to lowering the cost of acquiring and retaining customers is building a product that is intuitive from the very first moment.
Background:
The Inbox is designed to show you what you need to do next. It does that by highlighting reports with actions that need to be taken using a :greenlight: GBR or 🔴 RBR.
Problem:
When an average user looks at a GBR/RBR in the Inbox, they often don't realize they are supposed to tap it or why, resulting in low clickthrough, engagement, and conversion.
Solution:
Replace GBR/RBR dots with an "action badge" that consists of:
Additionally, this badge will look more like a button (and FullStory shows that people tend to click on badges in the LHN even if they don't understand what they do). The goal of both is that we get greater clickthrough, enabling more people to see and thus engage with the final task the GBR/RBR is promoting.
Note: Originally discussed in this thread, and formally proposed in this P/S.
Explanation of Change
This PR replaces the GBR/RBR dot indicators in the Left Hand Navigation (LHN) with "action badges" that display a verb describing the required action for expense reports.
The changes include:
ActionBadgecomponent that renders a small dot with a verb label (Submit, Approve, Pay, or Export)getReportActionBadgefunction that determines which action badge to show based on the report's current state in the workflowOptionRowLHNto conditionally render the action badge instead of the dot indicator for expense reportsACTION_BADGE_VERBSconstants for the action labelsThe action badge color uses:
The workflow priority follows the expense report lifecycle:
Non-expense reports retain the existing dot indicator behavior.
Fixed Issues
$ https://github.com/Expensify/Expensify/issues/471552
PROPOSAL:
Tests
Offline tests
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps./** comment above it */thisproperly so there are no scoping issues (i.e. foronClick={this.submit}the methodthis.submitshould be bound tothisin the constructor)thisare necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);ifthis.submitis never passed to a component event handler likeonClick)Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari