Skip to content

Commit e85767b

Browse files
nathan-castlehowRokt33r
authored andcommitted
feat: Added Context Menu for markdown preview mode and copy url when hyperlink
1 parent c83e5cc commit e85767b

File tree

4 files changed

+81
-31
lines changed

4 files changed

+81
-31
lines changed

browser/components/CodeEditor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import styles from '../components/CodeEditor.styl'
2020
const { ipcRenderer, remote, clipboard } = require('electron')
2121
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
2222
const spellcheck = require('browser/lib/spellcheck')
23-
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
23+
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu
2424
import TurndownService from 'turndown'
2525
import {languageMaps} from '../lib/CMLanguageList'
2626
import snippetManager from '../lib/SnippetManager'

browser/components/MarkdownPreview.js

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,20 @@ import mdurl from 'mdurl'
1818
import exportNote from 'browser/main/lib/dataApi/exportNote'
1919
import { escapeHtmlCharacters } from 'browser/lib/utils'
2020
import yaml from 'js-yaml'
21-
import context from 'browser/lib/context'
22-
import i18n from 'browser/lib/i18n'
23-
import fs from 'fs'
2421
import { render } from 'react-dom'
2522
import Carousel from 'react-image-carousel'
2623
import ConfigManager from '../main/lib/ConfigManager'
2724

2825
const { remote, shell } = require('electron')
2926
const attachmentManagement = require('../main/lib/dataApi/attachmentManagement')
27+
const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu
3028

3129
const { app } = remote
3230
const path = require('path')
3331
const fileUrl = require('file-url')
3432

3533
const dialog = remote.dialog
3634

37-
const uri2path = require('file-uri-to-path')
38-
3935
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
4036
const appPath = fileUrl(
4137
process.env.NODE_ENV === 'production' ? app.getAppPath() : path.resolve()
@@ -250,30 +246,9 @@ export default class MarkdownPreview extends React.Component {
250246
}
251247

252248
handleContextMenu (event) {
253-
// If a contextMenu handler was passed to us, use it instead of the self-defined one -> return
254-
if (_.isFunction(this.props.onContextMenu)) {
255-
this.props.onContextMenu(event)
256-
return
257-
}
258-
// No contextMenu was passed to us -> execute our own link-opener
259-
if (event.target.tagName.toLowerCase() === 'a' && event.target.getAttribute('href')) {
260-
const href = event.target.href
261-
const isLocalFile = href.startsWith('file:')
262-
if (isLocalFile) {
263-
const absPath = uri2path(href)
264-
try {
265-
if (fs.lstatSync(absPath).isFile()) {
266-
context.popup([
267-
{
268-
label: i18n.__('Show in explorer'),
269-
click: (e) => shell.showItemInFolder(absPath)
270-
}
271-
])
272-
}
273-
} catch (e) {
274-
console.log('Error while evaluating if the file is locally available', e)
275-
}
276-
}
249+
const menu = buildMarkdownPreviewContextMenu(this, event)
250+
if (menu != null) {
251+
setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
277252
}
278253
}
279254

browser/lib/contextMenuBuilder.js

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import i18n from 'browser/lib/i18n'
2+
import fs from 'fs'
3+
14
const {remote} = require('electron')
25
const {Menu} = remote.require('electron')
6+
const {clipboard} = remote.require('electron')
7+
const {shell} = remote.require('electron')
38
const spellcheck = require('./spellcheck')
9+
const uri2path = require('file-uri-to-path')
410

511
/**
612
* Creates the context menu that is shown when there is a right click in the editor of a (not-snippet) note.
@@ -62,4 +68,61 @@ const buildEditorContextMenu = function (editor, event) {
6268
return Menu.buildFromTemplate(template)
6369
}
6470

65-
module.exports = buildEditorContextMenu
71+
/**
72+
* Creates the context menu that is shown when there is a right click Markdown preview of a (not-snippet) note.
73+
* @param {MarkdownPreview} markdownPreview
74+
* @param {MouseEvent} event that has triggered the creation of the context menu
75+
* @returns {Electron.Menu} The created electron context menu
76+
*/
77+
const buildMarkdownPreviewContextMenu = function (markdownPreview, event) {
78+
if (markdownPreview == null || event == null || event.pageX == null || event.pageY == null) {
79+
return null
80+
}
81+
82+
// Default context menu inclusions
83+
const template = [{
84+
role: 'cut'
85+
}, {
86+
role: 'copy'
87+
}, {
88+
role: 'paste'
89+
}, {
90+
role: 'selectall'
91+
}]
92+
93+
if (event.target.tagName.toLowerCase() === 'a' && event.target.getAttribute('href')) {
94+
// Link opener for files on the local system pointed to by href
95+
const href = event.target.href
96+
const isLocalFile = href.startsWith('file:')
97+
if (isLocalFile) {
98+
const absPath = uri2path(href)
99+
try {
100+
if (fs.lstatSync(absPath).isFile()) {
101+
template.push(
102+
{
103+
label: i18n.__('Show in explorer'),
104+
click: (e) => shell.showItemInFolder(absPath)
105+
}
106+
)
107+
}
108+
} catch (e) {
109+
console.log('Error while evaluating if the file is locally available', e)
110+
}
111+
}
112+
113+
// Add option to context menu to copy url
114+
template.push(
115+
{
116+
label: i18n.__('Copy Url'),
117+
click: (e) => clipboard.writeText(href)
118+
}
119+
)
120+
}
121+
return Menu.buildFromTemplate(template)
122+
}
123+
124+
module.exports =
125+
{
126+
buildEditorContextMenu: buildEditorContextMenu,
127+
buildMarkdownPreviewContextMenu: buildMarkdownPreviewContextMenu
128+
}

tests/lib/contextMenuBuilder.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ jest.mock('electron', () => {
55

66
const spellcheck = require('browser/lib/spellcheck')
77
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
8+
const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder')
89

910
beforeEach(() => {
1011
menuBuilderParameter = null
1112
})
1213

14+
// Editor Context Menu
1315
it('should make sure that no context menu is build if the passed editor instance was null', function () {
1416
const event = {
1517
pageX: 12,
@@ -124,3 +126,13 @@ it('should make sure that word suggestions creates a correct menu if there was a
124126
expect(menuBuilderParameter[7].role).toEqual('selectall')
125127
expect(spellcheck.getSpellingSuggestion).toHaveBeenCalledWith(wordToCorrect)
126128
})
129+
130+
// Markdown Preview Context Menu
131+
it('should make sure that no context menu is built if the Markdown Preview instance was null', function () {
132+
const event = {
133+
pageX: 12,
134+
pageY: 12
135+
}
136+
buildMarkdownPreviewContextMenu(null, event)
137+
expect(menuBuilderParameter).toEqual(null)
138+
})

0 commit comments

Comments
 (0)