import { ToastWrapper } from '@Components/ToastWrapper' import { ActionSheetProvider } from '@expo/react-native-action-sheet' import { MobileApplication } from '@Lib/Application' import { ApplicationGroup } from '@Lib/ApplicationGroup' import { navigationRef } from '@Lib/NavigationService' import { DefaultTheme, NavigationContainer } from '@react-navigation/native' import { ApplicationGroupContext } from '@Root/ApplicationGroupContext' import { MobileThemeVariables } from '@Root/Style/Themes/styled-components' import { ApplicationGroupEvent, DeinitMode, DeinitSource } from '@standardnotes/snjs' import { ThemeService, ThemeServiceContext } from '@Style/ThemeService' import React, { useCallback, useEffect, useRef, useState } from 'react' import { ThemeProvider } from 'styled-components/native' import { ApplicationContext } from './ApplicationContext' import { NativeMainStackComponent } from './ModalStack' export type HeaderTitleParams = { title?: string subTitle?: string subTitleColor?: string } export type TEnvironment = 'prod' | 'dev' const AppComponent: React.FC<{ application: MobileApplication env: TEnvironment }> = ({ application, env }) => { const themeService = useRef() const appReady = useRef(false) const navigationReady = useRef(false) const [activeTheme, setActiveTheme] = useState() const setThemeServiceRef = useCallback((node: ThemeService | undefined) => { if (node) { node.addThemeChangeObserver(() => { setActiveTheme(node.variables) }) } /** * We check if both application and navigation are ready and launch application afterwads */ themeService.current = node }, []) /** * We check if both application and navigation are ready and launch application afterwads */ const launchApp = useCallback( (setAppReady: boolean, setNavigationReady: boolean) => { if (setAppReady) { appReady.current = true } if (setNavigationReady) { navigationReady.current = true } if (navigationReady.current && appReady.current) { void application.launch() } }, [application], ) useEffect(() => { let themeServiceInstance: ThemeService const loadApplication = async () => { themeServiceInstance = new ThemeService(application) setThemeServiceRef(themeServiceInstance) await application.prepareForLaunch({ receiveChallenge: async (challenge) => { application.promptForChallenge(challenge) }, }) await themeServiceInstance.init() launchApp(true, false) } void loadApplication() return () => { themeServiceInstance?.deinit() setThemeServiceRef(undefined) if (!application.hasStartedDeinit()) { application.deinit(DeinitMode.Soft, DeinitSource.Lock) } } }, [application, application.Uuid, env, launchApp, setThemeServiceRef]) if (!themeService.current || !activeTheme) { return null } return ( launchApp(false, true)} theme={{ ...DefaultTheme, colors: { ...DefaultTheme.colors, background: activeTheme.stylekitBackgroundColor, border: activeTheme.stylekitBorderColor, }, }} ref={navigationRef} > {themeService.current && ( <> )} ) } export const NativeApp = (props: { env: TEnvironment }) => { const [application, setApplication] = useState() const createNewAppGroup = useCallback(() => { const group = new ApplicationGroup() void group.initialize() return group }, []) const [appGroup, setAppGroup] = useState(() => createNewAppGroup()) useEffect(() => { const removeAppChangeObserver = appGroup.addEventObserver((event) => { if (event === ApplicationGroupEvent.PrimaryApplicationSet) { const mobileApplication = appGroup.primaryApplication as MobileApplication setApplication(mobileApplication) } else if (event === ApplicationGroupEvent.DeviceWillRestart) { setApplication(undefined) setAppGroup(createNewAppGroup()) } }) return removeAppChangeObserver }, [appGroup, appGroup.primaryApplication, createNewAppGroup]) if (!application) { return null } return ( ) }