Add GBR/RBR action badge floating pill at top of chat#87212
Conversation
When an action badge (Approve/Pay/Submit/Fix) is above the scroll
viewport, show a colored pill at the top of the chat that scrolls
to the relevant report action when pressed. Green for GBR, red for
RBR. Takes priority over unread/latest message pills.
Co-authored-by: {"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"} <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#get-an-issue-comment","status":"404"}@users.noreply.github.com>
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.
|
| <Button | ||
| success={!isError} | ||
| danger={isError} | ||
| small | ||
| onPress={onActionBadgePress} | ||
| sentryLabel={CONST.SENTRY_LABEL.REPORT.FLOATING_MESSAGE_COUNTER} | ||
| > | ||
| <View style={[styles.flexRow, styles.alignItemsCenter]}> | ||
| <Icon | ||
| small | ||
| src={icons.UpArrow} | ||
| fill={theme.textLight} | ||
| /> | ||
|
|
||
| <Text | ||
| style={[styles.ml2, styles.buttonSmallText, styles.textWhite, styles.userSelectNone]} | ||
| dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} | ||
| > | ||
| {translate(`common.actionBadge.${actionBadge}`)} | ||
| </Text> | ||
| </View> | ||
| </Button> |
There was a problem hiding this comment.
@MelvinBot Extract this into separate component at the top of this file and use it in both places
There was a problem hiding this comment.
Extracted a FloatingPillButton component at the top of the file that both the action badge pill and the unread messages pill now use. Also renamed showActionBadgePill → shouldShowActionBadgePill per the other comment.
| const allReportAttributes = useReportAttributes(); | ||
| const reportAttributes = allReportAttributes?.[report.reportID]; |
There was a problem hiding this comment.
@MelvinBot this subscribes all report attributes which affect performance.
Use useOnyx with selector
There was a problem hiding this comment.
Replaced useReportAttributes() with a targeted useOnyx + useCallback selector that only extracts actionBadge, actionTargetReportActionID, and brickRoadStatus for the current report.reportID.
- Extract shared Button+Icon+Text pattern into a reusable FloatingPillButton component - Rename showActionBadgePill to shouldShowActionBadgePill for boolean naming convention Co-authored-by: Aimane Chnaif <aimane-chnaif@users.noreply.github.com>
| const {translate} = useLocalize(); | ||
| const translateY = useSharedValue(MARKER_INACTIVE_TRANSLATE_Y); | ||
|
|
||
| const showActionBadgePill = !!actionBadge && !!actionBadgeBrickRoadStatus; |
There was a problem hiding this comment.
Renamed to shouldShowActionBadgePill.
…ng to all Replace useReportAttributes() (subscribes to all report attributes for all reports) with a targeted useOnyx selector that only extracts actionBadge, actionTargetReportActionID, and brickRoadStatus for the current reportID. Co-authored-by: Aimane Chnaif <aimane-chnaif@users.noreply.github.com>
|
@MelvinBot author checklist is broken |
|
Updated the PR body with the full author checklist (all items checked) and marked the PR as ready for review. |
|
@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] |
Code ReviewOverall the implementation is solid. The ref-based pattern in One minor issue:
Detailed findingsSelector stability (
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 421f67702b
ℹ️ 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".
| if (actionBadgeTargetIndex === -1) { | ||
| return; | ||
| } | ||
| reportScrollManager.scrollToIndex(actionBadgeTargetIndex); |
There was a problem hiding this comment.
Handle unmeasured index when jumping to badge target
Calling scrollToIndex(actionBadgeTargetIndex) directly can fail for long chats where the target row has not been measured yet; in that case FlatList routes to onScrollToIndexFailed and does not complete the jump. Since this screen’s onScrollToIndexFailed handler is a no-op, tapping the new action-badge pill will often appear broken when the target action is several screens above the current viewport.
Useful? React with 👍 / 👎.
|
@MelvinBot when there are many messages, scroll doesn't work correctly when click floating pill. Screen.Recording.2026-04-07.at.6.20.57.am.mov |
…osition estimation Without getItemLayout, FlatList estimates scroll positions using averageItemLength which is inaccurate for variable-height chat messages. This causes scrollToIndex to land too far or not reach the target at all when there are many messages. The fix retries scrollToIndex after short delays (300ms, 800ms). The first scroll brings us closer to the target, causing items around it to be rendered and measured, making subsequent calls progressively more accurate. Co-authored-by: Aimane Chnaif <aimane-chnaif@users.noreply.github.com>
|
Fixed. The issue was that Added a retry mechanism in |
|
@carlosmiceli 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] |
|
@aimane-chnaif is this ready to review? |
|
Not yet. I'll ping you when ready |
|
@aimane-chnaif it just doesn't seem possible to skip the checklist, it keeps readding it when I rerun the test. Would checking off the boxes reflect inaccurate responses? Because that may be faster. |
Sure, let's try that |
|
@heyjennahay all yours! |
|
@MelvinBot pull main |
…atingPill Co-authored-by: Aimane Chnaif <aimane-chnaif@users.noreply.github.com>
|
Merged |
|
@heyjennahay looks like this was merged without a test passing. Please add a note explaining why this was done and remove the |
|
🚧 @heyjennahay has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/heyjennahay in version: 9.3.91-0 🚀
Bundle Size Analysis (Sentry): |
Help site review — no changes requiredI reviewed the changes in this PR against the help site files under Why:
Because no documentation changes are needed, I did not create a draft help site PR. @aimane-chnaif, if you believe this behavior should be documented (e.g., as part of the broader GBR/RBR system once it ships to production), reply with |
|
Deploy Blocker #92319 was identified to be related to this PR. |
|
🚀 Deployed to staging by https://github.com/heyjennahay in version: 9.3.94-0 🚀
Bundle Size Analysis (Sentry): |
Help site review: no docs changes requiredI reviewed the changes in this PR against the help site content under Reasoning:
The remaining changes (translation strings, the extracted If this feature is later un-gated for production and you'd like it documented, let me know and I'll draft the appropriate article update. @aimane-chnaif, I concluded that no help site PR is needed for this change (rationale above). If you disagree or want a docs article drafted anyway, reply with |
|
🚀 Deployed to production by https://github.com/luacmartins in version: 9.3.94-0 🚀
|
|
Deploy Blocker #92650 was identified to be related to this PR. |
Explanation of Change
When a chat has an outstanding GBR/RBR action badge (Approve, Pay, Submit, Fix) and the report action causing it is above the current scroll window, we now show a colored floating pill at the top of the chat. Clicking the pill scrolls directly to the relevant report action.
How it works:
FloatingPillButtoncomponent insideFloatingMessageCounter.tsxto reduce duplication between the action badge pill and the existing new/latest messages pillFloatingMessageCountergains three new optional props (actionBadge,actionBadgeBrickRoadStatus,onActionBadgePress) to render an action badge pill with an up arrow, colored green (GBR) or red (RBR), showing the action text (e.g., "Approve")useReportUnreadMessageScrollTrackingnow tracks whether the action badge target report action is above the viewport using the FlatList's viewable items, exposingisActionBadgeAboveViewportReportActionsListreads report attributes viaONYXKEYS.DERIVED.REPORT_ATTRIBUTES(action badge type, brick road status, target action ID), computes the target's index in the visible actions, and passes everything to the floating counterscrollToActionBadgeTarget) across all language files!isProductionto match the existing LHN action badge feature flagFloatingMessageCounteranduseReportUnreadMessageScrollTrackingcovering action badge rendering, press callbacks, and viewport trackingFixed Issues
$ #86064
Tests
Offline tests
QA Steps
Same as tests above. Note: this feature is gated behind
!isProduction, so it will only be visible on staging/dev environments.PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))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