1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Desktop: WYSIWYG: Getting links to work

This commit is contained in:
Laurent Cozic 2020-03-27 18:26:52 +00:00
parent c3360d6c48
commit 11d8466db1
6 changed files with 174 additions and 7 deletions

View File

@ -24,6 +24,7 @@ const Resource = require('lib/models/Resource.js');
const { shim } = require('lib/shim');
const TemplateUtils = require('lib/TemplateUtils');
const { bridge } = require('electron').remote.require('./bridge');
const { urlDecode } = require('lib/string-utils');
interface NoteTextProps {
style: any,
@ -521,6 +522,132 @@ function NoteText2(props:NoteTextProps) {
});
}, [formNote, handleProvisionalFlag]);
const onMessage = useCallback((event:any) => {
const msg = event.name;
const args = event.args;
console.info('onMessage', msg, args);
if (msg === 'setMarkerCount') {
// const ls = Object.assign({}, this.state.localSearch);
// ls.resultCount = arg0;
// ls.searching = false;
// this.setState({ localSearch: ls });
} else if (msg.indexOf('markForDownload:') === 0) {
// const s = msg.split(':');
// if (s.length < 2) throw new Error(`Invalid message: ${msg}`);
// ResourceFetcher.instance().markForDownload(s[1]);
} else if (msg === 'percentScroll') {
// this.ignoreNextEditorScroll_ = true;
// this.setEditorPercentScroll(arg0);
} else if (msg === 'contextMenu') {
// const itemType = arg0 && arg0.type;
// const menu = new Menu();
// if (itemType === 'image' || itemType === 'resource') {
// const resource = await Resource.load(arg0.resourceId);
// const resourcePath = Resource.fullPath(resource);
// menu.append(
// new MenuItem({
// label: _('Open...'),
// click: async () => {
// const ok = bridge().openExternal(`file://${resourcePath}`);
// if (!ok) bridge().showErrorMessageBox(_('This file could not be opened: %s', resourcePath));
// },
// })
// );
// menu.append(
// new MenuItem({
// label: _('Save as...'),
// click: async () => {
// const filePath = bridge().showSaveDialog({
// defaultPath: resource.filename ? resource.filename : resource.title,
// });
// if (!filePath) return;
// await fs.copy(resourcePath, filePath);
// },
// })
// );
// menu.append(
// new MenuItem({
// label: _('Copy path to clipboard'),
// click: async () => {
// clipboard.writeText(toSystemSlashes(resourcePath));
// },
// })
// );
// } else if (itemType === 'text') {
// menu.append(
// new MenuItem({
// label: _('Copy'),
// click: async () => {
// clipboard.writeText(arg0.textToCopy);
// },
// })
// );
// } else if (itemType === 'link') {
// menu.append(
// new MenuItem({
// label: _('Copy Link Address'),
// click: async () => {
// clipboard.writeText(arg0.textToCopy);
// },
// })
// );
// } else {
// reg.logger().error(`Unhandled item type: ${itemType}`);
// return;
// }
// menu.popup(bridge().window());
} else if (msg.indexOf('joplin://') === 0) {
// const resourceUrlInfo = urlUtils.parseResourceUrl(msg);
// const itemId = resourceUrlInfo.itemId;
// const item = await BaseItem.loadItemById(itemId);
// if (!item) throw new Error(`No item with ID ${itemId}`);
// if (item.type_ === BaseModel.TYPE_RESOURCE) {
// const localState = await Resource.localState(item);
// if (localState.fetch_status !== Resource.FETCH_STATUS_DONE || !!item.encryption_blob_encrypted) {
// if (localState.fetch_status === Resource.FETCH_STATUS_ERROR) {
// bridge().showErrorMessageBox(`${_('There was an error downloading this attachment:')}\n\n${localState.fetch_error}`);
// } else {
// bridge().showErrorMessageBox(_('This attachment is not downloaded or not decrypted yet'));
// }
// return;
// }
// const filePath = Resource.fullPath(item);
// bridge().openItem(filePath);
// } else if (item.type_ === BaseModel.TYPE_NOTE) {
// this.props.dispatch({
// type: 'FOLDER_AND_NOTE_SELECT',
// folderId: item.parent_id,
// noteId: item.id,
// hash: resourceUrlInfo.hash,
// historyAction: 'goto',
// });
// } else {
// throw new Error(`Unsupported item type: ${item.type_}`);
// }
} else if (msg.indexOf('#') === 0) {
// This is an internal anchor, which is handled by the WebView so skip this case
} else if (msg === 'openExternal') {
if (args.url.indexOf('file://') === 0) {
// When using the file:// protocol, openExternal doesn't work (does nothing) with URL-encoded paths
bridge().openExternal(urlDecode(args.url));
} else {
bridge().openExternal(args.url);
}
} else {
bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
}
}, []);
const introductionPostLinkClick = useCallback(() => {
bridge().openExternal('https://www.patreon.com/posts/34246624');
}, []);
@ -541,6 +668,7 @@ function NoteText2(props:NoteTextProps) {
style: styles.tinyMCE,
onChange: onBodyChange,
onWillChange: onBodyWillChange,
onMessage: onMessage,
defaultEditorState: defaultEditorState,
markupToHtml: markupToHtml,
allAssets: allAssets,

View File

@ -12,6 +12,7 @@ interface TinyMCEProps {
style: any,
onChange(event: OnChangeEvent): void,
onWillChange(event:any): void,
onMessage(event:any): void,
defaultEditorState: DefaultEditorState,
markupToHtml: Function,
allAssets: Function,
@ -122,11 +123,34 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
};
const onEditorContentClick = useCallback((event:any) => {
if (event.target && event.target.nodeName === 'INPUT' && event.target.getAttribute('type') === 'checkbox') {
const nodeName = event.target ? event.target.nodeName : '';
if (nodeName === 'INPUT' && event.target.getAttribute('type') === 'checkbox') {
editor.fire('joplinChange');
dispatchDidUpdate(editor);
}
}, [editor]);
if (nodeName === 'A' && event.ctrlKey) {
const href = event.target.getAttribute('href');
if (href.indexOf('#') === 0) {
const anchorName = href.substr(1);
const anchor = editor.getDoc().getElementById(anchorName);
if (anchor) {
anchor.scrollIntoView();
} else {
reg.logger().warn('TinyMce: could not find anchor with ID ', anchorName);
}
} else {
props.onMessage({
name: 'openExternal',
args: {
url: href,
},
});
}
}
}, [editor, props.onMessage]);
useImperativeHandle(ref, () => {
return {
@ -368,9 +392,9 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
.filter((a:any) => a.mime === 'text/css' && !loadedAssetFiles_.includes(a.path))
.map((a:any) => a.path));
const jsFiles = pluginAssets
const jsFiles = ['gui/editors/TinyMCE/content_script.js'].concat(pluginAssets
.filter((a:any) => a.mime === 'application/javascript' && !loadedAssetFiles_.includes(a.path))
.map((a:any) => a.path);
.map((a:any) => a.path));
for (const cssFile of cssFiles) loadedAssetFiles_.push(cssFile);
for (const jsFile of jsFiles) loadedAssetFiles_.push(jsFile);
@ -405,6 +429,9 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
checkbox: {
renderingType: 2,
},
link_open: {
linkRenderingType: 2,
},
},
});
if (cancelled) return;

View File

@ -5,6 +5,8 @@ const urlUtils = require('../../urlUtils.js');
const { getClassNameForMimeType } = require('font-awesome-filetypes');
function installRule(markdownIt, mdOptions, ruleOptions) {
const pluginOptions = { linkRenderingType: 1, ...ruleOptions.plugins['link_open'] };
markdownIt.renderer.rules.link_open = function(tokens, idx) {
const token = tokens[idx];
let href = utils.getAttr(token.attrs, 'href');
@ -58,7 +60,7 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
let js = `${ruleOptions.postMessageSyntax}(${JSON.stringify(href)}); return false;`;
if (hrefAttr.indexOf('#') === 0 && href.indexOf('#') === 0) js = ''; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place
if (ruleOptions.plainResourceRendering) {
if (ruleOptions.plainResourceRendering || pluginOptions.linkRenderingType === 2) {
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${hrefAttr}' type='${htmlentities(mime)}'>`;
} else {
return `<a data-from-md ${resourceIdAttr} title='${htmlentities(title)}' href='${hrefAttr}' onclick='${js}' type='${htmlentities(mime)}'>${icon}`;

View File

@ -4,4 +4,5 @@ module.exports = {
HtmlToHtml: require('./HtmlToHtml'),
setupLinkify: require('./MdToHtml/setupLinkify'),
assetsToHeaders: require('./assetsToHeaders'),
utils: require('./utils'),
};

View File

@ -271,9 +271,13 @@ module.exports = function(theme) {
display: none;
}
/* =============================================== */
/* For TinyMCE */
/* =============================================== */
.mce-content-body {
padding: 5px 10px 10px 10px;
/* Note: we give a bit more padding at the bottom, to allow scrolling past the end of the document */
padding: 5px 10px 10em 10px;
}
.mce-content-body code {
@ -292,6 +296,10 @@ module.exports = function(theme) {
opacity: 0.5;
}
/* =============================================== */
/* For TinyMCE */
/* =============================================== */
@media print {
body {
height: auto !important;

View File

@ -96,7 +96,8 @@
"ElectronClient/pluginAssets",
"Modules/TinyMCE/JoplinLists/dist",
"Modules/TinyMCE/JoplinLists/lib",
"Modules/TinyMCE/JoplinLists/scratch"
"Modules/TinyMCE/JoplinLists/scratch",
"CliClient/tests/tmp"
],
"path": "."
}