import { ApplicationGroup } from '@/Application/ApplicationGroup' import { getPlatformString } from '@/Utils' import { ApplicationEvent, Challenge, removeFromArray, WebAppEvent } from '@standardnotes/snjs' import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants' import { alertDialog, RouteType } from '@standardnotes/ui-services' import { WebApplication } from '@/Application/Application' import Navigation from '@/Components/Tags/Navigation' import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView' import Footer from '@/Components/Footer/Footer' import SessionsModal from '@/Components/SessionsModal/SessionsModal' import PreferencesViewWrapper from '@/Components/Preferences/PreferencesViewWrapper' import ChallengeModal from '@/Components/ChallengeModal/ChallengeModal' import NotesContextMenu from '@/Components/NotesContextMenu/NotesContextMenu' import PurchaseFlowWrapper from '@/Components/PurchaseFlow/PurchaseFlowWrapper' import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react' import RevisionHistoryModal from '@/Components/RevisionHistoryModal/RevisionHistoryModal' import PremiumModalProvider from '@/Hooks/usePremiumModal' import ConfirmSignoutContainer from '@/Components/ConfirmSignoutModal/ConfirmSignoutModal' import { ToastContainer } from '@standardnotes/toast' import FilePreviewModalWrapper from '@/Components/FilePreview/FilePreviewModal' import ContentListView from '@/Components/ContentListView/ContentListView' import FileContextMenuWrapper from '@/Components/FileContextMenu/FileContextMenu' import PermissionsModalWrapper from '@/Components/PermissionsModal/PermissionsModalWrapper' import { PanelResizedData } from '@/Types/PanelResizedData' import TagContextMenuWrapper from '@/Components/Tags/TagContextMenuWrapper' import FileDragNDropProvider from '../FileDragNDropProvider/FileDragNDropProvider' import ResponsivePaneProvider from '../ResponsivePane/ResponsivePaneProvider' import AndroidBackHandlerProvider from '@/NativeMobileWeb/useAndroidBackHandler' import ConfirmDeleteAccountContainer from '@/Components/ConfirmDeleteAccountModal/ConfirmDeleteAccountModal' import DarkModeHandler from '../DarkModeHandler/DarkModeHandler' import ApplicationProvider from './ApplicationProvider' import { ErrorBoundary } from '@/Utils/ErrorBoundary' import CommandProvider from './CommandProvider' type Props = { application: WebApplication mainApplicationGroup: ApplicationGroup } const ApplicationView: FunctionComponent = ({ application, mainApplicationGroup }) => { const platformString = getPlatformString() const [launched, setLaunched] = useState(false) const [needsUnlock, setNeedsUnlock] = useState(true) const [challenges, setChallenges] = useState([]) const viewControllerManager = application.getViewControllerManager() const appColumnContainerRef = useRef(null) useEffect(() => { const desktopService = application.getDesktopService() if (desktopService) { application.componentManager.setDesktopManager(desktopService) } application .prepareForLaunch({ receiveChallenge: async (challenge) => { const challengesCopy = challenges.slice() challengesCopy.push(challenge) setChallenges(challengesCopy) }, }) .then(() => { void application.launch() }) .catch(console.error) // eslint-disable-next-line react-hooks/exhaustive-deps }, [application]) const removeChallenge = useCallback( (challenge: Challenge) => { const challengesCopy = challenges.slice() removeFromArray(challengesCopy, challenge) setChallenges(challengesCopy) }, [challenges], ) const onAppStart = useCallback(() => { setNeedsUnlock(application.hasPasscode()) }, [application]) const handleDemoSignInFromParamsIfApplicable = useCallback(() => { const route = application.routeService.getRoute() if (route.type !== RouteType.Demo) { return } const token = route.demoParams.token if (!token || application.hasAccount()) { return } void application.sessions.populateSessionFromDemoShareToken(token) }, [application]) const onAppLaunch = useCallback(() => { setLaunched(true) setNeedsUnlock(false) handleDemoSignInFromParamsIfApplicable() }, [handleDemoSignInFromParamsIfApplicable]) useEffect(() => { if (application.isStarted()) { onAppStart() } if (application.isLaunched()) { onAppLaunch() } const removeAppObserver = application.addEventObserver(async (eventName) => { if (eventName === ApplicationEvent.Started) { onAppStart() } else if (eventName === ApplicationEvent.Launched) { onAppLaunch() } else if (eventName === ApplicationEvent.LocalDatabaseReadError) { alertDialog({ text: 'Unable to load local database. Please restart the app and try again.', }).catch(console.error) } else if (eventName === ApplicationEvent.LocalDatabaseWriteError) { alertDialog({ text: 'Unable to write to local database. Please restart the app and try again.', }).catch(console.error) } else if (eventName === ApplicationEvent.BiometricsSoftLockEngaged) { setNeedsUnlock(true) } else if (eventName === ApplicationEvent.BiometricsSoftLockDisengaged) { setNeedsUnlock(false) } }) return () => { removeAppObserver() } }, [application, onAppLaunch, onAppStart]) useEffect(() => { const removeObserver = application.addWebEventObserver(async (eventName, data) => { if (eventName === WebAppEvent.PanelResized) { if (!appColumnContainerRef.current) { return } const { panel, collapsed } = data as PanelResizedData if (panel === PANEL_NAME_NOTES) { if (collapsed) { appColumnContainerRef.current.classList.add('collapsed-notes') } else { appColumnContainerRef.current.classList.remove('collapsed-notes') } } if (panel === PANEL_NAME_NAVIGATION) { if (collapsed) { appColumnContainerRef.current.classList.add('collapsed-navigation') } else { appColumnContainerRef.current.classList.remove('collapsed-navigation') } } } else if (eventName === WebAppEvent.WindowDidFocus) { if (!(await application.isLocked())) { application.sync.sync().catch(console.error) } } }) return () => { removeObserver() } }, [application]) const renderAppContents = useMemo(() => { return !needsUnlock && launched }, [needsUnlock, launched]) const renderChallenges = useCallback(() => { return challenges.map((challenge) => (
)) }, [viewControllerManager, challenges, mainApplicationGroup, removeChallenge, application]) if (!renderAppContents) { return {renderChallenges()} } return (
<>
) } export default ApplicationView