Files
notesnook/apps/mobile/app/share/editor.tsx
Abdullah Atta 3de225f7c9 mobile: upgrade react native 0.82
this commit also bump app version to 3.3.10-beta.0
2025-11-27 11:32:50 +05:00

298 lines
7.3 KiB
TypeScript

/*
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 { Note, NoteContent } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, {
RefObject,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState
} from "react";
import { Linking, Platform, TextInput, View, ViewStyle } from "react-native";
import { WebView } from "react-native-webview";
import {
ShouldStartLoadRequest,
WebViewMessageEvent
} from "react-native-webview/lib/WebViewTypes";
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
import { eOnLoadNote } from "../utils/events";
import { defaultBorderRadius } from "../utils/size";
const EditorMobileSourceUrl =
Platform.OS === "android"
? "file:///android_asset/plaineditor.html"
: "extension.bundle/plaineditor.html";
/**
* Replace this with dev url when debugging or working on the editor mobile repo.
* The url should be something like this: http://192.168.100.126:3000/index.html
*/
export const EDITOR_URI = __DEV__
? EditorMobileSourceUrl
: EditorMobileSourceUrl;
export async function post(
ref: RefObject<any>,
type: string,
value: any = null
) {
const message = {
type,
value
};
setImmediate(() => ref.current?.postMessage(JSON.stringify(message)));
}
const useEditor = () => {
const ref = useRef<WebView>(null);
const { colors } = useThemeColors("editor");
const currentNote = useRef<
Note & {
content: NoteContent<false>;
}
>(undefined);
const postMessage = useCallback(
async (type: string, data: any) => post(ref, type, data),
[]
);
const loadNote = useCallback(
(
note: Note & {
content: NoteContent<false>;
}
) => {
postMessage("html", note.content.data);
currentNote.current = note;
},
[postMessage]
);
useEffect(() => {
eSubscribeEvent(eOnLoadNote + "shareEditor", loadNote);
return () => {
eUnSubscribeEvent(eOnLoadNote + "shareEditor", loadNote);
};
}, [loadNote]);
const onLoad = () => {
setTimeout(() => {
postMessage(
"theme",
`
body * {
color: ${colors.primary.paragraph};
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: ${colors.primary.heading};
}
a {
color: ${colors.primary.accent};
}
`
);
}, 1);
};
return { ref, onLoad, currentNote };
};
const useEditorEvents = (
editor: ReturnType<typeof useEditor>,
onChange: (data: string) => void
) => {
const onMessage = (event: WebViewMessageEvent) => {
const data = event.nativeEvent.data;
const editorMessage = JSON.parse(data);
switch (editorMessage.type) {
case "content":
onChange(editorMessage.value);
break;
}
};
return onMessage;
};
const onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
if (request.url.includes("https")) {
if (Platform.OS === "ios" && !request.isTopFrame) return true;
Linking.openURL(request.url);
return false;
} else {
return true;
}
};
const style: ViewStyle = {
height: "100%",
maxHeight: "100%",
width: "100%",
alignSelf: "center",
backgroundColor: "transparent"
};
export type EditorRef = {
focus: () => void;
};
export const Editor = ({
onChange,
onLoad,
editorRef
}: {
onChange: (data: string) => void;
onLoad: () => void;
editorRef: RefObject<EditorRef | null>;
}) => {
const { colors } = useThemeColors();
const editor = useEditor();
const inputRef = useRef<TextInput>(null);
const onMessage = useEditorEvents(editor, onChange);
const [loading, setLoading] = useState(true);
useLayoutEffect(() => {
onLoad?.();
}, [onLoad]);
useEffect(() => {
if (editorRef) {
editorRef.current = {
focus: () => {
setTimeout(() => {
inputRef.current?.focus();
editor.ref.current?.injectJavaScript(`(() => {
const editor = document.getElementById('editor');
if (editor) {
editor.focus();
}
})();`);
editor.ref?.current?.requestFocus();
});
}
};
}
}, []);
return (
<View
style={{
flex: 1
}}
>
<TextInput
ref={inputRef}
style={{
width: 1,
height: 1,
position: "absolute",
zIndex: -1
}}
/>
<WebView
ref={editor.ref}
onLoad={() => {
editor.onLoad();
setTimeout(() => {
setLoading(false);
onLoad?.();
}, 1);
}}
nestedScrollEnabled
javaScriptEnabled={true}
setSupportMultipleWindows={false}
overScrollMode="never"
scrollEnabled={Platform.OS === "ios"}
keyboardDisplayRequiresUserAction={false}
cacheMode="LOAD_DEFAULT"
cacheEnabled={true}
domStorageEnabled={true}
bounces={false}
setBuiltInZoomControls={false}
setDisplayZoomControls={false}
allowFileAccess={true}
scalesPageToFit={true}
hideKeyboardAccessoryView={false}
allowsFullscreenVideo={true}
allowFileAccessFromFileURLs={true}
allowUniversalAccessFromFileURLs={true}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
originWhitelist={["*"]}
source={{
uri: EDITOR_URI
}}
style={style}
autoManageStatusBarEnabled={false}
onMessage={onMessage || undefined}
/>
{loading ? (
<View
style={{
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: colors.primary.background,
alignItems: "flex-start",
zIndex: 999,
paddingHorizontal: 12
}}
>
<View
style={{
height: 16,
width: "100%",
backgroundColor: colors.secondary.background,
borderRadius: defaultBorderRadius,
marginTop: 10
}}
/>
<View
style={{
height: 16,
width: "100%",
backgroundColor: colors.secondary.background,
borderRadius: defaultBorderRadius,
marginTop: 10
}}
/>
<View
style={{
height: 16,
width: "60%",
backgroundColor: colors.secondary.background,
borderRadius: defaultBorderRadius,
marginTop: 10
}}
/>
</View>
) : null}
</View>
);
};