import React, { useEffect, useRef, useState } from 'react'; import { Platform, View } from 'react-native'; import RNBootSplash from 'react-native-bootsplash'; import { checkVersion } from 'react-native-check-version'; import { editorState } from '../../screens/editor/tiptap/utils'; import BackupService from '../../services/backup'; import BiometricService from '../../services/biometrics'; import { DDS } from '../../services/device-detection'; import { eSendEvent, presentSheet, ToastEvent } from '../../services/event-manager'; import { setRateAppMessage } from '../../services/message'; import PremiumService from '../../services/premium'; import SettingsService from '../../services/settings'; import { initialize } from '../../stores'; import { useMessageStore } from '../../stores/use-message-store'; import { useNoteStore } from '../../stores/use-notes-store'; import { useSettingStore } from '../../stores/use-setting-store'; import { useThemeStore } from '../../stores/use-theme-store'; import { useUserStore } from '../../stores/use-user-store'; import { db, loadDatabase } from '../../utils/database'; import { MMKV } from '../../utils/database/mmkv'; import { eOpenAnnouncementDialog } from '../../utils/events'; import { tabBarRef } from '../../utils/global-refs'; import { SIZE } from '../../utils/size'; import { sleep } from '../../utils/time'; import { SVG } from '../auth/background'; import Intro from '../intro'; import NewFeature from '../sheets/new-feature/index'; import { Update } from '../sheets/update'; import { Button } from '../ui/button'; import { IconButton } from '../ui/icon-button'; import Input from '../ui/input'; import Seperator from '../ui/seperator'; import { SvgView } from '../ui/svg'; import Heading from '../ui/typography/heading'; import Paragraph from '../ui/typography/paragraph'; import { Walkthrough } from '../walkthroughs'; const Launcher = React.memo( () => { const colors = useThemeStore(state => state.colors); const setLoading = useNoteStore(state => state.setLoading); const loading = useNoteStore(state => state.loading); const user = useUserStore(state => state.user); const verifyUser = useUserStore(state => state.verifyUser); const setVerifyUser = useUserStore(state => state.setVerifyUser); const deviceMode = useSettingStore(state => state.deviceMode); const passwordInputRef = useRef(); const password = useRef(); const introCompleted = false; //SettingsService.get().introCompleted; const dbInitCompleted = useRef(false); const loadNotes = async () => { if (verifyUser) { return; } await restoreEditorState(); setImmediate(() => { db.notes.init().then(() => { Walkthrough.init(); initialize(); setImmediate(() => { setLoading(false); setImmediate(() => doAppLoadActions()); }); }); }); }; const init = async () => { if (!dbInitCompleted.current) { if (!verifyUser) await RNBootSplash.hide({ fade: true }); await loadDatabase(); await db.init(); dbInitCompleted.current = true; } if (!verifyUser) { loadNotes(); } }; // useEffect(() => { // hideSplashScreen(); // }, [verifyUser]); useEffect(() => { if (!loading) { doAppLoadActions(); } return () => { dbInitCompleted.current = false; }; }, [loading]); const doAppLoadActions = async () => { await sleep(500); if (SettingsService.get().sessionExpired) { eSendEvent('session_expired'); return; } if (NewFeature.present()) return; if (await checkAppUpdateAvailable()) return; if (await checkForRateAppRequest()) return; if (await checkNeedsBackup()) return; if (await PremiumService.getRemainingTrialDaysStatus()) return; await useMessageStore.getState().setAnnouncement(); if (PremiumService.get() && user) { if (SettingsService.get().reminder === 'off') { SettingsService.set({ reminder: 'daily' }); } if (BackupService.checkBackupRequired()) { sleep(2000).then(() => BackupService.checkAndRun()); } } if (introCompleted) { useMessageStore.subscribe(state => { let dialogs = state.dialogs; if (dialogs.length > 0) { eSendEvent(eOpenAnnouncementDialog, dialogs[0]); } }); } }; const checkAppUpdateAvailable = async () => { try { const version = await checkVersion(); if (!version.needsUpdate) return false; presentSheet({ component: ref => }); return true; } catch (e) { return false; } }; const restoreEditorState = async () => { let appState = MMKV.getString('appState'); if (appState) { appState = JSON.parse(appState); if ( appState.note && !appState.note.locked && !appState.movedAway && Date.now() < appState.timestamp + 3600000 ) { editorState().currentlyEditing = true; editorState().isRestoringState = true; editorState().movedAway = false; if (!DDS.isTab) { tabBarRef.current?.goToPage(1); } eSendEvent('loadingNote', appState.note); } } }; const checkForRateAppRequest = async () => { let rateApp = SettingsService.get().rateApp; if (rateApp && rateApp < Date.now() && !useMessageStore.getState().message?.visible) { setRateAppMessage(); return true; } return false; }; const checkNeedsBackup = async () => { return false; // let { nextBackupRequestTime, reminder } = SettingsService.get(); // if (reminder === 'off' || !reminder) { // if (nextBackupRequestTime < Date.now()) { // presentSheet({ // title: 'Backup & restore', // paragraph: 'Please enable automatic backups to keep your data safe', // component: // }); // return true; // } // } // return false; }; const onUnlockBiometrics = async () => { if (!(await BiometricService.isBiometryAvailable())) { ToastEvent.show({ heading: 'Biometrics unavailable', message: 'Try unlocking the app with your account password' }); return; } let verified = await BiometricService.validateUser('Unlock to access your notes', ''); if (verified) { setVerifyUser(false); password.current = null; } }; useEffect(() => { if (verifyUser) { onUnlockBiometrics(); } init(); }, [verifyUser]); const onSubmit = async () => { if (!password.current) return; try { let verified = await db.user.verifyPassword(password.current); if (verified) { setVerifyUser(false); password.current = null; } } catch (e) {} }; return verifyUser ? ( Unlock to access your notes Please verify it's you {user ? ( <> (password.current = v)} onSubmit={onSubmit} /> ) : null} {user ? ( <>