diff --git a/src/Expensify.tsx b/src/Expensify.tsx index f5d4655c4861..7822ec16b879 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, use import type {NativeEventSubscription} from 'react-native'; import {AppState, Linking, NativeModules, Platform} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import Onyx, {useOnyx, withOnyx} from 'react-native-onyx'; +import Onyx, {useOnyx} from 'react-native-onyx'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; @@ -55,7 +55,7 @@ Onyx.registerLogger(({level, message}) => { } }); -type ExpensifyOnyxProps = { +type ExpensifyProps = { /** Whether the app is waiting for the server's response to determine if a room is public */ isCheckingPublicRoom: OnyxEntry; @@ -77,18 +77,7 @@ type ExpensifyOnyxProps = { /** Last visited path in the app */ lastVisitedPath: OnyxEntry; }; - -type ExpensifyProps = ExpensifyOnyxProps; - -function Expensify({ - isCheckingPublicRoom = true, - updateAvailable, - isSidebarLoaded = false, - screenShareRequest, - updateRequired = false, - focusModeNotification = false, - lastVisitedPath, -}: ExpensifyProps) { +function Expensify() { const appStateChangeListener = useRef(null); const [isNavigationReady, setIsNavigationReady] = useState(false); const [isOnyxMigrated, setIsOnyxMigrated] = useState(false); @@ -98,7 +87,15 @@ function Expensify({ const [account] = useOnyx(ONYXKEYS.ACCOUNT); const [session] = useOnyx(ONYXKEYS.SESSION); const [lastRoute] = useOnyx(ONYXKEYS.LAST_ROUTE); + const [userMetadata] = useOnyx(ONYXKEYS.USER_METADATA); const [shouldShowRequire2FAModal, setShouldShowRequire2FAModal] = useState(false); + const [isCheckingPublicRoom] = useOnyx(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM); + const [updateAvailable] = useOnyx(ONYXKEYS.UPDATE_AVAILABLE); + const [updateRequired] = useOnyx(ONYXKEYS.UPDATE_REQUIRED); + const [isSidebarLoaded] = useOnyx(ONYXKEYS.IS_SIDEBAR_LOADED); + const [screenShareRequest] = useOnyx(ONYXKEYS.SCREEN_SHARE_REQUEST); + const [focusModeNotification] = useOnyx(ONYXKEYS.FOCUS_MODE_NOTIFICATION); + const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH); useEffect(() => { if (!account?.needsTwoFactorAuthSetup || account.requiresTwoFactorAuth) { @@ -148,15 +145,17 @@ function Expensify({ // Initialize this client as being an active client ActiveClientManager.init(); - // Initialize Fullstory lib - FS.init(); - // Used for the offline indicator appearing when someone is offline const unsubscribeNetInfo = NetworkConnection.subscribeToNetInfo(); return unsubscribeNetInfo; }, []); + useEffect(() => { + // Initialize Fullstory lib + FS.init(userMetadata); + }, [userMetadata]); + // Log the platform and config to debug .env issues useEffect(() => { Log.info('App launched', false, {Platform, CONFIG}); @@ -304,30 +303,4 @@ function Expensify({ Expensify.displayName = 'Expensify'; -export default withOnyx({ - isCheckingPublicRoom: { - key: ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, - initWithStoredValues: false, - }, - updateAvailable: { - key: ONYXKEYS.UPDATE_AVAILABLE, - initWithStoredValues: false, - }, - updateRequired: { - key: ONYXKEYS.UPDATE_REQUIRED, - initWithStoredValues: false, - }, - isSidebarLoaded: { - key: ONYXKEYS.IS_SIDEBAR_LOADED, - }, - screenShareRequest: { - key: ONYXKEYS.SCREEN_SHARE_REQUEST, - }, - focusModeNotification: { - key: ONYXKEYS.FOCUS_MODE_NOTIFICATION, - initWithStoredValues: false, - }, - lastVisitedPath: { - key: ONYXKEYS.LAST_VISITED_PATH, - }, -})(Expensify); +export default Expensify; diff --git a/src/libs/Fullstory/index.native.ts b/src/libs/Fullstory/index.native.ts index f560b286a79c..8d97b8d4307e 100644 --- a/src/libs/Fullstory/index.native.ts +++ b/src/libs/Fullstory/index.native.ts @@ -1,9 +1,7 @@ import FullStory, {FSPage} from '@fullstory/react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import * as Environment from '@src/libs/Environment/Environment'; -import ONYXKEYS from '@src/ONYXKEYS'; import type {UserMetadata} from '@src/types/onyx'; /** @@ -14,18 +12,8 @@ const FS = { /** * Initializes FullStory */ - init: () => { - Environment.getEnvironment().then((envName: string) => { - // We only want to start fullstory if the app is running in production - // Since we don't use it in other environments, it is also disabled in build.gradle to speed up Android build times - // See https://github.com/Expensify/App/pull/50206 for more information - if (envName !== CONST.ENVIRONMENT.PRODUCTION) { - return; - } - FullStory.restart(); - const [session] = useOnyx(ONYXKEYS.USER_METADATA); - FS.fsIdentify(session); - }); + init: (value: OnyxEntry) => { + FS.consentAndIdentify(value); }, /** @@ -42,10 +30,23 @@ const FS = { * Initializes the FullStory metadata with the provided metadata information. */ consentAndIdentify: (value: OnyxEntry) => { + // On the first subscribe for UserMetadta, this function will be called. We need + // to confirm that we actually have any value here before proceeding. + if (!value?.accountID) { + return; + } try { - // We only use FullStory in production environment - FullStory.consent(true); - FS.fsIdentify(value); + // We only use FullStory in production environment. We need to check this here + // after the init function since this function is also called on updates for + // UserMetadata onyx key. + Environment.getEnvironment().then((envName: string) => { + if (envName !== CONST.ENVIRONMENT.PRODUCTION) { + return; + } + FullStory.restart(); + FullStory.consent(true); + FS.fsIdentify(value, envName); + }); } catch (e) { // error handler } @@ -53,21 +54,11 @@ const FS = { /** * Sets the FullStory user identity based on the provided metadata information. - * If the metadata is null or the email is 'undefined', the user identity is anonymized. - * If the metadata contains an accountID, the user identity is defined with it. */ - fsIdentify: (metadata: OnyxEntry) => { - if (!metadata?.accountID) { - // anonymize FullStory user identity metadata - FullStory.anonymize(); - } else { - Environment.getEnvironment().then((envName: string) => { - // define FullStory user identity - const localMetadata = metadata; - localMetadata.environment = envName; - FullStory.identify(String(localMetadata.accountID), localMetadata); - }); - } + fsIdentify: (metadata: UserMetadata, envName: string) => { + const localMetadata = metadata; + localMetadata.environment = envName; + FullStory.identify(String(localMetadata.accountID), localMetadata); }, }; diff --git a/src/libs/Fullstory/index.ts b/src/libs/Fullstory/index.ts index 8419bccabb90..df65af358a55 100644 --- a/src/libs/Fullstory/index.ts +++ b/src/libs/Fullstory/index.ts @@ -29,17 +29,11 @@ const FS = { */ onReady: () => new Promise((resolve) => { - Environment.getEnvironment().then((envName: string) => { - if (CONST.ENVIRONMENT.PRODUCTION !== envName) { - return; - } - // Initialised via HEAD snippet - if (!isInitialized()) { - init({orgId: ''}, resolve); - } else { - FullStory('observe', {type: 'start', callback: resolve}); - } - }); + if (!isInitialized()) { + init({orgId: ''}, resolve); + } else { + FullStory('observe', {type: 'start', callback: resolve}); + } }), /** @@ -56,6 +50,11 @@ const FS = { * Initializes the FullStory metadata with the provided metadata information. */ consentAndIdentify: (value: OnyxEntry) => { + // On the first subscribe for UserMetadata, this function will be called. We need + // to confirm that we actually have any value here before proceeding. + if (!value?.accountID) { + return; + } try { Environment.getEnvironment().then((envName: string) => { if (CONST.ENVIRONMENT.PRODUCTION !== envName) { @@ -63,11 +62,9 @@ const FS = { } FS.onReady().then(() => { FS.consent(true); - if (value) { - const localMetadata = value; - localMetadata.environment = envName; - FS.fsIdentify(localMetadata); - } + const localMetadata = value; + localMetadata.environment = envName; + FS.fsIdentify(localMetadata); }); }); } catch (e) { @@ -80,23 +77,18 @@ const FS = { * If the metadata does not contain an email, the user identity is anonymized. * If the metadata contains an accountID, the user identity is defined with it. */ - fsIdentify: (metadata: OnyxEntry) => { - if (!metadata?.accountID) { - // anonymize FullStory user identity metadata - FS.anonymize(); - } else { - // define FullStory user identity - FullStory('setIdentity', { - uid: String(metadata.accountID), - properties: metadata, - }); - } + fsIdentify: (metadata: UserMetadata) => { + FullStory('setIdentity', { + uid: String(metadata.accountID), + properties: metadata, + }); }, /** * Init function, created so we're consistent with the native file */ - init: () => {}, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + init: (_value: OnyxEntry) => {}, }; export default FS;