mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
mobile: fix numerous issues with tabs
This commit is contained in:
committed by
Abdullah Atta
parent
6b6ee76c39
commit
ad338aeefc
@@ -56,6 +56,7 @@ import { useUserStore } from "../../../stores/use-user-store";
|
||||
import {
|
||||
eClearEditor,
|
||||
eCloseFullscreenEditor,
|
||||
eEditorTabFocused,
|
||||
eOnLoadNote,
|
||||
eOpenFullscreenEditor,
|
||||
eOpenLoginDialog,
|
||||
@@ -377,7 +378,7 @@ export const useEditorEvents = (
|
||||
logger.info("[WEBVIEW LOG]", editorMessage.value);
|
||||
break;
|
||||
case EventTypes.contentchange:
|
||||
editor.onContentChanged();
|
||||
editor.onContentChanged(editorMessage.noteId);
|
||||
break;
|
||||
case EventTypes.selection:
|
||||
break;
|
||||
@@ -531,22 +532,25 @@ export const useEditorEvents = (
|
||||
}
|
||||
case EventTypes.tabFocused: {
|
||||
// Reload the note
|
||||
console.log("Focused tab", editorMessage.tabId);
|
||||
eSendEvent("tabsFocused", editorMessage.tabId);
|
||||
console.log(
|
||||
"Focused tab",
|
||||
editorMessage.tabId,
|
||||
editorMessage.noteId,
|
||||
"Content:",
|
||||
editorMessage.value
|
||||
);
|
||||
|
||||
// const note = await db.notes.note(editorMessage.noteId);
|
||||
// if (note) {
|
||||
// eSendEvent(eOnLoadNote, {
|
||||
// item: note,
|
||||
// forced: true
|
||||
// });
|
||||
// }
|
||||
// TODO
|
||||
// Handle any updates that occured in an note while the tab was not focused.
|
||||
// If editor has no content, reload the note, because it might be an app reload
|
||||
// or the tab was destroyed in background...
|
||||
// Maybe cache changes, something like pendingUpdates list.
|
||||
// Do proper cleanup when a tab is destroyed though.
|
||||
eSendEvent(eEditorTabFocused, editorMessage.tabId);
|
||||
if (!editorMessage.value && editorMessage.noteId) {
|
||||
const note = await db.notes.note(editorMessage.noteId);
|
||||
if (note) {
|
||||
eSendEvent(eOnLoadNote, {
|
||||
item: note,
|
||||
forced: true,
|
||||
tabId: editorMessage.tabId
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,11 @@ import SettingsService from "../../../services/settings";
|
||||
import { TipManager } from "../../../services/tip-manager";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { useTagStore } from "../../../stores/use-tag-store";
|
||||
import { eClearEditor, eOnLoadNote } from "../../../utils/events";
|
||||
import {
|
||||
eClearEditor,
|
||||
eEditorTabFocused,
|
||||
eOnLoadNote
|
||||
} from "../../../utils/events";
|
||||
import { tabBarRef } from "../../../utils/global-refs";
|
||||
import { onNoteCreated } from "../../notes/common";
|
||||
import Commands from "./commands";
|
||||
@@ -109,12 +113,11 @@ export const useEditor = (
|
||||
const commands = useMemo(() => new Commands(editorRef), [editorRef]);
|
||||
const editorSessionHistory = useMemo(() => new SessionHistory(), []);
|
||||
const state = useRef<Partial<EditorState>>(defaultState);
|
||||
const placeholderTip = useRef(TipManager.placeholderTip());
|
||||
const tags = useTagStore((state) => state.items);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const isDefaultEditor = editorId === "";
|
||||
const saveCount = useRef(0);
|
||||
const lastContentChangeTime = useRef<number>(0);
|
||||
const lastContentChangeTime = useRef<Record<string, number>>({});
|
||||
const lock = useRef(false);
|
||||
const currentLoadingNoteId = useRef<string>();
|
||||
const loadingState = useRef<string>();
|
||||
@@ -149,7 +152,7 @@ export const useEditor = (
|
||||
}, [commands, tags]);
|
||||
|
||||
useEffect(() => {
|
||||
const event = eSubscribeEvent("tabsFocused", (tabId) => {
|
||||
const event = eSubscribeEvent(eEditorTabFocused, (tabId) => {
|
||||
lastTabFocused.current = tabId as number;
|
||||
console.log(tabId);
|
||||
});
|
||||
@@ -192,6 +195,7 @@ export const useEditor = (
|
||||
currentNotes.current[noteId] = null;
|
||||
currentContents.current[noteId] = null;
|
||||
editorSessionHistory.clearSession(noteId);
|
||||
lastContentChangeTime.current[noteId] = 0;
|
||||
}
|
||||
|
||||
clearTimeout(timers.current["loading-images" + noteId]);
|
||||
@@ -200,13 +204,12 @@ export const useEditor = (
|
||||
loadingState.current = undefined;
|
||||
lock.current = false;
|
||||
resetContent && postMessage(EditorEvents.title, "", tabId);
|
||||
lastContentChangeTime.current = 0;
|
||||
|
||||
resetContent && (await commands.clearContent(tabId));
|
||||
resetContent && (await commands.clearTags(tabId));
|
||||
// TODO ?
|
||||
// useTabStore.getState().updateTab(tabId, {
|
||||
// noteId: undefined
|
||||
// });
|
||||
useTabStore.getState().updateTab(tabId, {
|
||||
noteId: undefined
|
||||
});
|
||||
},
|
||||
[commands, editorSessionHistory, postMessage]
|
||||
);
|
||||
@@ -269,16 +272,15 @@ export const useEditor = (
|
||||
if (!locked) {
|
||||
id = await db.notes?.add(noteData);
|
||||
if (!note && id) {
|
||||
useTabStore.getState().updateTab(tabId, {
|
||||
noteId: id
|
||||
});
|
||||
|
||||
editorSessionHistory.newSession(id);
|
||||
|
||||
if (id) {
|
||||
currentNotes.current[id] = await db.notes?.note(id);
|
||||
}
|
||||
|
||||
useTabStore.getState().updateTab(tabId, {
|
||||
noteId: id
|
||||
});
|
||||
|
||||
const defaultNotebook = db.settings.getDefaultNotebook();
|
||||
if (!state.current.onNoteCreated && defaultNotebook) {
|
||||
onNoteCreated(id, {
|
||||
@@ -290,9 +292,10 @@ export const useEditor = (
|
||||
}
|
||||
|
||||
if (!noteData.title) {
|
||||
console.log("posting title to tab", tabId);
|
||||
postMessage(
|
||||
EditorEvents.title,
|
||||
currentNotes.current?.title,
|
||||
currentNotes.current[id]?.title,
|
||||
tabId
|
||||
);
|
||||
}
|
||||
@@ -315,7 +318,7 @@ export const useEditor = (
|
||||
tabId
|
||||
);
|
||||
|
||||
lastContentChangeTime.current = note.dateEdited;
|
||||
lastContentChangeTime.current[id] = note.dateEdited;
|
||||
|
||||
if (
|
||||
saveCount.current < 2 ||
|
||||
@@ -369,7 +372,12 @@ export const useEditor = (
|
||||
);
|
||||
|
||||
const loadNote = useCallback(
|
||||
async (event: { item?: Note; forced?: boolean; newNote?: boolean }) => {
|
||||
async (event: {
|
||||
item?: Note;
|
||||
forced?: boolean;
|
||||
newNote?: boolean;
|
||||
tabId?: number;
|
||||
}) => {
|
||||
state.current.currentlyEditing = true;
|
||||
if (
|
||||
!state.current.ready &&
|
||||
@@ -383,21 +391,17 @@ export const useEditor = (
|
||||
}
|
||||
|
||||
if (event.newNote) {
|
||||
console.log("Create new note");
|
||||
useTabStore.getState().focusEmptyTab();
|
||||
const tabId = useTabStore.getState().currentTab;
|
||||
console.log("empty tab", tabId);
|
||||
currentNotes.current && (await reset(tabId));
|
||||
setTimeout(() => {
|
||||
if (state.current?.ready) commands.focus(tabId);
|
||||
lastContentChangeTime.current = 0;
|
||||
});
|
||||
} else {
|
||||
if (!event.item) return;
|
||||
const item = event.item;
|
||||
console.log("load note called again...", event.forced, event.item.id);
|
||||
// If note was already opened in a tab, focus that tab and return. Once the tab is focused
|
||||
// the note will load.
|
||||
// If note was already opened in a tab, focus that tab.
|
||||
if (typeof event.tabId !== "number") {
|
||||
if (useTabStore.getState().hasTabForNote(event.item.id)) {
|
||||
const tabId = useTabStore.getState().getTabForNote(event.item.id);
|
||||
if (typeof tabId === "number") {
|
||||
@@ -406,33 +410,35 @@ export const useEditor = (
|
||||
});
|
||||
useTabStore.getState().focusTab(tabId);
|
||||
}
|
||||
console.log("Note already loaded, focusing the tab");
|
||||
} else {
|
||||
console.log("opening note in preview tab");
|
||||
console.log("Opening note in preview tab");
|
||||
// Otherwise we focus the preview tab or create one to open the note in.
|
||||
useTabStore.getState().focusPreviewTab(event.item.id, {
|
||||
readonly: event.item.readonly,
|
||||
locked: false
|
||||
});
|
||||
}
|
||||
const tabId = useTabStore.getState().currentTab;
|
||||
console.log(lastTabFocused.current, tabId);
|
||||
if (lastTabFocused.current !== tabId) {
|
||||
if ((await waitForEvent("tabsFocused", 1000)) !== tabId) {
|
||||
console.log("tab id did not match after focus in 1000ms");
|
||||
return;
|
||||
} else {
|
||||
if (lastTabFocused.current !== event.tabId) {
|
||||
console.log("Focused tab");
|
||||
useTabStore.getState().focusTab(event.tabId);
|
||||
}
|
||||
}
|
||||
|
||||
// If note is already loaded and forced reload is not requested, return.
|
||||
if (!event.forced && currentNotes.current[item.id]) return;
|
||||
const tabId = event.tabId || useTabStore.getState().currentTab;
|
||||
if (lastTabFocused.current !== tabId) {
|
||||
// if ((await waitForEvent(eEditorTabFocused, 1000)) !== tabId) {
|
||||
// console.log("tab id did not match after focus in 1000ms");
|
||||
// return;
|
||||
// }
|
||||
console.log("Waiting for tab to focus");
|
||||
return;
|
||||
}
|
||||
|
||||
state.current.movedAway = false;
|
||||
state.current.currentlyEditing = true;
|
||||
|
||||
if (!currentNotes.current[item.id]) {
|
||||
// Reset current tab if note isn't already loaded.
|
||||
currentNotes.current && (await reset(tabId, false, false));
|
||||
}
|
||||
await loadContent(item);
|
||||
|
||||
if (
|
||||
@@ -441,6 +447,7 @@ export const useEditor = (
|
||||
currentContents.current[item.id]?.data &&
|
||||
loadingState.current === currentContents.current[item.id]?.data
|
||||
) {
|
||||
// If note is already loading, return.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -449,7 +456,7 @@ export const useEditor = (
|
||||
return;
|
||||
}
|
||||
|
||||
lastContentChangeTime.current = item.dateEdited;
|
||||
lastContentChangeTime.current[item.id] = item.dateEdited;
|
||||
currentLoadingNoteId.current = item.id;
|
||||
currentNotes.current[item.id] = item;
|
||||
|
||||
@@ -466,6 +473,7 @@ export const useEditor = (
|
||||
await postMessage(EditorEvents.title, item.title, tabId);
|
||||
loadingState.current = currentContents.current[item.id]?.data;
|
||||
if (currentContents.current[item.id]?.data) {
|
||||
console.log("loading content for note...");
|
||||
await postMessage(
|
||||
EditorEvents.html,
|
||||
currentContents.current[item.id]?.data,
|
||||
@@ -512,7 +520,8 @@ export const useEditor = (
|
||||
|
||||
const note = await db.notes?.note(noteId);
|
||||
|
||||
if (lastContentChangeTime.current >= (data as Note).dateEdited) return;
|
||||
if (lastContentChangeTime.current[noteId] >= (data as Note).dateEdited)
|
||||
return;
|
||||
|
||||
lock.current = true;
|
||||
|
||||
@@ -538,7 +547,7 @@ export const useEditor = (
|
||||
} else {
|
||||
const _nextContent = data.data;
|
||||
if (_nextContent === currentContents.current?.data) return;
|
||||
lastContentChangeTime.current = note.dateEdited;
|
||||
lastContentChangeTime.current[note.id] = note.dateEdited;
|
||||
await postMessage(EditorEvents.updatehtml, _nextContent, tabId);
|
||||
if (!isEncryptedContent(data)) {
|
||||
currentContents.current[note.id] = data as UnencryptedContentItem;
|
||||
@@ -596,7 +605,9 @@ export const useEditor = (
|
||||
)
|
||||
return;
|
||||
|
||||
lastContentChangeTime.current = Date.now();
|
||||
if (noteId) {
|
||||
lastContentChangeTime.current[noteId] = Date.now();
|
||||
}
|
||||
|
||||
if (type === EditorEvents.content && noteId) {
|
||||
currentContents.current[noteId as string] = {
|
||||
@@ -655,14 +666,12 @@ export const useEditor = (
|
||||
};
|
||||
}, [editorId, loadNote, restoreEditorState, isDefaultEditor]);
|
||||
|
||||
const onContentChanged = () => {
|
||||
lastContentChangeTime.current = Date.now();
|
||||
const onContentChanged = (noteId?: string) => {
|
||||
if (noteId) {
|
||||
lastContentChangeTime.current[noteId] = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
state.current.saveCount = 0;
|
||||
}, [loading]);
|
||||
|
||||
const onReady = useCallback(async () => {
|
||||
if (
|
||||
!(await isEditorLoaded(
|
||||
@@ -671,15 +680,9 @@ export const useEditor = (
|
||||
useTabStore.getState().currentTab
|
||||
))
|
||||
) {
|
||||
console.log(
|
||||
"ready failed....",
|
||||
sessionIdRef.current,
|
||||
useTabStore.getState().currentTab
|
||||
);
|
||||
eSendEvent("webview_reset", "onReady");
|
||||
return false;
|
||||
} else {
|
||||
console.log("onReady", "sync tabs");
|
||||
syncTabs();
|
||||
isDefaultEditor && restoreEditorState();
|
||||
return true;
|
||||
@@ -691,55 +694,43 @@ export const useEditor = (
|
||||
clearTimeout(timers.current["editor:loaded"]);
|
||||
timers.current["editor:loaded"] = setTimeout(async () => {
|
||||
postMessage(EditorEvents.theme, theme);
|
||||
|
||||
commands.setInsets(
|
||||
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
|
||||
);
|
||||
await commands.setSettings();
|
||||
timers.current["editor:loaded"] = setTimeout(async () => {
|
||||
|
||||
if (!state.current.ready && (await onReady())) {
|
||||
state.current.ready = true;
|
||||
}
|
||||
overlay(false);
|
||||
|
||||
// TODO: Improve handling this on app launch from a link etc.
|
||||
const noteId = useTabStore.getState().getCurrentNoteId();
|
||||
async function restoreTabNote() {
|
||||
if (!noteId) return;
|
||||
const note = await db.notes.note(noteId);
|
||||
if (note) {
|
||||
loadNote({ item: note, forced: true });
|
||||
} else {
|
||||
console.log("Editor loaded with blank note");
|
||||
loadNote({ newNote: true });
|
||||
if (tabBarRef.current?.page === 1) {
|
||||
state.current.currentlyEditing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (noteId) {
|
||||
if (useSettingStore.getState().isAppLoading) {
|
||||
const unsub = useSettingStore.subscribe(async (s) => {
|
||||
if (!s.isAppLoading) {
|
||||
try {
|
||||
const note = await db.notes.note(noteId);
|
||||
if (note) {
|
||||
loadNote({ item: note, forced: true });
|
||||
} else {
|
||||
console.log("new note after app load");
|
||||
loadNote({ newNote: true });
|
||||
if (tabBarRef.current?.page === 1) {
|
||||
state.current.currentlyEditing = false;
|
||||
}
|
||||
}
|
||||
const unsub = useSettingStore.subscribe(async (state) => {
|
||||
if (!state.isAppLoading) {
|
||||
restoreTabNote();
|
||||
unsub();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const note = await db.notes.note(noteId);
|
||||
if (note) {
|
||||
loadNote({ item: note, forced: true });
|
||||
} else {
|
||||
console.log("new note");
|
||||
loadNote({ newNote: true });
|
||||
if (tabBarRef.current?.page === 1) {
|
||||
state.current.currentlyEditing = false;
|
||||
restoreTabNote();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [
|
||||
onReady,
|
||||
|
||||
@@ -166,3 +166,4 @@ export const eOnRefreshSearch = "612";
|
||||
|
||||
export const eOpenAppLockPasswordDialog = "613";
|
||||
export const eCloseAppLocKPasswordDailog = "614";
|
||||
export const eEditorTabFocused = "613";
|
||||
|
||||
26
apps/mobile/package-lock.json
generated
26
apps/mobile/package-lock.json
generated
@@ -2655,6 +2655,8 @@
|
||||
"mdi-react": "9.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-freeze": "^1.0.3",
|
||||
"tinycolor2": "1.6.0",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -43516,6 +43518,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom/node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-freeze": {
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
@@ -61595,7 +61619,9 @@
|
||||
"mdi-react": "9.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-freeze": "^1.0.3",
|
||||
"react-scripts": "^5.0.1",
|
||||
"tinycolor2": "1.6.0",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -149,7 +149,7 @@ export function useEditorController(update: () => void): EditorController {
|
||||
const contentChange = useCallback(
|
||||
(editor: Editor, ignoreEdit?: boolean) => {
|
||||
const currentSessionId = globalThis.sessionId;
|
||||
post(EventTypes.contentchange);
|
||||
post(EventTypes.contentchange, undefined, tab.id, tab.noteId);
|
||||
if (!editor) return;
|
||||
if (typeof timers.current.change === "number") {
|
||||
clearTimeout(timers.current?.change);
|
||||
@@ -198,14 +198,15 @@ export function useEditorController(update: () => void): EditorController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab.id === message.tabId) {
|
||||
logger(
|
||||
"info",
|
||||
"webview message for tab",
|
||||
message.type,
|
||||
tab.id,
|
||||
message.tabId,
|
||||
useTabStore.getState().currentTab
|
||||
tab.noteId,
|
||||
"Focused:",
|
||||
tab.id === useTabStore.getState().currentTab
|
||||
);
|
||||
}
|
||||
|
||||
const editor = editors[tab.id];
|
||||
switch (type) {
|
||||
|
||||
Reference in New Issue
Block a user