[Onyx audit] Migrate keys to RAM-only - part 1/2 (including bugfix)#85980
[Onyx audit] Migrate keys to RAM-only - part 1/2 (including bugfix)#85980JKobrynski wants to merge 5 commits intoExpensify:mainfrom
Conversation
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.
|
|
@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] |
| Linking.getInitialURL(), | ||
| new Promise<null>((resolve) => { | ||
| setTimeout(() => resolve(null), CONST.TIMING.GET_INITIAL_URL_TIMEOUT); | ||
| }), |
There was a problem hiding this comment.
❌ PERF-12 (docs)
The setTimeout inside the Promise.race is never cleared in the effect's cleanup function. When the effect re-runs (e.g., conciergeReportID changes) or the component unmounts, the cleanup sets cancelled = true and removes the linking listener but does not call clearTimeout. The orphaned timer will still fire and resolve the racing promise, which is harmless due to the cancelled guard, but the timer itself is a leaked resource. If dependencies change rapidly, multiple orphaned timers accumulate.
Capture the timer ID and clear it in cleanup:
let timeoutId: ReturnType<typeof setTimeout>;
Promise.race([
Linking.getInitialURL(),
new Promise<null>((resolve) => {
timeoutId = setTimeout(() => resolve(null), CONST.TIMING.GET_INITIAL_URL_TIMEOUT);
}),
])
.then((url) => {
// ...
})
.catch(() => {
// ...
});
// In cleanup:
return () => {
cancelled = true;
clearTimeout(timeoutId);
linkingChangeListener.current?.remove();
};Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
There was a problem hiding this comment.
Addressed — captured the timeout ID and added clearTimeout(timeoutId) in the effect cleanup.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d91ba124c
ℹ️ 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".
| Promise.race([ | ||
| Linking.getInitialURL(), | ||
| new Promise<null>((resolve) => { | ||
| setTimeout(() => resolve(null), CONST.TIMING.GET_INITIAL_URL_TIMEOUT); | ||
| }), |
There was a problem hiding this comment.
Don't discard initial deep links after the 10s timeout
In HybridApp launches where Linking.getInitialURL() is slow but not permanently hung, this race resolves null after 10s and permanently drops the eventual deeplink. Initial launch URLs do not come back through the later 'url' event subscription, so NavigationRoot is initialized without the route and the mount-only deeplink login path in AuthScreensInitHandler (getReportIDFromLink(initialURL)) never runs. In that case users land on Home/sign-in instead of the requested report even though the bridge eventually returned a URL.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Thanks for the suggestion. The scenario described (slow-but-eventual resolution) can't occur with the current native implementation. On both platforms, getInitialURL() is a synchronous property read:
- Android (
ReactNativeHybridApp.kt):promise.resolve(ReactNativeManager.initialURL)— reads a pre-setvar - iOS (
ReactNativeHybridApp.mm):resolve([ReactNativeManagerWrapper getInitialURL])— reads a stored property
The URL is set by OldDot before React Native boots. By the time JS calls getInitialURL(), the value is already available (or null). The promise resolves essentially immediately.
The timeout exists for the case where the bridge call itself hangs (e.g., TurboModule initialization failure), meaning the promise never resolves at all — and in that case, there is no URL to lose.
|
No product review needed |
|
Will review tomorrow. |
|
Reviewing... |
|
@JKobrynski The deeplink issue on Android has reappeared. android_standalone.mp4 |
Explanation of Change
This PR is part 1/2 of RAM-only migration - it migrates the obvious keys.
It also resolved the bug that reverted the original PR in the Hybrid App.
Fixed Issues
$ #80091
PROPOSAL: N/A
Tests
1. App Launch & Deep Linking (
IS_CHECKING_PUBLIC_ROOM)2. App Update Modals (
UPDATE_AVAILABLE,UPDATE_REQUIRED)It's difficult to test these, as the related actions are only triggered by API
3. Search / User Selection Loading States (
IS_SEARCHING_FOR_REPORTS)New Chat
Money Request (Expense) Participant Selector
Workspace Invite
Search Router
4. Wallet / Onfido Identity Verification (
WALLET_ONFIDO)5. General Regression Checks
Offline tests
N/A
QA Steps
Same as Tests section above.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
android_hybrid-compressed.webm
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
web-compressed.mov