Skip to content

Commit

Permalink
Merge pull request #55139 from janicduplessis/@janic/fix-open-app
Browse files Browse the repository at this point in the history
Fix open app
  • Loading branch information
mjasikowski authored Feb 10, 2025
2 parents 631006e + 9bad6a2 commit 15d9f03
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 37 deletions.
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ const ONYXKEYS = {
/** Is report data loading? */
IS_LOADING_APP: 'isLoadingApp',

/** Is the app loaded? */
HAS_LOADED_APP: 'hasLoadedApp',

/** Is the test tools modal open? */
IS_TEST_TOOLS_MODAL_OPEN: 'isTestToolsModalOpen',

Expand Down Expand Up @@ -1011,6 +1014,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN]: boolean;
[ONYXKEYS.APP_PROFILING_IN_PROGRESS]: boolean;
[ONYXKEYS.IS_LOADING_APP]: boolean;
[ONYXKEYS.HAS_LOADED_APP]: boolean;
[ONYXKEYS.WALLET_TRANSFER]: OnyxTypes.WalletTransfer;
[ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string;
[ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean;
Expand Down
20 changes: 19 additions & 1 deletion src/libs/actions/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ Onyx.connect({
},
});

let hasLoadedApp: boolean | undefined;
Onyx.connect({
key: ONYXKEYS.HAS_LOADED_APP,
callback: (value) => {
hasLoadedApp = value;
},
});

const KEYS_TO_PRESERVE: OnyxKey[] = [
ONYXKEYS.ACCOUNT,
ONYXKEYS.IS_CHECKING_PUBLIC_ROOM,
Expand Down Expand Up @@ -239,7 +247,13 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false, isFullReconnect = fals
value: true,
},
],
successData: [],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.HAS_LOADED_APP,
value: true,
},
],
finallyData: [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand Down Expand Up @@ -291,6 +305,10 @@ function openApp() {
* @param [updateIDFrom] the ID of the Onyx update that we want to start fetching from
*/
function reconnectApp(updateIDFrom: OnyxEntry<number> = 0) {
if (!hasLoadedApp) {
openApp();
return;
}
console.debug(`[OnyxUpdates] App reconnecting with updateIDFrom: ${updateIDFrom}`);
getPolicyParamsForOpenOrReconnect().then((policyParams) => {
const params: ReconnectAppParams = policyParams;
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/Delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [
ONYXKEYS.SESSION,
ONYXKEYS.STASHED_SESSION,
ONYXKEYS.IS_LOADING_APP,
ONYXKEYS.HAS_LOADED_APP,
ONYXKEYS.STASHED_CREDENTIALS,

// We need to preserve the sidebar loaded state since we never unrender the sidebar when connecting as a delegate
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/QueuedOnyxUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function flushQueue(): Promise<void> {
ONYXKEYS.NVP_PREFERRED_LOCALE,
ONYXKEYS.SESSION,
ONYXKEYS.IS_LOADING_APP,
ONYXKEYS.HAS_LOADED_APP,
ONYXKEYS.CREDENTIALS,
ONYXKEYS.IS_SIDEBAR_LOADED,
ONYXKEYS.ACCOUNT,
Expand Down
2 changes: 2 additions & 0 deletions tests/actions/AppTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('actions/App', () => {

test('lastFullReconnectTime - full reconnectApp', async () => {
// When a full ReconnectApp runs
await Onyx.set(ONYXKEYS.HAS_LOADED_APP, true);
App.reconnectApp();
App.confirmReadyToOpenApp();
await waitForBatchedUpdates();
Expand All @@ -48,6 +49,7 @@ describe('actions/App', () => {

test('lastFullReconnectTime - incremental reconnectApp', async () => {
// When an incremental ReconnectApp runs
await Onyx.set(ONYXKEYS.HAS_LOADED_APP, true);
App.reconnectApp(123);
App.confirmReadyToOpenApp();
await waitForBatchedUpdates();
Expand Down
84 changes: 53 additions & 31 deletions tests/actions/SessionTest.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {beforeEach, jest, test} from '@jest/globals';
import Onyx from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import * as App from '@libs/actions/App';
import {confirmReadyToOpenApp, openApp, reconnectApp} from '@libs/actions/App';
import OnyxUpdateManager from '@libs/actions/OnyxUpdateManager';
import {getAll as getAllPersistedRequests} from '@libs/actions/PersistedRequests';
import {WRITE_COMMANDS} from '@libs/API/types';
import HttpUtils from '@libs/HttpUtils';
import PushNotification from '@libs/Notification/PushNotification';
// This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection
import '@libs/Notification/PushNotification/subscribePushNotification';
import * as PersistedRequests from '@userActions/PersistedRequests';
import CONST from '@src/CONST';
import * as SessionUtil from '@src/libs/actions/Session';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -88,8 +88,8 @@ describe('Session', () => {
);

// When we attempt to fetch the initial app data via the API
App.confirmReadyToOpenApp();
App.openApp();
confirmReadyToOpenApp();
openApp();
await waitForBatchedUpdates();

// Then it should fail and reauthenticate the user adding the new authToken to the session
Expand All @@ -111,78 +111,100 @@ describe('Session', () => {

test('ReconnectApp should push request to the queue', async () => {
await TestHelper.signInWithTestUser();
await Onyx.set(ONYXKEYS.HAS_LOADED_APP, true);
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});

App.confirmReadyToOpenApp();
App.reconnectApp();
confirmReadyToOpenApp();
reconnectApp();

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(1);
expect(PersistedRequests.getAll().at(0)?.command).toBe(WRITE_COMMANDS.RECONNECT_APP);
expect(getAllPersistedRequests().length).toBe(1);
expect(getAllPersistedRequests().at(0)?.command).toBe(WRITE_COMMANDS.RECONNECT_APP);

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(0);
expect(getAllPersistedRequests().length).toBe(0);
});

test('ReconnectApp should open if app is not loaded', async () => {
await TestHelper.signInWithTestUser();
await Onyx.set(ONYXKEYS.HAS_LOADED_APP, false);
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});

confirmReadyToOpenApp();
reconnectApp();

await waitForBatchedUpdates();

expect(getAllPersistedRequests().length).toBe(1);
expect(getAllPersistedRequests().at(0)?.command).toBe(WRITE_COMMANDS.OPEN_APP);

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

await waitForBatchedUpdates();

expect(getAllPersistedRequests().length).toBe(0);
});

test('ReconnectApp should replace same requests from the queue', async () => {
await TestHelper.signInWithTestUser();
await Onyx.set(ONYXKEYS.HAS_LOADED_APP, true);
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});

App.confirmReadyToOpenApp();
App.reconnectApp();
App.reconnectApp();
App.reconnectApp();
App.reconnectApp();
confirmReadyToOpenApp();
reconnectApp();
reconnectApp();
reconnectApp();
reconnectApp();

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(1);
expect(PersistedRequests.getAll().at(0)?.command).toBe(WRITE_COMMANDS.RECONNECT_APP);
expect(getAllPersistedRequests().length).toBe(1);
expect(getAllPersistedRequests().at(0)?.command).toBe(WRITE_COMMANDS.RECONNECT_APP);

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

expect(PersistedRequests.getAll().length).toBe(0);
expect(getAllPersistedRequests().length).toBe(0);
});

test('OpenApp should push request to the queue', async () => {
await TestHelper.signInWithTestUser();
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});

App.openApp();
openApp();

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(1);
expect(PersistedRequests.getAll().at(0)?.command).toBe(WRITE_COMMANDS.OPEN_APP);
expect(getAllPersistedRequests().length).toBe(1);
expect(getAllPersistedRequests().at(0)?.command).toBe(WRITE_COMMANDS.OPEN_APP);

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(0);
expect(getAllPersistedRequests().length).toBe(0);
});

test('OpenApp should replace same requests from the queue', async () => {
await TestHelper.signInWithTestUser();
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true});

App.openApp();
App.openApp();
App.openApp();
App.openApp();
openApp();
openApp();
openApp();
openApp();

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(1);
expect(PersistedRequests.getAll().at(0)?.command).toBe(WRITE_COMMANDS.OPEN_APP);
expect(getAllPersistedRequests().length).toBe(1);
expect(getAllPersistedRequests().at(0)?.command).toBe(WRITE_COMMANDS.OPEN_APP);

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

expect(PersistedRequests.getAll().length).toBe(0);
expect(getAllPersistedRequests().length).toBe(0);
});

test('LogOut should replace same requests from the queue instead of adding new one', async () => {
Expand All @@ -197,11 +219,11 @@ describe('Session', () => {

await waitForBatchedUpdates();

expect(PersistedRequests.getAll().length).toBe(1);
expect(PersistedRequests.getAll().at(0)?.command).toBe(WRITE_COMMANDS.LOG_OUT);
expect(getAllPersistedRequests().length).toBe(1);
expect(getAllPersistedRequests().at(0)?.command).toBe(WRITE_COMMANDS.LOG_OUT);

await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

expect(PersistedRequests.getAll().length).toBe(0);
expect(getAllPersistedRequests().length).toBe(0);
});
});
11 changes: 6 additions & 5 deletions tests/unit/NetworkTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {Mock} from 'jest-mock';
import type {OnyxEntry} from 'react-native-onyx';
import MockedOnyx from 'react-native-onyx';
import TestToolMenu from '@components/TestToolMenu';
import * as App from '@libs/actions/App';
import {confirmReadyToOpenApp, reconnectApp} from '@libs/actions/App';
import {resetReauthentication} from '@libs/Middleware/Reauthentication';
import CONST from '@src/CONST';
import * as NetworkActions from '@src/libs/actions/Network';
Expand Down Expand Up @@ -136,6 +136,7 @@ describe('NetworkTests', () => {

// Sign in test user and wait for updates
await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN);
await Onyx.set(ONYXKEYS.HAS_LOADED_APP, true);
await waitForBatchedUpdates();

const initialAuthToken = sessionState?.authToken;
Expand Down Expand Up @@ -165,8 +166,8 @@ describe('NetworkTests', () => {
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

// Trigger reconnect which will fail due to expired token
App.confirmReadyToOpenApp();
App.reconnectApp();
confirmReadyToOpenApp();
reconnectApp();
await waitForBatchedUpdates();

// 4. First API Call Verification - Check ReconnectApp
Expand All @@ -183,8 +184,8 @@ describe('NetworkTests', () => {
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false});

// 7.Trigger another reconnect due to network change
App.confirmReadyToOpenApp();
App.reconnectApp();
confirmReadyToOpenApp();
reconnectApp();

// 8. Now fail the pending authentication request
resolveAuthRequest(Promise.reject(new Error('Network request failed')));
Expand Down

0 comments on commit 15d9f03

Please sign in to comment.