mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-20 18:48:28 +02:00
Desktop: Resolves #8931: Improve support for plugins in the Rich Text Editor (implement webviewApi.postMesage
) (#10158)
This commit is contained in:
parent
eb06ac673b
commit
eecad1aefc
@ -290,6 +290,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -270,6 +270,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/shouldPasteResources.
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useWebViewApi.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteEditor.js
|
||||
packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.js
|
||||
packages/app-desktop/gui/NoteEditor/commands/focusElementNoteBody.js
|
||||
|
@ -3,7 +3,7 @@ const leftPad = require('left-pad');
|
||||
export default function(context) {
|
||||
return {
|
||||
plugin: function(markdownIt, _options) {
|
||||
const pluginId = context.pluginId;
|
||||
const contentScriptId = context.contentScriptId;
|
||||
|
||||
const defaultRender = markdownIt.renderer.rules.fence || function(tokens, idx, options, env, self) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
@ -14,15 +14,30 @@ export default function(context) {
|
||||
if (token.info !== 'justtesting') return defaultRender(tokens, idx, options, env, self);
|
||||
|
||||
const postMessageWithResponseTest = `
|
||||
webviewApi.postMessage('${pluginId}', 'justtesting').then(function(response) {
|
||||
webviewApi.postMessage('${contentScriptId}', 'justtesting').then(function(response) {
|
||||
console.info('Got response in content script: ' + response);
|
||||
});
|
||||
return false;
|
||||
`;
|
||||
|
||||
// Rich text editor support:
|
||||
// The joplin-editable and joplin-source CSS classes mark the generated div
|
||||
// as a region that needs special processing when converting back to markdown.
|
||||
// This element helps Joplin reconstruct the original markdown.
|
||||
const richTextEditorMetadata = `
|
||||
<pre
|
||||
class="joplin-source"
|
||||
data-joplin-language="justtesting"
|
||||
data-joplin-source-open="\`\`\`justtesting\n"
|
||||
data-joplin-source-close="\`\`\`"
|
||||
>${markdownIt.utils.escapeHtml(token.content)}</pre>
|
||||
`;
|
||||
|
||||
return `
|
||||
<div class="just-testing">
|
||||
<p>JUST TESTING: <pre>${leftPad(token.content.trim(), 10, 'x')}</pre></p>
|
||||
<div class="just-testing joplin-editable">
|
||||
${richTextEditorMetadata}
|
||||
|
||||
<p>JUST TESTING: <pre>${markdownIt.utils.escapeHtml(leftPad(token.content.trim(), 10, 'x'))}</pre></p>
|
||||
<p><a href="#" onclick="${postMessageWithResponseTest.replace(/\n/g, ' ')}">Click to post a message "justtesting" to plugin and check the response in the console</a></p>
|
||||
</div>
|
||||
`;
|
||||
|
@ -31,6 +31,7 @@ import { Options as NoteStyleOptions } from '@joplin/renderer/noteStyle';
|
||||
import markupRenderOptions from '../../utils/markupRenderOptions';
|
||||
import { DropHandler } from '../../utils/useDropHandler';
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import useWebViewApi from './utils/useWebViewApi';
|
||||
const md5 = require('md5');
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
@ -348,6 +349,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useWebViewApi(editor);
|
||||
|
||||
useEffect(() => {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const backgroundColor = props.whiteBackgroundNoteRendering ? lightTheme.backgroundColor : theme.backgroundColor;
|
||||
|
@ -0,0 +1,72 @@
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import { useEffect } from 'react';
|
||||
import { Editor } from 'tinymce';
|
||||
|
||||
const useWebViewApi = (editor: Editor) => {
|
||||
useEffect(() => {
|
||||
if (!editor) return ()=>{};
|
||||
|
||||
const scriptElement = document.createElement('script');
|
||||
const channelId = `plugin-post-message-${Math.random()}`;
|
||||
scriptElement.appendChild(document.createTextNode(`
|
||||
window.webviewApi = {
|
||||
postMessage: (contentScriptId, message) => {
|
||||
const channelId = ${JSON.stringify(channelId)};
|
||||
const messageId = Math.random();
|
||||
window.parent.postMessage({
|
||||
channelId,
|
||||
messageId,
|
||||
contentScriptId,
|
||||
message,
|
||||
}, '*');
|
||||
|
||||
const waitForResponse = async () => {
|
||||
while (true) {
|
||||
const messageEvent = await new Promise(resolve => {
|
||||
window.addEventListener('message', event => {
|
||||
resolve(event);
|
||||
}, {once: true});
|
||||
});
|
||||
|
||||
if (messageEvent.source !== window.parent || messageEvent.data.messageId !== messageId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = messageEvent.data;
|
||||
return data.response;
|
||||
}
|
||||
};
|
||||
|
||||
return waitForResponse();
|
||||
},
|
||||
};
|
||||
`));
|
||||
const editorWindow = editor.getWin();
|
||||
editorWindow.document.head.appendChild(scriptElement);
|
||||
|
||||
const onMessageHandler = async (event: MessageEvent) => {
|
||||
if (event.source !== editorWindow || event.data.channelId !== channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentScriptId = event.data.contentScriptId;
|
||||
const pluginService = PluginService.instance();
|
||||
const plugin = pluginService.pluginById(
|
||||
pluginService.pluginIdByContentScriptId(contentScriptId),
|
||||
);
|
||||
const result = await plugin.emitContentScriptMessage(contentScriptId, event.data.message);
|
||||
editorWindow.postMessage({
|
||||
messageId: event.data.messageId,
|
||||
response: result,
|
||||
}, '*');
|
||||
};
|
||||
window.addEventListener('message', onMessageHandler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', onMessageHandler);
|
||||
scriptElement.remove();
|
||||
};
|
||||
}, [editor]);
|
||||
};
|
||||
|
||||
export default useWebViewApi;
|
Loading…
x
Reference in New Issue
Block a user