From 9e5dbf3294a641d1e8e71e1b4d6c52e58742a4ae Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 16 Oct 2023 10:59:37 +0200 Subject: [PATCH 1/8] [TS migration] Migrate 'App.js' lib --- src/libs/Browser/index.ts | 25 +-- src/libs/Browser/index.web.ts | 25 ++- src/libs/Browser/types.ts | 13 ++ src/libs/Navigation/Navigation.js | 2 +- src/libs/actions/{App.js => App.ts} | 263 +++++++++++++++------------- src/types/onyx/Session.ts | 2 +- 6 files changed, 181 insertions(+), 149 deletions(-) create mode 100644 src/libs/Browser/types.ts rename src/libs/actions/{App.js => App.ts} (70%) diff --git a/src/libs/Browser/index.ts b/src/libs/Browser/index.ts index f37aa3d481bb..9569d3583809 100644 --- a/src/libs/Browser/index.ts +++ b/src/libs/Browser/index.ts @@ -1,22 +1,15 @@ -function getBrowser() { - return ''; -} +import type {GetBrowser, IsMobile, IsMobileChrome, IsMobileSafari, IsSafari, OpenRouteInDesktopApp} from './types'; -function isMobile() { - return false; -} +const getBrowser: GetBrowser = () => ''; -function isMobileSafari() { - return false; -} -function isMobileChrome() { - return false; -} +const isMobile: IsMobile = () => false; -function isSafari() { - return false; -} +const isMobileSafari: IsMobileSafari = () => false; -function openRouteInDesktopApp() {} +const isMobileChrome: IsMobileChrome = () => false; + +const isSafari: IsSafari = () => false; + +const openRouteInDesktopApp: OpenRouteInDesktopApp = () => {}; export {getBrowser, isMobile, isMobileSafari, isSafari, isMobileChrome, openRouteInDesktopApp}; diff --git a/src/libs/Browser/index.web.ts b/src/libs/Browser/index.web.ts index 064358c6bed5..683b32e18bb0 100644 --- a/src/libs/Browser/index.web.ts +++ b/src/libs/Browser/index.web.ts @@ -1,3 +1,4 @@ +import type {GetBrowser, IsMobile, IsMobileChrome, IsMobileSafari, IsSafari, OpenRouteInDesktopApp} from './types'; import CONST from '../../CONST'; import CONFIG from '../../CONFIG'; import ROUTES from '../../ROUTES'; @@ -6,7 +7,7 @@ import ROUTES from '../../ROUTES'; * Fetch browser name from UA string * */ -function getBrowser(): string { +const getBrowser: GetBrowser = () => { const {userAgent} = window.navigator; const match = userAgent.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))/i) ?? []; let temp: RegExpMatchArray | null; @@ -30,43 +31,39 @@ function getBrowser(): string { browserName = match[1] ?? navigator.appName; return browserName ? browserName.toLowerCase() : CONST.BROWSER.OTHER; -} +}; /** * Whether the platform is a mobile browser. * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent * */ -function isMobile() { - return /Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Silk|Opera Mini/i.test(navigator.userAgent); -} +const isMobile: IsMobile = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Silk|Opera Mini/i.test(navigator.userAgent); /** * Checks if requesting user agent is Safari browser on a mobile device * */ -function isMobileSafari() { +const isMobileSafari: IsMobileSafari = () => { const userAgent = navigator.userAgent; return /iP(ad|od|hone)/i.test(userAgent) && /WebKit/i.test(userAgent) && !/(CriOS|FxiOS|OPiOS|mercury)/i.test(userAgent); -} +}; /** * Checks if requesting user agent is Chrome browser on a mobile device * */ -function isMobileChrome() { +const isMobileChrome: IsMobileChrome = () => { const userAgent = navigator.userAgent; return /Android/i.test(userAgent) && /chrome|chromium|crios/i.test(userAgent); -} +}; -function isSafari() { - return getBrowser() === 'safari' || isMobileSafari(); -} +const isSafari: IsSafari = () => getBrowser() === 'safari' || isMobileSafari(); /** * The session information needs to be passed to the Desktop app, and the only way to do that is by using query params. There is no other way to transfer the data. */ -function openRouteInDesktopApp(shortLivedAuthToken = '', email = '') { +const openRouteInDesktopApp: OpenRouteInDesktopApp = (shortLivedAuthToken = '', email = '') => { const params = new URLSearchParams(); // If the user is opening the desktop app through a third party signin flow, we need to manually add the exitTo param // so that the desktop app redirects to the correct home route after signin is complete. @@ -102,6 +99,6 @@ function openRouteInDesktopApp(shortLivedAuthToken = '', email = '') { } else { window.location.href = expensifyDeeplinkUrl; } -} +}; export {getBrowser, isMobile, isMobileSafari, isSafari, isMobileChrome, openRouteInDesktopApp}; diff --git a/src/libs/Browser/types.ts b/src/libs/Browser/types.ts new file mode 100644 index 000000000000..2e84dca3fd7f --- /dev/null +++ b/src/libs/Browser/types.ts @@ -0,0 +1,13 @@ +type GetBrowser = () => string; + +type IsMobile = () => boolean; + +type IsMobileSafari = () => boolean; + +type IsMobileChrome = () => boolean; + +type IsSafari = () => boolean; + +type OpenRouteInDesktopApp = (shortLivedAuthToken?: string, email?: string) => void; + +export type {GetBrowser, IsMobile, IsMobileSafari, IsMobileChrome, IsSafari, OpenRouteInDesktopApp}; diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 07b12486b8b2..db806f71fab0 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -80,7 +80,7 @@ const getActiveRouteIndex = function (route, index) { /** * Main navigation method for redirecting to a route. * @param {String} route - * @param {String} type - Type of action to perform. Currently UP is supported. + * @param {String} [type] - Type of action to perform. Currently UP is supported. */ function navigate(route = ROUTES.HOME, type) { if (!canNavigate('navigate', {route})) { diff --git a/src/libs/actions/App.js b/src/libs/actions/App.ts similarity index 70% rename from src/libs/actions/App.js rename to src/libs/actions/App.ts index a1d64154906c..7c0e6a0dd240 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.ts @@ -1,11 +1,10 @@ // Do not remove this import until moment package is fully removed. // Issue - https://github.com/Expensify/App/issues/26719 import 'moment/locale/es'; -import {AppState} from 'react-native'; -import Onyx from 'react-native-onyx'; -import lodashGet from 'lodash/get'; +import {AppState, AppStateStatus} from 'react-native'; +import Onyx, {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; -import _ from 'underscore'; +import {ValueOf} from 'type-fest'; import * as API from '../API'; import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; @@ -20,45 +19,58 @@ import * as Session from './Session'; import * as ReportActionsUtils from '../ReportActionsUtils'; import Timing from './Timing'; import * as Browser from '../Browser'; +import * as OnyxTypes from '../../types/onyx'; -let currentUserAccountID; -let currentUserEmail; +type OnyxData = { + successData?: OnyxUpdate[]; + failureData?: OnyxUpdate[]; + optimisticData?: OnyxUpdate[]; +}; + +type PolicyParamsForOpenOrReconnect = { + policyIDList: string[]; +}; + +type Locale = ValueOf; + +let currentUserAccountID: number | string; +let currentUserEmail: string; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { - currentUserAccountID = lodashGet(val, 'accountID', ''); - currentUserEmail = lodashGet(val, 'email', ''); + currentUserAccountID = val?.accountID ?? ''; + currentUserEmail = val?.email ?? ''; }, }); -let isSidebarLoaded; +let isSidebarLoaded: boolean | null; Onyx.connect({ key: ONYXKEYS.IS_SIDEBAR_LOADED, callback: (val) => (isSidebarLoaded = val), initWithStoredValues: false, }); -let preferredLocale; +let preferredLocale: string | null; Onyx.connect({ key: ONYXKEYS.NVP_PREFERRED_LOCALE, callback: (val) => (preferredLocale = val), }); -let priorityMode; +let priorityMode: ValueOf | null; Onyx.connect({ key: ONYXKEYS.NVP_PRIORITY_MODE, callback: (nextPriorityMode) => { // When someone switches their priority mode we need to fetch all their chats because only #focus mode works with a subset of a user's chats. This is only possible via the OpenApp command. if (nextPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { - // eslint-disable-next-line no-use-before-define + // eslint-disable-next-line no-use-before-define,@typescript-eslint/no-use-before-define openApp(); } priorityMode = nextPriorityMode; }, }); -let resolveIsReadyPromise; -const isReadyToOpenApp = new Promise((resolve) => { +let resolveIsReadyPromise: () => void; +const isReadyToOpenApp = new Promise((resolve) => { resolveIsReadyPromise = resolve; }); @@ -67,21 +79,16 @@ function confirmReadyToOpenApp() { } /** - * @param {Array} policies - * @return {Array} array of policy ids + * @return array of policy ids */ -function getNonOptimisticPolicyIDs(policies) { - return _.chain(policies) - .reject((policy) => lodashGet(policy, 'pendingAction', null) === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) - .pluck('id') - .compact() - .value(); +function getNonOptimisticPolicyIDs(policies: OnyxCollection): string[] { + return Object.values(policies ?? {}) + .filter((policy) => policy && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) + .map((policy) => policy?.id) + .filter((id): id is string => !!id); } -/** - * @param {String} locale - */ -function setLocale(locale) { +function setLocale(locale: Locale) { if (locale === preferredLocale) { return; } @@ -93,7 +100,7 @@ function setLocale(locale) { } // Optimistically change preferred locale - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_PREFERRED_LOCALE, @@ -101,19 +108,18 @@ function setLocale(locale) { }, ]; - API.write( - 'UpdatePreferredLocale', - { - value: locale, - }, - {optimisticData}, - ); + type UpdatePreferredLocaleParams = { + value: Locale; + }; + + const parameters: UpdatePreferredLocaleParams = { + value: locale, + }; + + API.write('UpdatePreferredLocale', parameters, {optimisticData}); } -/** - * @param {String} locale - */ -function setLocaleAndNavigate(locale) { +function setLocaleAndNavigate(locale: Locale) { setLocale(locale); Navigation.goBack(ROUTES.SETTINGS_PREFERENCES); } @@ -128,7 +134,7 @@ function setSidebarLoaded() { Performance.markStart(CONST.TIMING.REPORT_INITIAL_RENDER); } -let appState; +let appState: AppStateStatus; AppState.addEventListener('change', (nextAppState) => { if (nextAppState.match(/inactive|background/) && appState === 'active') { Log.info('Flushing logs as app is going inactive', true, {}, true); @@ -138,9 +144,8 @@ AppState.addEventListener('change', (nextAppState) => { /** * Gets the policy params that are passed to the server in the OpenApp and ReconnectApp API commands. This includes a full list of policy IDs the client knows about as well as when they were last modified. - * @returns {Promise} */ -function getPolicyParamsForOpenOrReconnect() { +function getPolicyParamsForOpenOrReconnect(): Promise { return new Promise((resolve) => { isReadyToOpenApp.then(() => { const connectionID = Onyx.connect({ @@ -157,11 +162,9 @@ function getPolicyParamsForOpenOrReconnect() { /** * Returns the Onyx data that is used for both the OpenApp and ReconnectApp API commands. - * @param {Boolean} isOpenApp - * @returns {Object} */ -function getOnyxDataForOpenOrReconnect(isOpenApp = false) { - const defaultData = { +function getOnyxDataForOpenOrReconnect(isOpenApp = false): OnyxData { + const defaultData: OnyxData = { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, @@ -189,7 +192,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { } return { optimisticData: [ - ...defaultData.optimisticData, + ...(defaultData?.optimisticData ?? []), { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, @@ -197,7 +200,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { }, ], successData: [ - ...defaultData.successData, + ...(defaultData.successData ?? []), { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, @@ -205,7 +208,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { }, ], failureData: [ - ...defaultData.failureData, + ...(defaultData.failureData ?? []), { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, @@ -219,20 +222,31 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { * Fetches data needed for app initialization */ function openApp() { - getPolicyParamsForOpenOrReconnect().then((policyParams) => { - const params = {enablePriorityModeFilter: true, ...policyParams}; + getPolicyParamsForOpenOrReconnect().then((policyParams: PolicyParamsForOpenOrReconnect) => { + type OpenAppParams = PolicyParamsForOpenOrReconnect & { + enablePriorityModeFilter: boolean; + }; + + const params: OpenAppParams = {enablePriorityModeFilter: true, ...policyParams}; + API.read('OpenApp', params, getOnyxDataForOpenOrReconnect(true)); }); } /** * Fetches data when the app reconnects to the network - * @param {Number} [updateIDFrom] the ID of the Onyx update that we want to start fetching from + * @param [updateIDFrom] the ID of the Onyx update that we want to start fetching from */ function reconnectApp(updateIDFrom = 0) { console.debug(`[OnyxUpdates] App reconnecting with updateIDFrom: ${updateIDFrom}`); getPolicyParamsForOpenOrReconnect().then((policyParams) => { - const params = {...policyParams}; + type ReconnectParams = { + mostRecentReportActionLastModified?: string; + updateIDFrom?: number; + }; + type ReconnectAppParams = PolicyParamsForOpenOrReconnect & ReconnectParams; + + const params: ReconnectAppParams = {...policyParams}; // When the app reconnects we do a fast "sync" of the LHN and only return chats that have new messages. We achieve this by sending the most recent reportActionID. // we have locally. And then only update the user about chats with messages that have occurred after that reportActionID. @@ -257,12 +271,13 @@ function reconnectApp(updateIDFrom = 0) { * Fetches data when the app will call reconnectApp without params for the last time. This is a separate function * because it will follow patterns that are not recommended so we can be sure we're not putting the app in a unusable * state because of race conditions between reconnectApp and other pusher updates being applied at the same time. - * @return {Promise} */ -function finalReconnectAppAfterActivatingReliableUpdates() { +function finalReconnectAppAfterActivatingReliableUpdates(): Promise { console.debug(`[OnyxUpdates] Executing last reconnect app with promise`); return getPolicyParamsForOpenOrReconnect().then((policyParams) => { - const params = {...policyParams}; + type ReconnectAppParams = PolicyParamsForOpenOrReconnect & {mostRecentReportActionLastModified?: string}; + + const params: ReconnectAppParams = {...policyParams}; // When the app reconnects we do a fast "sync" of the LHN and only return chats that have new messages. We achieve this by sending the most recent reportActionID. // we have locally. And then only update the user about chats with messages that have occurred after that reportActionID. @@ -284,37 +299,39 @@ function finalReconnectAppAfterActivatingReliableUpdates() { /** * Fetches data when the client has discovered it missed some Onyx updates from the server - * @param {Number} [updateIDFrom] the ID of the Onyx update that we want to start fetching from - * @param {Number} [updateIDTo] the ID of the Onyx update that we want to fetch up to - * @return {Promise} + * @param [updateIDFrom] the ID of the Onyx update that we want to start fetching from + * @param [updateIDTo] the ID of the Onyx update that we want to fetch up to */ -function getMissingOnyxUpdates(updateIDFrom = 0, updateIDTo = 0) { +function getMissingOnyxUpdates(updateIDFrom = 0, updateIDTo = 0): Promise { console.debug(`[OnyxUpdates] Fetching missing updates updateIDFrom: ${updateIDFrom} and updateIDTo: ${updateIDTo}`); + type GetMissingOnyxMessagesParams = { + updateIDFrom: number; + updateIDTo: number; + }; + + const parameters: GetMissingOnyxMessagesParams = { + updateIDFrom, + updateIDTo, + }; + // It is SUPER BAD FORM to return promises from action methods. // DO NOT FOLLOW THIS PATTERN!!!!! // It was absolutely necessary in order to block OnyxUpdates while fetching the missing updates from the server or else the udpates aren't applied in the proper order. // eslint-disable-next-line rulesdir/no-api-side-effects-method - return API.makeRequestWithSideEffects( - 'GetMissingOnyxMessages', - { - updateIDFrom, - updateIDTo, - }, - getOnyxDataForOpenOrReconnect(), - ); + return API.makeRequestWithSideEffects('GetMissingOnyxMessages', parameters, getOnyxDataForOpenOrReconnect()); } /** * This promise is used so that deeplink component know when a transition is end. * This is necessary because we want to begin deeplink redirection after the transition is end. */ -let resolveSignOnTransitionToFinishPromise; -const signOnTransitionToFinishPromise = new Promise((resolve) => { +let resolveSignOnTransitionToFinishPromise: () => void; +const signOnTransitionToFinishPromise = new Promise((resolve) => { resolveSignOnTransitionToFinishPromise = resolve; }); -function waitForSignOnTransitionToFinish() { +function waitForSignOnTransitionToFinish(): Promise { return signOnTransitionToFinishPromise; } @@ -325,11 +342,11 @@ function endSignOnTransition() { /** * Create a new workspace and navigate to it * - * @param {String} [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param {Boolean} [makeMeAdmin] Optional, leave the calling account as an admin on the policy - * @param {String} [policyName] Optional, custom policy name we will use for created workspace - * @param {Boolean} [transitionFromOldDot] Optional, if the user is transitioning from old dot - * @param {Boolean} [shouldNavigateToAdminChat] Optional, navigate to the #admin room after creation + * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy + * @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy + * @param [policyName] Optional, custom policy name we will use for created workspace + * @param [transitionFromOldDot] Optional, if the user is transitioning from old dot + * @param [shouldNavigateToAdminChat] Optional, navigate to the #admin room after creation */ function createWorkspaceAndNavigateToIt(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', transitionFromOldDot = false, shouldNavigateToAdminChat = true) { const policyID = Policy.generatePolicyID(); @@ -368,10 +385,9 @@ function createWorkspaceAndNavigateToIt(policyOwnerEmail = '', makeMeAdmin = fal * We subscribe to the session using withOnyx in the AuthScreens and * pass it in as a parameter. withOnyx guarantees that the value has been read * from Onyx because it will not render the AuthScreens until that point. - * @param {Object} session - * @param {Boolean} shouldNavigateToAdminChat Should we navigate to admin chat after creating workspace + * @param shouldNavigateToAdminChat Should we navigate to admin chat after creating workspace */ -function setUpPoliciesAndNavigate(session, shouldNavigateToAdminChat) { +function setUpPoliciesAndNavigate(session: OnyxTypes.Session, shouldNavigateToAdminChat: boolean) { const currentUrl = getCurrentUrl(); if (!session || !currentUrl || !currentUrl.includes('exitTo')) { return; @@ -383,9 +399,9 @@ function setUpPoliciesAndNavigate(session, shouldNavigateToAdminChat) { // Approved Accountants and Guides can enter a flow where they make a workspace for other users, // and those are passed as a search parameter when using transition links - const policyOwnerEmail = url.searchParams.get('ownerEmail'); - const makeMeAdmin = url.searchParams.get('makeMeAdmin'); - const policyName = url.searchParams.get('policyName'); + const policyOwnerEmail = url.searchParams.get('ownerEmail') ?? ''; + const makeMeAdmin = !!url.searchParams.get('makeMeAdmin'); + const policyName = url.searchParams.get('policyName') ?? ''; // Sign out the current user if we're transitioning with a different user const isTransitioning = Str.startsWith(url.pathname, Str.normalizeUrl(ROUTES.TRANSITION_BETWEEN_APPS)); @@ -424,51 +440,54 @@ function redirectThirdPartyDesktopSignIn() { } } -function openProfile(personalDetails) { - const oldTimezoneData = personalDetails.timezone || {}; +function openProfile(personalDetails: OnyxTypes.PersonalDetails) { + const oldTimezoneData = personalDetails.timezone ?? {}; let newTimezoneData = oldTimezoneData; - if (lodashGet(oldTimezoneData, 'automatic', true)) { + if (oldTimezoneData?.automatic ?? true) { newTimezoneData = { automatic: true, selected: Intl.DateTimeFormat().resolvedOptions().timeZone, }; } - API.write( - 'OpenProfile', - { - timezone: JSON.stringify(newTimezoneData), - }, - { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [currentUserAccountID]: { - timezone: newTimezoneData, - }, + type OpenProfileParams = { + timezone: string; + }; + + const parameters: OpenProfileParams = { + timezone: JSON.stringify(newTimezoneData), + }; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: { + [currentUserAccountID]: { + timezone: newTimezoneData, }, }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [currentUserAccountID]: { - timezone: oldTimezoneData, - }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: { + [currentUserAccountID]: { + timezone: oldTimezoneData, }, }, - ], - }, - ); + }, + ], + }; + + API.write('OpenProfile', parameters, onyxData); } /** - * @param {boolean} shouldAuthenticateWithCurrentAccount Optional, indicates whether default authentication method (shortLivedAuthToken) should be used + * @param shouldAuthenticateWithCurrentAccount Optional, indicates whether default authentication method (shortLivedAuthToken) should be used */ function beginDeepLinkRedirect(shouldAuthenticateWithCurrentAccount = true) { // There's no support for anonymous users on desktop @@ -483,8 +502,14 @@ function beginDeepLinkRedirect(shouldAuthenticateWithCurrentAccount = true) { return; } + type OpenOldDotLinkParams = { + shouldRetry: boolean; + }; + + const parameters: OpenOldDotLinkParams = {shouldRetry: false}; + // eslint-disable-next-line rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects('OpenOldDotLink', {shouldRetry: false}, {}).then((response) => { + API.makeRequestWithSideEffects('OpenOldDotLink', parameters, {}).then((response) => { if (!response) { Log.alert( 'Trying to redirect via deep link, but the response is empty. User likely not authenticated.', @@ -499,16 +524,20 @@ function beginDeepLinkRedirect(shouldAuthenticateWithCurrentAccount = true) { } /** - * @param {boolean} shouldAuthenticateWithCurrentAccount Optional, indicates whether default authentication method (shortLivedAuthToken) should be used + * @param shouldAuthenticateWithCurrentAccount Optional, indicates whether default authentication method (shortLivedAuthToken) should be used */ function beginDeepLinkRedirectAfterTransition(shouldAuthenticateWithCurrentAccount = true) { waitForSignOnTransitionToFinish().then(() => beginDeepLinkRedirect(shouldAuthenticateWithCurrentAccount)); } -function handleRestrictedEvent(eventName) { - API.write('HandleRestrictedEvent', { - eventName, - }); +function handleRestrictedEvent(eventName: string) { + type HandleRestrictedEventParams = { + eventName: string; + }; + + const parameters: HandleRestrictedEventParams = {eventName}; + + API.write('HandleRestrictedEvent', parameters); } export { diff --git a/src/types/onyx/Session.ts b/src/types/onyx/Session.ts index 75cb4f4818ad..6c78f825e718 100644 --- a/src/types/onyx/Session.ts +++ b/src/types/onyx/Session.ts @@ -1,6 +1,6 @@ type Session = { /** The user's email for the current session */ - email?: string; + email: string; /** Currently logged in user authToken */ authToken?: string; From 8b55df1e06cb332707ca4ae11eb78912eb57b601 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 17 Oct 2023 15:38:36 +0200 Subject: [PATCH 2/8] Update typing after API file was migrated to TS --- src/libs/actions/App.ts | 34 ++++++++++++++-------------------- src/types/onyx/Response.ts | 1 + src/types/onyx/index.ts | 2 ++ 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 364236cb9dde..62b0f316a2ac 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -20,12 +20,7 @@ import * as ReportActionsUtils from '../ReportActionsUtils'; import Timing from './Timing'; import * as Browser from '../Browser'; import * as OnyxTypes from '../../types/onyx'; - -type OnyxData = { - successData?: OnyxUpdate[]; - failureData?: OnyxUpdate[]; - optimisticData?: OnyxUpdate[]; -}; +import type {OnyxData} from '../../types/onyx/Request'; type PolicyParamsForOpenOrReconnect = { policyIDList: string[]; @@ -272,7 +267,7 @@ function reconnectApp(updateIDFrom = 0) { * because it will follow patterns that are not recommended so we can be sure we're not putting the app in a unusable * state because of race conditions between reconnectApp and other pusher updates being applied at the same time. */ -function finalReconnectAppAfterActivatingReliableUpdates(): Promise { +function finalReconnectAppAfterActivatingReliableUpdates(): Promise { console.debug(`[OnyxUpdates] Executing last reconnect app with promise`); return getPolicyParamsForOpenOrReconnect().then((policyParams) => { type ReconnectAppParams = PolicyParamsForOpenOrReconnect & {mostRecentReportActionLastModified?: string}; @@ -302,7 +297,7 @@ function finalReconnectAppAfterActivatingReliableUpdates(): Promise { * @param [updateIDFrom] the ID of the Onyx update that we want to start fetching from * @param [updateIDTo] the ID of the Onyx update that we want to fetch up to */ -function getMissingOnyxUpdates(updateIDFrom = 0, updateIDTo = 0): Promise { +function getMissingOnyxUpdates(updateIDFrom = 0, updateIDTo = 0): Promise { console.debug(`[OnyxUpdates] Fetching missing updates updateIDFrom: ${updateIDFrom} and updateIDTo: ${updateIDTo}`); type GetMissingOnyxMessagesParams = { @@ -370,9 +365,9 @@ function createWorkspaceAndNavigateToIt(policyOwnerEmail = '', makeMeAdmin = fal /** * Create a new draft workspace and navigate to it * - * @param {String} [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param {String} [policyName] Optional, custom policy name we will use for created workspace - * @param {Boolean} [transitionFromOldDot] Optional, if the user is transitioning from old dot + * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy + * @param [policyName] Optional, custom policy name we will use for created workspace + * @param [transitionFromOldDot] Optional, if the user is transitioning from old dot */ function createWorkspaceWithPolicyDraftAndNavigateToIt(policyOwnerEmail = '', policyName = '', transitionFromOldDot = false) { const policyID = Policy.generatePolicyID(); @@ -392,12 +387,12 @@ function createWorkspaceWithPolicyDraftAndNavigateToIt(policyOwnerEmail = '', po /** * Create a new workspace and delete the draft * - * @param {String} [policyID] the ID of the policy to use - * @param {String} [policyName] custom policy name we will use for created workspace - * @param {String} [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param {Boolean} [makeMeAdmin] Optional, leave the calling account as an admin on the policy + * @param [policyID] the ID of the policy to use + * @param [policyName] custom policy name we will use for created workspace + * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy + * @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy */ -function savePolicyDraftByNewWorkspace(policyID, policyName, policyOwnerEmail = '', makeMeAdmin = false) { +function savePolicyDraftByNewWorkspace(policyID?: string, policyName?: string, policyOwnerEmail = '', makeMeAdmin = false) { Policy.createWorkspace(policyOwnerEmail, makeMeAdmin, policyName, policyID); } @@ -492,7 +487,8 @@ function openProfile(personalDetails: OnyxTypes.PersonalDetails) { const parameters: OpenProfileParams = { timezone: JSON.stringify(newTimezoneData), }; - const onyxData: OnyxData = { + + API.write('OpenProfile', parameters, { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, @@ -515,9 +511,7 @@ function openProfile(personalDetails: OnyxTypes.PersonalDetails) { }, }, ], - }; - - API.write('OpenProfile', parameters, onyxData); + }); } /** diff --git a/src/types/onyx/Response.ts b/src/types/onyx/Response.ts index 3d834d0bcb2b..780d3917ad1c 100644 --- a/src/types/onyx/Response.ts +++ b/src/types/onyx/Response.ts @@ -7,6 +7,7 @@ type Response = { onyxData?: OnyxUpdate[]; requestID?: string; shouldPauseQueue?: boolean; + shortLivedAuthToken?: string; authToken?: string; encryptedAuthToken?: string; message?: string; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index e50925e7adf2..39db54385a4b 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -46,6 +46,7 @@ import RecentWaypoint from './RecentWaypoint'; import RecentlyUsedCategories from './RecentlyUsedCategories'; import RecentlyUsedTags from './RecentlyUsedTags'; import PolicyTag from './PolicyTag'; +import Response from './Response'; export type { Account, @@ -98,4 +99,5 @@ export type { RecentlyUsedCategories, RecentlyUsedTags, PolicyTag, + Response, }; From 935c2df12880083b0ec05fd4f12f69978e16b24c Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 20 Oct 2023 10:26:09 +0200 Subject: [PATCH 3/8] Minor code improvements --- src/libs/actions/App.ts | 2 +- src/types/onyx/Response.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index b5624f6c980a..7a8cd1d10379 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -57,7 +57,7 @@ Onyx.connect({ callback: (nextPriorityMode) => { // When someone switches their priority mode we need to fetch all their chats because only #focus mode works with a subset of a user's chats. This is only possible via the OpenApp command. if (nextPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { - // eslint-disable-next-line no-use-before-define,@typescript-eslint/no-use-before-define + // eslint-disable-next-line @typescript-eslint/no-use-before-define openApp(); } priorityMode = nextPriorityMode; diff --git a/src/types/onyx/Response.ts b/src/types/onyx/Response.ts index 780d3917ad1c..aee2f6824ad3 100644 --- a/src/types/onyx/Response.ts +++ b/src/types/onyx/Response.ts @@ -7,10 +7,12 @@ type Response = { onyxData?: OnyxUpdate[]; requestID?: string; shouldPauseQueue?: boolean; - shortLivedAuthToken?: string; authToken?: string; encryptedAuthToken?: string; message?: string; + + /** Short-lived authToken to sign in a user */ + shortLivedAuthToken?: string; }; export default Response; From 103d4aff51ca3981aff7582fb2a34a245b47c883 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 30 Oct 2023 09:45:38 +0100 Subject: [PATCH 4/8] Run linter after merging main --- src/libs/actions/App.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 7a8cd1d10379..cad4e9de111f 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -1,26 +1,26 @@ // Do not remove this import until moment package is fully removed. // Issue - https://github.com/Expensify/App/issues/26719 +import Str from 'expensify-common/lib/str'; import 'moment/locale/es'; import {AppState, AppStateStatus} from 'react-native'; import Onyx, {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; -import Str from 'expensify-common/lib/str'; import {ValueOf} from 'type-fest'; -import * as API from '../API'; -import ONYXKEYS from '../../ONYXKEYS'; -import CONST from '../../CONST'; -import Log from '../Log'; -import Performance from '../Performance'; +import * as API from '@libs/API'; +import * as Browser from '@libs/Browser'; +import Log from '@libs/Log'; +import getCurrentUrl from '@libs/Navigation/currentUrl'; +import Navigation from '@libs/Navigation/Navigation'; +import Performance from '@libs/Performance'; +import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import * as SessionUtils from '@libs/SessionUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import * as OnyxTypes from '@src/types/onyx'; +import type {OnyxData} from '@src/types/onyx/Request'; import * as Policy from './Policy'; -import Navigation from '../Navigation/Navigation'; -import ROUTES from '../../ROUTES'; -import * as SessionUtils from '../SessionUtils'; -import getCurrentUrl from '../Navigation/currentUrl'; import * as Session from './Session'; -import * as ReportActionsUtils from '../ReportActionsUtils'; import Timing from './Timing'; -import * as Browser from '../Browser'; -import * as OnyxTypes from '../../types/onyx'; -import type {OnyxData} from '../../types/onyx/Request'; type PolicyParamsForOpenOrReconnect = { policyIDList: string[]; From 25e9ec78451c2dc3ec9c89a5b6dd2fe90250d46b Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 30 Oct 2023 10:04:01 +0100 Subject: [PATCH 5/8] Fix ts error --- src/libs/actions/App.ts | 2 +- src/types/onyx/Session.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index cad4e9de111f..420eb8dea919 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -422,7 +422,7 @@ function setUpPoliciesAndNavigate(session: OnyxTypes.Session, shouldNavigateToAd return; } - const isLoggingInAsNewUser = SessionUtils.isLoggingInAsNewUser(currentUrl, session.email); + const isLoggingInAsNewUser = !!session.email && SessionUtils.isLoggingInAsNewUser(currentUrl, session.email); const url = new URL(currentUrl); const exitTo = url.searchParams.get('exitTo'); diff --git a/src/types/onyx/Session.ts b/src/types/onyx/Session.ts index 6941e38b31d3..faaa493b1286 100644 --- a/src/types/onyx/Session.ts +++ b/src/types/onyx/Session.ts @@ -6,7 +6,7 @@ type AutoAuthState = ValueOf; type Session = { /** The user's email for the current session */ - email: string; + email?: string; /** Currently logged in user authToken */ authToken?: string; From f6ccc56ffd5df1300efbee291c4828012ace9375 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 6 Nov 2023 18:01:23 +0100 Subject: [PATCH 6/8] Remove extra optional chaining --- src/libs/actions/App.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 1e8f76b33b83..e67fde2cd5f8 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -187,7 +187,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false): OnyxData { } return { optimisticData: [ - ...(defaultData?.optimisticData ?? []), + ...(defaultData.optimisticData ?? []), { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, From 062d12024fcf607b729016a8bdae90afc38b35be Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 7 Nov 2023 15:51:33 +0100 Subject: [PATCH 7/8] Get rid of extra nullish coalescing operators --- src/libs/actions/App.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index e67fde2cd5f8..db6d2bb70913 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -159,7 +159,7 @@ function getPolicyParamsForOpenOrReconnect(): Promise = { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, @@ -187,7 +187,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false): OnyxData { } return { optimisticData: [ - ...(defaultData.optimisticData ?? []), + ...defaultData.optimisticData, { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, @@ -195,7 +195,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false): OnyxData { }, ], successData: [ - ...(defaultData.successData ?? []), + ...defaultData.successData, { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, @@ -203,7 +203,7 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false): OnyxData { }, ], failureData: [ - ...(defaultData.failureData ?? []), + ...defaultData.failureData, { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.IS_LOADING_APP, From 6955074da0769f9b5a812e0681297f0a6f5e302c Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Thu, 9 Nov 2023 08:24:51 +0100 Subject: [PATCH 8/8] Remove extra comment --- src/libs/actions/App.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index db6d2bb70913..ff673d173613 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -73,9 +73,6 @@ function confirmReadyToOpenApp() { resolveIsReadyPromise(); } -/** - * @return array of policy ids - */ function getNonOptimisticPolicyIDs(policies: OnyxCollection): string[] { return Object.values(policies ?? {}) .filter((policy) => policy && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)