Skip to content

refactor: extract common selection mode logic into shared hooks#92518

Merged
puneetlath merged 10 commits into
Expensify:mainfrom
Krishna2323:krishna2323/issue/72502-refactor
Jun 29, 2026
Merged

refactor: extract common selection mode logic into shared hooks#92518
puneetlath merged 10 commits into
Expensify:mainfrom
Krishna2323:krishna2323/issue/72502-refactor

Conversation

@Krishna2323

@Krishna2323 Krishna2323 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Fixed Issues

$ #72502
PROPOSAL:

Tests

Prerequisites: Have a workspace with at least 2-3 expense reports containing multiple transactions. Some reports should have held expenses, some should be ready for approval/payment.

1. Mobile - Submit flow (selection mode)

  1. Open a report with submittable expenses on mobile
  2. Long-press a transaction to enter selection mode
  3. Select all transactions
  4. Tap "Submit" from the action menu
  5. Verify the report is submitted successfully and selection mode turns off

2. Mobile - Submit with pending RTER

  1. Open a report that has only pending card transactions (RTER)
  2. Enter selection mode and select all
  3. Tap "Submit"
  4. Verify a confirmation modal appears warning about pending transactions
  5. Confirm and verify the report submits, selection mode turns off

3. Mobile - Approve flow (selection mode)

  1. Open an approvable report on mobile (submitted to you)
  2. Enter selection mode, select all transactions
  3. Tap "Approve"
  4. Verify the report is approved and selection mode turns off

4. Mobile - Approve with held expenses

  1. Open an approvable report that has held expenses
  2. Enter selection mode, select all transactions
  3. Tap "Approve"
  4. Verify the hold menu appears (asking about held expenses)
  5. Confirm or dismiss and verify correct behavior

5. Mobile - Pay flow (selection mode)

  1. Open a payable report on mobile
  2. Enter selection mode, select all transactions
  3. Tap the "Pay" option, then select a payment method (e.g., "Pay elsewhere")
  4. Verify the payment goes through and selection mode turns off

6. Mobile - Pay with held expenses

  1. Open a payable report that has some held expenses
  2. Enter selection mode, select all transactions
  3. Tap "Pay" → choose a method
  4. Verify the hold menu appears
  5. Confirm payment of non-held amount, verify selection mode turns off

7. Mobile - Pay invoice

  1. Open an invoice report that you can pay
  2. Enter selection mode, select all transactions
  3. Tap "Pay" → choose a method
  4. Verify the invoice is paid and selection mode turns off

8. Mobile - Delegate access restriction

  1. Log in as a delegate with restricted access
  2. Open a payable/approvable report, enter selection mode
  3. Tap "Pay" or "Approve"
  4. Verify the "Delegate No Access" modal appears immediately (no lag)

9. Mobile - Locked account

  1. Use a locked account
  2. Enter selection mode on a payable report
  3. Tap "Pay"
  4. Verify the locked account modal appears immediately

10. Desktop - Submit flow (selection mode)

  1. Open a submittable report on desktop/web
  2. Select transactions using checkboxes
  3. Click the dropdown → "Submit"
  4. Verify the report is submitted and selections clear

11. Desktop - Approve flow (selection mode)

  1. Open an approvable report on desktop/web
  2. Select transactions → click dropdown → "Approve"
  3. Verify approval succeeds, animation plays, selections clear

12. Desktop - Pay flow (selection mode)

  1. Open a payable report on desktop/web
  2. Select all transactions → click dropdown → choose "Pay" → select method
  3. Verify the payment animation plays, payment succeeds, selections clear

13. Desktop - Pay with held expenses

  1. Open a payable report with held expenses on desktop
  2. Select all → "Pay" → choose method
  3. Verify the hold menu modal appears
  4. Confirm → verify non-held amount is paid, selections clear

14. Desktop - Workspace policy payment option

  1. Be an admin of a workspace with a verified business bank account
  2. Open a payable IOU report (not from your own account) on desktop
  3. Select all → "Pay"
  4. Verify you see the workspace bank account option in the submenu
  5. Select it, verify KYC/payment flow works correctly

15. Regression - Search refresh after payment

  1. Navigate to the Search tab (reports view)
  2. Open a payable report from search results
  3. Enter selection mode, pay the report
  4. Verify the search results refresh automatically after payment
  • Verify that no errors appear in the JS console

Offline tests

  • Same as tests

QA Steps

  • Same as tests

  • 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
    • MacOS: Desktop
  • 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 verified there are no new alerts related to the canBeMissing param for useOnyx
  • 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
andorid_hybrid.mp4
Android: mWeb Chrome
android_mWeb.mp4
iOS: Native
ios_hybrid.mp4
iOS: mWeb Safari
ios_mWeb.mp4
MacOS: Chrome / Safari
web_chrome.mp4

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
…e payment select

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@Krishna2323

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

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

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@codecov

codecov Bot commented Jun 3, 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 Δ
...tReportView/SelectionToolbar/SelectionDropdown.tsx 64.28% <ø> (ø)
src/hooks/useLifecycleActions.tsx 55.30% <100.00%> (+55.30%) ⬆️
src/hooks/useSelectionModeReportActions.ts 91.17% <95.65%> (+5.21%) ⬆️
.../MoneyRequestReportView/SelectionToolbar/index.tsx 76.04% <0.00%> (-1.62%) ⬇️
src/libs/SearchRefreshUtils.ts 0.00% <0.00%> (ø)
...aderActions/MoneyReportHeaderSelectionDropdown.tsx 0.93% <4.54%> (+0.34%) ⬆️
src/hooks/useSelectionModePayment.ts 61.61% <61.61%> (ø)
... and 12 files with indirect coverage changes

@Krishna2323 Krishna2323 marked this pull request as ready for review June 4, 2026 09:07
@Krishna2323 Krishna2323 requested review from a team as code owners June 4, 2026 09:07
@melvin-bot melvin-bot Bot requested review from JmillsExpensify and situchan and removed request for a team June 4, 2026 09:07
@melvin-bot

melvin-bot Bot commented Jun 4, 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]

@Krishna2323

Copy link
Copy Markdown
Contributor Author

I'll add the recordings later today.

@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: 3460dabf33

ℹ️ 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/hooks/useSelectionModePayment.ts
Signed-off-by: krishna2323 <belivethatkg@gmail.com>
…obile

Signed-off-by: krishna2323 <belivethatkg@gmail.com>

@JmillsExpensify JmillsExpensify left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test cases look good to me, though no product review required.

@Krishna2323

Copy link
Copy Markdown
Contributor Author

Recordings added. @situchan could you please start reviewing?

@Krishna2323

Copy link
Copy Markdown
Contributor Author

@situchan friendly bump

@situchan

situchan commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

On my list today

@JmillsExpensify

Copy link
Copy Markdown
Contributor

Is this still on your list?

@situchan

Copy link
Copy Markdown
Contributor

oh sorry

@Krishna2323 please fix conflict

policyID: moneyRequestReport?.policyID,
onPress: (params: PaymentActionParams) => confirmPaymentRef.current(params),
shouldHidePaymentOptions,
shouldShowApproveButton: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Behavior change to confirm (mobile): the shared hook hardcodes shouldShowApproveButton: false / shouldDisableApproveButton: false, but the old mobile useSelectionModeReportActions passed real values:

const shouldShowApproveButton = canApproveIOU(report, policy, reportMetadata, currentUserAccountID, transactions) && !hasOnlyPendingTransactions;
const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(report);

usePaymentOptions uses these to append (or, when pay is hidden, return only) an Approve entry inside the Pay sub‑menu. With both pinned to false, that Approve item no longer appears in the mobile Pay sub‑menu.

In practice this looks harmless — mobile already renders a dedicated top‑level Approve action (hasApproveAction), and the desktop dropdown always passed false here, so this aligns mobile with desktop and removes a duplicate. Flagging only to confirm the removal of the sub‑menu Approve entry is intentional and that there's no report state where the sub‑menu was the only path to Approve.

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Code review — clean refactor, no high-confidence bugs.

I traced the extraction of selection-mode/dropdown money-report logic into the new shared hooks (useSelectionModePayment, useLifecycleActions) and SearchRefreshUtils, and compared each moved path against main. The submit / approve / pay / hold-menu flows, the KYC onWorkspacePolicySelect routing, and the handleHoldMenuConfirm cleanup are all behavior-preserving. Nicely structured de-duplication.

I left one inline comment on a concrete behavior change worth an explicit confirm, plus two low-confidence notes below.

Behavior change to confirm (inline on src/hooks/useSelectionModePayment.ts): shouldShowApproveButton/shouldDisableApproveButton are now hardcoded false, whereas the old mobile useSelectionModeReportActions passed real values. This removes the Approve entry from the mobile Pay sub-menu. In practice it looks harmless (mobile already renders a dedicated top-level Approve action, and desktop always passed false here, so this aligns the two) — flagging only to confirm it's intentional.

Low-confidence notes (no action needed if intentional):

  • src/hooks/useSelectionModePayment.ts:99 now reads the user email from session?.email, where the old MoneyReportHeaderSelectionDropdown used useCurrentUserPersonalDetails().email. Both resolve to the current user, but worth a glance to be sure there's no edge case where they diverge.
  • Mobile submit/approve in useLifecycleActions now derives its own transaction set from the full report (useTransactionsAndViolationsForReport, including pending-delete) and report actions via getFilteredReportActionsForReportView, rather than the previously-passed selection values. This matches the existing desktop path, so it should be fine — calling it out since the input source changed.

No blocking issues from my side.

@situchan

Copy link
Copy Markdown
Contributor

@Krishna2323 please fix conflict

^

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@Krishna2323

Copy link
Copy Markdown
Contributor Author

@situchan conflicts resolved

Comment thread src/hooks/useSelectionModePayment.ts Outdated

@situchan situchan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's conflict with #93038

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
…ode payment

Signed-off-by: krishna2323 <belivethatkg@gmail.com>
@Krishna2323

Copy link
Copy Markdown
Contributor Author

@situchan could you please review this with priority? It's been 3 weeks since the PR was opened, and the longer it sits, the higher the chance of regressions since this is a refactor. Thanks!

@situchan

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: d6f2663743

ℹ️ 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 (post conflict-resolution) — still a clean, behavior-preserving extraction.

I re-traced the shared hooks against main now that conflicts are resolved and the earlier review comments are addressed. The two fixes look correct:

  • onWorkspacePolicySelect is now passed with the shouldBlockAction() guard to the mobile MoneyReportHeaderKYCDropdown, matching desktop ✅
  • Account-validation now routes via Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.VERIFY_ACCOUNT.path)) per situchan's note ✅

Payment, hold-menu, pay-invoice-vs-pay-request, KYC routing, delegate/locked-account guards, and search refresh are all byte-equivalent to the originals. Two things still worth an explicit confirm before merge:

1. Mobile submit/approve guards now include pending-delete transactions (medium confidence — likely intentional)

useLifecycleActions.tsx:87 derives its set as Object.values(reportTransactions) from useTransactionsAndViolationsForReport, which includes pendingAction === DELETE transactions. The old mobile path passed transactionsWithoutPendingDelete. The actual submitted/approved set is unchanged (both pass the whole moneyRequestReport to the server), but the guards now run over the full set, so edge cases can flip:

  • hasOnlyPendingCardTransactions(transactions) (.every(...)) — a report whose only non-deleted transactions are pending card transactions, but which also has a pending-delete non-card transaction, used to show the "pending transactions" block modal and now won't.
  • Strict-policy / hold blocking could similarly shift if a pending-delete transaction is held or carries a violation.

This matches the canonical desktop useLifecycleActions behavior, so it's probably the intended unification — just confirm it's deliberate.

2. Approve option dropped from the mobile pay sub-menu — already tracked in the open inline thread on useSelectionModePayment.ts:224 (shouldShowApproveButton/shouldDisableApproveButton hardcoded false). Mobile previously computed these; desktop always passed false, so this aligns the two. Approve is still reachable via the top-level mobile action, so low impact — just resolve that thread to confirm intent.

Note: test coverage gap (no action strictly required)

tests/unit/hooks/useSelectionModeReportActions.test.ts is net -197 lines, removing direct tests of the delegate/locked/unvalidated guard branches and the confirmPayment/confirmApproval hold/invoice branches. Those weren't backfilled — there is no test file for the new useSelectionModePayment or for useLifecycleActions. So the moved guard/payment logic (including the Finding 1 edge cases) is now unit-test-uncovered on this path. Worth a follow-up if not added here.

No blocking issues from my side — both items above are "confirm it's intentional," not "this is broken."

@situchan

situchan commented Jun 26, 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
android.mov
Android: mWeb Chrome
iOS: HybridApp
ios.mov
ios2.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
web.mov

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Regression hunt — 2 behavior changes worth confirming, no new breakage found.

I ran an adversarial sweep across every changed file (desktop dropdown −248 lines, mobile hook −381 lines, toolbar plumbing, and the new shared hooks), comparing each moved path against main (merge-base 76bc375). Two genuine behavior changes — both already noted in my prior review — are the only regressions. Everything else is a faithful extraction.

1. Mobile submit/approve guards now include pending-delete transactions
useLifecycleActions.tsx:87 derives its set from the full report (includes pendingAction === DELETE), where the old mobile path used transactionsWithoutPendingDelete. The submitted/approved set is unchanged (server gets the whole report), but guards shift in edge cases — e.g. hasOnlyPendingCardTransactions (.every(...)) and strict-policy/hold blocking. Matches desktop, so likely the intended unification — confirm it's deliberate.

2. Approve option dropped from the mobile pay sub-menu
useSelectionModePayment.ts:224-225 hardcodes shouldShowApproveButton/shouldDisableApproveButton to false; mobile previously computed them. Aligns mobile to desktop and Approve is still reachable via the top-level action, so low impact. Tracked in the open inline thread — resolve to confirm intent.

Specifically verified as NOT regressed (the traps I checked)
  • 'Building' icon dropped from PAYMENT_ICONS (desktop) → not a regression: the workspace-policy submenu moved into the hook, which has its own useMemoizedLazyExpensifyIcons([...,'Building']) and supplies the icon. Test case Move the keyChanged trigger to after persistent storage is set #14 (workspace bank account pay option) is safe.
  • All removed desktop Onyx subscriptions (nextStep, introSelected, betas, billing-grace-period keys, amountOwed, activePolicyID, isSelfTourViewed, allTransactionViolations, delegateEmail, isUserValidated) are reproduced inside the shared hook and fed into identical payInvoice/payMoneyRequest/selectPaymentType/hasViolations calls.
  • onWorkspacePolicySelect is fully threaded (SelectionDropdown → MoneyReportHeaderKYCDropdown) and correctly guarded by shouldBlockAction().
  • Mobile behaviors preserved: turnOffMobileSelectionMode after each action, the iOS InteractionManager hold-menu deferral (Pay only, matching original), selectedVBBAToPayFromHoldMenu, RTER/pending-card block modal on submit, and search-refresh-after-payment (refreshSearchAfterReportAction is byte-equivalent to the old inline search() guard).
  • email source (session?.email vs useCurrentUserPersonalDetails().email) resolves to the same value.
  • No hook-ordering, stale-closure, dependency-array, or return-contract regressions — all useOnyx/useMemo/custom hooks run unconditionally before any early return; every field each consumer destructures exists in the hooks' return objects; the new confirmPaymentRef + per-render useEffect avoids stale closures.

Net: a clean refactor. The only open questions are the two confirms above — neither is a hard break.

@melvin-bot melvin-bot Bot requested a review from puneetlath June 29, 2026 09:43
@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Re-review — still a clean refactor. No new findings; CI green, Codex 👍.

I re-read the full diff against the current head (d6f2663, unchanged since my last pass) and re-verified each moved path against main. The extraction into useSelectionModePayment + useLifecycleActions + SearchRefreshUtils remains behavior-preserving across submit / approve / pay / hold-menu / KYC / workspace-policy flows. All 33 checks pass.

Only two intentional behavior changes remain open to confirm (both flagged previously, neither a hard break):

  1. Mobile submit/approve guards now include pending-delete transactionsuseLifecycleActions.tsx:87 derives its set from the full report (matching desktop), where the old mobile path excluded pendingAction === DELETE. The submitted set is unchanged; only the guard inputs shift in edge cases.
  2. Approve dropped from the mobile pay sub-menuuseSelectionModePayment.ts:224-225 hardcodes shouldShowApproveButton/shouldDisableApproveButton to false. Approve is still reachable via the top-level action, and this aligns mobile with desktop.

Both align mobile to the existing desktop behavior, so they look deliberate — just resolve the open inline thread to confirm intent.

One non-blocking observation: on mobile, onSelectionModePaymentSelect calls shouldBlockAction(iouPaymentType) synchronously and then basePaymentSelect calls it again inside runAfterInteractions. It's harmless (the sync call returns early when blocked, so the deferred one only runs when not blocked, where it has no side effects) and the code comment already explains it.

Net: no blocking issues from my side. The reviewer (situchan) should drive the final functional/QA sign-off, especially test cases #6/#13 (hold menu) and #14 (workspace bank-account pay option) since those flows moved.

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: d6f2663743

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

@puneetlath

Copy link
Copy Markdown
Contributor

@Krishna2323 can you add a PR description that explains what the PR is doing and why. Thanks!

@puneetlath puneetlath merged commit f3b2ca3 into Expensify:main Jun 29, 2026
34 of 35 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

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

@OSBotify

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.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/puneetlath in version: 9.4.25-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@mitarachim

mitarachim commented Jul 1, 2026

Copy link
Copy Markdown

@Krishna2323 This PR is failing in test 9 because of a regression issue #95026

Screenshot 2026-07-01 at 1 23 03 PM

The issue is reproducible in: iOS

Bug7195925_1782859559884.App_Freeze.mp4

@OSBotify

OSBotify commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/cristipaval in version: 9.4.25-2 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

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.

7 participants