mobile: fix numerous issues with tabs

This commit is contained in:
Ammar Ahmed
2023-12-25 10:00:02 +05:00
committed by Abdullah Atta
parent 6b6ee76c39
commit ad338aeefc
5 changed files with 153 additions and 130 deletions

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -166,3 +166,4 @@ export const eOnRefreshSearch = "612";
export const eOpenAppLockPasswordDialog = "613";
export const eCloseAppLocKPasswordDailog = "614";
export const eEditorTabFocused = "613";

View File

@@ -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": {

View File

@@ -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) {