diff --git a/apps/mobile/App.js b/apps/mobile/App.js index 2837bfd7c..229110bc0 100644 --- a/apps/mobile/App.js +++ b/apps/mobile/App.js @@ -1,16 +1,11 @@ import * as NetInfo from '@react-native-community/netinfo'; -import * as Sentry from '@sentry/react-native'; import {EV} from 'notes-core/common'; import React, {useEffect, useState} from 'react'; import {Appearance, AppState, Platform, StatusBar} from 'react-native'; import {enabled} from 'react-native-privacy-snapshot'; import {SafeAreaProvider} from 'react-native-safe-area-context'; -import SplashScreen from 'react-native-splash-screen'; -import {RootView} from './RootView'; import {useTracked} from './src/provider'; import {Actions} from './src/provider/Actions'; -import Backup from './src/services/Backup'; -import {DDS} from './src/services/DeviceDetection'; import { eSendEvent, eSubscribeEvent, @@ -19,27 +14,21 @@ import { } from './src/services/EventManager'; import IntentService from './src/services/IntentService'; import {setLoginMessage} from './src/services/Message'; -import SettingsService from './src/services/SettingsService'; +import Navigation from './src/services/Navigation'; +import {editing} from './src/utils'; import {COLOR_SCHEME} from './src/utils/Colors'; import {db} from './src/utils/DB'; -import { - eDispatchAction, - eOnLoadNote, - eResetApp, - eStartSyncer, -} from './src/utils/Events'; +import {eDispatchAction, eOnLoadNote, eStartSyncer} from './src/utils/Events'; import {MMKV} from './src/utils/mmkv'; import {tabBarRef} from './src/utils/Refs'; -import {sleep} from './src/utils/TimeUtils'; -import {getNote} from './src/views/Editor/Functions'; +import {getNote, setIntentNote} from './src/views/Editor/Functions'; -Sentry.init({ - dsn: - 'https://317a5c31caf64d1e9b27abf15eb1a554@o477952.ingest.sentry.io/5519681', -}); -let note = null; +let AppRootView = require('./initializer.intent').IntentView; +let SettingsService = null; +let Sentry = null; const onAppStateChanged = async (state) => { + console.log('app state', state); if (state === 'active') { StatusBar.setBarStyle( COLOR_SCHEME.night ? 'light-content' : 'dark-content', @@ -52,7 +41,18 @@ const onAppStateChanged = async (state) => { if (SettingsService.get().privacyScreen) { enabled(false); } + MMKV.removeItem('appState'); } else { + if (editing.currentlyEditing) { + MMKV.setItem( + 'appState', + JSON.stringify({ + editing: editing.currentlyEditing, + note: getNote(), + }), + ); + } + if (SettingsService.get().privacyScreen) { enabled(true); } @@ -75,35 +75,22 @@ const onNetworkStateChanged = (netInfo) => { const App = () => { const [, dispatch] = useTracked(), - [init, setInit] = useState(false); + [init, setInit] = useState(false), + [intent, setIntent] = useState(false); const syncChanges = async () => { - dispatch({type: Actions.ALL}); - }, - resetApp = async () => { - note = getNote(); - setInit(false); - await SettingsService.init(); - setInit(true); - await sleep(300); - if (note && note.id) { - eSendEvent(eOnLoadNote, note); - if (!DDS.isLargeTablet()) { - tabBarRef.current?.goToPage(1); - } - note = null; + dispatch({type: Actions.ALL}); + }; + const startSyncer = async () => { + try { + let user = await db.user.get(); + if (user) { + EV.subscribe('db:refresh', syncChanges); } - }, - startSyncer = async () => { - try { - let user = await db.user.get(); - if (user) { - EV.subscribe('db:refresh', syncChanges); - } - } catch (e) { - console.log(e); - } - }; + } catch (e) { + console.log(e); + } + }; const onSystemThemeChanged = async () => { await SettingsService.setTheme(); @@ -111,70 +98,72 @@ const App = () => { useEffect(() => { eSubscribeEvent(eStartSyncer, startSyncer); - eSubscribeEvent(eResetApp, resetApp); eSubscribeEvent(eDispatchAction, (type) => { dispatch(type); }); AppState.addEventListener('change', onAppStateChanged); + eSubscribeEvent('nointent', loadMainApp); Appearance.addChangeListener(onSystemThemeChanged); let unsub = NetInfo.addEventListener(onNetworkStateChanged); return () => { EV.unsubscribe('db:refresh', syncChanges); eUnSubscribeEvent(eStartSyncer, startSyncer); - eUnSubscribeEvent(eResetApp, resetApp); eUnSubscribeEvent(eDispatchAction, (type) => { dispatch(type); }); + eUnSubscribeEvent('nointent', loadMainApp); AppState.removeEventListener('change', onAppStateChanged); Appearance.removeChangeListener(onSystemThemeChanged); unsub(); }; }, []); + const loadMainApp = () => { + setIntent(false); + AppRootView = require('./initializer.root').RootView; + setInit(true); + backupData().then((r) => r); + Sentry = require('@sentry/react-native'); + Sentry.init({ + dsn: + 'https://317a5c31caf64d1e9b27abf15eb1a554@o477952.ingest.sentry.io/5519681', + }); + }; + const getUser = async () => { - let user = await db.user.get(); - if (user) { - dispatch({type: Actions.USER, user: user}); - dispatch({type: Actions.SYNCING, syncing: true}); - await db.sync(); - dispatch({type: Actions.SYNCING, syncing: false}); - dispatch({type: Actions.ALL}); - await startSyncer(); - } else { - setLoginMessage(dispatch); + try { + let user = await db.user.get(); + if (user) { + dispatch({type: Actions.USER, user: user}); + dispatch({type: Actions.SYNCING, syncing: true}); + await db.sync(); + dispatch({type: Actions.SYNCING, syncing: false}); + dispatch({type: Actions.ALL}); + await startSyncer(); + } else { + setLoginMessage(dispatch); + } + } catch (e) { + console.log(e); } }; useEffect(() => { - SettingsService.init().then((r) => r); - let error = null; - (async () => { - try { - await db.init(); - } catch (e) { - error = e; - console.log(e, 'ERROR DB'); - } finally { - dispatch({type: Actions.ALL}); - getUser().catch((e) => console.log); - backupData().then((r) => r); - } - setInit(true); - setTimeout(() => { - if (error) { - console.log(error); - ToastEvent.show('Error initializing database.'); - } - IntentService.check(); - SplashScreen.hide(); - }, 100); - })(); + SettingsService = require('./src/services/SettingsService').default; + SettingsService.init().finally(() => { + db.init().finally(runAfterInit); + }); }, []); + const runAfterInit = () => { + setIntent(true); + dispatch({type: Actions.ALL}); + getUser().catch((e) => console.log); + }; + async function backupData() { - await sleep(1000); - let settings = await MMKV.getStringAsync('settings'); - settings = JSON.parse(settings); + let settings = SettingsService.get(); + let Backup = require('./src/services/Backup').default; if (await Backup.checkBackupRequired(settings.reminder)) { try { await Backup.run(); @@ -185,11 +174,10 @@ const App = () => { } return ( - <> - - <>{!init ? <> : } - - + + {intent && } + {init && !intent && } + ); }; diff --git a/apps/mobile/initializer.intent.js b/apps/mobile/initializer.intent.js new file mode 100644 index 000000000..cc8bc5873 --- /dev/null +++ b/apps/mobile/initializer.intent.js @@ -0,0 +1,20 @@ +import React from 'react'; +import { DialogManager } from './src/components/DialogManager'; +import { DummyText } from './src/components/DummyText'; +import { Toast } from './src/components/Toast'; +import { useTracked } from './src/provider'; +import { EditorWrapper } from './src/views/Editor/EditorWrapper'; + +export const IntentView = () => { + const [state] = useTracked(); + const {colors} = state; + + return ( + <> + + + + + + ); +}; diff --git a/apps/mobile/initializer.root.js b/apps/mobile/initializer.root.js new file mode 100644 index 000000000..24e15731a --- /dev/null +++ b/apps/mobile/initializer.root.js @@ -0,0 +1,269 @@ +import React, {useEffect, useState} from 'react'; +import {Dimensions, View} from 'react-native'; +import ScrollableTabView from 'react-native-scrollable-tab-view'; +import SplashScreen from 'react-native-splash-screen'; +import ContextMenu from './src/components/ContextMenu'; +import {DialogManager} from './src/components/DialogManager'; +import {DummyText} from './src/components/DummyText'; +import {Menu} from './src/components/Menu'; +import {Toast} from './src/components/Toast'; +import {NavigationStack} from './src/navigation/Drawer'; +import {NavigatorStack} from './src/navigation/NavigatorStack'; +import {useTracked} from './src/provider'; +import {Actions} from './src/provider/Actions'; +import {DDS} from './src/services/DeviceDetection'; +import { + eSendEvent, + eSubscribeEvent, + eUnSubscribeEvent, +} from './src/services/EventManager'; +import {editing, setWidthHeight} from './src/utils'; +import { + eCloseFullscreenEditor, + eCloseSideMenu, + eOnLoadNote, + eOpenFullscreenEditor, + eOpenSideMenu, +} from './src/utils/Events'; +import {editorRef, tabBarRef} from './src/utils/Refs'; +import {sleep} from './src/utils/TimeUtils'; +import {EditorWrapper} from './src/views/Editor/EditorWrapper'; +import {getIntent, getNote, post} from './src/views/Editor/Functions'; + +let {width, height} = Dimensions.get('window'); +let movedAway = true; +let layoutTimer = null; + +const onChangeTab = async (obj) => { + if (obj.i === 1) { + eSendEvent(eCloseSideMenu); + if (getIntent()) return; + movedAway = false; + if (!editing.currentlyEditing || !getNote()) { + eSendEvent(eOnLoadNote, {type: 'new'}); + editing.currentlyEditing = true; + } + } else { + if (obj.from === 1) { + movedAway = true; + post('blur'); + } + eSendEvent(eOpenSideMenu); + } +}; + +export const RootView = () => { + const [state] = useTracked(); + const {colors} = state; + + return ( + <> + + + + + + + ); +}; + +const AppStack = React.memo( + () => { + const [state, dispatch] = useTracked(); + const {colors} = state; + const [mode, setMode] = useState(null); + const [dimensions, setDimensions] = useState({width, height}); + + const showFullScreenEditor = () => { + dispatch({type: Actions.FULLSCREEN, state: true}); + editorRef.current?.setNativeProps({ + style: { + position: 'absolute', + width: dimensions.width, + zIndex: 999, + paddingHorizontal: dimensions.width * 0.15, + backgroundColor: colors.bg, + }, + }); + }; + + const closeFullScreenEditor = () => { + dispatch({type: Actions.FULLSCREEN, state: false}); + editorRef.current?.setNativeProps({ + style: { + position: 'relative', + width: dimensions.width * 0.55, + zIndex: null, + paddingHorizontal: 0, + }, + }); + }; + + useEffect(() => { + eSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor); + eSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor); + + return () => { + eUnSubscribeEvent(eOpenFullscreenEditor, showFullScreenEditor); + eUnSubscribeEvent(eCloseFullscreenEditor, closeFullScreenEditor); + }; + }, []); + + const _onLayout = async (event) => { + if (layoutTimer) { + clearTimeout(layoutTimer); + layoutTimer = null; + } + let size = event?.nativeEvent?.layout; + if (!size || (size.width === dimensions.width && mode !== null)) { + return; + } + layoutTimer = setTimeout(async () => { + checkDeviceType(size); + }, 500); + }; + + function checkDeviceType(size) { + setDimensions({ + width: size.width, + height: size.height, + }); + setWidthHeight(size); + DDS.setSize(size); + DDS.checkSmallTab(size.width > size.height ? 'LANDSCAPE' : 'PORTRAIT'); + if (DDS.isLargeTablet()) { + setDeviceMode('tablet', size); + } else if (DDS.isSmallTab) { + setDeviceMode('smallTablet', size); + } else { + setDeviceMode('mobile', size); + } + } + + function setDeviceMode(current, size) { + eSendEvent(current !== 'mobile' ? eCloseSideMenu : eOpenSideMenu); + setMode(current); + dispatch({type: Actions.DEVICE_MODE, state: current}); + dispatch({type: Actions.FULLSCREEN, state: false}); + editorRef.current?.setNativeProps({ + style: { + position: 'relative', + width: current === 'tablet' ? size.width * 0.55 : size.width, + zIndex: null, + paddingHorizontal: 0, + }, + }); + if (!movedAway && current !== 'tablet') { + tabBarRef.current?.goToPage(1); + } + sleep(50).then(() => SplashScreen.hide()); + } + + const _responder = (e) => { + const swiperLeftAreaLocation = 60; + const swiperRightAreaLocation = dimensions.width - 100; + let pageX = e.nativeEvent.pageX; + if (pageX <= swiperLeftAreaLocation || pageX >= swiperRightAreaLocation) { + tabBarRef.current?.setScrollEnabled(true); + } else { + tabBarRef.current?.setScrollEnabled(false); + } + return false; + }; + + return ( + + {mode && ( + <>}> + {mode !== 'tablet' && ( + + {mode === 'smallTablet' && ( + + + + )} + + + + + + )} + + + {mode === 'tablet' && ( + + + + + + + + + + )} + + + + )} + + ); + }, + () => true, +);