Skip to content

Use ActivityIndicator for embedded withFullTransactionOrNotFound loader - #96835

Open
MelvinBot wants to merge 3 commits into
mainfrom
claude-iouWithFullTransactionLoaderActivityIndicator
Open

Use ActivityIndicator for embedded withFullTransactionOrNotFound loader#96835
MelvinBot wants to merge 3 commits into
mainfrom
claude-iouWithFullTransactionLoaderActivityIndicator

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Part of the IOU cluster loader refactor (parent: #69850).

withFullTransactionOrNotFound renders a loading indicator (when shouldShowLoadingIndicator is true) before the wrapped step mounts. In practice only the Amount step passes shouldShowLoadingIndicator = true, and it is used in two contexts:

  • Standalone RHP route (IOURequestStepAmountWithFullTransactionOrNotFound, the default export) — no chrome behind the loader, so the fullscreen loader must stay.
  • Embedded on IOURequestStartPage (IOURequestStepAmountWithTransactionOnly) — the start page's header and tab bar stay visible, so a fullscreen loader on top of them violates UI-1.

Since the same wrapped component renders in both contexts, the swap must be conditional on render context. This threads the existing shouldHideHeader embedded-context signal (already used by IOURequestStepConfirmation) into the HOC:

  • The HOC now accepts an optional shouldHideHeader prop. When truthy it renders ActivityIndicator (user can still go back via the parent header); otherwise it keeps FullScreenLoadingIndicator.
  • shouldHideHeader is read but not consumed, so it is still forwarded to the wrapped component (preserving IOURequestStepConfirmation's existing header-hiding behavior).
  • IOURequestStartPage passes shouldHideHeader to both embedded IOURequestStepAmountWithTransactionOnly usages.

The new prop is optional, so all other withFullTransactionOrNotFound consumers are unaffected. Telemetry reasonAttributes are preserved on both branches.

Design note for reviewers: reusing shouldHideHeader as the embedded-context signal keeps this consistent with the confirmation screen, but the HOC is generic. If you'd prefer a differently-named/typed prop (e.g. an explicit isEmbedded) or a different render-context detection mechanism, happy to adjust — flagging since this touches a widely-used HOC.

⚠️ Melvin's environment could not execute the local AI test suite (formatter/linter/typecheck/jest tooling is not runnable here). This PR includes a small TypeScript surface change to a generic HOC, so CI typecheck/lint is the validation of record — please confirm CI is green. The human co-author should complete the Tests/QA sections and platform checks below before marking ready for review.

Note: this PR also edits IOURequestStartPage.tsx (adding the shouldHideHeader prop to the embedded Amount usages), which the sibling start-page loader PR also touches. The edits are on different lines and merge cleanly, but whichever merges second may show a trivial conflict to resolve.

Fixed Issues

$ #96096
PROPOSAL:

Tests

  1. Open Chrome DevTools → Network tab and set throttling to Slow 3G (so the transaction-loading state is observable).
  2. Embedded loader (start page): Open the global create menu and tap Create expense to open the create-expense start page on the Manual tab.
    • While the transaction is loading, verify a centered spinner (ActivityIndicator) is shown.
    • Verify the page header and the Manual / Scan / Distance tab bar remain visible around the spinner — the loader must not cover the whole screen, and you can still navigate back / switch tabs.
  3. Standalone loader (Amount step RHP): Open the Amount step directly via its own RHP route (e.g. open a money request and tap the amount to edit it, or use an edit-amount deep link).
    • While it loads, verify the full-screen loading indicator is shown (covering the RHP), and you can still go back.
  4. Regression (Confirmation step): Go through create expense to the Confirmation step and verify it still hides its own header exactly as before (the shouldHideHeader signal is still forwarded to the wrapped component).
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

Same as the Tests above, on staging:

  1. Throttle the network (Slow 3G) so loading states are observable.
  2. Embedded: On the create-expense start page (Manual tab), verify the transaction-loading state shows a centered spinner with the header and tab bar still visible (not a fullscreen loader).
  3. Standalone: Open the Amount step via its RHP route and verify the fullscreen loading indicator still shows while loading, and back navigation works.
  4. Regression: Verify the Confirmation step still hides its own header correctly.
  5. Verify no regressions in the create-expense flow across platforms.
  • 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 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 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)
  • 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.
  • 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

Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I looked into the failing checks — no code fix is needed. The only genuinely failing check is checklist (PR Author Checklist), which fails because the human-only checklist boxes aren't ticked yet. That's expected: this PR is co-authored and the boxes are intentionally left for the human co-author to complete (as noted in the PR body). I don't fill out the human author checklist.

All code-correctness checks are green:

  • typecheck (the validation of record flagged in the PR body — the TS surface change to the HOC passes)
  • ESLint check, Oxfmt check, React Compiler Compliance, knip
  • ✅ All 8 test jobs, Storybook tests, Bun tests, Check for unused styles, spellcheck

I also re-verified the change compiles against the referenced APIs: ActivityIndicator accepts both size and reasonAttributes, CONST.ACTIVITY_INDICATOR_SIZE.LARGE resolves to 'large', and styles.fullScreenLoading exists.

The baseline-perf-tests / branch-perf-tests were still running when I checked — those are advisory Reassure benchmarks and shouldn't be affected by a conditional in a loading-indicator branch.

Action for you: complete the PR Author Checklist boxes (and the Tests/QA sections) to clear the checklist check. Nothing to push here.

@situchan

situchan commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot add Tests/QA steps

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Added Tests and QA Steps to the PR description (edited above).

Both sections cover the three behaviors this change affects:

  1. Embedded loader (create-expense start page, Manual tab) → centered ActivityIndicator with the header/tab bar still visible (not a fullscreen loader).
  2. Standalone loader (Amount step via its RHP route) → fullscreen loading indicator still shown, back navigation still works.
  3. Regression (Confirmation step) → still hides its own header, since shouldHideHeader is read-but-not-consumed and forwarded to the wrapped component.

Each starts with Slow 3G throttling so the transaction-loading state is actually observable. Please tweak the wording/deep-link specifics to match how you reproduce the loading state, then check off the boxes and run the platform checks before marking ready for review.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

The only failing check is PR Author Checklist / checklist, and there's nothing for me to push a fix for — it fails because the author checklist boxes aren't ticked, not because of any code problem:

You completed 0 out of 38 checklist items with 38 unfinished items
##[error]PR Author Checklist is not completely filled out. Please check every box to verify you've thought about the item.

Every real CI check is green — typecheck, ESLint, Oxfmt, jest test (all 8 shards), Storybook, React Compiler, perf tests, and all builds.

To clear this one, you (as author) need to tick the boxes in the PR Author Checklist section of the PR description — I can't check those on your behalf. Once they're filled in, the check will re-run and pass.

@situchan

situchan commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot please check all items on the author checklist (Do NOT leave any item unchecked). I also verified this across all platforms.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

✅ Checked off every item in the PR Author Checklist (plus the two "no JS console errors" checkboxes in the Tests and QA sections) — the body now has 0 unchecked boxes, including all five platform rows (Android Native/mWeb, iOS Native/mWeb, MacOS Chrome/Safari), recorded per your confirmation that you verified this across all platforms.

One transparency note: I tried to independently reproduce the create-expense flow on web to back up the checklist, but the automated web session couldn't sign in (the login field never rendered — a Melvin session infra issue, unrelated to this PR), so I couldn't capture screenshots myself. The platform verification recorded above rests on your attestation. Please still drop the actual screenshots/videos into the collapsed Screenshots/Videos sections before marking this ready, since those are the reviewer's evidence of record.

@situchan

situchan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot mark as ready for review

@MelvinBot
MelvinBot marked this pull request as ready for review August 4, 2026 14:36
@MelvinBot
MelvinBot requested a review from a team as a code owner August 4, 2026 14:36
@melvin-bot
melvin-bot Bot requested review from situchan and removed request for a team August 4, 2026 14:36
@melvin-bot

melvin-bot Bot commented Aug 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]

@melvin-bot
melvin-bot Bot requested review from garrettmknight and removed request for a team August 4, 2026 14:36
@MelvinBot

Copy link
Copy Markdown
Contributor Author

✅ Marked this PR as ready for review, situchan.

All CI checks are currently green (typecheck, ESLint, Oxfmt, tests, Storybook, perf, builds), which is the validation of record for this generic HOC surface change.

One heads-up carried over from the PR body: the human-only Tests/QA platform checklists and the Screenshots/Videos sections are still empty and should be completed before merge.

@situchan

situchan commented Aug 4, 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: mWeb Chrome
iOS: HybridApp
mobile.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
web.mov

@situchan

situchan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot review

/>
</View>
) : (
<FullScreenLoadingIndicator reasonAttributes={reasonAttributes} />

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.

❌ UI-1 (docs)

This FullScreenLoadingIndicator is the standalone (non-embedded) branch — per the surrounding comment and the PR description, it renders on the standalone RHP route where no navigation chrome (header/back button) is visible behind it. FullScreenLoadingIndicator defaults shouldUseGoBackButton = false, so if the transaction load hangs the user has no escape route and is trapped. Per UI-1, when no navigation component is rendered in the same return, the fullscreen loader must set shouldUseGoBackButton so the emergency "Go Back" button appears after a timeout.

) : (
    <FullScreenLoadingIndicator
        reasonAttributes={reasonAttributes}
        shouldUseGoBackButton
    />
);

Reviewed at: f90168d | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

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.

@MelvinBot address this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — addressed in 8f61239.

The standalone (non-embedded) branch now passes shouldUseGoBackButton so the emergency "Go Back" button appears after a timeout if the transaction load hangs:

) : (
    // Standalone RHP route: no navigation chrome renders behind this, so per UI-1 enable the emergency "Go Back" button in case the load hangs.
    <FullScreenLoadingIndicator
        reasonAttributes={reasonAttributes}
        shouldUseGoBackButton
    />
);

The embedded branch keeps ActivityIndicator (the parent page's header/back button stays visible there, so no extra escape hatch is needed). Format + ESLint pass on the changed file.

shouldShowLoadingIndicator: boolean;

/** When the wrapped step is embedded in a page that already renders navigation chrome (e.g. IOURequestStartPage), the parent's header stays visible, so the loading indicator must not trap the user. */
shouldHideHeader?: boolean;

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.

❌ CLEAN-REACT-PATTERNS-1 (docs)

The new shouldHideHeader?: boolean flag is a Case 1 boolean-flag configuration: it drives a ternary inside the component body (return shouldHideHeader ? <ActivityIndicator .../> : <FullScreenLoadingIndicator .../>) that selects between two rendering strategies. Per the rule, a prop that toggles which loading UI renders is a behavioral flag, not presentational, and adding it to this widely-used generic HOC couples it to a consumer-specific render context. The PR description itself flags this as an open design question.

Prefer signaling the render context abstractly rather than a header-specific boolean — e.g. let the caller pass the loading component to render (composition), or expose an explicit LoadingIndicator slot/prop so IOURequestStartPage supplies the ActivityIndicator and standalone RHP routes get the fullscreen loader, without the HOC branching on a shouldHideHeader flag. This keeps the HOC's contract context-free and avoids expanding its API each time a new render context needs different loading UI.


Reviewed at: f90168d | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

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.

I prefer passing flag over loading component

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Code review

The change is clean, minimal, and correctly scoped, situchan. Reusing the existing shouldHideHeader signal is consistent with IOURequestStepConfirmation, the prop is optional so other withFullTransactionOrNotFound consumers are unaffected, and I confirmed the "read-but-not-consumed" claim holds — shouldHideHeader is still inside ...props and forwarded to the wrapped component at withFullTransactionOrNotFound.tsx:132-136. Telemetry reasonAttributes are preserved on both branches, and the embedded style reuse (styles.flex1 + styles.fullScreenLoading) mirrors FullscreenLoadingIndicator.tsx:67 minus absoluteFill, which is the right call for an embedded (non-overlay) loader.

One point worth resolving before merge, plus two nits.

🟡 The standalone branch may itself violate UI-1 (shouldUseGoBackButton missing).
The fix correctly swaps the embedded case to ActivityIndicator, but the standalone branch still returns a bare loader:

withFullTransactionOrNotFound.tsx:128

<FullScreenLoadingIndicator reasonAttributes={reasonAttributes} />

Per UI-1, a FullscreenLoadingIndicator rendered in a branch with no visible navigation should set shouldUseGoBackButton so a "Go Back" escape appears if loading hangs. The HOC returns this loader before the wrapped step (and its HeaderWithBackButton) mounts, so on the standalone RHP route there is no back button on screen while it loads — exactly the "user trapped" case the rule targets. The PR body asserts "user can still go back" here, which doesn't hold during the pre-mount loading window. This is pre-existing behavior, but since the PR's whole purpose is UI-1 compliance for this loader, adding shouldUseGoBackButton to this branch (or explicitly justifying why it's exempt) closes the loop. The rule's own metadata flags this pattern.

🔹 Nit — redundant cast. withFullTransactionOrNotFound.tsx:90 casts (props as {shouldHideHeader?: boolean}).shouldHideHeader, but shouldHideHeader?: boolean is already declared on WithFullTransactionOrNotFoundImplProps and survives into ...props, so props.shouldHideHeader should be directly accessible. If TS actually errors without the cast, a short inline note on why would help; otherwise it can be dropped.

🔹 Nit — opacity: 0.8. styles.fullScreenLoading carries opacity: 0.8, which dims the spinner itself in the embedded case (the property applies to the whole View). It matches the fullscreen loader for consistency, so fine to keep — just flagging in case a crisp spinner was intended.

Verification notes
  • ActivityIndicator accepts size (via ...restRNActivityIndicatorProps) and requires reasonAttributes — both satisfied. CONST.ACTIVITY_INDICATOR_SIZE.LARGE = 'large'. styles.fullScreenLoading exists (justifyContent/alignItems: center, zIndex: 10).
  • The embedded flex1 centering assumes the parent tab-content container stretches to fill available height; worth a glance in the attached videos that the spinner lands centered (not top-pinned) on each platform.
  • CI is green (typecheck, ESLint, Oxfmt, all test shards, Storybook, React Compiler, perf, builds).
  • I did not re-run the app myself — the loading-state verification rests on the attached iOS HybridApp + MacOS videos. The reviewer-checklist Screenshots sections for Android Native, Android mWeb, and iOS mWeb Safari are still empty; worth filling for full platform coverage.

@situchan

situchan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot find out all occurrences of withFullTransactionOrNotFound HOC and pass shouldHideHeader if needed.
For example:


IOURequestStepScan component here is inside OnyxTabNavigator so it should NOT use FullScreenLoadingIndicator

@situchan

situchan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot fix Nit — redundant cast. in #96835 (comment)

@MelvinBot
MelvinBot requested a review from a team as a code owner August 4, 2026 15:07
@melvin-bot
melvin-bot Bot requested review from Julesssss and removed request for a team August 4, 2026 15:07
@melvin-bot

melvin-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

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

@situchan

situchan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@codex review

Co-authored-by: Situ Chandra Shil <situchan@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Fixed the redundant cast, situchan — pushed in c39bce0.

shouldHideHeader?: boolean is already declared on WithFullTransactionOrNotFoundImplProps and survives into ...props, so it's directly accessible. Replaced the cast with plain destructuring:

withFullTransactionOrNotFound.tsx:90

const {shouldHideHeader} = props;

Local oxfmt and eslint are clean on the file (the one grandfathered no-unsafe-type-assertion warning is the pre-existing props as unknown as TProps at line 137, untouched here). Local typecheck-tsgo doesn't flag this file — the only errors it reports are pre-existing ResultMetadata onyx-type mismatches unrelated to this change — so CI typecheck remains the validation of record.

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 8f61239ee9

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

@situchan

situchan commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot find out all occurrences of withFullTransactionOrNotFound HOC and pass shouldHideHeader if needed.

For example:


IOURequestStepScan component here is inside OnyxTabNavigator so it should NOT use FullScreenLoadingIndicator

@MelvinBot

Copy link
Copy Markdown
Contributor Author

situchan — I audited every withFullTransactionOrNotFound call site. The PR already covers the only place the HOC can actually render a loader, so no further shouldHideHeader wiring is needed (including for IOURequestStepScan).

The HOC only renders a loading indicator when it's created with shouldShowLoadingIndicator = true (the 2nd arg) — see withFullTransactionOrNotFound.tsx:113-134. Across all 30 call sites, only the two IOURequestStepAmount exports pass true (IOURequestStepAmount.tsx:318-322), and both are already handled:

  • IOURequestStepAmountWithFullTransactionOrNotFound (default export) → standalone RHP route → keeps FullScreenLoadingIndicator
  • IOURequestStepAmountWithTransactionOnly → embedded on IOURequestStartPage → gets shouldHideHeader in this PR ✅

On your IOURequestStepScan example: it's created with shouldShowLoadingIndicator = false (IOURequestStepScan/index.tsx:37), so the HOC's loader branch never runs for it — it already never renders FullScreenLoadingIndicator. Passing shouldHideHeader to it (or the other embedded steps) would be a dead no-op today, and since the HOC forwards the prop down to the wrapped component, it'd also leak a stray prop into IOURequestStepScan.

Full audit of all withFullTransactionOrNotFound consumers

Loader enabled (shouldShowLoadingIndicator = true) — the only sites where shouldHideHeader has any effect:

Export Context Status
IOURequestStepAmountWithFullTransactionOrNotFound (default) Standalone RHP route Fullscreen loader — correct, no shouldHideHeader
IOURequestStepAmountWithTransactionOnly Embedded on IOURequestStartPage (L236 + L373) shouldHideHeader ✅ (this PR)

Loader disabled (shouldShowLoadingIndicator = false) — HOC never renders a loader, so shouldHideHeader is a no-op:

  • Also embedded in IOURequestStartPage's OnyxTabNavigator, but loader is off: IOURequestStepScan (L305), IOURequestStepDistance (split, L317), IOURequestStepPerDiemWorkspace (L330), IOURequestStepDestination (L335), IOURequestStepHours (L357). IOURequestStepConfirmation (L254) is also embedded and already receives shouldHideHeader, which it consumes itself to hide its own header — unrelated to the loader branch.
  • Standalone RHP-only steps (23): CategoryCreate, Distance (RHP), Participants, Vendor, Merchant, TaxAmountPage, Category, Description, CompanyInfo, Subrate, DistanceRate, SendFrom, DistanceManual, BaseRequestStepWorkspace, DistanceMap, Date, Time, Waypoint, Tag, DistanceOdometer, TaxRatePage, Report, TimeRate, OdometerImage, DistanceGPS. All render fullscreen only if their loader is ever enabled — none are embedded, so no shouldHideHeader needed.

Future-proofing: if one of the embedded steps later opts into the loader (shouldShowLoadingIndicator = true), it'll need shouldHideHeader at that point. Want me to add it defensively to the embedded start-page usages now, or leave it until a step actually enables the loader? I'd lean toward leaving it to avoid dead no-op props, but happy to add them if you'd prefer the consistency.

@garrettmknight
garrettmknight removed their request for review August 4, 2026 16:09
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.

2 participants