1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

feat: Added Context Menu for markdown preview mode and copy url when hyperlink

This commit is contained in:
nathan-castlehow
2019-06-09 13:28:53 +08:00
committed by Junyoung Choi
parent c83e5cc7d8
commit e85767b4a0
4 changed files with 81 additions and 31 deletions

View File

@@ -20,7 +20,7 @@ import styles from '../components/CodeEditor.styl'
const { ipcRenderer, remote, clipboard } = require('electron') const { ipcRenderer, remote, clipboard } = require('electron')
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
const spellcheck = require('browser/lib/spellcheck') const spellcheck = require('browser/lib/spellcheck')
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu
import TurndownService from 'turndown' import TurndownService from 'turndown'
import {languageMaps} from '../lib/CMLanguageList' import {languageMaps} from '../lib/CMLanguageList'
import snippetManager from '../lib/SnippetManager' import snippetManager from '../lib/SnippetManager'

View File

@@ -18,15 +18,13 @@ import mdurl from 'mdurl'
import exportNote from 'browser/main/lib/dataApi/exportNote' import exportNote from 'browser/main/lib/dataApi/exportNote'
import { escapeHtmlCharacters } from 'browser/lib/utils' import { escapeHtmlCharacters } from 'browser/lib/utils'
import yaml from 'js-yaml' import yaml from 'js-yaml'
import context from 'browser/lib/context'
import i18n from 'browser/lib/i18n'
import fs from 'fs'
import { render } from 'react-dom' import { render } from 'react-dom'
import Carousel from 'react-image-carousel' import Carousel from 'react-image-carousel'
import ConfigManager from '../main/lib/ConfigManager' import ConfigManager from '../main/lib/ConfigManager'
const { remote, shell } = require('electron') const { remote, shell } = require('electron')
const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement')
const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu
const { app } = remote const { app } = remote
const path = require('path') const path = require('path')
@@ -34,8 +32,6 @@ const fileUrl = require('file-url')
const dialog = remote.dialog const dialog = remote.dialog
const uri2path = require('file-uri-to-path')
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1] const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
const appPath = fileUrl( const appPath = fileUrl(
process.env.NODE_ENV === 'production' ? app.getAppPath() : path.resolve() process.env.NODE_ENV === 'production' ? app.getAppPath() : path.resolve()
@@ -250,30 +246,9 @@ export default class MarkdownPreview extends React.Component {
} }
handleContextMenu (event) { handleContextMenu (event) {
// If a contextMenu handler was passed to us, use it instead of the self-defined one -> return const menu = buildMarkdownPreviewContextMenu(this, event)
if (_.isFunction(this.props.onContextMenu)) { if (menu != null) {
this.props.onContextMenu(event) setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
return
}
// No contextMenu was passed to us -> execute our own link-opener
if (event.target.tagName.toLowerCase() === 'a' && event.target.getAttribute('href')) {
const href = event.target.href
const isLocalFile = href.startsWith('file:')
if (isLocalFile) {
const absPath = uri2path(href)
try {
if (fs.lstatSync(absPath).isFile()) {
context.popup([
{
label: i18n.__('Show in explorer'),
click: (e) => shell.showItemInFolder(absPath)
}
])
}
} catch (e) {
console.log('Error while evaluating if the file is locally available', e)
}
}
} }
} }

View File

@@ -1,6 +1,12 @@
import i18n from 'browser/lib/i18n'
import fs from 'fs'
const {remote} = require('electron') const {remote} = require('electron')
const {Menu} = remote.require('electron') const {Menu} = remote.require('electron')
const {clipboard} = remote.require('electron')
const {shell} = remote.require('electron')
const spellcheck = require('./spellcheck') const spellcheck = require('./spellcheck')
const uri2path = require('file-uri-to-path')
/** /**
* Creates the context menu that is shown when there is a right click in the editor of a (not-snippet) note. * 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) {
return Menu.buildFromTemplate(template) return Menu.buildFromTemplate(template)
} }
module.exports = buildEditorContextMenu /**
* Creates the context menu that is shown when there is a right click Markdown preview of a (not-snippet) note.
* @param {MarkdownPreview} markdownPreview
* @param {MouseEvent} event that has triggered the creation of the context menu
* @returns {Electron.Menu} The created electron context menu
*/
const buildMarkdownPreviewContextMenu = function (markdownPreview, event) {
if (markdownPreview == null || event == null || event.pageX == null || event.pageY == null) {
return null
}
// Default context menu inclusions
const template = [{
role: 'cut'
}, {
role: 'copy'
}, {
role: 'paste'
}, {
role: 'selectall'
}]
if (event.target.tagName.toLowerCase() === 'a' && event.target.getAttribute('href')) {
// Link opener for files on the local system pointed to by href
const href = event.target.href
const isLocalFile = href.startsWith('file:')
if (isLocalFile) {
const absPath = uri2path(href)
try {
if (fs.lstatSync(absPath).isFile()) {
template.push(
{
label: i18n.__('Show in explorer'),
click: (e) => shell.showItemInFolder(absPath)
}
)
}
} catch (e) {
console.log('Error while evaluating if the file is locally available', e)
}
}
// Add option to context menu to copy url
template.push(
{
label: i18n.__('Copy Url'),
click: (e) => clipboard.writeText(href)
}
)
}
return Menu.buildFromTemplate(template)
}
module.exports =
{
buildEditorContextMenu: buildEditorContextMenu,
buildMarkdownPreviewContextMenu: buildMarkdownPreviewContextMenu
}

View File

@@ -5,11 +5,13 @@ jest.mock('electron', () => {
const spellcheck = require('browser/lib/spellcheck') const spellcheck = require('browser/lib/spellcheck')
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder')
beforeEach(() => { beforeEach(() => {
menuBuilderParameter = null menuBuilderParameter = null
}) })
// Editor Context Menu
it('should make sure that no context menu is build if the passed editor instance was null', function () { it('should make sure that no context menu is build if the passed editor instance was null', function () {
const event = { const event = {
pageX: 12, pageX: 12,
@@ -124,3 +126,13 @@ it('should make sure that word suggestions creates a correct menu if there was a
expect(menuBuilderParameter[7].role).toEqual('selectall') expect(menuBuilderParameter[7].role).toEqual('selectall')
expect(spellcheck.getSpellingSuggestion).toHaveBeenCalledWith(wordToCorrect) expect(spellcheck.getSpellingSuggestion).toHaveBeenCalledWith(wordToCorrect)
}) })
// Markdown Preview Context Menu
it('should make sure that no context menu is built if the Markdown Preview instance was null', function () {
const event = {
pageX: 12,
pageY: 12
}
buildMarkdownPreviewContextMenu(null, event)
expect(menuBuilderParameter).toEqual(null)
})