diff --git a/apps/mobile/src/components/container/floatingbutton.js b/apps/mobile/src/components/container/floatingbutton.js index 5b22de256..023e6d277 100644 --- a/apps/mobile/src/components/container/floatingbutton.js +++ b/apps/mobile/src/components/container/floatingbutton.js @@ -4,6 +4,7 @@ import Animated, { Easing } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import { notesnook } from '../../../e2e/test.ids'; +import { editorState } from '../../screens/editor/tiptap/utils'; import { useSelectionStore, useSettingStore } from '../../stores/stores'; import { editing, getElevation, showTooltip, TOOLTIP_POSITIONS } from '../../utils'; import { normalize, SIZE } from '../../utils/size'; @@ -28,13 +29,13 @@ export const FloatingButton = ({ title, onPress, color = 'accent', shouldShow = } const onKeyboardHide = async () => { - editing.keyboardState = false; + editorState().keyboardState = false; if (deviceMode !== 'mobile') return; animate(0); }; const onKeyboardShow = async () => { - editing.keyboardState = true; + editorState().keyboardState = true; if (deviceMode !== 'mobile') return; animate(150); }; diff --git a/apps/mobile/src/components/launcher/index.js b/apps/mobile/src/components/launcher/index.js index 088f2333e..5c800d10d 100644 --- a/apps/mobile/src/components/launcher/index.js +++ b/apps/mobile/src/components/launcher/index.js @@ -37,6 +37,7 @@ import Heading from '../ui/typography/heading'; import Paragraph from '../ui/typography/paragraph'; import { Walkthrough } from '../walkthroughs'; import NewFeature from '../sheets/new-feature/index'; +import { editorState } from '../../screens/editor/tiptap/utils'; const Launcher = React.memo( () => { @@ -166,9 +167,9 @@ const Launcher = React.memo( !appState.movedAway && Date.now() < appState.timestamp + 3600000 ) { - editing.isRestoringState = true; - editing.currentlyEditing = true; - editing.movedAway = false; + editorState().currentlyEditing = true; + editorState().isRestoringState = true; + editorState().movedAway = false; if (!DDS.isTab) { tabBarRef.current?.goToPage(1); } diff --git a/apps/mobile/src/components/premium/premiumtoast.js b/apps/mobile/src/components/premium/premiumtoast.js index 7a619e6f6..36f8e8318 100644 --- a/apps/mobile/src/components/premium/premiumtoast.js +++ b/apps/mobile/src/components/premium/premiumtoast.js @@ -13,6 +13,7 @@ import tiny from '../../screens/editor/tiny/tiny'; import { Button } from '../ui/button'; import Heading from '../ui/typography/heading'; import Paragraph from '../ui/typography/paragraph'; +import { editorState } from '../../screens/editor/tiptap/utils'; export const translatePrem = new Animated.Value(-dWidth); export const opacityPrem = new Animated.Value(0); @@ -94,7 +95,7 @@ export const PremiumToast = ({ close, context = 'global', offset = 0 }) => { const onPress = async () => { open(null); eSendEvent(eCloseActionSheet); - if (editing.isFocused) { + if (editorState().isFocused) { tiny.call(EditorWebView, tiny.blur); } await sleep(300); diff --git a/apps/mobile/src/components/sheetprovider/index.js b/apps/mobile/src/components/sheetprovider/index.js index 53494e84e..889eecb28 100644 --- a/apps/mobile/src/components/sheetprovider/index.js +++ b/apps/mobile/src/components/sheetprovider/index.js @@ -14,6 +14,7 @@ import { Button } from '../ui/button'; import SheetWrapper from '../ui/sheet'; import Heading from '../ui/typography/heading'; import Paragraph from '../ui/typography/paragraph'; +import { editorState } from '../../screens/editor/tiptap/utils'; const SheetProvider = ({ context = 'global' }) => { const colors = useThemeStore(state => state.colors); @@ -45,7 +46,7 @@ const SheetProvider = ({ context = 'global' }) => { setVisible(true); if (data.editor) { editor.current.refocus = false; - if (editing.keyboardState) { + if (editorState().keyboardState) { tiny.call(EditorWebView, tiny.cacheRange + tiny.blur); editor.current.refocus = true; } @@ -59,8 +60,8 @@ const SheetProvider = ({ context = 'global' }) => { actionSheetRef.current?.setModalVisible(true); return; } else { - if (editor.current.refocus) { - editing.isFocused = true; + if (editor.current?.refocus) { + editorState().isFocused = true; await reFocusEditor(); tiny.call(EditorWebView, tiny.restoreRange + tiny.clearRange); editor.current.refocus = false; diff --git a/apps/mobile/src/components/tabs/index.js b/apps/mobile/src/components/tabs/index.js index 9f56b4a4a..4ebf8fe22 100644 --- a/apps/mobile/src/components/tabs/index.js +++ b/apps/mobile/src/components/tabs/index.js @@ -2,6 +2,7 @@ import React, { Component, createRef } from 'react'; import { Platform } from 'react-native'; import { Keyboard } from 'react-native'; import { FlatList, TextInput, View } from 'react-native'; +import { editorState } from '../../screens/editor/tiptap/utils'; import { DDS } from '../../services/device-detection'; import { editing } from '../../utils'; @@ -34,7 +35,7 @@ export default class Tabs extends Component { this.hideKeyboardIfVisible(); let cOffset = this.scrollOffset.toFixed(0); let pOffset = this.props.offsets.b.toFixed(0); - // let heightCheck = !editing.tooltip + // let heightCheck = !editorState().tooltip // ? this.props.dimensions.height - 70 // : this.props.dimensions.height - 140; @@ -86,13 +87,13 @@ export default class Tabs extends Component { hideKeyboardIfVisible(close) { if (!close && this.nextPage === 1) return; if (Platform.OS === 'ios') return; - if (editing.movedAway) return; + if (editorState().movedAway) return; if ( - (editing.keyboardState || editing.isFocused) && + (editorState().keyboardState || editorState().isFocused) && this.scrollOffset < this.props.offsets.b - 50 ) { - editing.keyboardState = false; + editorState().keyboardState = false; Keyboard.dismiss(); } } @@ -270,7 +271,7 @@ export default class Tabs extends Component { snapToAlignment="start" snapToOffsets={[this.props.offsets.a, this.props.offsets.b, this.props.offsets.c]} contentOffset={{ - x: editing.movedAway ? this.props.offsets.a : this.props.offsets.b + x: editorState().movedAway ? this.props.offsets.a : this.props.offsets.b }} data={['drawer', 'navigation', 'editor']} renderItem={this.renderItem} diff --git a/apps/mobile/src/navigation/tabsholder.js b/apps/mobile/src/navigation/tabsholder.js index 9f9ac572c..83ec25595 100644 --- a/apps/mobile/src/navigation/tabsholder.js +++ b/apps/mobile/src/navigation/tabsholder.js @@ -8,6 +8,7 @@ import { notesnook } from '../../e2e/test.ids'; import { SideMenu } from '../components/side-menu'; import Tabs from '../components/tabs'; import { EditorWrapper } from '../screens/editor/EditorWrapper'; +import { editorState } from '../screens/editor/tiptap/utils'; import { DDS } from '../services/device-detection'; import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent } from '../services/event-manager'; import { useEditorStore, useSettingStore } from '../stores/stores'; @@ -151,10 +152,10 @@ export const TabsHolder = React.memo( if (current === 'tablet') { tabBarRef.current?.goToIndex(0); } else { - if (!editing.movedAway) { + if (!editorState().movedAway) { tabBarRef.current?.goToIndex(2); } else { - console.log('index one', editing.movedAway); + console.log('index one', editorState().movedAway); tabBarRef.current?.goToIndex(1); } } @@ -316,17 +317,17 @@ let layoutTimer = null; const onChangeTab = async obj => { if (obj.i === 1) { - editing.movedAway = false; - editing.isFocused = true; + editorState().movedAway = false; + editorState().isFocused = true; activateKeepAwake(); - if (!editing.currentlyEditing) { + if (!editorState().currentlyEditing) { eSendEvent(eOnLoadNote, { type: 'new' }); } } else { if (obj.from === 1) { deactivateKeepAwake(); - editing.movedAway = true; - editing.isFocused = false; + editorState().movedAway = true; + editorState().isFocused = false; eSendEvent(eClearEditor, 'removeHandler'); setTimeout(() => useEditorStore.getState().setSearchReplace(false), 1); let id = useEditorStore.getState().currentEditingNote; diff --git a/apps/mobile/src/screens/editor/EditorHeader.js b/apps/mobile/src/screens/editor/EditorHeader.js index 33de2008c..6c9685f39 100644 --- a/apps/mobile/src/screens/editor/EditorHeader.js +++ b/apps/mobile/src/screens/editor/EditorHeader.js @@ -36,6 +36,7 @@ import tiny, { safeKeyboardDismiss } from './tiny/tiny'; import { endSearch } from './tiny/toolbar/commands'; import { toolbarRef } from './tiny/toolbar/constants'; import picker from './tiny/toolbar/picker'; +import { editorState } from './tiptap/utils'; const EditorHeader = ({ editor }) => { const colors = useThemeStore(state => state.colors); @@ -59,7 +60,7 @@ const EditorHeader = ({ editor }) => { return; } if (deviceMode === 'mobile') { - editing.movedAway = true; + editorState().movedAway = true; } eSendEvent('showTooltip'); toolbarRef.current?.scrollTo({ @@ -83,7 +84,7 @@ const EditorHeader = ({ editor }) => { setTimeout(() => { useEditorStore.getState().setCurrentlyEditingNote(null); }, 1); - editing.currentlyEditing = false; + editorState().currentlyEditing = false; keyboardListener.current?.remove(); editor?.reset(); } @@ -122,9 +123,9 @@ const EditorHeader = ({ editor }) => { }); return; } - if (editing.isFocused) { + if (editorState().isFocused) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } eSendEvent(eOpenPublishNoteDialog, note); }; @@ -142,9 +143,9 @@ const EditorHeader = ({ editor }) => { return; } - if (editing.isFocused || editing.keyboardState) { + if (editorState().isFocused || editorState().keyboardState) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } Properties.present(note, ['Dark Mode']); @@ -208,13 +209,13 @@ const EditorHeader = ({ editor }) => { handleBack.current = BackHandler.addEventListener('hardwareBackPress', _onHardwareBackPress); return; } - if (editing.currentlyEditing) { + if (editorState().currentlyEditing) { await _onBackPress(); } }; const _onHardwareBackPress = async () => { - if (editing.currentlyEditing) { + if (editorState().currentlyEditing) { await _onBackPress(); return true; } @@ -280,9 +281,9 @@ const EditorHeader = ({ editor }) => { }} top={50} onPress={async () => { - if (editing.isFocused) { + if (editorState().isFocused) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } umami.pageView('/pro-screen', '/editor'); eSendEvent(eOpenPremiumDialog); @@ -343,7 +344,7 @@ const EditorHeader = ({ editor }) => { top={50} onPress={() => { eSendEvent(eOpenFullscreenEditor); - editing.isFullscreen = true; + editorState().isFullscreen = true; }} /> ) : null} diff --git a/apps/mobile/src/screens/editor/EditorOverlay.js b/apps/mobile/src/screens/editor/EditorOverlay.js index 0fad6181e..389544478 100644 --- a/apps/mobile/src/screens/editor/EditorOverlay.js +++ b/apps/mobile/src/screens/editor/EditorOverlay.js @@ -10,6 +10,7 @@ import { editing } from '../../utils'; import { eOnLoadNote } from '../../utils/events'; import { SIZE } from '../../utils/size'; import { sleep, timeConverter } from '../../utils/time'; +import { editorState } from './tiptap/utils'; let timer = null; let timerError = null; @@ -21,7 +22,7 @@ const EditorOverlay = () => { const opacity = useValue(1); const load = async _loading => { - editing.overlay = true; + editorState().overlay = true; clearTimeout(timer); clearTimeout(timerError); clearTimeout(timerClosing); @@ -42,7 +43,7 @@ const EditorOverlay = () => { clearTimeout(timerError); clearTimeout(timerClosing); setError(false); - editing.overlay = false; + editorState().overlay = false; timing(opacity, { toValue: 0, duration: 150, diff --git a/apps/mobile/src/screens/editor/EditorWrapper.js b/apps/mobile/src/screens/editor/EditorWrapper.js index 6f4621f5d..3a3f0ae9c 100644 --- a/apps/mobile/src/screens/editor/EditorWrapper.js +++ b/apps/mobile/src/screens/editor/EditorWrapper.js @@ -16,7 +16,7 @@ import { useThemeStore } from '../../stores/theme'; import { editorRef } from '../../utils/global-refs'; import useIsFloatingKeyboard from '../../utils/hooks/use-is-floating-keyboard'; import EditorOverlay from './EditorOverlay'; -import { textInput } from './tiptap/utils'; +import { editorController, editorState, textInput } from './tiptap/utils'; export const EditorWrapper = ({ width }) => { const colors = useThemeStore(state => state.colors); @@ -27,9 +27,9 @@ export const EditorWrapper = ({ width }) => { const onAppStateChanged = async state => { if (state === 'active') { - // if (!editing.movedAway) { - // await checkStatus(false); - // } + if (!editorState().movedAway) { + editorController.current.onReady(); + } } }; diff --git a/apps/mobile/src/screens/editor/HistoryComponent.js b/apps/mobile/src/screens/editor/HistoryComponent.js index 3fc217315..f7b179252 100644 --- a/apps/mobile/src/screens/editor/HistoryComponent.js +++ b/apps/mobile/src/screens/editor/HistoryComponent.js @@ -1,13 +1,13 @@ import React, { useEffect, useState } from 'react'; import { Platform, View } from 'react-native'; import { IconButton } from '../../components/ui/icon-button'; -import { useThemeStore } from '../../stores/theme'; import { eSubscribeEvent, eUnSubscribeEvent } from '../../services/event-manager'; -import { editing } from '../../utils'; -import { SIZE } from '../../utils/size'; +import { useThemeStore } from '../../stores/theme'; import useKeyboard from '../../utils/hooks/use-keyboard'; +import { SIZE } from '../../utils/size'; import { EditorWebView } from './Functions'; import tiny, { safeKeyboardDismiss } from './tiny/tiny'; +import { editorState } from './tiptap/utils'; const HistoryComponent = () => { const colors = useThemeStore(state => state.colors); @@ -16,7 +16,7 @@ const HistoryComponent = () => { redo: false }); const keyboard = useKeyboard(); - editing.keyboardState = keyboard.keyboardShown; + editorState().keyboardState = keyboard.keyboardShown; const onHistoryChange = data => { setHistoryState(data); @@ -47,7 +47,7 @@ const HistoryComponent = () => { height: 35 }} onPress={() => { - editing.keyboardState = true; + editorState().keyboardState = true; safeKeyboardDismiss(); }} /> diff --git a/apps/mobile/src/screens/editor/index.js b/apps/mobile/src/screens/editor/index.js index 408ada906..cf1c6bf4d 100755 --- a/apps/mobile/src/screens/editor/index.js +++ b/apps/mobile/src/screens/editor/index.js @@ -2,10 +2,11 @@ import React from 'react'; import { Platform, View } from 'react-native'; import WebView from 'react-native-webview'; import { notesnook } from '../../../e2e/test.ids'; -import { useEditorStore, useUserStore } from '../../stores/stores'; +import { useUserStore } from '../../stores/stores'; import EditorHeader from './EditorHeader'; import { sourceUri, _onShouldStartLoadWithRequest } from './Functions'; import { useEditor } from './tiptap/use-editor'; +import { editorController } from './tiptap/utils'; const source = { uri: sourceUri + 'index.html' }; @@ -20,8 +21,8 @@ const style = { const Editor = React.memo( () => { const premiumUser = useUserStore(state => state.premium); - const sessionId = useEditorStore(state => state.sessionId); const editor = useEditor(); + editorController.current = editor; return editor.loading ? null : ( <> @@ -43,7 +44,7 @@ const Editor = React.memo( // onError={() => { // onResetRequested(); // }} - injectedJavaScript={`globalThis.sessionId="${sessionId}";`} + injectedJavaScript={`globalThis.sessionId="${editor.sessionId}";`} javaScriptEnabled={true} focusable={true} keyboardDisplayRequiresUserAction={false} diff --git a/apps/mobile/src/screens/editor/tiny/tiny.js b/apps/mobile/src/screens/editor/tiny/tiny.js index 092786416..145b43060 100644 --- a/apps/mobile/src/screens/editor/tiny/tiny.js +++ b/apps/mobile/src/screens/editor/tiny/tiny.js @@ -1,7 +1,7 @@ import { Platform } from 'react-native'; import { editing } from '../../../utils'; import { EditorWebView, getWebviewInit, post } from '../Functions'; -import { textInput } from '../tiptap/utils'; +import { editorState, textInput } from '../tiptap/utils'; /** * @@ -206,8 +206,8 @@ function call(webview, func, noqueue) { } export function safeKeyboardDismiss() { - console.log('keyboard state', editing.keyboardState); - if (!editing.keyboardState) return; + console.log('keyboard state', editorState().keyboardState); + if (!editorState().keyboardState) return; if (Platform.OS === 'android') { textInput.current?.focus(); textInput.current?.blur(); @@ -224,10 +224,10 @@ const redo = `tinymce.activeEditor.undoManager.redo();`; const clearHistory = `tinymce.activeEditor.undoManager.clear();`; const onKeyboardShow = () => { - if (!editing.movedAway) { - editing.isFocused = true; + if (!editorState().movedAway) { + editorState().isFocused = true; if (Platform.OS === 'ios') { - if (editing.focusType === 'title') return; + if (editorState().focusType === 'title') return; call(EditorWebView, keyboardStateChanged); } } diff --git a/apps/mobile/src/screens/editor/tiny/toolbar/constants.js b/apps/mobile/src/screens/editor/tiny/toolbar/constants.js index 0fac4c959..21dd7aad7 100644 --- a/apps/mobile/src/screens/editor/tiny/toolbar/constants.js +++ b/apps/mobile/src/screens/editor/tiny/toolbar/constants.js @@ -4,6 +4,7 @@ import { eSendEvent } from '../../../../services/event-manager'; import { editing } from '../../../../utils'; import { sleep } from '../../../../utils/time'; import { EditorWebView, textInput } from '../../Functions'; +import { editorState } from '../../tiptap/utils'; import tiny from '../tiny'; export const properties = { @@ -34,13 +35,13 @@ export async function focusEditor(format, kill = true) { } export async function reFocusEditor() { - if (editing.isFocused === true) { + if (editorState().isFocused === true) { if (Platform.OS === 'android') { await sleep(300); textInput.current?.focus(); } await sleep(300); - if (editing.focusType == 'editor') { + if (editorState().focusType == 'editor') { focusEditor(null, false); } else { Platform.OS === 'android' && EditorWebView.current?.requestFocus(); diff --git a/apps/mobile/src/screens/editor/tiny/toolbar/item.js b/apps/mobile/src/screens/editor/tiny/toolbar/item.js index 5c6ba9807..3f322472a 100644 --- a/apps/mobile/src/screens/editor/tiny/toolbar/item.js +++ b/apps/mobile/src/screens/editor/tiny/toolbar/item.js @@ -32,6 +32,7 @@ import { import ToolbarItemPin from './itempin'; import ToolbarListFormat from './listformat'; import { Table } from './table'; +import { editorState } from '../../tiptap/utils'; const ToolbarItem = ({ format, @@ -90,7 +91,7 @@ const ToolbarItem = ({ properties.selection = data; let formats = Object.keys(data); if (!data['link'] && type === 'tooltip') { - if (editing.tooltip) { + if (editorState().tooltip) { eSendEvent('showTooltip'); } } @@ -203,9 +204,9 @@ const ToolbarItem = ({ const onPress = async event => { if (premium && !isPro) { let user = await db.user.getUser(); - if (editing.isFocused) { + if (editorState().isFocused) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } if (user && !isPro && !user.isEmailConfirmed) { PremiumService.showVerifyEmailDialog(); @@ -223,15 +224,15 @@ const ToolbarItem = ({ } if (type === 'settings') { - if (editing.isFocused) { + if (editorState().isFocused) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } eSendEvent('openEditorSettings'); return; } - if (editing.tooltip === format && !formatValue) { + if (editorState().tooltip === format && !formatValue) { focusEditor(format); eSendEvent('showTooltip'); @@ -316,7 +317,7 @@ const ToolbarItem = ({ } focusEditor(format); - editing.tooltip = null; + editorState().tooltip = null; }; const isdefaultColorFormat = /^(dhilitecolor|dforecolor)$/.test(format); diff --git a/apps/mobile/src/screens/editor/tiny/toolbar/linkinput.js b/apps/mobile/src/screens/editor/tiny/toolbar/linkinput.js index 4e71b3ba6..4abbc24c7 100644 --- a/apps/mobile/src/screens/editor/tiny/toolbar/linkinput.js +++ b/apps/mobile/src/screens/editor/tiny/toolbar/linkinput.js @@ -14,6 +14,7 @@ import tiny from '../tiny'; import { execCommands } from './commands'; import { focusEditor, formatSelection, INPUT_MODE, properties } from './constants'; import LinkPreview from './linkpreview'; +import { editorState } from '../../tiptap/utils'; let inputValue = null; @@ -30,11 +31,11 @@ const ToolbarLinkInput = ({ format, value, setVisible }) => { } inputValue = value; properties.inputMode = value ? INPUT_MODE.NO_EDIT : INPUT_MODE.EDITING; - editing.tooltip = format; + editorState().tooltip = format; properties.userBlur = false; return () => { properties.inputMode = null; - editing.tooltip = null; + editorState().tooltip = null; inputValue = null; }; }, [format]); @@ -87,7 +88,7 @@ const ToolbarLinkInput = ({ format, value, setVisible }) => { tiny.call(EditorWebView, tiny.clearRange); } - editing.tooltip = null; + editorState().tooltip = null; if (inputValue) { properties.pauseSelectionChange = true; diff --git a/apps/mobile/src/screens/editor/tiny/toolbar/picker.js b/apps/mobile/src/screens/editor/tiny/toolbar/picker.js index b26f625ee..242e31739 100644 --- a/apps/mobile/src/screens/editor/tiny/toolbar/picker.js +++ b/apps/mobile/src/screens/editor/tiny/toolbar/picker.js @@ -12,6 +12,7 @@ import { db } from '../../../../utils/database'; import { eCloseProgressDialog } from '../../../../utils/events'; import { sleep } from '../../../../utils/time'; import { EditorWebView, getNote } from '../../Functions'; +import { editorState } from '../../tiptap/utils'; import tiny, { safeKeyboardDismiss } from '../tiny'; const FILE_SIZE_LIMIT = 500 * 1024 * 1024; @@ -199,9 +200,9 @@ const gallery = async options => { const pick = async options => { if (!PremiumService.get()) { let user = await db.user.getUser(); - if (editing.isFocused) { + if (editorState().isFocused) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } if (user && !PremiumService.get() && !user.isEmailConfirmed) { PremiumService.showVerifyEmailDialog(); @@ -220,9 +221,9 @@ const pick = async options => { return; } - if (editing.isFocused) { + if (editorState().isFocused) { safeKeyboardDismiss(); - editing.isFocused = true; + editorState().isFocused = true; } presentSheet({ diff --git a/apps/mobile/src/screens/editor/tiny/toolbar/tooltip.js b/apps/mobile/src/screens/editor/tiny/toolbar/tooltip.js index 8dd33824d..fc65c4d88 100644 --- a/apps/mobile/src/screens/editor/tiny/toolbar/tooltip.js +++ b/apps/mobile/src/screens/editor/tiny/toolbar/tooltip.js @@ -1,11 +1,11 @@ import React, { useEffect, useState } from 'react'; import { ScrollView, View } from 'react-native'; -import { useThemeStore } from '../../../../stores/theme'; import { eSubscribeEvent, eUnSubscribeEvent } from '../../../../services/event-manager'; -import { editing } from '../../../../utils'; +import { useThemeStore } from '../../../../stores/theme'; import layoutmanager from '../../../../utils/layout-manager'; import { normalize } from '../../../../utils/size'; import { EditorWebView } from '../../Functions'; +import { editorState } from '../../tiptap/utils'; import tiny from '../tiny'; import ColorGroup from './colorgroup'; import { properties } from './constants'; @@ -28,7 +28,7 @@ const Tooltip = () => { const show = async data => { properties.userBlur = true; if (!data) { - editing.tooltip = null; + editorState().tooltip = null; if (group) { layoutmanager.withAnimation(150); setGroup(null); @@ -36,14 +36,14 @@ const Tooltip = () => { return; } if (!data) return; - editing.tooltip = data.title; + editorState().tooltip = data.title; if (!data.type) { data.type = data.title; } layoutmanager.withSpringAnimation(200); setGroup(data); setTimeout(() => { - if (editing.tooltip !== 'link') { + if (editorState().tooltip !== 'link') { properties.pauseSelectionChange = false; } tiny.call( diff --git a/apps/mobile/src/screens/editor/tiptap/commands.ts b/apps/mobile/src/screens/editor/tiptap/commands.ts index 3a9fea98c..0fe64e800 100644 --- a/apps/mobile/src/screens/editor/tiptap/commands.ts +++ b/apps/mobile/src/screens/editor/tiptap/commands.ts @@ -1,10 +1,10 @@ -import { RefObject } from 'react'; +import { createRef, MutableRefObject, RefObject } from 'react'; import { Platform } from 'react-native'; import WebView from 'react-native-webview'; import { sleep } from '../../../utils/time'; import { textInput } from './utils'; -function call(webview: RefObject | null, func?: string) { +function call(webview: RefObject, func?: string) { if (!webview || !func) return; webview.current?.injectJavaScript(func); } @@ -14,8 +14,8 @@ const fn = (fn: string) => `(() => { })();`; class Commands { - ref: RefObject | null = null; - constructor(ref: RefObject) { + ref = createRef(); + constructor(ref: MutableRefObject) { this.ref = ref; } @@ -50,9 +50,9 @@ statusBar.current.set({date:"",saved:""}); ); }; - setSessionId = (id: string) => call(this.ref, fn(`globalThis.sessionId = "${id}"`)); + setSessionId = (id: string | null) => call(this.ref, fn(`globalThis.sessionId = "${id}"`)); - setStatus = (date: string, saved: string) => + setStatus = (date: string | undefined, saved: string) => call(this.ref, fn(`statusBar.current.set({date:"${date}",saved:"${saved}"})`)); } diff --git a/apps/mobile/src/screens/editor/tiptap/types.ts b/apps/mobile/src/screens/editor/tiptap/types.ts new file mode 100644 index 000000000..ec43afa07 --- /dev/null +++ b/apps/mobile/src/screens/editor/tiptap/types.ts @@ -0,0 +1,16 @@ +import { useEditor } from './use-editor'; + +export type useEditorType = ReturnType; + +export type EditorState = { + currentlyEditing: boolean; + isFullscreen: boolean; + onNoteCreated: (id: string) => void; + isFocused: boolean; + focusType: 'title' | 'editor' | null; + movedAway: boolean; + tooltip: boolean; + isRestoringState: boolean; + keyboardState: boolean; + ready: boolean; +}; diff --git a/apps/mobile/src/screens/editor/tiptap/useEditor.js b/apps/mobile/src/screens/editor/tiptap/useEditor.js deleted file mode 100644 index 92967cd9f..000000000 --- a/apps/mobile/src/screens/editor/tiptap/useEditor.js +++ /dev/null @@ -1,216 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; -import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager'; -import { useEditorStore } from '../../../stores/stores'; -import { editing } from '../../../utils'; -import { db } from '../../../utils/database'; -import { eOnLoadNote } from '../../../utils/events'; -import { timeConverter } from '../../../utils/time'; -import Commands from './commands'; -import { EditorEvents, isEditorLoaded, makeSessionId, post } from './utils'; - -export const useEditor = () => { - const [loading, setLoading] = useState(false); - const sessionId = useEditorStore(state => state.sessionId); - const editorRef = useRef(); - const currentNote = useRef(); - const currentContent = useRef(); - const postMessage = async (type, data) => await post(editorRef, type, data); - const timers = useRef({}); - const commands = new Commands(editorRef); - const sessionHistoryId = useRef(); - - useEffect(() => { - console.log('sessionId:', sessionId); - commands.setSessionId(sessionId); - if (sessionId) { - (async () => { - if (!(await isEditorLoaded(editorRef))) { - console.log('should reload'); - setLoading(true); - } - })(); - } - }, [sessionId, loading]); - - useEffect(() => { - if (loading) { - setLoading(false); - } - }, [loading]); - - const withTimer = (id, fn, duration) => { - clearTimeout(id); - timers.current[id] = setTimeout(fn, duration); - }; - - const reset = () => { - currentNote.current = null; - currentContent.current = null; - useEditorStore.getState().setCurrentlyEditingNote(null); - commands.clearContent(); - }; - - async function saveNote(title, id, data, type, sessionId) { - console.log('saving note', id); - try { - if (id && !db.notes.note(id)) { - useEditorStore.getState().setCurrentlyEditingNote(null); - return; - } - let note = id && db.notes.note(id)?.data; - let locked = note?.locked; - if (note?.conflicted) return; - - if (!sessionHistoryId.current) { - if (note) { - sessionHistoryId.current = note.dateEdited; - } else { - sessionHistoryId.current = Date.now(); - } - } - - let noteData = { - id, - sessionId: sessionHistoryId.current - }; - - if (title) { - noteData.title = title; - } - - if (data) { - noteData.content = { - data: data, - type: type - }; - } - - if (!locked) { - id = await db.notes.add(noteData); - if (!note) currentNote.current = db.notes.note(id).data; - - if (useEditorStore.getState().currentEditingNote !== id) { - setTimeout(() => { - useEditorStore.getState().setCurrentlyEditingNote(id); - }); - } - } else { - noteData.contentId = note.contentId; - await db.vault.save(noteData); - } - - commands.setStatus(timeConverter(db.notes.note(id)?.data?.dateEdited), 'Saved'); - - return id; - - //let n = db.notes.note(id)?.data?.dateEdited; - // tiny.call(EditorWebView, tiny.updateDateEdited(n ? timeConverter(n) : '')); - // tiny.call(EditorWebView, tiny.updateSavingState(!n ? '' : 'Saved')); - } catch (e) { - console.log('error saving: ', e); - } - } - - const loadContent = async note => { - currentNote.current = note; - if (note.locked) { - currentContent.current = { - data: note.content.data, - type: note.content.type - }; - } else { - let data = await db.content.raw(note.contentId); - if (data) { - data = await db.content.insertPlaceholders(data, 'placeholder.svg'); - currentContent.current = { - data: data.data, - type: data.type - }; - } - } - }; - - const loadNote = async item => { - console.log('loading note', item.type); - editing.currentlyEditing = true; - const editorState = useEditorStore.getState(); - - if (item && item.type === 'new') { - currentNote.current && reset(); - editorState.setSessionId(makeSessionId()); - commands.focus(); - } else { - if (!item.forced && currentNote.current?.id === item.id) return; - currentNote.current && reset(); - await loadContent(item); - editorState.setSessionId(makeSessionId(item)); - editorState.setCurrentlyEditingNote(item.id); - currentNote.current = item; - commands.setStatus(timeConverter(item.dateEdited), 'Saved'); - await postMessage(EditorEvents.title, item.title); - await postMessage(EditorEvents.html, currentContent.current?.data); - } - }; - - useEffect(() => { - eSubscribeEvent(eOnLoadNote, loadNote); - return () => { - eUnSubscribeEvent(eOnLoadNote, loadNote); - }; - }, []); - - const onMessage = useCallback( - event => { - let message = event.nativeEvent.data; - message = JSON.parse(message); - - console.log(message.type); - - if (message.sessionId !== sessionId && message.type !== EditorEvents.status) { - console.log( - 'useEditor: message recieved from invalid session', - message.type, - sessionId, - message.sessionId - ); - return; - } - switch (message.type) { - case EditorEvents.logger: - console.log(message.type, message.value); - break; - case 'editor-event:content': - saveContent(null, message.value); - break; - case 'editor-event:selection': - break; - case 'editor-event:title': - saveContent(message.value); - break; - } - if (message.type.startsWith('native:')) { - eSendEvent(message.type, message); - } - }, - [sessionId] - ); - - const saveContent = (title, content) => { - currentContent.current = { - data: content, - type: 'tiny' - }; - let params = [title, currentNote.current?.id, content, 'tiny', sessionId]; - withTimer(currentNote.current?.id, () => saveNote(...params)); - }; - - const onLoad = () => { - console.log('on editor load'); - if (currentNote.current) { - console.log('force reload note'); - loadNote({ ...currentNote.current, forced: true }); - } - }; - - return { onMessage, ref: editorRef, onLoad, commands, reset, loading, setLoading }; -}; diff --git a/apps/mobile/src/screens/editor/tiptap/useEditor.ts b/apps/mobile/src/screens/editor/tiptap/useEditor.ts new file mode 100644 index 000000000..125fbeabc --- /dev/null +++ b/apps/mobile/src/screens/editor/tiptap/useEditor.ts @@ -0,0 +1,367 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import WebView from 'react-native-webview'; +import { DDS } from '../../../services/device-detection'; +import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager'; +import { useEditorStore } from '../../../stores/stores'; +import { useThemeStore } from '../../../stores/theme'; +import { db } from '../../../utils/database'; +import { MMKV } from '../../../utils/database/mmkv'; +import { eOnLoadNote } from '../../../utils/events'; +import { tabBarRef } from '../../../utils/global-refs'; +import { timeConverter } from '../../../utils/time'; +import Commands from './commands'; +import { EditorState } from './types'; +import { defaultState, EditorEvents, isEditorLoaded, makeSessionId, post } from './utils'; + +type Note = { + [name: string]: any; + id: string | null; + type: string; + contentId: string; + title: string; + locked: boolean; + conflicted: boolean; + dateEdited: number; +}; + +type Content = { + data?: string; + type: string; +}; + +type SavePayload = { + title?: string; + id?: string | null; + data?: Content['data']; + type?: Content['type']; + sessionId?: string | null; +}; + +export const useEditor = () => { + const [loading, setLoading] = useState(false); + const [sessionId, setSessionId] = useState(makeSessionId()); + const editorRef = useRef(); + const currentNote = useRef(); + const currentContent = useRef(); + const timers = useRef<{ [name: string]: NodeJS.Timeout }>({}); + const commands = useMemo(() => new Commands(editorRef), []); + const sessionHistoryId = useRef(); + const state = useRef>(defaultState); + console.log('state: ', defaultState); + const postMessage = useCallback( + async (type: string, data: any) => await post(editorRef, type, data), + [] + ); + + useEffect(() => { + let unsub = useThemeStore.subscribe(state => { + postMessage(EditorEvents.theme, state.colors); + }); + + return () => { + unsub(); + }; + }, []); + + useEffect(() => { + console.log('sessionId:', sessionId); + commands.setSessionId(sessionId); + if (sessionId) { + (async () => { + if (!state.current?.ready) return; + onReady(); + })(); + } + }, [sessionId, loading]); + + const overlay = (show: boolean, data = { type: 'new' }) => { + eSendEvent('loadingNote', show ? currentNote.current || data : false); + }; + + const onReady = useCallback(async () => { + if (!(await isEditorLoaded(editorRef))) { + console.log('reload editor'); + overlay(true); + setLoading(true); + } + }, []); + + useEffect(() => { + if (loading) { + setLoading(false); + } + }, [loading]); + + const withTimer = useCallback((id: string, fn: () => void, duration: number) => { + clearTimeout(timers.current[id]); + timers.current[id] = setTimeout(fn, duration); + }, []); + + const reset = useCallback(() => { + currentNote.current = null; + currentContent.current = null; + useEditorStore.getState().setCurrentlyEditingNote(null); + commands.clearContent(); + }, []); + + const saveNote = useCallback( + async ({ title, id, data, type, sessionId: currentSessionId }: SavePayload) => { + console.log('saving note', id); + try { + if (id && !db.notes?.note(id)) { + useEditorStore.getState().setCurrentlyEditingNote(null); + reset(); + return; + } + let note = id ? (db.notes?.note(id)?.data as Note) : null; + let locked = note?.locked; + if (note?.conflicted) return; + + if (!sessionHistoryId.current) { + if (note) { + sessionHistoryId.current = note.dateEdited; + } else { + sessionHistoryId.current = Date.now(); + } + } + + let noteData: Partial = { + id, + sessionId: sessionHistoryId.current + }; + + if (title) { + noteData.title = title; + } + + if (data) { + noteData.content = { + data: data, + type: type + }; + } + + if (!locked) { + id = await db.notes?.add(noteData); + if (!note && id) { + currentNote.current = db.notes?.note(id).data as Note; + state.current?.onNoteCreated && state.current.onNoteCreated(id); + } + + if (useEditorStore.getState().currentEditingNote !== id) { + setTimeout(() => { + id && useEditorStore.getState().setCurrentlyEditingNote(id); + }); + } + } else { + //@ts-ignore + noteData.contentId = note.contentId; + //@ts-ignore + await db.vault?.save(noteData); + } + if (id && sessionId === currentSessionId) { + note = db.notes?.note(id)?.data as Note; + commands.setStatus(timeConverter(note.dateEdited), 'Saved'); + } + + return id; + } catch (e) { + console.log('error saving: ', e); + } + }, + [commands, reset] + ); + + const loadContent = useCallback(async (note: Note) => { + currentNote.current = note; + if (note.locked) { + currentContent.current = { + data: note.content.data, + type: note.content.type + }; + } else { + let data = await db.content?.raw(note.contentId); + if (data) { + data = await db.content?.insertPlaceholders(data, 'placeholder.svg'); + currentContent.current = { + data: data.data, + type: data.type + }; + } + } + }, []); + + const loadNote = useCallback( + async (item: Note) => { + console.log('loading note', item.type); + state.current.currentlyEditing = true; + const editorState = useEditorStore.getState(); + + if (item && item.type === 'new') { + currentNote.current && reset(); + setSessionId(makeSessionId()); + commands.focus(); + } else { + if (!item.forced && currentNote.current?.id === item.id) return; + overlay(true, item); + currentNote.current && reset(); + await loadContent(item); + setSessionId(makeSessionId(item)); + editorState.setCurrentlyEditingNote(item.id); + currentNote.current = item; + commands.setStatus(timeConverter(item.dateEdited), 'Saved'); + await postMessage(EditorEvents.title, item.title); + await postMessage(EditorEvents.html, currentContent.current?.data); + overlay(false); + } + }, + [setSessionId] + ); + + useEffect(() => { + eSubscribeEvent(eOnLoadNote, loadNote); + return () => { + eUnSubscribeEvent(eOnLoadNote, loadNote); + }; + }, []); + + const onMessage = useCallback( + event => { + let message = event.nativeEvent.data; + message = JSON.parse(message); + + console.log(message.type); + + if (message.sessionId !== sessionId && message.type !== EditorEvents.status) { + console.log( + 'useEditor: message recieved from invalid session', + message.type, + sessionId, + message.sessionId + ); + return; + } + switch (message.type) { + case EditorEvents.logger: + console.log(message.type, message.value); + break; + case 'editor-event:content': + saveContent({ + type: message.type, + content: message.value + }); + break; + case 'editor-event:selection': + break; + case 'editor-event:title': + saveContent({ + type: message.type, + title: message.value + }); + break; + } + if (message.type.startsWith('native:')) { + eSendEvent(message.type, message); + } + }, + [sessionId] + ); + + const saveContent = useCallback( + ({ title, content, type }: { title?: string; content?: string; type: string }) => { + if (type === EditorEvents.content) { + currentContent.current = { + data: content, + type: 'tiny' + }; + } + let params = { + title, + data: content, + type: 'tiptap', + sessionId, + id: currentNote.current?.id + }; + + withTimer( + currentNote.current?.id || 'newnote', + () => { + if (currentNote.current && !params.id) { + params.id = currentNote.current?.id; + } + saveNote(params); + }, + 300 + ); + }, + [] + ); + + const onLoad = useCallback(() => { + console.log('on editor load'); + state.current.ready = true; + onReady(); + postMessage(EditorEvents.theme, useThemeStore.getState().colors); + if (currentNote.current) { + console.log('force reload note'); + loadNote({ ...currentNote.current, forced: true }); + } else { + restoreEditorState(); + } + }, [state, currentNote, loadNote]); + + async function restoreEditorState() { + let appState = await MMKV.getItem('appState'); + if (appState) { + appState = JSON.parse(appState); + + if ( + //@ts-ignore + appState.editing && + //@ts-ignore + appState.note && + //@ts-ignore + !appState.note.locked && + //@ts-ignore + appState.note.id && + //@ts-ignore + Date.now() < appState.timestamp + 3600000 + ) { + state.current.isRestoringState = true; + //@ts-ignore + overlay(true, appState.note); + state.current.currentlyEditing = true; + if (!DDS.isTab) { + tabBarRef.current?.goToPage(1); + } + setTimeout(() => { + //@ts-ignore + loadNote(appState.note); + }, 1); + MMKV.removeItem('appState'); + state.current.movedAway = false; + eSendEvent('load_overlay', 'hide_editor'); + state.current.isRestoringState = false; + return; + } + state.current.isRestoringState = false; + return; + } + state.current.isRestoringState = false; + } + + return { + onMessage, + ref: editorRef, + onLoad, + commands, + reset, + loading, + setLoading, + state, + sessionId, + setSessionId, + note: currentNote, + onReady + }; +}; diff --git a/apps/mobile/src/screens/editor/tiptap/utils.js b/apps/mobile/src/screens/editor/tiptap/utils.js deleted file mode 100644 index abe5a6247..000000000 --- a/apps/mobile/src/screens/editor/tiptap/utils.js +++ /dev/null @@ -1,60 +0,0 @@ -import { createRef } from 'react'; -import { Platform } from 'react-native'; -import { eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager'; -import { useEditorStore } from '../../../stores/stores'; -import { sleep } from '../../../utils/time'; -import commands from './commands'; -export const textInput = createRef(); - -export const EditorEvents = { - html: 'native:html', - title: 'native:title', - theme: 'native:theme', - titleplaceholder: 'native:titleplaceholder', - logger: 'native:logger', - status: 'native:status' -}; - -function randId(prefix) { - return Math.random() - .toString(36) - .replace('0.', prefix || ''); -} - -export function makeSessionId(item) { - return item?.id ? item.id + randId('_session_') : randId('session_'); -} - -export async function isEditorLoaded(ref) { - return await post(ref, EditorEvents.status); -} - -export async function post(ref, type, value = null) { - let sessionId = useEditorStore.getState().sessionId; - if (!sessionId) { - console.warn('post called without sessionId of type:', type); - return; - } - let message = { - type, - value, - sessionId: sessionId - }; - ref.current?.postMessage(JSON.stringify(message)); - let response = await getResponse(type); - console.log('post: ', type, 'result:', response); - return response; -} - -const getResponse = async type => { - return new Promise(resolve => { - let callback = data => { - eUnSubscribeEvent(type, callback); - resolve(data); - }; - eSubscribeEvent(type, callback); - setTimeout(() => { - resolve(false); - }, 5000); - }); -}; diff --git a/apps/mobile/src/screens/editor/tiptap/utils.ts b/apps/mobile/src/screens/editor/tiptap/utils.ts new file mode 100644 index 000000000..3fd140752 --- /dev/null +++ b/apps/mobile/src/screens/editor/tiptap/utils.ts @@ -0,0 +1,78 @@ +import { createRef, MutableRefObject } from 'react'; +import { TextInput } from 'react-native'; +import WebView from 'react-native-webview'; +import { eSubscribeEvent, eUnSubscribeEvent } from '../../../services/event-manager'; +import { useEditorType } from './types'; +export const textInput = createRef(); +export const editorController = createRef(); + +export const defaultState = { + movedAway: true +}; + +export function editorState() { + if (!editorController.current?.state.current) { + console.warn('Editor state not ready'); + } + return editorController.current?.state.current || defaultState; +} + +export const EditorEvents: { [name: string]: string } = { + html: 'native:html', + title: 'native:title', + theme: 'native:theme', + titleplaceholder: 'native:titleplaceholder', + logger: 'native:logger', + status: 'native:status' +}; + +function randId(prefix: string) { + return Math.random() + .toString(36) + .replace('0.', prefix || ''); +} + +export function makeSessionId(item?: any) { + return item?.id ? item.id + randId('_session_') : randId('session_'); +} + +export async function isEditorLoaded(ref: MutableRefObject) { + return await post(ref, EditorEvents.status); +} + +export async function post(ref: MutableRefObject, type: string, value = null) { + let sessionId = editorController.current?.sessionId; + if (!sessionId) { + console.warn('post called without sessionId of type:', type); + return; + } + let message = { + type, + value, + sessionId: sessionId + }; + ref.current?.postMessage(JSON.stringify(message)); + let response = await getResponse(type); + console.log('post: ', type, sessionId, 'result:', !!response); + return response; +} + +type WebviewResponseData = { + [name: string]: any; + sessionId: string | null; + type: string; + value: any; +}; + +const getResponse = async (type: string): Promise => { + return new Promise(resolve => { + let callback = (data: WebviewResponseData) => { + eUnSubscribeEvent(type, callback); + resolve(data); + }; + eSubscribeEvent(type, callback); + setTimeout(() => { + resolve(false); + }, 5000); + }); +}; diff --git a/apps/mobile/src/screens/home/index.js b/apps/mobile/src/screens/home/index.js index c52b3fd83..b8970c4be 100755 --- a/apps/mobile/src/screens/home/index.js +++ b/apps/mobile/src/screens/home/index.js @@ -9,11 +9,12 @@ import { eSendEvent } from '../../services/event-manager'; import Navigation from '../../services/navigation'; import SearchService from '../../services/search'; import { useNoteStore } from '../../stores/stores'; -import { editing, InteractionManager } from '../../utils'; +import { InteractionManager } from '../../utils'; import { db } from '../../utils/database'; import { eOnLoadNote } from '../../utils/events'; import { tabBarRef } from '../../utils/global-refs'; import { getNote } from '../editor/Functions'; +import { editorState } from '../editor/tiptap/utils'; export const Home = ({ navigation }) => { const notes = useNoteStore(state => state.notes); @@ -83,8 +84,8 @@ export const Home = ({ navigation }) => { if (!DDS.isTab) { if (getNote()) { eSendEvent(eOnLoadNote, { type: 'new' }); - editing.currentlyEditing = true; - editing.movedAway = false; + editorState().currentlyEditing = true; + editorState().movedAway = false; } tabBarRef.current?.goToPage(1); } else { diff --git a/apps/mobile/src/screens/notes/index.js b/apps/mobile/src/screens/notes/index.js index 1b5934e68..9c8f55b32 100644 --- a/apps/mobile/src/screens/notes/index.js +++ b/apps/mobile/src/screens/notes/index.js @@ -1,23 +1,24 @@ import { groupArray } from 'notes-core/utils/grouping'; import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { FloatingButton } from '../../components/container/floating-button'; import { ContainerHeader } from '../../components/container/containerheader'; +import { FloatingButton } from '../../components/container/floating-button'; import { Header } from '../../components/header'; -import { MoveNotes } from '../../components/sheets/move-notes/movenote'; -import SelectionHeader from '../../components/selection-header'; import List from '../../components/list'; -import { useThemeStore } from '../../stores/theme'; -import { useNoteStore } from '../../stores/stores'; +import SelectionHeader from '../../components/selection-header'; +import { MoveNotes } from '../../components/sheets/move-notes/movenote'; import { DDS } from '../../services/device-detection'; import { eSendEvent, eSubscribeEvent, eUnSubscribeEvent } from '../../services/event-manager'; import Navigation from '../../services/navigation'; import SearchService from '../../services/search'; -import { editing, InteractionManager } from '../../utils'; +import { useMenuStore, useNoteStore } from '../../stores/stores'; +import { useThemeStore } from '../../stores/theme'; +import { InteractionManager } from '../../utils'; import { db } from '../../utils/database'; import { eOnLoadNote, eOpenAddTopicDialog, refreshNotesPage } from '../../utils/events'; import { openLinkInBrowser } from '../../utils/functions'; import { tabBarRef } from '../../utils/global-refs'; import { getNote } from '../editor/Functions'; +import { editorState } from '../editor/tiptap/utils'; const getNotes = (params, group = true) => { if (!params) return []; @@ -46,6 +47,44 @@ function getAlias(params) { return alias || ''; } +async function onNoteCreated(id, params) { + if (!params.current || !id) return; + switch (params.current.type) { + case 'topic': { + await db.notes.move( + { + topic: params.current.id, + id: params.current.notebook + }, + id + ); + editorState().onNoteCreated = null; + Navigation.setRoutesToUpdate([ + Navigation.routeNames.Notebooks, + Navigation.routeNames.NotesPage, + Navigation.routeNames.Notebook + ]); + break; + } + case 'tag': { + await db.notes.note(id).tag(params.current.id); + editorState().onNoteCreated = null; + Navigation.setRoutesToUpdate([Navigation.routeNames.Tags, Navigation.routeNames.NotesPage]); + break; + } + case 'color': { + await db.notes.note(id).color(params.current.id); + editorState().onNoteCreated = null; + Navigation.setRoutesToUpdate([Navigation.routeNames.NotesPage]); + useMenuStore.getState().setColorNotes(); + break; + } + default: { + break; + } + } +} + export const Notes = ({ route, navigation }) => { const colors = useThemeStore(state => state.colors); const params = useRef(route?.params); @@ -70,19 +109,13 @@ export const Notes = ({ route, navigation }) => { eSubscribeEvent(refreshNotesPage, init); return () => { eUnSubscribeEvent(refreshNotesPage, init); - editing.actionAfterFirstSave = { - type: null - }; + editorState().onNoteCreated = null; }; }, []); const setActionAfterFirstSave = () => { if (params.current?.get === 'monographs') return; - editing.actionAfterFirstSave = { - type: params.current?.type, - id: params.current?.id, - notebook: params.current?.notebookId - }; + editorState().onNoteCreated = id => onNoteCreated(id, params); }; const init = data => { @@ -110,18 +143,14 @@ export const Notes = ({ route, navigation }) => { InteractionManager.runAfterInteractions(() => { setNotes([]); }, 150); - editing.actionAfterFirstSave = { - type: null - }; + editorState().onNoteCreated = null; }, []); useEffect(() => { navigation.addListener('focus', onFocus); navigation.addListener('blur', onBlur); return () => { - editing.actionAfterFirstSave = { - type: null - }; + editorState().onNoteCreated = null; navigation.removeListener('focus', onFocus); navigation.removeListener('blur', onBlur); }; @@ -162,8 +191,8 @@ export const Notes = ({ route, navigation }) => { if (!DDS.isTab) { if (getNote()) { eSendEvent(eOnLoadNote, { type: 'new' }); - editing.currentlyEditing = true; - editing.movedAway = false; + editorState().currentlyEditing = true; + editorState().movedAway = false; } tabBarRef.current?.goToPage(1); } else { diff --git a/apps/mobile/src/services/notifications.ts b/apps/mobile/src/services/notifications.ts index 7251411a9..1ebabcc2d 100644 --- a/apps/mobile/src/services/notifications.ts +++ b/apps/mobile/src/services/notifications.ts @@ -12,6 +12,7 @@ import { useNoteStore } from '../stores/stores'; import { DDS } from './device-detection'; import { eSendEvent } from './event-manager'; import SettingsService from './settings'; +import { editorState } from '../screens/editor/tiptap/utils'; const NOTIFICATION_TAG = 'notesnook'; const CHANNEL_ID = 'com.streetwriters.notesnook'; @@ -36,7 +37,7 @@ function init() { if (Platform.OS === 'ios') return; PushNotification.configure({ onNotification: async function (notification) { - editing.movedAway = false; + editorState().movedAway = false; MMKV.removeItem('appState'); if (useNoteStore?.getState()?.loading === false) { //@ts-ignore diff --git a/apps/mobile/src/stores/stores.ts b/apps/mobile/src/stores/stores.ts index 46648fbd0..259e63f1c 100644 --- a/apps/mobile/src/stores/stores.ts +++ b/apps/mobile/src/stores/stores.ts @@ -3,16 +3,15 @@ import { groupArray } from 'notes-core/utils/grouping'; import { Dimensions, Platform } from 'react-native'; import create from 'zustand'; import { APP_VERSION } from '../../version'; +import { endSearch } from '../screens/editor/tiny/toolbar/commands'; import { eSubscribeEvent, eUnSubscribeEvent } from '../services/event-manager'; import PremiumService from '../services/premium'; import { history } from '../utils'; +import { ACCENT } from '../utils/color-scheme'; import { SUBSCRIPTION_STATUS } from '../utils/constants'; import { db } from '../utils/database'; import { MMKV } from '../utils/database/mmkv'; import layoutmanager from '../utils/layout-manager'; -import { EditorWebView } from '../screens/editor/Functions'; -import tiny from '../screens/editor/tiny/tiny'; -import { endSearch } from '../screens/editor/tiny/toolbar/commands'; import { Announcement, EditorStore, @@ -28,7 +27,6 @@ import { TrashStore, UserStore } from './interfaces'; -import { ACCENT } from '../utils/color-scheme'; export const useNoteStore = create((set, get) => ({ notes: [], @@ -185,18 +183,18 @@ export const useAttachmentStore = create((set, get) => ({ let _p = get().progress; if (!_p) return; _p[hash] = null; - tiny.call( - EditorWebView, - ` - (function() { - let progress = ${JSON.stringify({ - loaded: 1, - total: 1, - hash - })} - tinymce.activeEditor._updateAttachmentProgress(progress); - })()` - ); + // tiny.call( + // EditorWebView, + // ` + // (function() { + // let progress = ${JSON.stringify({ + // loaded: 1, + // total: 1, + // hash + // })} + // tinymce.activeEditor._updateAttachmentProgress(progress); + // })()` + // ); set({ progress: { ..._p } }); }, setProgress: (sent, total, hash, recieved, type) => { @@ -204,14 +202,14 @@ export const useAttachmentStore = create((set, get) => ({ if (!_p) return; _p[hash] = { sent, total, hash, recieved, type }; let progress = { total, hash, loaded: type === 'download' ? recieved : sent }; - tiny.call( - EditorWebView, - ` - (function() { - let progress = ${JSON.stringify(progress)} - tinymce.activeEditor._updateAttachmentProgress(progress); - })()` - ); + // tiny.call( + // EditorWebView, + // ` + // (function() { + // let progress = ${JSON.stringify(progress)} + // tinymce.activeEditor._updateAttachmentProgress(progress); + // })()` + // ); set({ progress: { ..._p } }); }, encryptionProgress: 0, @@ -321,14 +319,14 @@ export const useEditorStore = create((set, get) => ({ set({ searchSelection: value, searchReplace: true }); }; eSubscribeEvent('selectionvalue', func); - tiny.call( - EditorWebView, - `(function() { - if (editor) { - reactNativeEventHandler('selectionvalue',editor.selection.getContent()); - } - })();` - ); + // tiny.call( + // EditorWebView, + // `(function() { + // if (editor) { + // reactNativeEventHandler('selectionvalue',editor.selection.getContent()); + // } + // })();` + // ); } })); diff --git a/apps/mobile/src/utils/hooks/useAppEvents.js b/apps/mobile/src/utils/hooks/useAppEvents.js index b3c46a00c..6f90065b7 100644 --- a/apps/mobile/src/utils/hooks/useAppEvents.js +++ b/apps/mobile/src/utils/hooks/useAppEvents.js @@ -13,13 +13,7 @@ import * as RNIap from 'react-native-iap'; import { enabled } from 'react-native-privacy-snapshot'; import { doInBackground, editing } from '..'; import { Walkthrough } from '../../components/walkthroughs'; -import { - EditorWebView, - getNote, - getWebviewInit, - updateNoteInEditor -} from '../../screens/editor/Functions'; -import tiny from '../../screens/editor/tiny/tiny'; +import { editorState } from '../../screens/editor/tiptap/utils'; import Backup from '../../services/backup'; import BiometricService from '../../services/biometrics'; import { @@ -42,6 +36,7 @@ import { clearAllStores, initialize, useAttachmentStore, + useEditorStore, useMessageStore, useNoteStore, useSettingStore, @@ -73,15 +68,15 @@ export const useAppEvents = () => { const onMediaDownloaded = ({ hash, groupId, src }) => { if (groupId?.startsWith('monograph')) return; - tiny.call( - EditorWebView, - ` - (function(){ - let image = ${JSON.stringify({ hash, src })}; - tinymce.activeEditor._replaceImage(image); - })(); - ` - ); + // tiny.call( + // EditorWebView, + // ` + // (function(){ + // let image = ${JSON.stringify({ hash, src })}; + // tinymce.activeEditor._replaceImage(image); + // })(); + // ` + // ); }; const onLoadingAttachmentProgress = data => { @@ -170,8 +165,10 @@ export const useAppEvents = () => { initialize(); setLastSynced(await db.lastSynced()); setSyncing(false); - if (getNote()) { - await updateNoteInEditor(); + let id = useEditorStore.getState().currentEditingNote; + let note = id && db.notes.note(id).data; + if (note) { + //await updateNoteInEditor(); } }; @@ -407,9 +404,7 @@ export const useAppEvents = () => { await reconnectSSE(); await checkIntentState(); - if (getWebviewInit()) { - await MMKV.removeItem('appState'); - } + await MMKV.removeItem('appState'); let user = await db.user.getUser(); if (user && !user.isEmailConfirmed) { try { @@ -421,7 +416,9 @@ export const useAppEvents = () => { } } else { refValues.current.prevState = 'background'; - if (getNote()?.locked && SettingsService.get().appLockMode === 'background') { + let id = useEditorStore.getState().currentEditingNote; + let note = id && db.notes.note(id).data; + if (note?.locked && SettingsService.get().appLockMode === 'background') { eSendEvent(eClearEditor); } await storeAppState(); @@ -462,12 +459,14 @@ export const useAppEvents = () => { } async function storeAppState() { - if (editing.currentlyEditing) { - if (getNote()?.locked) return; + if (editorState().currentlyEditing) { + let id = useEditorStore.getState().currentEditingNote; + let note = id && db.notes.note(id).data; + if (note?.locked) return; let state = JSON.stringify({ - editing: editing.currentlyEditing, - note: getNote(), - movedAway: editing.movedAway, + editing: editorState().currentlyEditing, + note: note, + movedAway: editorState().movedAway, timestamp: Date.now() }); await MMKV.setItem('appState', state); @@ -490,7 +489,9 @@ export const useAppEvents = () => { eSendEvent(refreshNotesPage); } if (notesAddedFromIntent || shareExtensionOpened) { - eSendEvent('loadingNote', getNote()); + let id = useEditorStore.getState().currentEditingNote; + let note = id && db.notes.note(id).data; + eSendEvent('loadingNote', note); eSendEvent('webviewreset', true); MMKV.removeItem('shareExtensionOpened'); } diff --git a/apps/mobile/src/utils/time/index.ts b/apps/mobile/src/utils/time/index.ts index 48eff4669..ffd1efaab 100644 --- a/apps/mobile/src/utils/time/index.ts +++ b/apps/mobile/src/utils/time/index.ts @@ -34,7 +34,7 @@ export function timeSince(date: number) { return Math.floor(seconds) < 0 ? '0s ago' : Math.floor(seconds) + 's ago'; } -export const timeConverter = (timestamp: number) => { +export const timeConverter = (timestamp: number | undefined | null) => { if (!timestamp) return; let d = new Date(timestamp), // Convert the passed timestamp to milliseconds yyyy = d.getFullYear(),