Skip to content

[Onyx Audit] Migrate keys to RAM-only part 2/2#83450

Merged
roryabraham merged 39 commits into
Expensify:mainfrom
callstack-internal:JKobrynski/feat/80091-migrate-keys-to-ram-only-part-2
Apr 2, 2026
Merged

[Onyx Audit] Migrate keys to RAM-only part 2/2#83450
roryabraham merged 39 commits into
Expensify:mainfrom
callstack-internal:JKobrynski/feat/80091-migrate-keys-to-ram-only-part-2

Conversation

@JKobrynski

@JKobrynski JKobrynski commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

This PR is part 2/2 of RAM-only migration - it migrates the keys that were not obvious RAM-only candidates. It basically includes two scenarios for these keys that used to be paired with initWithStoredValues: false:

  1. Migrate the key to RAM-only and remove initWithStoredValues
  2. Turn into a regular key - just remove initWithStoredValues

This also includes making sure that the baseline component logic is maintained - making the necessary code changes.

Fixed Issues

$ #80091
PROPOSAL: N/A

Tests

RAM-Only Key Migration Part 2 — Testing Steps

Prerequisites

  • Logged-in account with a workspace that has Expensify Cards enabled
  • Access to a Plaid-supported bank (or test credentials)
  • A magic link email (for ValidateLoginPage testing on web)

1. Plaid Bank Account Flow (PLAID_LINK_TOKEN)

  1. Go to Settings > Wallet > Add Bank Account (or workspace bank account setup)
  2. Verify the Plaid modal opens and loads correctly
  3. Select a bank, authenticate, and pick an account
  4. Verify the selected account appears with the correct bank name and mask
  5. OAuth flow: If redirected to a bank's OAuth page and back, verify Plaid resumes correctly using the existing token

2. Enable Payments / Wallet (USER_WALLET)

  1. Go to Settings > Wallet > Enable Payments
  2. Verify a loading spinner appears briefly while wallet data loads
  3. Verify the correct step renders after loading (AdditionalDetails, Onfido, Terms, or Activate depending on wallet state)
  4. Navigate away and come back — verify the page still loads correctly and doesn't get stuck on the spinner

3. Magic Link — ValidateLoginPage (Web only)

  1. Open a magic link URL in the browser while logged out
  2. Verify the ValidateCodeModal appears (not a stale FAILED state from a previous attempt)
  3. Verify sign-in completes and transitions through SIGNING_IN > JUST_SIGNED_IN states correctly
  4. Open an expired/invalid magic link — verify the ExpiredValidateCodeModal shows

4. Issue New Expensify Card (ISSUE_NEW_EXPENSIFY_CARD)

  1. Go to Workspace > Expensify Card > Issue New Card
  2. Walk through the full flow: Assignee > Card Type > Limit > Card Name > Confirmation
  3. Navigate away mid-flow and return — verify the flow restarts cleanly (no stale step from a previous session)

5. Sidebar & App Loading (IS_SIDEBAR_LOADED, IS_LOADING_APP, IS_LOADING_REPORT_DATA)

  1. Open the app fresh (clear cache / hard refresh on web)
  2. Verify the LHN (sidebar) loads with reports
  3. Verify no perpetual loading states — the app should transition from loading to ready smoothly
  4. Open a workspace avatar modal while the app is loading — verify isLoadingApp doesn't cause issues (WorkspaceAvatarModalContent)

6. Translations Loading (ARE_TRANSLATIONS_LOADING)

  1. Change locale in Settings > Preferences > Language (e.g. English → Spanish)
  2. Verify all UI text updates correctly without flicker or missing translations
  3. Verify no blank/broken strings appear during the transition

7. Mobile Selection Mode (MOBILE_SELECTION_MODE)

  1. On mobile (or responsive web), long-press on a chat/report to enter selection mode
  2. Verify the selection toolbar appears
  3. Select/deselect items, then exit selection mode
  4. Verify it resets cleanly

8. General Regression Checks

  • Offline: Toggle airplane mode at various points — verify no crashes, loading states recover when back online

  • App restart: Force-close and reopen — verify RAM-only keys start fresh (no stale data persisted from previous session)

  • Console errors: Monitor JS console throughout all tests — verify no new warnings or errors related to Onyx keys

  • Verify that no errors appear in the JS console

Offline tests

N/A

QA Steps

Same as Tests section above.

  • 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
web-compressed.mov

@codecov

codecov Bot commented Feb 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/DelegateAccessHandler.tsx 94.11% <ø> (ø)
src/ONYXKEYS.ts 100.00% <ø> (ø)
src/components/LocaleContextProvider.tsx 83.72% <100.00%> (ø)
.../MoneyRequestReportView/MoneyRequestReportView.tsx 0.00% <ø> (ø)
src/components/PriorityModeController.tsx 77.77% <ø> (ø)
src/components/ProductTrainingContext/index.tsx 91.22% <100.00%> (ø)
...archList/ListItem/TransactionGroupListExpanded.tsx 66.66% <ø> (ø)
src/hooks/useMobileSelectionMode.ts 100.00% <100.00%> (ø)
src/hooks/useOnboardingFlow.ts 88.88% <100.00%> (ø)
src/hooks/useSearchBackPress/index.android.ts 0.00% <ø> (ø)
... and 65 more
... and 101 files with indirect coverage changes

Comment thread src/setup/index.ts Outdated
@JKobrynski JKobrynski requested a review from fabioh8010 March 3, 2026 16:56
@JKobrynski JKobrynski marked this pull request as ready for review March 4, 2026 07:27
@JKobrynski JKobrynski requested review from a team as code owners March 4, 2026 07:27
@melvin-bot melvin-bot Bot requested a review from Krishna2323 March 4, 2026 07:27
@melvin-bot

melvin-bot Bot commented Mar 4, 2026

Copy link
Copy Markdown

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

<ValidateCodeModal
accountID={Number(accountID)}
code={validateCode}
/>
)}
{(!autoAuthState ? shouldStartSignInWithValidateCode : autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.SIGNING_IN) && <FullScreenLoadingIndicator />}
{(!effectiveAutoAuthState ? shouldStartSignInWithValidateCode : autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.SIGNING_IN) && <FullScreenLoadingIndicator />}
</>

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)

FullScreenLoadingIndicator is rendered here without shouldUseGoBackButton and no navigation component (e.g., HeaderWithBackButton) is visible in the same return statement or conditional branch. If loading hangs, the user has no escape route.

Note: This is a pre-existing issue (only the condition variable name changed from autoAuthState to effectiveAutoAuthState), but worth addressing. Add shouldUseGoBackButton to provide an emergency exit:

{(!effectiveAutoAuthState ? shouldStartSignInWithValidateCode : autoAuthStateWithDefault === CONST.AUTO_AUTH_STATE.SIGNING_IN) && <FullScreenLoadingIndicator shouldUseGoBackButton />}

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

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.

This is a pre-existing issue — our change only renamed the condition variable from autoAuthState to effectiveAutoAuthState, the FullScreenLoadingIndicator was already rendered without shouldUseGoBackButton before this PR. Adding it would be out of scope for this migration.

@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: 4a1e83bf41

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

// stored values here.
initWithStoredValues: false,
});
const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fetch wallet before redirecting on pending/failure flags

Dropping initWithStoredValues: false here makes useOnyx(ONYXKEYS.USER_WALLET) consume cached disk data first, so stale isPendingOnfidoResult/hasFailedOnfido values can trigger the early redirect path before openEnablePaymentsPage() runs. In that case the page navigates away and never refreshes wallet state, which can block affected users from re-entering the enable-payments flow until another screen updates USER_WALLET.

Useful? React with 👍 / 👎.

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.

Addressed — the redirect logic is now deferred using a wasLoadingRef that tracks the isLoading: true → false cycle. The effect that calls openEnablePaymentsPage() always fires on mount, and a separate effect only evaluates the redirect after isLoading has transitioned from true back to false, ensuring we act on fresh API data rather than stale cached values.

@@ -38,6 +41,8 @@ function ValidateLoginPage({
const [preferredLocale] = useOnyx(ONYXKEYS.NVP_PREFERRED_LOCALE);

useEffect(() => {
setHasInitialized(true);

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 Initialize auto-auth state before unmasking stored session state

This sets hasInitialized to true before initAutoAuthState() resets session.autoAuthState, so a persisted value from a prior magic-link attempt (for example FAILED) can be read and rendered for one frame depending on update timing. That reintroduces the stale-state flash that the prior initWithStoredValues: false behavior avoided for logged-out users.

Useful? React with 👍 / 👎.

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.

This isn't a real issue. For logged-out users, hasInitialized starts as false (line 32: useState(isSignedIn)), so effectiveAutoAuthState is undefined on the first render — no stale value is ever exposed. The useEffect then calls setHasInitialized(true) and initAutoAuthState() in the same synchronous block. React batches these updates, so there's no intermediate render where hasInitialized is true but autoAuthState is still stale.

@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.

No product review required.

@mountiny

mountiny commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

@JKobrynski Confclits

@Krishna2323 can you prioritize this review please?

@Krishna2323

Copy link
Copy Markdown
Contributor

Yes, I’ll be on it today. The first one looks good now, so I’ll finish that first and then move on to this one.

@tgolen

tgolen commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

We need to figure out what to do with the ESLint failures. If they are not caused by this PR, we need someone working on them to get them fixed. If they are caused by this PR, they should be fixed here.

@Krishna2323

Copy link
Copy Markdown
Contributor

@JKobrynski there are some stale references:

  • .claude/skills/coding-standards/rules/clean-react-4-no-side-effect-spaghetti.md line 43 — still uses ONYXKEYS.IS_SIDEBAR_LOADED
  • tests/unit/SidebarFilterTest.ts line 18 — local mock ONYXKEYS still uses IS_LOADING_APP (16 usages, suite is xdescribe/disabled so won't fail, but should be updated for consistency)

@Krishna2323 Krishna2323 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.

PR was already approved and tested. The failing check doesn't seem related.

Checklist here: #83450 (review)

roryabraham
roryabraham previously approved these changes Mar 31, 2026

@roryabraham roryabraham 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.

Agree that the use of deprecated StyleSheet.absoluteFillObject wasn't introduced in this PR

@roryabraham

Copy link
Copy Markdown
Contributor

@JKobrynski you have conflicts here

@JKobrynski

Copy link
Copy Markdown
Contributor Author

@roryabraham addressing them at this very moment!

@JKobrynski JKobrynski dismissed stale reviews from roryabraham and tgolen via e4c5b48 March 31, 2026 17:10
roryabraham
roryabraham previously approved these changes Mar 31, 2026
@roryabraham roryabraham merged commit 3ef0bdd into Expensify:main Apr 2, 2026
31 checks passed
@OSBotify

OSBotify commented Apr 2, 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.

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

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

@OSBotify

OSBotify commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/roryabraham in version: 9.3.53-0 🚀

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

Bundle Size Analysis (Sentry):

@MelvinBot

Copy link
Copy Markdown
Contributor

I reviewed all changes in this PR. This is a purely internal technical refactoring that migrates Onyx keys to RAM-only storage (adding RAM_ONLY_ prefix to key names and removing initWithStoredValues: false options). The minor logic adjustments (e.g., isPlaidTokenReady state in AddPlaidBankAccount, wallet loading in EnablePaymentsPage) maintain existing behavior under the new storage model.

No changes are required to the help site files under docs/articles. None of the changes affect user-facing features, UI labels, workflows, or navigation paths that would be documented on the help site.

@OSBotify

OSBotify commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/roryabraham in version: 9.3.53-0 🚀

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

Bundle Size Analysis (Sentry):

@m-natarajan

Copy link
Copy Markdown

@JKobrynski @roryabraham @tgolen This PR was reverted as per this comment, Can I checkoff this one?

@OSBotify

OSBotify commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/roryabraham in version: 9.3.53-7 🚀

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

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.

10 participants