import Setting from '@joplin/lib/models/Setting' ; import shim from '@joplin/lib/shim' ; import { themeStyle } from '@joplin/lib/theme' ; const React = require ( 'react' ) ; const { forwardRef , useImperativeHandle , useEffect , useState , useCallback , useRef } = require ( 'react' ) ; const { WebView } = require ( 'react-native-webview' ) ; const { editorFont } = require ( '../global-style' ) ; export interface ChangeEvent { value : string ; } export interface UndoRedoDepthChangeEvent { undoDepth : number ; redoDepth : number ; } type ChangeEventHandler = ( event : ChangeEvent ) => void ; type UndoRedoDepthChangeHandler = ( event : UndoRedoDepthChangeEvent ) => void ; interface Props { themeId : number ; initialText : string ; style : any ; onChange : ChangeEventHandler ; onUndoRedoDepthChange : UndoRedoDepthChangeHandler ; } function fontFamilyFromSettings ( ) { const f = editorFont ( Setting . value ( 'style.editor.fontFamily' ) ) ; return [ f , 'sans-serif' ] . join ( ', ' ) ; } function useHtml ( css ) { const [ html , setHtml ] = useState ( '' ) ; useEffect ( ( ) => { setHtml ( `
				<!DOCTYPE html>
				<html>
					<head>
						<meta charset="UTF-8">
						<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
						<style>
							.cm-editor {
								height: 100%;
							}

							${ css }
						</style>
					</head>
					<body style="margin:0; height:100vh; width:100vh; width:100vw; min-width:100vw; box-sizing: border-box; padding: 10px;">
						<div class="CodeMirror" style="height:100%;" autocapitalize="on"></div>
					</body>
				</html>
			` ) ; } , [ css ] ) ; return html ; } function editorTheme ( themeId ) { return { ... themeStyle ( themeId ) , fontSize : 15 , fontFamily : fontFamilyFromSettings ( ) , } ; } function NoteEditor ( props , ref ) { const [ source , setSource ] = useState ( undefined ) ; const webviewRef = useRef ( null ) ; const injectedJavaScript = `
		function postMessage(name, data) {
			window.ReactNativeWebView.postMessage(JSON.stringify({
				data,
				name,	
			}));
		}

		function logMessage(...msg) {
			postMessage('onLog', { value: msg });
		}

		// This variable is not used within this script
		// but is called using "injectJavaScript" from
		// the wrapper component.
		let cm = null;

		try {
			${ shim . injectedJs ( 'codeMirrorBundle' ) };

			const parentElement = document.getElementsByClassName('CodeMirror')[0];
			const theme = ${ JSON . stringify ( editorTheme ( props . themeId ) ) };
			const initialText = ${ JSON . stringify ( props . initialText ) };

			cm = codeMirrorBundle.initCodeMirror(parentElement, initialText, theme);
		} catch (e) {
			window.ReactNativeWebView.postMessage("error:" + e.message + ": " + JSON.stringify(e))
		} finally {
			true;
		}
	` ; const html = useHtml ( '' ) ; useImperativeHandle ( ref , ( ) => { return { undo : function ( ) { webviewRef . current . injectJavaScript ( 'cm.undo(); true;' ) ; } , redo : function ( ) { webviewRef . current . injectJavaScript ( 'cm.redo(); true;' ) ; } , } ; } ) ; useEffect ( ( ) => { let cancelled = false ; async function createHtmlFile ( ) { const tempFile = `${ Setting . value ( 'resourceDir' ) }/NoteEditor.html` ; await shim . fsDriver ( ) . writeFile ( tempFile , html , 'utf8' ) ; if ( cancelled ) return ; setSource ( { uri : `file://${ tempFile }?r=${ Math . round ( Math . random ( ) * 100000000 ) }` , baseUrl : `file://${ Setting . value ( 'resourceDir' ) }/` , } ) ; } void createHtmlFile ( ) ; return ( ) => { cancelled = true ; } ; } , [ html ] ) ; const onMessage = useCallback ( ( event ) => { const data = event . nativeEvent . data ; if ( data . indexOf ( 'error:' ) === 0 ) { console . error ( 'CodeMirror:' , data ) ; return ; } const msg = JSON . parse ( data ) ; const handlers = { onLog : ( event ) => { console . info ( 'CodeMirror:' , ... event . value ) ; } , onChange : ( event ) => { props . onChange ( event ) ; } , onUndoRedoDepthChange : ( event ) => { console . info ( 'onUndoRedoDepthChange' , event ) ; props . onUndoRedoDepthChange ( event ) ; } , } ; if ( handlers [ msg . name ] ) { handlers [ msg . name ] ( msg . data ) ; } else { console . info ( 'Unsupported CodeMirror message:' , msg ) ; } } , [ props . onChange ] ) ; const onError = useCallback ( ( ) => { console . error ( 'NoteEditor: webview error' ) ; } ) ; return < WebView style = { props . style } ref = { webviewRef } useWebKit = { true } source = { source } setSupportMultipleWindows = { true } allowingReadAccessToURL = { `file://${ Setting . value ( 'resourceDir' ) }` } originWhitelist = { [ 'file://*' , './*' , 'http://*' , 'https://*' ] } allowFileAccess = { true } injectedJavaScript = { injectedJavaScript } onMessage = { onMessage } onError = { onError } / > ; } export default forwardRef ( NoteEditor ) ;