2022-08-31 06:33:37 +05:00
|
|
|
/*
|
|
|
|
|
This file is part of the Notesnook project (https://notesnook.com/)
|
|
|
|
|
|
2023-01-16 13:44:52 +05:00
|
|
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
2022-08-31 06:33:37 +05:00
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
|
*/
|
2022-08-30 16:13:11 +05:00
|
|
|
|
2022-08-30 11:05:10 +05:00
|
|
|
import { Editor } from "@notesnook/editor";
|
2022-07-04 16:14:26 +05:00
|
|
|
import {
|
|
|
|
|
MutableRefObject,
|
|
|
|
|
useCallback,
|
|
|
|
|
useEffect,
|
|
|
|
|
useRef,
|
2022-08-30 11:05:10 +05:00
|
|
|
useState
|
2022-07-04 16:14:26 +05:00
|
|
|
} from "react";
|
2022-06-23 19:14:55 +05:00
|
|
|
import { useEditorThemeStore } from "../state/theme";
|
2022-07-20 23:15:04 +05:00
|
|
|
import { EventTypes, isReactNative, post } from "../utils";
|
2022-06-23 19:14:55 +05:00
|
|
|
|
2022-07-04 16:14:26 +05:00
|
|
|
type Attachment = {
|
|
|
|
|
hash: string;
|
|
|
|
|
filename: string;
|
|
|
|
|
type: string;
|
|
|
|
|
size: number;
|
|
|
|
|
};
|
|
|
|
|
|
2022-06-23 19:14:55 +05:00
|
|
|
export type Selection = {
|
|
|
|
|
[name: string]: {
|
|
|
|
|
text?: string;
|
|
|
|
|
length?: number;
|
2022-08-30 11:05:10 +05:00
|
|
|
attributes?: Record<string, unknown>;
|
2022-06-23 19:14:55 +05:00
|
|
|
type?: "mark" | "node";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type Timers = {
|
|
|
|
|
selectionChange: NodeJS.Timeout | null;
|
|
|
|
|
change: NodeJS.Timeout | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type EditorController = {
|
|
|
|
|
selectionChange: (editor: Editor) => void;
|
|
|
|
|
titleChange: (title: string) => void;
|
|
|
|
|
contentChange: (editor: Editor) => void;
|
|
|
|
|
scroll: (event: React.UIEvent<HTMLDivElement, UIEvent>) => void;
|
|
|
|
|
title: string;
|
|
|
|
|
setTitle: React.Dispatch<React.SetStateAction<string>>;
|
2022-06-27 18:39:59 +05:00
|
|
|
openFilePicker: (type: "image" | "file" | "camera") => void;
|
2022-06-23 19:14:55 +05:00
|
|
|
downloadAttachment: (attachment: Attachment) => void;
|
2023-03-17 14:43:53 +05:00
|
|
|
previewAttachment: (attachment: Attachment) => void;
|
2022-07-04 16:14:26 +05:00
|
|
|
content: MutableRefObject<string | null>;
|
2022-07-07 18:40:21 +05:00
|
|
|
onUpdate: () => void;
|
2022-07-08 23:52:45 +05:00
|
|
|
titlePlaceholder: string;
|
2022-09-16 12:10:06 +05:00
|
|
|
openLink: (url: string) => boolean;
|
2022-07-08 23:52:45 +05:00
|
|
|
setTitlePlaceholder: React.Dispatch<React.SetStateAction<string>>;
|
2022-06-23 19:14:55 +05:00
|
|
|
};
|
|
|
|
|
|
2022-07-25 15:44:53 +05:00
|
|
|
export function useEditorController(update: () => void): EditorController {
|
2022-06-23 19:14:55 +05:00
|
|
|
const [title, setTitle] = useState("");
|
2022-07-08 23:52:45 +05:00
|
|
|
const [titlePlaceholder, setTitlePlaceholder] = useState("Note title");
|
2022-07-04 16:14:26 +05:00
|
|
|
const htmlContentRef = useRef<string | null>(null);
|
2022-06-23 19:14:55 +05:00
|
|
|
const timers = useRef<Timers>({
|
|
|
|
|
selectionChange: null,
|
2022-08-30 11:05:10 +05:00
|
|
|
change: null
|
2022-06-23 19:14:55 +05:00
|
|
|
});
|
|
|
|
|
|
2022-08-30 11:05:10 +05:00
|
|
|
const selectionChange = useCallback((_editor: Editor) => {}, []);
|
2022-06-23 19:14:55 +05:00
|
|
|
|
2022-07-25 15:44:53 +05:00
|
|
|
const titleChange = useCallback((title: string) => {
|
2022-12-05 17:54:19 +05:00
|
|
|
post(EventTypes.contentchange);
|
2022-06-23 19:14:55 +05:00
|
|
|
post(EventTypes.title, title);
|
2022-07-25 15:44:53 +05:00
|
|
|
}, []);
|
2022-06-23 19:14:55 +05:00
|
|
|
|
2022-07-20 23:15:04 +05:00
|
|
|
const contentChange = useCallback((editor: Editor) => {
|
2023-04-05 02:05:52 +05:00
|
|
|
const currentSessionId = globalThis.sessionId;
|
2022-12-05 17:54:19 +05:00
|
|
|
post(EventTypes.contentchange);
|
2022-07-20 23:15:04 +05:00
|
|
|
if (!editor) return;
|
|
|
|
|
if (typeof timers.current.change === "number") {
|
|
|
|
|
clearTimeout(timers.current?.change);
|
|
|
|
|
}
|
|
|
|
|
timers.current.change = setTimeout(() => {
|
|
|
|
|
htmlContentRef.current = editor.getHTML();
|
2023-04-05 02:05:52 +05:00
|
|
|
post(EventTypes.content, htmlContentRef.current, currentSessionId);
|
2022-07-20 23:15:04 +05:00
|
|
|
}, 300);
|
|
|
|
|
}, []);
|
2022-06-23 19:14:55 +05:00
|
|
|
|
|
|
|
|
const scroll = useCallback(
|
2022-08-30 11:05:10 +05:00
|
|
|
(_event: React.UIEvent<HTMLDivElement, UIEvent>) => {},
|
2022-06-23 19:14:55 +05:00
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
|
2022-07-25 15:44:53 +05:00
|
|
|
const onUpdate = useCallback(() => {
|
2022-07-08 18:33:54 +05:00
|
|
|
update();
|
2022-07-25 15:44:53 +05:00
|
|
|
}, [update]);
|
2022-07-07 18:40:21 +05:00
|
|
|
|
2022-06-23 19:14:55 +05:00
|
|
|
const onMessage = useCallback(
|
2022-08-30 11:05:10 +05:00
|
|
|
(event: Event & { data?: string }) => {
|
|
|
|
|
if (event?.data?.[0] !== "{") return;
|
|
|
|
|
|
|
|
|
|
const message = JSON.parse(event.data);
|
|
|
|
|
const type = message.type;
|
|
|
|
|
const value = message.value;
|
2022-06-23 19:14:55 +05:00
|
|
|
global.sessionId = message.sessionId;
|
|
|
|
|
switch (type) {
|
2022-09-20 18:33:55 +05:00
|
|
|
case "native:updatehtml": {
|
|
|
|
|
htmlContentRef.current = value;
|
|
|
|
|
if (!editor) break;
|
|
|
|
|
const { from, to } = editor.state.selection;
|
2023-02-23 16:34:46 +05:00
|
|
|
editor?.commands.setContent(htmlContentRef.current, false, {
|
2023-04-06 01:40:55 +05:00
|
|
|
preserveWhitespace: true
|
2023-02-23 16:34:46 +05:00
|
|
|
});
|
2022-09-20 18:33:55 +05:00
|
|
|
editor.commands.setTextSelection({
|
|
|
|
|
from,
|
|
|
|
|
to
|
|
|
|
|
});
|
2023-04-01 16:16:52 +05:00
|
|
|
statusBar.current?.updateWords();
|
2022-09-20 18:33:55 +05:00
|
|
|
break;
|
|
|
|
|
}
|
2022-06-23 19:14:55 +05:00
|
|
|
case "native:html":
|
2022-07-04 16:14:26 +05:00
|
|
|
htmlContentRef.current = value;
|
2022-07-08 18:33:54 +05:00
|
|
|
update();
|
2023-04-01 16:16:52 +05:00
|
|
|
statusBar.current?.updateWords();
|
2022-06-23 19:14:55 +05:00
|
|
|
break;
|
|
|
|
|
case "native:theme":
|
|
|
|
|
useEditorThemeStore.getState().setColors(message.value);
|
|
|
|
|
break;
|
|
|
|
|
case "native:title":
|
|
|
|
|
setTitle(value);
|
|
|
|
|
break;
|
|
|
|
|
case "native:titleplaceholder":
|
2022-07-08 23:52:45 +05:00
|
|
|
setTitlePlaceholder(value);
|
2022-06-23 19:14:55 +05:00
|
|
|
break;
|
|
|
|
|
case "native:status":
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
post(type); // Notify that message was delivered successfully.
|
|
|
|
|
},
|
2022-07-07 18:40:21 +05:00
|
|
|
[update]
|
2022-06-23 19:14:55 +05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!isReactNative()) return; // Subscribe only in react native webview.
|
2022-08-30 11:05:10 +05:00
|
|
|
const isSafari = navigator.vendor.match(/apple/i);
|
|
|
|
|
let root: Document | Window = document;
|
2022-06-23 19:14:55 +05:00
|
|
|
if (isSafari) {
|
|
|
|
|
root = window;
|
|
|
|
|
}
|
|
|
|
|
root.addEventListener("message", onMessage);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
root.removeEventListener("message", onMessage);
|
|
|
|
|
};
|
|
|
|
|
}, [onMessage]);
|
|
|
|
|
|
|
|
|
|
const openFilePicker = useCallback((type) => {
|
|
|
|
|
post(EventTypes.filepicker, type);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const downloadAttachment = useCallback((attachment: Attachment) => {
|
|
|
|
|
post(EventTypes.download, attachment);
|
|
|
|
|
}, []);
|
2023-03-17 14:43:53 +05:00
|
|
|
const previewAttachment = useCallback((attachment: Attachment) => {
|
|
|
|
|
post(EventTypes.previewAttachment, attachment);
|
|
|
|
|
}, []);
|
2022-09-16 12:10:06 +05:00
|
|
|
const openLink = useCallback((url: string) => {
|
|
|
|
|
post(EventTypes.link, url);
|
|
|
|
|
return true;
|
|
|
|
|
}, []);
|
|
|
|
|
|
2022-06-23 19:14:55 +05:00
|
|
|
return {
|
|
|
|
|
contentChange,
|
|
|
|
|
selectionChange,
|
|
|
|
|
titleChange,
|
|
|
|
|
scroll,
|
|
|
|
|
title,
|
|
|
|
|
setTitle,
|
2022-07-08 23:52:45 +05:00
|
|
|
titlePlaceholder,
|
|
|
|
|
setTitlePlaceholder,
|
2022-06-23 19:14:55 +05:00
|
|
|
openFilePicker,
|
|
|
|
|
downloadAttachment,
|
2023-03-17 14:43:53 +05:00
|
|
|
previewAttachment,
|
2022-07-04 16:14:26 +05:00
|
|
|
content: htmlContentRef,
|
2022-09-16 12:10:06 +05:00
|
|
|
openLink,
|
2022-08-30 11:05:10 +05:00
|
|
|
onUpdate: onUpdate
|
2022-06-23 19:14:55 +05:00
|
|
|
};
|
|
|
|
|
}
|