diff --git a/apps/mobile/app/hooks/use-app-events.tsx b/apps/mobile/app/hooks/use-app-events.tsx index 80b58c730..f1934dea4 100644 --- a/apps/mobile/app/hooks/use-app-events.tsx +++ b/apps/mobile/app/hooks/use-app-events.tsx @@ -627,17 +627,10 @@ export const useAppEvents = () => { if (locked) { useTabStore.getState().updateTab(tab.id, { session: { - locked: true + locked: true, + noteLocked: true } }); - if ( - tab.id === useTabStore.getState().currentTab && - locked && - !editorState().movedAway - ) { - // Show unlock note screen. - eSendEvent(eUnlockNote); - } } } }), diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor-events.tsx b/apps/mobile/app/screens/editor/tiptap/use-editor-events.tsx index 8f56a5f21..439eb053c 100644 --- a/apps/mobile/app/screens/editor/tiptap/use-editor-events.tsx +++ b/apps/mobile/app/screens/editor/tiptap/use-editor-events.tsx @@ -612,14 +612,6 @@ export const useEditorEvents = ( break; } case EditorEvents.tabFocused: { - console.log( - "Focused tab", - editorMessage.tabId, - editorMessage.noteId, - "Content:", - editorMessage.value - ); - eSendEvent(eEditorTabFocused, editorMessage.tabId); if (editorMessage.noteId) { diff --git a/apps/mobile/app/screens/editor/tiptap/use-editor.ts b/apps/mobile/app/screens/editor/tiptap/use-editor.ts index 5b793bfa8..c03424885 100644 --- a/apps/mobile/app/screens/editor/tiptap/use-editor.ts +++ b/apps/mobile/app/screens/editor/tiptap/use-editor.ts @@ -32,9 +32,9 @@ import { isEncryptedContent, isTrashItem } from "@notesnook/core"; -import { strings } from "@notesnook/intl"; import { EditorEvents } from "@notesnook/editor-mobile/src/utils/editor-events"; import { NativeEvents } from "@notesnook/editor-mobile/src/utils/native-events"; +import { strings } from "@notesnook/intl"; import { useThemeEngineStore } from "@notesnook/theme"; import { Mutex } from "async-mutex"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -500,6 +500,7 @@ export const useEditor = ( blockId?: string; session?: TabSessionItem; newTab?: boolean; + resetTabState?: boolean; }) => { loadNoteMutex.runExclusive(async () => { if (!event) return; @@ -544,7 +545,15 @@ export const useEditor = ( } const item = event.item; - currentLoadingNoteId.current = item.id; + + // If note is already open in a tab, focus that tab. + if (useTabStore.getState().hasTabForNote(item.id)) { + const tabId = useTabStore.getState().getTabForNote(item.id); + if (tabId !== useTabStore.getState().currentTab) { + useTabStore.getState().focusTab(tabId as string); + return; + } + } const isLockedNote = await db.vaults.itemExists( event.item as ItemReference @@ -553,25 +562,24 @@ export const useEditor = ( const tabLocked = isLockedNote && !(event.item as NoteWithContent).content; - let tabId = event.tabId; - if (tabId === undefined) tabId = useTabStore.getState().currentTab; + const tabId = event.tabId + ? event.tabId + : useTabStore.getState().currentTab; + // Check if tab needs to be refreshed. if ( + !event.resetTabState && tabId && + event.item.id === useTabStore.getState().getNoteIdForTab(tabId) && !localTabState.current?.needsRefresh( tabId, isLockedNote, item.readonly ) ) { - console.log( - "Note is already updated, skipping refresh request on focus" - ); - currentLoadingNoteId.current = undefined; - + commands.setLoading(false, tabId); return; } else { - console.log("updating the tab in focus..."); localTabState.current?.setEditTime( item.id, localTabState.current?.noteEditedTime[item.id] || item.dateEdited @@ -583,6 +591,9 @@ export const useEditor = ( }); } + currentLoadingNoteId.current = item.id; + + // Show loading overlay if note is not already loaded. if ( tabId && (event.item?.id !== useTabStore.getState().getNoteIdForTab(tabId) || @@ -592,38 +603,35 @@ export const useEditor = ( } const session: Partial = event.session || { - readonly: event.item.readonly, - locked: tabLocked, - noteLocked: isLockedNote, noteId: event.item.id }; + session.noteLocked = isLockedNote; + session.locked = tabLocked; + session.readonly = item.readonly; + const tab = useTabStore.getState().getTab(tabId!); if (useTabStore.getState().tabs.length === 0 || event.newTab) { useTabStore.getState().newTab({ session: session }); - console.log("Creating a new tab..."); } else { + // A new session is created if the note is changed. + // If the note is already opened, the session is updated. if ( - event.item.id !== tab?.session?.noteId && - tab?.session?.noteId + !tab?.session || + (event.item.id !== tab?.session?.noteId && tab?.session?.noteId) ) { useTabStore.getState().newTabSession(tabId!, session); - console.log("Creating a new tab session"); } else { - console.log("Updating tab session"); useTabStore.getState().updateTab(tabId!, { session: session }); } } - if (lastTabFocused.current !== tabId) { - console.log("Waiting for tab to get focus"); - return; - } + if (lastTabFocused.current !== tabId) return; if (tabBarRef.current?.page() === 2) { state.current.movedAway = false; @@ -651,8 +659,6 @@ export const useEditor = ( await postMessage(NativeEvents.title, item.title, tabId); overlay(false); - console.log("LOADING NOTE....", item.id, item.title); - await postMessage( NativeEvents.html, { @@ -699,38 +705,34 @@ export const useEditor = ( isLocal?: boolean ) => { try { - await (async () => { - if (SettingsService.get().disableRealtimeSync && !isLocal) return; - if (!data) return; + if (SettingsService.get().disableRealtimeSync && !isLocal) return; + if (!data) return; - if (isDeleted(data) || isTrashItem(data)) { - const tabId = useTabStore.getState().getTabForNote(data.id); - if (tabId !== undefined) { - await commands.clearContent(tabId); - useTabStore.getState().removeTab(tabId); - } - return; + if (isDeleted(data) || isTrashItem(data)) { + const tabId = useTabStore.getState().getTabForNote(data.id); + if (tabId !== undefined) { + await commands.clearContent(tabId); + useTabStore.getState().removeTab(tabId); } + return; + } - const noteId = - (data as ContentItem).type === "tiptap" - ? (data as ContentItem).noteId - : data.id; + const noteId = + (data as ContentItem).type === "tiptap" + ? (data as ContentItem).noteId + : data.id; - if (!useTabStore.getState().hasTabForNote(noteId)) return; - const tabId = useTabStore.getState().getTabForNote(noteId) as string; + const note = data.type === "note" ? data : await db.notes?.note(noteId); - const tab = useTabStore.getState().getTab(tabId); + lock.current = true; - const note = - data.type === "note" ? data : await db.notes?.note(noteId); + // Handle this case where note was locked on another device and synced. + const locked = note + ? await db.vaults.itemExists(note as ItemReference) + : false; - lock.current = true; - - // Handle this case where note was locked on another device and synced. - const locked = note - ? await db.vaults.itemExists(note as ItemReference) - : false; + useTabStore.getState().forEachNoteTab(noteId, async (tab) => { + const tabId = tab.id; if (note) { if (!locked && tab?.session?.noteLocked) { @@ -738,8 +740,7 @@ export const useEditor = ( if (tab.session?.locked) { if (useTabStore.getState().currentTab === tabId) { eSendEvent(eOnLoadNote, { - item: note, - forced: true + item: note }); } else { useTabStore.getState().updateTab(tabId, { @@ -748,7 +749,9 @@ export const useEditor = ( noteLocked: false } }); - commands.setLoading(true, tabId); + localTabState.current?.set(tabId, { + editedAt: 0 + }); } } } else if (!tab?.session?.noteLocked && locked) { @@ -759,10 +762,13 @@ export const useEditor = ( noteLocked: true } }); + commands.clearContent(tabId); if (useTabStore.getState().currentTab !== tabId) { - commands.clearContent(tabId); commands.setLoading(true, tabId); } + localTabState.current?.set(tabId, { + editedAt: 0 + }); } if (currentNotes.current[noteId]?.title !== note.title) { @@ -776,12 +782,13 @@ export const useEditor = ( tabId as string ); } - - useTabStore.getState().updateTab(tabId, { - session: { - readonly: note.readonly - } - }); + if (tab.session?.readonly !== note.readonly) { + useTabStore.getState().updateTab(tabId, { + session: { + readonly: note.readonly + } + }); + } } if (data.type === "tiptap" && note && !isLocal) { @@ -792,7 +799,7 @@ export const useEditor = ( if (locked && isEncryptedContent(data)) { const decryptedContent = await db.vault?.decryptContent(data); if (!decryptedContent) { - useTabStore.getState().updateTab(tabId, { + useTabStore.getState().updateTab(tab.id, { session: { locked: true, noteLocked: true @@ -823,7 +830,7 @@ export const useEditor = ( } } } - })(); + }); } catch (e) { DatabaseLogger.error(e as Error, "Error when applying sync changes"); } finally { @@ -980,6 +987,9 @@ export const useEditor = ( useTabStore.getState().currentTab! )) ) { + localTabState.current?.set(useTabStore.getState().currentTab!, { + editedAt: 0 + }); eSendEvent(eEditorReset, "onReady"); return false; } else { @@ -998,6 +1008,10 @@ export const useEditor = ( ); await commands.setSettings(); + localTabState.current?.set(useTabStore.getState().currentTab!, { + editedAt: 0 + }); + if (!state.current.ready) { state.current.ready = true; } diff --git a/apps/mobile/app/screens/editor/tiptap/use-tab-store.ts b/apps/mobile/app/screens/editor/tiptap/use-tab-store.ts index f11a96e78..4adf63fe7 100644 --- a/apps/mobile/app/screens/editor/tiptap/use-tab-store.ts +++ b/apps/mobile/app/screens/editor/tiptap/use-tab-store.ts @@ -148,7 +148,6 @@ export const tabSessionHistory = new TabSessionHistory({ return useTabStore.getState(); }, set(state) { - console.log("Set state", state.canGoBack, state.canGoForward); useTabStore.setState({ ...state }); @@ -217,12 +216,37 @@ export const useTabStore = create( ) => { const tabId = _id || (get().currentTab as string); - const sessionId = tabSessionHistory.add(tabId); - const session = { - id: sessionId, - ...options - }; - TabSessionStorage.set(sessionId, session); + const sessionHistory = get().tabSessionHistory[tabId]; + + let oldSessionId: string | undefined = undefined; + if (sessionHistory) { + const allSessions = sessionHistory.backStack.concat( + sessionHistory.forwardStack + ); + allSessions.forEach((id) => { + if (TabSessionStorage.get(id)?.noteId === options?.noteId) { + oldSessionId = id; + } + }); + } + + const sessionId = tabSessionHistory.add(tabId, oldSessionId); + + let session: Partial; + + if (!oldSessionId) { + session = { + id: sessionId, + ...options + }; + TabSessionStorage.set(sessionId, session as TabSessionItem); + } else { + session = { + ...TabSessionStorage.get(oldSessionId), + ...options + }; + } + const index = get().tabs.findIndex((t) => t.id === tabId); if (index == -1) return; const tabs = [...get().tabs]; @@ -308,10 +332,9 @@ export const useTabStore = create( if (note) { const isLocked = await db.vaults.itemExists(note); - if (isLocked && !session?.noteLocked) { - session.locked = true; - session.noteLocked = true; - } + session.locked = isLocked; + session.noteLocked = isLocked && !session?.noteLocked; + session.readonly = note.readonly; } else if (session.noteId) { console.log("Failed to load session..."); @@ -325,12 +348,12 @@ export const useTabStore = create( }); } - console.log("Loading session", session); eSendEvent(eOnLoadNote, { item: note, newNote: !note, tabId: get().currentTab, - session: session + session: session, + resetTabState: true }); return true; @@ -418,10 +441,7 @@ export const useTabStore = create( return get().tabs.find((t) => t.id === id)?.session?.noteId; }, hasTabForNote: (noteId: string) => { - return ( - typeof get().tabs.find((t) => t.session?.noteId === noteId)?.id === - "number" - ); + return !!get().tabs.find((t) => t.session?.noteId === noteId); }, getTabForNote: (noteId: string) => { return get().tabs.find((t) => t.session?.noteId === noteId)?.id; @@ -442,7 +462,7 @@ export const useTabStore = create( } }), { - name: "tabs-storage", + name: "tabs-storage-v1", getStorage: () => MMKV as unknown as StateStorage, onRehydrateStorage: () => { return (state) => { diff --git a/packages/editor-mobile/src/components/editor.tsx b/packages/editor-mobile/src/components/editor.tsx index ed1586044..5849f5945 100644 --- a/packages/editor-mobile/src/components/editor.tsx +++ b/packages/editor-mobile/src/components/editor.tsx @@ -243,8 +243,10 @@ const Tiptap = ({ ); setTimeout(() => { editorControllers[tabRef.current.id]?.setLoading(false); - restoreNoteSelection(scrollTop, selection); - }, 300); + setTimeout(() => { + restoreNoteSelection(scrollTop, selection); + }, 300); + }, 1); }, [restoreNoteSelection] );