Skip to content

PR7: Remove global Onyx reference from getOriginalReportID - #96669

Merged
mollfpr merged 10 commits into
Expensify:mainfrom
cretadn22:refactor/66419-finish-getOriginalReportID
Aug 4, 2026
Merged

PR7: Remove global Onyx reference from getOriginalReportID #96669
mollfpr merged 10 commits into
Expensify:mainfrom
cretadn22:refactor/66419-finish-getOriginalReportID

Conversation

@cretadn22

@cretadn22 cretadn22 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Fixed Issues

$ #66419
PROPOSAL: #66419

Tests

  • Verify that no errors appear in the JS console

Offline tests

QA Steps

// TODO: These must be filled out, or the issue title must include "[No QA]."

  1. Go to any chat
  2. Hover over any message
  3. Add an emoji reaction, then click the emoji again to remove it
  4. Verify that adding and clearing the reaction works as expected
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Screen.Recording.2026-07-28.at.00.39.10.mov

@cretadn22
cretadn22 requested review from a team as code owners July 21, 2026 17:53
@melvin-bot
melvin-bot Bot requested review from heyjennahay and situchan and removed request for a team July 21, 2026 17:53
@melvin-bot

melvin-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

@situchan 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]

@melvin-bot
melvin-bot Bot removed request for a team and heyjennahay July 21, 2026 17:53
@cretadn22
cretadn22 marked this pull request as draft July 21, 2026 17:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2cc87765ad

ℹ️ 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".

Comment thread src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions.tsx Outdated
Comment thread src/components/Reactions/ReportActionItemEmojiReactions.tsx Outdated
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

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.

Files with missing lines Coverage Δ
...nents/Reactions/ReportActionItemEmojiReactions.tsx 48.38% <100.00%> (+1.72%) ⬆️
src/libs/ReportUtils.ts 86.67% <100.00%> (-0.03%) ⬇️
src/libs/actions/EmojiReactions.ts 93.93% <100.00%> (ø)
src/libs/actions/Report/index.ts 72.12% <100.00%> (+0.04%) ⬆️
...report/ContextMenu/BaseReportActionContextMenu.tsx 86.14% <ø> (ø)
...ort/ContextMenu/PopoverReportActionContextMenu.tsx 44.49% <ø> (ø)
...pages/inbox/report/ReportActionItemMessageEdit.tsx 58.33% <100.00%> (+0.23%) ⬆️
...rt/ReportActionCompose/ComposerWithSuggestions.tsx 61.20% <60.00%> (-0.10%) ⬇️
...es/inbox/report/ContextMenu/ContextMenuActions.tsx 39.53% <25.00%> (ø)
... and 22 files with indirect coverage changes

@cretadn22
cretadn22 marked this pull request as ready for review July 27, 2026 17:41

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ebc9743265

ℹ️ 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".

Comment thread src/components/Reactions/ReportActionItemEmojiReactions.tsx
@situchan

Copy link
Copy Markdown
Contributor

job 2, typescript failing

@cretadn22

Copy link
Copy Markdown
Contributor Author

@situchan The PR is ready

@MelvinBot

Copy link
Copy Markdown
Contributor

Review

Looks good — this is a clean, correct continuation of the "remove global Onyx from getOriginalReportID" refactor. I traced every caller and the wiring is consistent. No blocking issues.

What changed: getOriginalReportID drops the reportActionsParam ?? allReportActions?.[…] fallback, so callers must now thread in the report's actions. toggleEmojiReaction, deleteReportComment, and saveReportActionDraft grow a reportActions param and pass it through.

Verified:

  • All 13 getOriginalReportID callers pass an explicit reportActions — none rely on the removed global fallback.
  • At every call site the reportActions argument matches the reportID argument (e.g. ReportActionItemEmojiReactions, PopoverReportActionContextMenu both key off the same reportID). Since the global cache and useOnyx(REPORT_ACTIONS + reportID) both resolve to the same collection, behavior is preserved.
  • The context-menu payload is built once in BaseReportActionContextMenu and feeds both renderContent and onPress, so reportActions/originalReportActions reach the emoji toggle and the edit action.
  • deleteReportComment correctly keeps originalReportActions for whisper-hiding and uses the new reportActions only for getOriginalReportID — the added param comments match the actual usage.
  • All test callers (ReportTest, AttachmentTest, UnreadIndicatorsTest) were updated to the new arity.
Minor observations (non-blocking)
  • The editAction fallback originalReportID ?? reportID paired with originalReportActions ?? reportActions is safe: even in the mixed case (using originalReportID but falling back to reportActions), getOriginalReportID still resolves to originalReportID because the parent action is present in reportActions.
  • Behavioral tradeoff inherent to this refactor series: useOnyx can briefly return undefined before hydration, whereas the old global cache was eagerly populated. In practice these interactions (react/edit/delete) only fire once the report's actions are loaded and rendered, so this shouldn't regress — just flagging it as the one place the two sources aren't identical.

I did not run typecheck/CI locally; the automated checks on the PR remain the source of truth for that.

Comment thread src/libs/actions/Report/index.ts Outdated
@MelvinBot

Copy link
Copy Markdown
Contributor

Potential regression found

Draft auto-save while editing a message can silently stop working in active reports.

In ComposerWithSuggestions.tsx:290-297 this PR adds reportActions to the dependency array of the useCallback that is fed into useDebouncedSaveDraft:

const {saveDraft: debouncedSaveReportActionDraft} = useDebouncedSaveDraft(
    useCallback(
        (comment) => saveReportActionDraft(reportID, editingReportAction, reportActions, comment),
        [reportID, editingReportAction, reportActions], // reportActions is new
    ),
);

reportActions comes from useOnyx(REPORT_ACTIONS + reportID), so its reference changes on any report-action activity (new message, reaction, read receipt, optimistic update, Pusher event). Every time it changes, the useCallback gets a new identity, which makes useDebounce's effect re-run and cancel() the pending debounced save without rescheduling it. useDebounce's own docstring warns about exactly this: "Every time the identity of any of the arguments changes, the debounce operation will restart (canceling any ongoing debounce)… pass stable references."

The debounce window is 1000ms. So in a busy report, if a report action arrives within 1s of the user's last keystroke, the queued draft-save is dropped. Before this PR the closure depended only on [reportID, editingReportAction] (stable during an edit), so the save fired reliably.

Repro: open a high-traffic chat, start editing a message, type, pause, and while another message/reaction lands in that report (<1s), navigate away or reload before typing again → the in-progress edit is not persisted as a draft. This defeats the stated purpose of the debounced save ("allows one to navigate somewhere else and come back to the comment and still have it in edit mode").

Suggested fix: keep the debounced callback identity stable — read the latest reportActions through a ref inside the callback (updated via useEffect) instead of adding it to the dependency array, so the debounce isn't reset on every report-action change. The same applies to the handleKeyPress/onSelectionChange useCallbacks that gained reportActions in their deps.

Other things I checked (no functional regression)
  • getOriginalReportID branch changes: No regression. The removed global fallback was allReportActions[reportID]; every updated caller now passes useOnyx(REPORT_ACTIONS + reportID) for the same reportID, which is the identical collection. I verified the reportID/reportActions pairing holds at all sites, including the originalReportID ?? reportID / originalReportActions ?? reportActions fallback in ContextMenuActions.tsx:628 (both paths resolve to the same result).
  • useOnyx hydration timing: during the brief pre-hydration window reportActions is undefined, but for the common (non-thread-parent, non-transaction-thread) message this converges to the same reportID the old path returned, so no user-visible change.
  • Secondary (perf, not correctness): ReportActionItemEmojiReactions.tsx:82 now subscribes each reaction row to the whole REPORT_ACTIONS collection just to resolve the original report ID on click, so every reaction row re-renders on any report-action change. Consider resolving it lazily inside the click handler instead of subscribing.

I analyzed this from the code; I did not runtime-verify the draft-loss repro. Confidence: medium-high on the mechanism.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd4f9140b1

ℹ️ 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".

saveReportActionDraft(reportID, editingReportAction, reportActions, comment);
},
[reportID, editingReportAction],
[reportID, editingReportAction, reportActions],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep edit-draft debounce independent of report actions

Including the whole reportActions object in this debounced callback makes any report-action Onyx update in the active chat (for example, another message arriving or being edited) recreate the callback; useDebounce cancels the pending trailing call whenever its func changes. In narrow-layout edit mode, if that happens within the draft-save debounce window after the user types and they then navigate away or reload, the latest edit draft is never written to Onyx, so keep the debounced saver stable and pass/resolve the needed owner actions without depending on the mutable collection.

Useful? React with 👍 / 👎.

@situchan

situchan commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
android2.mov
Android: mWeb Chrome
iOS: HybridApp
ios2.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
web2.mov

Comment thread src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions.tsx Outdated
@situchan

situchan commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Regression Test Cases

Emoji reactions (toggleEmojiReaction)

  1. Go to any chat, hover a message, add an emoji reaction, then click the same emoji to remove it → reaction adds and clears correctly.
  2. Open a thread (reply to a message, enter the thread). Add and remove a reaction on the thread's parent message shown at the top → reaction adds/removes and persists after leaving and re-entering the thread.
  3. On a money-request/expense report, add a reaction to the request preview message → reaction applies to the correct message.
  4. React while offline, then go back online → the reaction syncs; no duplicate or orphaned reaction.

Editing a message + draft persistence (saveReportActionDraft)

  1. Edit a sent message, type some text, then close the edit and reopen it → the drafted (unsent) edit is still there.
  2. Edit the parent message of a thread from inside the thread, type, navigate away, come back → the edit draft is preserved under the correct message.
  3. Busy-report draft check (regression-sensitive): In a high-traffic chat (or have a second account send messages), start editing a message, type a few words, pause ~1–2s while new messages/reactions arrive in that report, then navigate away and back without typing again → the in-progress edit must still be saved as a draft. (This is the debounce-cancel scenario; verify the draft isn't lost.)
  4. Repeat step 7 on mWeb / narrow layout (inline edit in the composer), which is the path that uses the debounced save.

Deleting a comment (deleteReportComment)

  1. Delete a normal comment in a chat → it's removed and the LHN last-message preview updates correctly.
  2. Delete a comment that has an actionable mention whisper attached (e.g. mention a user not in the room, then delete that message) → the associated whisper is also hidden/removed.
  3. Delete the parent message of a thread → the thread reflects the deletion correctly.
  4. Delete a comment while offline, then reconnect → deletion syncs without error.

Priority focus: Steps 7 and 8 target the one real regression risk (debounced edit-draft save being cancelled in active reports). Steps 2, 6, 11 cover thread-parent resolution, which is where the removed global fallback mattered most. If the debounce fix is applied, re-verify 7–8 specifically.

@situchan

situchan commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@cretadn22 please address above comments.
The debounce regressions AI reported are edge-cases so I don't think we should block on this.

…component to simplify parameters and improve readability. Remove unnecessary comments in deleteReportComment function.
@cretadn22

Copy link
Copy Markdown
Contributor Author

@codex review

@situchan

situchan commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: b71a299344

ℹ️ 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".

@MelvinBot

Copy link
Copy Markdown
Contributor

Re-review — the flagged regression is fixed ✅

The debounce regression I raised earlier has been correctly addressed in b71a299, and all CI checks are green (typecheck, React Compiler Compliance, and all test jobs pass). No blocking issues.

How the fix resolves it: ComposerWithSuggestions.tsx:290 now passes the stable module-level saveReportActionDraft import straight into useDebouncedSaveDraft and threads reportID, editingReportAction, reportActions in at call time, instead of closing over reportActions in a useCallback. Because the debounced func identity is now stable, useDebounce's [func] effect no longer re-runs and cancel()s the pending save on every report-action change. This is exactly the pattern already used by ReportActionItemMessageEdit.tsx:158, so it's the idiomatic fix. lodash-debounce's trailing invocation uses the args from the latest call, so the newest reportActions/comment are still what gets persisted. 👍

Verification detail & remaining minor notes (non-blocking)
  • Correctness of the pattern: The wrapper arrow function inside useDebouncedSaveDraftImpl is stable only because React Compiler memoizes it (the documented reason for the "non-generic implementation" note). This is the same production-proven dependency ReportActionItemMessageEdit already relies on, and React Compiler Compliance passes on this PR, so it holds here.
  • Cosmetic: b71a299 also drops the two param-description comments in deleteReportComment (originalReportActions vs reportActions). Those two params are easy to mix up, so I'd lean toward keeping a one-line note there — purely optional.
  • Still open (perf, not correctness — same as before): ReportActionItemEmojiReactions.tsx:82 still subscribes every reaction row to the whole REPORT_ACTIONS collection just to resolve the original report ID on click, so each row re-renders on any report-action change. Could be resolved lazily inside the click handler. Not a regression, fine to defer.
  • getOriginalReportID rewiring: unchanged since my earlier review and still correct — every caller passes useOnyx(REPORT_ACTIONS + reportID) for the same reportID the removed global fallback used.

Runtime check: I tried to smoke-test the emoji-reaction add/remove and edit-draft-persist happy paths on web, but couldn't — those flows are gated behind a message-row hover, and the web driver has no hover/long-press/right-click primitive, so the UI never surfaced. This is a test-tooling limitation, not a code concern; the assessment above is from code analysis + green CI.

I agree with situchan's earlier call that the original debounce concern was edge-case-y and shouldn't block — and it's now been fixed outright regardless.

@MelvinBot

Copy link
Copy Markdown
Contributor

Regression sweep — no new regressions found ✅

I traced every path this PR touches. The one real regression previously flagged (debounced edit-draft save being cancelled in busy reports) has been fixed in b71a299, and all CI is green. Everything else resolves identically to the pre-PR behavior. Remaining items are perf/cosmetic only.

Why the core change is safe: getOriginalReportID dropped the global allReportActions fallback, so correctness now depends on every caller passing a reportActions collection keyed to the same reportID. I checked all five production call sites and that invariant holds at each one:

Call-site-by-call-site verification
  • ReportActionItemEmojiReactions.tsx:82reportActions and reportID are the same key. ✓
  • ComposerWithSuggestions.tsx:274 (both edit-draft paths + the ArrowUp/backspace saveReportActionDraft) → same key. ✓
  • ReportActionItemMessageEdit.tsx:94 → same key. ✓
  • ContextMenuActions.tsx emoji-toggle passes reportActions (the viewed report's actions) + reportID; the edit action uses the originalReportID ?? reportID / originalReportActions ?? reportActions pair. Both resolve correctly even in the mixed fallback case — for a thread-parent action, getOriginalReportID returns the parent via the report's parentReportID (Onyx report data), independent of which actions object is passed. ✓
  • PopoverReportActionContextMenu.tsx delete: both report (L347) and reportActions (L69) come from the same reportIDRef.current, and originalReportActions (used only for whisper-hiding) is unchanged. ✓

Cases I explicitly checked for divergence from the old global-fallback behavior — all match:

  • Normal message: action is in reportActions[reportID] → returns reportID. Same as before.
  • Thread-parent message (viewed from the thread): action is absent from the thread's own actions, so the isThreadReportParentAction branch returns parentReportID. Correct — and this actually requires passing the viewed report's actions (not the parent's), which the emoji/delete paths do. Covered by the new tests in ReportTest/ReportUtilsTest.
  • One-transaction expense report: getOneTransactionThreadReportID receives the same full report actions it did via the global before, so it resolves the transaction thread identically.
  • withDEWRoutedActionsObject selector on the context-menu reportActions (BaseReportActionContextMenu.tsx:178) is additive only (injects synthetic routed actions, never removes real ones), so the reportActions[reportActionID] presence check is never broken by it.
  • Onyx hydration window: if reportActions is briefly undefined, a normal message still returns reportID (the not-found branch falls through to reportID), and these interactions only fire after the report's actions are rendered anyway.
Remaining non-blocking items (unchanged from prior review)
  • Perf (not a regression): ReportActionItemEmojiReactions.tsx:82 subscribes every reaction row to the whole REPORT_ACTIONS collection just to resolve the original report ID on click, so each row re-renders on any report-action change. Could be resolved lazily inside the click handler.
  • Cosmetic: b71a299 removed the two param-description comments distinguishing originalReportActions from reportActions in deleteReportComment — those two params are easy to confuse, so a one-line note there wouldn't hurt.
  • Note: a richer useOriginalReportID hook already exists and verifies transaction-thread membership more strictly than the legacy getOriginalReportID being edited here. Not in scope for this PR, but worth keeping in mind for the rest of the refactor series.

Runtime check: I couldn't drive these flows on web — reactions/edit/delete are all reached through a hover-revealed toolbar, and the ArrowUp/Escape edit path needs raw key events; the web test driver supports neither. So the assessment above is from code analysis + the green test suite (which includes the new thread-parent and one-transaction unit tests for exactly these paths).

Bottom line: the debounce regression is fixed, and I found no further regressions in the getOriginalReportID rewiring. Good to go from a regression standpoint.

@situchan

situchan commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@cretadn22 are all comments addressed? Please pull main

@cretadn22

Copy link
Copy Markdown
Contributor Author

@situchan All requested updates are in place and ready for review

@melvin-bot
melvin-bot Bot requested a review from mollfpr August 3, 2026 16:34
@mollfpr
mollfpr merged commit 7a99624 into Expensify:main Aug 4, 2026
35 checks passed
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🚧 mollfpr has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

OSBotify commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants