import 'react-native-get-random-values'; import React, {useContext, useEffect, useState} from 'react'; import {AppLayout} from './screens/AppLayout'; import {useFont} from './shared/hooks/useFont'; import {GlobalContextProvider} from './components/GlobalContextProvider'; import {GlobalContext} from './shared/GlobalContext'; import {useSelector} from '@xstate/react'; import {useTranslation} from 'react-i18next'; import { APP_EVENTS, selectIsDecryptError, selectIsKeyInvalidateError, selectIsDeepLinkDetected, selectIsReadError, selectIsReady, } from './machines/app'; import {DualMessageOverlay} from './components/DualMessageOverlay'; import {useApp} from './screens/AppController'; import {Alert, AppState} from 'react-native'; import { configureTelemetry, getErrorEventData, sendErrorEvent, } from './shared/telemetry/TelemetryUtils'; import {TelemetryConstants} from './shared/telemetry/TelemetryConstants'; import {MessageOverlay} from './components/MessageOverlay'; import {NativeModules} from 'react-native'; import {isHardwareKeystoreExists} from './shared/cryptoutil/cryptoUtil'; import i18n from './i18n'; import {CopilotProvider} from 'react-native-copilot'; import {CopilotTooltip} from './components/CopilotTooltip'; import {Theme} from './components/ui/styleUtils'; import {selectAppSetupComplete} from './machines/auth'; const {RNSecureKeystoreModule} = NativeModules; // kludge: this is a bad practice but has been done temporarily to surface // an occurrence of a bug with minimal residual code changes, this should // be removed once the bug cause is determined & fixed, ref: INJI-222 const DecryptErrorAlert = (controller, t) => { const heading = t('errors.decryptionFailed'); const desc = t('errors.decryptionFailed'); const ignoreBtnTxt = t('ignore'); Alert.alert(heading, desc, [ { text: ignoreBtnTxt, onPress: () => controller.ignoreDecrypt(), style: 'cancel', }, ]); }; const AppLayoutWrapper: React.FC = () => { const {appService} = useContext(GlobalContext); const isDecryptError = useSelector(appService, selectIsDecryptError); const isDeepLinkFlow = useSelector(appService, selectIsDeepLinkDetected); const controller = useApp(); const {t} = useTranslation('WelcomeScreen'); const authService = appService.children.get('auth'); const isAppSetupComplete = useSelector(authService, selectAppSetupComplete); const [isDeepLinkOverlayVisible, setDeepLinkOverlayVisible] = useState(isDeepLinkFlow); useEffect(() => { if (AppState.currentState === 'active') { appService.send(APP_EVENTS.ACTIVE()); } else { appService.send(APP_EVENTS.INACTIVE()); } }, []); useEffect(() => { setDeepLinkOverlayVisible(isDeepLinkFlow); }, [isDeepLinkFlow]); if (isDecryptError) { DecryptErrorAlert(controller, t); } configureTelemetry(); return ( <> { setDeepLinkOverlayVisible(false); }} buttonText={t('common:ok')} minHeight={'auto'} /> ); }; const AppLoadingWrapper: React.FC = () => { const {appService} = useContext(GlobalContext); const isReadError = useSelector(appService, selectIsReadError); const isKeyInvalidateError = useSelector( appService, selectIsKeyInvalidateError, ); const controller = useApp(); const {t} = useTranslation('WelcomeScreen'); useEffect(() => { if (isKeyInvalidateError) { configureTelemetry(); sendErrorEvent( getErrorEventData( TelemetryConstants.FlowType.appLogin, TelemetryConstants.ErrorId.appWasReset, TelemetryConstants.ErrorMessage.appWasReset, ), ); } }, [isKeyInvalidateError]); return ( <> {isReadError ? ( ) : null} ); }; const AppInitialization: React.FC = () => { const {appService} = useContext(GlobalContext); const hasFontsLoaded = useFont(); const isReady = useSelector(appService, selectIsReady); const {t} = useTranslation('common'); useEffect(() => { if (isHardwareKeystoreExists) { RNSecureKeystoreModule.updatePopup( t('biometricPopup.title'), t('biometricPopup.description'), ); } }, [i18n.language]); return isReady && hasFontsLoaded ? ( ) : ( ); }; export default function App() { return ( null} animated> ); }