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:
parent
c3360d6c48
commit
11d8466db1
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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}`;
|
||||
|
@ -4,4 +4,5 @@ module.exports = {
|
||||
HtmlToHtml: require('./HtmlToHtml'),
|
||||
setupLinkify: require('./MdToHtml/setupLinkify'),
|
||||
assetsToHeaders: require('./assetsToHeaders'),
|
||||
utils: require('./utils'),
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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": "."
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user