mobile: fix commands

This commit is contained in:
Ammar Ahmed
2025-01-31 15:01:10 +05:00
parent c286b1d595
commit 3b86e51c49
5 changed files with 253 additions and 182 deletions

View File

@@ -16,8 +16,8 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Note } from "@notesnook/core";
import { EVENTS } from "@notesnook/core";
import { EVENTS, Note } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect } from "react";
import { View } from "react-native";
@@ -33,12 +33,10 @@ import { editorController } from "../../../screens/editor/tiptap/utils";
import { eSendEvent, presentSheet } from "../../../services/event-manager";
import { eUnlockNote } from "../../../utils/events";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import { Pressable } from "../../ui/pressable";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { strings } from "@notesnook/intl";
const TabItemComponent = (props: {
tab: TabItem;

View File

@@ -18,9 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Note } from "@notesnook/core";
import type { Attachment } from "@notesnook/editor";
import type { ImageAttributes } from "@notesnook/editor";
import type { LinkAttributes } from "@notesnook/editor";
import type {
Attachment,
ImageAttributes,
LinkAttributes
} from "@notesnook/editor";
import { createRef, RefObject } from "react";
import { Platform } from "react-native";
import { EdgeInsets } from "react-native-safe-area-context";
@@ -37,9 +39,6 @@ async function call(webview: RefObject<WebView | undefined>, action?: Action) {
if (!webview.current || !action) return;
setImmediate(() => webview.current?.injectJavaScript(action.job));
const response = await getResponse(action.id);
// if (!response) {
// console.warn("webview job failed", action.id);
// }
return response ? response.value : response;
}
@@ -75,6 +74,17 @@ class Commands {
return call(this.ref, fn(job, name)) as Promise<T>;
}
async sendCommand<T>(command: string, ...args: any[]) {
return this.doAsync(
`response = globalThis.commands.${command}(${args
.map((arg) =>
typeof arg === "string" ? `"${arg}"` : JSON.stringify(arg)
)
.join(",")})`,
command
);
}
focus = async (tabId: string) => {
if (!this.ref.current) return;
@@ -84,107 +94,45 @@ class Commands {
setTimeout(async () => {
if (!this.ref) return;
textInput.current?.focus();
await this.doAsync(
locked
? `editorControllers["${tabId}"]?.focusPassInput();`
: `editors["${tabId}"]?.commands.focus()`,
"focus"
);
await this.sendCommand("focus", tabId, locked);
this.ref?.current?.requestFocus();
}, 1);
} else {
await sleep(400);
await this.doAsync(
locked
? `editorControllers["${tabId}"]?.focusPassInput();`
: `editors["${tabId}"]?.commands.focus()`,
"focus"
);
await this.sendCommand("focus", tabId, locked);
}
};
blur = async (tabId: string) =>
await this.doAsync(
`
const editor = editors["${tabId}"];
const editorTitle = editorTitles["${tabId}"];
typeof editor !== "undefined" && editor.commands.blur();
typeof editorTitle !== "undefined" && editorTitle.current && editorTitle.current.blur();
editorControllers["${tabId}"]?.blurPassInput();
`,
"blur"
);
blur = async (tabId: string) => this.sendCommand("blur", tabId);
clearContent = async (tabId: string) => {
this.previousSettings = null;
await this.doAsync(
`
const editor = editors["${tabId}"];
const editorController = editorControllers["${tabId}"];
const editorTitle = editorTitles["${tabId}"];
const statusBar = statusBars["${tabId}"];
if (typeof editor !== "undefined") {
editor.commands.blur();
editor.commands.clearContent(false);
}
typeof editorTitle !== "undefined" && editorTitle.current && editorTitle.current?.blur();
if (typeof editorController.content !== undefined) editorController.content.current = '';
editorController.onUpdate();
editorController.setTitle('');
if (typeof statusBar !== "undefined") {
statusBar.current.resetWords();
statusBar.current.set({date:"",saved:""});
}`,
"clearContent"
);
await this.sendCommand("clearContent", tabId);
};
setSessionId = async (id: string | null) =>
await this.doAsync(`globalThis.sessionId = "${id}";`);
await this.sendCommand("setSessionId", id);
setStatus = async (
date: string | undefined,
saved: string,
tabId: string
) => {
await this.doAsync(
`
const statusBar = statusBars["${tabId}"];
typeof statusBar !== "undefined" && statusBar.current.set({date:"${date}",saved:"${saved}"})`,
"setStatus"
this.sendCommand("setStatus", date, saved, tabId);
};
setPlaceholder = async (placeholder: string) => {};
setLoading = async (loading?: boolean, tabId?: string) => {
this.sendCommand(
"setLoading",
loading,
tabId === undefined ? useTabStore.getState().currentTab : tabId
);
};
setPlaceholder = async (placeholder: string) => {
// await this.doAsync(`
// const element = document.querySelector(".is-editor-empty");
// if (element) {
// element.setAttribute("data-placeholder","${placeholder}");
// }
// `);
};
setLoading = async (loading?: boolean, tabId?: string) => {
await this.doAsync(`
const editorController = editorControllers["${
tabId === undefined ? useTabStore.getState().currentTab : tabId
}"];
editorController.setLoading(${loading})
logger("info", editorController.setLoading);
`);
};
setInsets = async (insets: EdgeInsets) => {
await this.doAsync(`
if (typeof safeAreaController !== "undefined") {
safeAreaController.update(${JSON.stringify(insets)})
}
`);
this.sendCommand("setInsets", insets);
};
updateSettings = async (settings?: Partial<Settings>) => {
@@ -193,13 +141,7 @@ if (typeof statusBar !== "undefined") {
...this.previousSettings,
...settings
};
await this.doAsync(`
if (typeof globalThis.settingsController !== "undefined") {
globalThis.settingsController.update(${JSON.stringify(
this.previousSettings
)})
}
`);
this.sendCommand("updateSettings", settings);
};
setSettings = async (settings?: Partial<Settings>) => {
@@ -212,11 +154,7 @@ if (typeof statusBar !== "undefined") {
return;
}
}
await this.doAsync(`
if (typeof globalThis.settingsController !== "undefined") {
globalThis.settingsController.update(${JSON.stringify(settings)})
}
`);
this.sendCommand("setSettings", settings);
};
setTags = async (note: Note | null | undefined) => {
@@ -224,60 +162,23 @@ if (typeof statusBar !== "undefined") {
useTabStore.getState().forEachNoteTab(note.id, async (tab) => {
const tabId = tab.id;
const tags = await db.relations.to(note, "tag").resolve();
await this.doAsync(
`
const tags = editorTags["${tabId}"];
if (tags && tags.current) {
tags.current.setTags(${JSON.stringify(
tags.map((tag) => ({
title: tag.title,
alias: tag.title,
id: tag.id,
type: tag.type
}))
)});
}
`,
"setTags"
);
await this.sendCommand("setTags", tabId, tags);
});
};
clearTags = async (tabId: string) => {
await this.doAsync(
`
const tags = editorTags["${tabId}"];
logger("info", Object.keys(editorTags), typeof editorTags[0]);
if (tags && tags.current) {
tags.current.setTags([]);
}
`,
"clearTags"
);
await this.sendCommand("clearTags", tabId);
};
insertAttachment = async (attachment: Attachment, tabId: number) => {
await this.doAsync(
`const editor = editors["${tabId}"];
editor && editor.commands.insertAttachment(${JSON.stringify(attachment)})`
);
await this.sendCommand("insertAttachment", attachment, tabId);
};
setAttachmentProgress = async (
attachmentProgress: Partial<Attachment>,
tabId: number
) => {
await this.doAsync(
`const editor = editors["${tabId}"];
editor && editor.commands.updateAttachment(${JSON.stringify(
attachmentProgress
)}, {
preventUpdate: true,
query: (attachment) => {
return attachment.hash === "${attachmentProgress.hash}";
}
})`
);
await this.sendCommand("setAttachmentProgress", attachmentProgress, tabId);
};
insertImage = async (
@@ -286,50 +187,30 @@ editor && editor.commands.updateAttachment(${JSON.stringify(
},
tabId: number
) => {
await this.doAsync(
`const editor = editors["${tabId}"];
const image = toBlobURL("${image.dataurl}", "${image.hash}");
editor && editor.commands.insertImage({
...${JSON.stringify({
...image,
dataurl: undefined
})},
bloburl: image
})`
);
await this.sendCommand("insertImage", image, tabId);
};
handleBack = async () => {
return this.doAsync<boolean>(
'response = window.dispatchEvent(new Event("handleBackPress",{cancelable:true}));'
);
return this.sendCommand("handleBack");
};
keyboardShown = async (keyboardShown: boolean) => {
return this.doAsync(`globalThis['keyboardShown']=${keyboardShown};`);
return this.sendCommand("keyboardShown", keyboardShown);
};
getTableOfContents = async () => {
const tabId = useTabStore.getState().currentTab;
return this.doAsync(`
response = editorControllers["${tabId}"]?.getTableOfContents() || [];
`);
return this.sendCommand("getTableOfContents", tabId);
};
focusPassInput = async () => {
const tabId = useTabStore.getState().currentTab;
return this.doAsync(`
response = editorControllers["${tabId}"]?.focusPassInput() || [];
`);
return this.sendCommand("focusPassInput", tabId);
};
blurPassInput = async () => {
const tabId = useTabStore.getState().currentTab;
return this.doAsync(`
response = editorControllers["${tabId}"]?.blurPassInput() || [];
`);
return this.sendCommand("blurPassInput", tabId);
};
createInternalLink = async (
@@ -337,28 +218,17 @@ editor && editor.commands.insertImage({
resolverId: string
) => {
if (!resolverId) return;
return this.doAsync(`
if (globalThis.pendingResolvers["${resolverId}"]) {
globalThis.pendingResolvers["${resolverId}"](${JSON.stringify(
attributes
)});
}`);
return this.sendCommand("createInternalLink", attributes, resolverId);
};
dismissCreateInternalLinkRequest = async (resolverId: string) => {
if (!resolverId) return;
return this.doAsync(`
if (globalThis.pendingResolvers["${resolverId}"]) {
globalThis.pendingResolvers["${resolverId}"](undefined);
}
`);
return this.sendCommand("dismissCreateInternalLinkRequest", resolverId);
};
scrollIntoViewById = async (id: string) => {
const tabId = useTabStore.getState().currentTab;
return this.doAsync(`
response = editorControllers["${tabId}"]?.scrollIntoView("${id}") || [];
`);
return this.sendCommand("scrollIntoViewById", id, tabId);
};
}

View File

@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./utils/index";
import "./utils/commands";
global.Buffer = require("buffer").Buffer;
import { i18n } from "@lingui/core";
import "@notesnook/editor/styles/fonts.mobile.css";

View File

@@ -0,0 +1,200 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Attachment, ImageAttributes, LinkAttributes } from "@notesnook/editor";
import { Settings } from ".";
globalThis.commands = {
clearContent: (tabId: string) => {
try {
const editor = editors[tabId];
const editorController = editorControllers[tabId];
const editorTitle = editorTitles[tabId];
const statusBar = statusBars[tabId];
if (editor) {
editor?.commands.blur();
editor?.commands.clearContent(false);
}
if (editorController) {
editorController.content.current = "";
editorController.onUpdate();
editorController.setTitle("");
}
if (editorTitle?.current) {
editorTitle.current?.blur();
editorTitle.current.value = "";
}
if (statusBar) {
statusBar.current.resetWords();
statusBar.current.set({ date: "", saved: "" });
}
} catch (error) {
logger("error", "clearContent", error, (error as Error).stack);
}
},
focus: (tabId: string, locked: boolean) => {
const editorController = editorControllers[tabId];
if (locked) {
editorController?.focusPassInput();
} else {
editors[tabId]?.commands.focus();
}
},
blur: (tabId: string) => {
const editor = editors[tabId];
const editorTitle = editorTitles[tabId];
if (editor) editor.commands.blur();
if (editorTitle?.current) editorTitle.current.blur();
editorControllers[tabId]?.blurPassInput();
},
setSessionId: (id: string | undefined) => {
globalThis.sessionId = id;
},
setStatus: (date: string | undefined, saved: string, tabId: string) => {
const statusBar = statusBars[tabId];
if (statusBar?.current) {
statusBar.current.set({ date: date || "", saved });
}
},
setLoading: (loading?: boolean, tabId?: string) => {
if (tabId) {
const editorController = editorControllers[tabId];
editorController?.setLoading(loading || false);
logger("info", editorController?.setLoading);
}
},
setInsets: (insets: any) => {
if (typeof safeAreaController !== "undefined") {
safeAreaController.update(insets);
}
},
updateSettings: (settings?: Partial<Settings>) => {
if (typeof globalThis.settingsController !== "undefined") {
globalThis.settingsController.update(settings as Settings);
}
},
setSettings: (settings?: Partial<Settings>) => {
if (typeof globalThis.settingsController !== "undefined") {
globalThis.settingsController.update(settings as Settings);
}
},
setTags: async (tabId: string, tags: any) => {
const current = globalThis.editorTags[tabId];
if (current?.current) {
current.current.setTags(
tags.map((tag: any) => ({
title: tag.title,
alias: tag.title,
id: tag.id,
type: tag.type
}))
);
}
},
clearTags: (tabId: string) => {
const tags = editorTags[tabId];
if (tags?.current) {
tags.current.setTags([]);
}
},
insertAttachment: (attachment: Attachment, tabId: number) => {
const editor = editors[tabId];
if (editor) {
editor.commands.insertAttachment(attachment);
}
},
setAttachmentProgress: (
attachmentProgress: Partial<Attachment>,
tabId: number
) => {
const editor = editors[tabId];
if (editor) {
editor.commands.updateAttachment(attachmentProgress, {
preventUpdate: true,
query: (attachment) => attachment.hash === attachmentProgress.hash
});
}
},
insertImage: (
image: Omit<ImageAttributes, "bloburl"> & { dataurl: string },
tabId: number
) => {
const editor = editors[tabId];
if (editor) {
editor.commands.insertImage({
...image
});
}
},
handleBack: () => {
return window.dispatchEvent(
new Event("handleBackPress", { cancelable: true })
);
},
keyboardShown: (keyboardShown: boolean) => {
globalThis["keyboardShown"] = keyboardShown;
},
getTableOfContents: (tabId: string) => {
return editorControllers[tabId]?.getTableOfContents() || [];
},
focusPassInput: (tabId: string) => {
return editorControllers[tabId]?.focusPassInput() || [];
},
blurPassInput: (tabId: string) => {
return editorControllers[tabId]?.blurPassInput() || [];
},
createInternalLink: (attributes: LinkAttributes, resolverId: string) => {
if (globalThis.pendingResolvers[resolverId]) {
globalThis.pendingResolvers[resolverId](attributes);
}
},
dismissCreateInternalLinkRequest: (resolverId: string) => {
if (globalThis.pendingResolvers[resolverId]) {
globalThis.pendingResolvers[resolverId](undefined);
}
},
scrollIntoViewById: (id: string, tabId: string) => {
return editorControllers[tabId]?.scrollIntoView(id) || [];
}
};

View File

@@ -83,10 +83,12 @@ declare global {
var noHeader: boolean;
function toBlobURL(dataurl: string, id?: string): string | undefined;
var pendingResolvers: { [name: string]: (value: any) => void };
var commands: any;
/**
* Id of current session
*/
var sessionId: string;
var sessionId: string | undefined;
var tabStore: any;
/**