1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-04-11 11:12:03 +02:00

Desktop: Upgrade Electron from v10 to v14

This commit is contained in:
Laurent Cozic 2021-10-01 19:35:27 +01:00
parent 85e20a61d9
commit 4a7746beb2
58 changed files with 67261 additions and 60756 deletions

View File

@ -40,6 +40,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/supportedLocales.js
packages/app-desktop/locales
packages/app-desktop/node_modules
packages/app-desktop/packageInfo.js
packages/app-desktop/services/electron-context-menu.js
packages/app-mobile/android
packages/app-mobile/components/NoteEditor/CodeMirror.bundle.js
packages/app-mobile/ios

View File

@ -29,6 +29,7 @@ const { _ } = require('@joplin/lib/locale');
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local.js');
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
const envFromArgs = require('@joplin/lib/envFromArgs');
const nodeSqlite = require('sqlite3');
const env = envFromArgs(process.argv);
@ -64,7 +65,7 @@ function appVersion() {
return p.version;
}
shimInit(sharp, keytar, null, appVersion);
shimInit({ sharp, keytar, appVersion, nodeSqlite });
const application = app();

View File

@ -2,6 +2,7 @@ const { afterEachCleanUp } = require('@joplin/lib/testing/test-utils.js');
const { shimInit } = require('@joplin/lib/shim-init-node.js');
const shim = require('@joplin/lib/shim').default;
const sharp = require('sharp');
const nodeSqlite = require('sqlite3');
let keytar;
try {
@ -11,7 +12,7 @@ try {
keytar = null;
}
shimInit(sharp, keytar);
shimInit({ sharp, keytar, nodeSqlite });
global.afterEach(async () => {
await afterEachCleanUp();

File diff suppressed because it is too large Load Diff

View File

@ -57,6 +57,7 @@
"server-destroy": "^1.0.1",
"sharp": "^0.26.2",
"sprintf-js": "^1.1.2",
"sqlite3": "^5.0.2",
"string-padding": "^1.0.2",
"strip-ansi": "^4.0.0",
"terminal-kit": "^1.30.0",

View File

@ -86,6 +86,7 @@ export default class ElectronAppWrapper {
backgroundColor: '#fff', // required to enable sub pixel rendering, can't be in css
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
spellcheck: true,
enableRemoteModule: true,
},
@ -101,6 +102,8 @@ export default class ElectronAppWrapper {
this.win_ = new BrowserWindow(windowOptions);
require('@electron/remote/main').enable(this.win_.webContents);
if (!screen.getDisplayMatching(this.win_.getBounds())) {
const { width: windowWidth, height: windowHeight } = this.win_.getBounds();
const { width: primaryDisplayWidth, height: primaryDisplayHeight } = screen.getPrimaryDisplay().workArea;

View File

@ -5,7 +5,7 @@ import { ExportOptions, FileSystemItem, Module } from '@joplin/lib/services/inte
import { _ } from '@joplin/lib/locale';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
import Setting from '@joplin/lib/models/Setting';
import Note from '@joplin/lib/models/Note';
const { friendlySafeFilename } = require('@joplin/lib/path-utils');
@ -86,10 +86,18 @@ export default class InteropServiceHelper {
cleanup();
}
} else {
// TODO: it is crashing at this point :(
// Appears to be a Chromium bug: https://github.com/electron/electron/issues/19946
// Maybe can be fixed by doing everything from main process?
// i.e. creating a function `print()` that takes the `htmlFile` variable as input.
// TODO: it is crashing at this point :( Appears to
// be a Chromium bug:
// https://github.com/electron/electron/issues/19946
// Maybe can be fixed by doing everything from main
// process? i.e. creating a function `print()` that
// takes the `htmlFile` variable as input.
//
// 2021-10-01: This old bug is fixed, and has been
// replaced by a brand new bug:
// https://github.com/electron/electron/issues/28192
// Still doesn't work but at least it doesn't crash
// the app.
win.webContents.print(options, (success: boolean, reason: string) => {
// TODO: This is correct but broken in Electron 4. Need to upgrade to 5+
@ -97,7 +105,7 @@ export default class InteropServiceHelper {
cleanup();
if (!success && reason !== 'cancelled') reject(new Error(`Could not print: ${reason}`));
resolve();
resolve(null);
});
}
}, 2000);

View File

@ -43,6 +43,7 @@ import noteListControlsCommands from './gui/NoteListControls/commands/index';
import sidebarCommands from './gui/Sidebar/commands/index';
import appCommands from './commands/index';
import libCommands from '@joplin/lib/commands/index';
const electronContextMenu = require('./services/electron-context-menu');
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
const commands = mainScreenCommands
@ -191,11 +192,23 @@ class Application extends BaseApplication {
}
setupContextMenu() {
// bridge().setupContextMenu((misspelledWord: string, dictionarySuggestions: string[]) => {
// let output = SpellCheckerService.instance().contextMenuItems(misspelledWord, dictionarySuggestions);
// console.info(misspelledWord, dictionarySuggestions);
// console.info(output);
// output = output.map(o => {
// delete o.click;
// return o;
// });
// return output;
// });
const MenuItem = bridge().MenuItem;
// The context menu must be setup in renderer process because that's where
// the spell checker service lives.
require('electron-context-menu')({
electronContextMenu({
shouldShowMenu: (_event: any, params: any) => {
// params.inputFieldType === 'none' when right-clicking the text editor. This is a bit of a hack to detect it because in this
// case we don't want to use the built-in context menu but a custom one.
@ -325,11 +338,9 @@ class Application extends BaseApplication {
}
async start(argv: string[]): Promise<any> {
const electronIsDev = require('electron-is-dev');
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
// insert an extra argument so that they can be processed in a consistent way everywhere.
if (!electronIsDev) argv.splice(1, 0, '.');
if (!bridge().electronIsDev()) argv.splice(1, 0, '.');
argv = await super.start(argv);
@ -494,7 +505,7 @@ class Application extends BaseApplication {
ExternalEditWatcher.instance().setLogger(reg.logger());
ExternalEditWatcher.instance().initialize(bridge, this.store().dispatch);
ResourceEditWatcher.instance().initialize(reg.logger(), (action: any) => { this.store().dispatch(action); });
ResourceEditWatcher.instance().initialize(reg.logger(), (action: any) => { this.store().dispatch(action); }, (path: string) => bridge().openItem(path));
RevisionService.instance().runInBackground();

View File

@ -30,6 +30,10 @@ export class Bridge {
return this.electronWrapper_;
}
electronIsDev() {
return !this.electronApp().electronApp().isPackaged;
}
env() {
return this.electronWrapper_.env();
}
@ -38,6 +42,52 @@ export class Bridge {
return process.argv;
}
// Applies to electron-context-menu@3:
//
// For now we have to disable spell checking in non-editor text
// areas (such as the note title) because the context menu lives in
// the main process, and the spell checker service is in the
// renderer process. To get the word suggestions, we need to call
// the spellchecker service but that can only be done in an async
// way, and the menu is built synchronously.
//
// Moving the spellchecker to the main process would be hard because
// it depends on models and various other classes which are all in
// the renderer process.
//
// Perhaps the easiest would be to patch electron-context-menu to
// support the renderer process again. Or possibly revert to an old
// version of electron-context-menu.
public setupContextMenu(_spellCheckerMenuItemsHandler: Function) {
require('electron-context-menu')({
allWindows: [this.window()],
electronApp: this.electronApp(),
shouldShowMenu: (_event: any, params: any) => {
// params.inputFieldType === 'none' when right-clicking the text
// editor. This is a bit of a hack to detect it because in this
// case we don't want to use the built-in context menu but a
// custom one.
return params.isEditable && params.inputFieldType !== 'none';
},
// menu: (actions: any, props: any) => {
// const items = spellCheckerMenuItemsHandler(props.misspelledWord, props.dictionarySuggestions);
// const spellCheckerMenuItems = items.map((item: any) => new MenuItem(item)); //SpellCheckerService.instance().contextMenuItems(props.misspelledWord, props.dictionarySuggestions).map((item: any) => new MenuItem(item));
// const output = [
// actions.cut(),
// actions.copy(),
// actions.paste(),
// ...spellCheckerMenuItems,
// ];
// return output;
// },
});
}
window() {
return this.electronWrapper_.window();
}

View File

@ -1,6 +1,6 @@
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
const app = require('electron').remote.app;
const app = require('@electron/remote').app;
const { clipboard } = require('electron');
export const declaration: CommandDeclaration = {

View File

@ -3,7 +3,7 @@ import { _ } from '@joplin/lib/locale';
import { stateUtils } from '@joplin/lib/reducer';
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
import Note from '@joplin/lib/models/Note';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'startExternalEditing',

View File

@ -3,7 +3,7 @@ import ButtonBar from './ConfigScreen/ButtonBar';
import { _ } from '@joplin/lib/locale';
const { connect } = require('react-redux');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const { themeStyle } = require('@joplin/lib/theme');
const Shared = require('@joplin/lib/components/shared/dropbox-login-shared');

View File

@ -1,5 +1,5 @@
const React = require('react');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const styleSelector = require('./style/ExtensionBadge');
const { _ } = require('@joplin/lib/locale');

View File

@ -9,7 +9,7 @@ import useCommandStatus from './utils/useCommandStatus';
import styles_ from './styles';
import { _ } from '@joplin/lib/locale';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
import shim from '@joplin/lib/shim';
const keymapService = KeymapService.instance();

View File

@ -3,7 +3,7 @@ import shim from '@joplin/lib/shim';
import InteropServiceHelper from '../../../InteropServiceHelper';
import { _ } from '@joplin/lib/locale';
import Note from '@joplin/lib/models/Note';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'exportPdf',

View File

@ -1,7 +1,7 @@
import { CommandContext, CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import Folder from '@joplin/lib/models/Folder';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'newFolder',

View File

@ -1,6 +1,6 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'print',

View File

@ -1,7 +1,7 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import Folder from '@joplin/lib/models/Folder';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'renameFolder',

View File

@ -1,7 +1,7 @@
import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
import { _ } from '@joplin/lib/locale';
import Tag from '@joplin/lib/models/Tag';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
export const declaration: CommandDeclaration = {
name: 'renameTag',

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import NoteListUtils from './utils/NoteListUtils';
const { buildStyle } = require('@joplin/lib/theme');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
interface MultiNoteActionsProps {
themeId: number;

View File

@ -2,7 +2,7 @@ const React = require('react');
const Component = React.Component;
const Setting = require('@joplin/lib/models/Setting').default;
const { connect } = require('react-redux');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
class NavigatorComponent extends Component {
UNSAFE_componentWillReceiveProps(newProps) {

View File

@ -37,7 +37,7 @@ const NoteSearchBar = require('../NoteSearchBar.min.js');
import { reg } from '@joplin/lib/registry';
import Note from '@joplin/lib/models/Note';
import Folder from '@joplin/lib/models/Folder';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const NoteRevisionViewer = require('../NoteRevisionViewer.min');
const commands = [

View File

@ -2,7 +2,7 @@ import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index'
import { _ } from '@joplin/lib/locale';
import { copyHtmlToClipboard } from './clipboardUtils';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
import Resource from '@joplin/lib/models/Resource';

View File

@ -3,7 +3,7 @@ import Setting from '@joplin/lib/models/Setting';
import Note from '@joplin/lib/models/Note';
import BaseModel from '@joplin/lib/BaseModel';
import Resource from '@joplin/lib/models/Resource';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
import htmlUtils from '@joplin/lib/htmlUtils';
import Logger from '@joplin/lib/Logger';

View File

@ -4,7 +4,7 @@ import contextMenu, { openItemById } from './contextMenu';
import { _ } from '@joplin/lib/locale';
import CommandService from '@joplin/lib/services/CommandService';
import PostMessageService from '@joplin/lib/services/PostMessageService';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const { urlDecode } = require('@joplin/lib/string-utils');
const urlUtils = require('@joplin/lib/urlUtils');
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';

View File

@ -6,7 +6,7 @@ const DialogButtonRow = require('./DialogButtonRow').default;
const Datetime = require('react-datetime');
const Note = require('@joplin/lib/models/Note').default;
const formatcoords = require('formatcoords');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const shim = require('@joplin/lib/shim').default;
const { clipboard } = require('electron');

View File

@ -14,7 +14,7 @@ const { MarkupToHtml } = require('@joplin/renderer');
const time = require('@joplin/lib/time').default;
const ReactTooltip = require('react-tooltip');
const { urlDecode } = require('@joplin/lib/string-utils');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const markupLanguageUtils = require('../utils/markupLanguageUtils').default;
class NoteRevisionViewerComponent extends React.PureComponent {

View File

@ -5,7 +5,7 @@ import { _ } from '@joplin/lib/locale';
const { connect } = require('react-redux');
import { reg } from '@joplin/lib/registry';
import Setting from '@joplin/lib/models/Setting';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const { themeStyle } = require('@joplin/lib/theme');
const { OneDriveApiNodeUtils } = require('@joplin/lib/onedrive-api-node-utils.js');

View File

@ -4,7 +4,7 @@ import { _ } from '@joplin/lib/locale';
const { connect } = require('react-redux');
const { themeStyle } = require('@joplin/lib/theme');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const prettyBytes = require('pretty-bytes');
import Resource from '@joplin/lib/models/Resource';

View File

@ -26,7 +26,7 @@ const { ResourceScreen } = require('./ResourceScreen.js');
const { Navigator } = require('./Navigator.min.js');
const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
const { ThemeProvider, StyleSheetManager, createGlobalStyle } = require('styled-components');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
interface Props {
themeId: number;

View File

@ -5,7 +5,7 @@ import useSyncTargetUpgrade, { SyncTargetUpgradeResult } from '@joplin/lib/servi
const { render } = require('react-dom');
const ipcRenderer = require('electron').ipcRenderer;
import Setting from '@joplin/lib/models/Setting';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
function useAppCloseHandler(upgradeResult: SyncTargetUpgradeResult) {
useEffect(function() {

View File

@ -23,7 +23,7 @@ import { store } from '@joplin/lib/reducer';
const { connect } = require('react-redux');
const shared = require('@joplin/lib/components/shared/side-menu-shared.js');
const { themeStyle } = require('@joplin/lib/theme');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const { substrWithEllipsis } = require('@joplin/lib/string-utils');

View File

@ -1,5 +1,5 @@
const React = require('react');
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
class VerticalResizer extends React.PureComponent {
constructor() {

View File

@ -8,7 +8,7 @@ import { _ } from '@joplin/lib/locale';
import { MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
import BaseModel from '@joplin/lib/BaseModel';
const bridge = require('electron').remote.require('./bridge').default;
const bridge = require('@electron/remote').require('./bridge').default;
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
import Note from '@joplin/lib/models/Note';

View File

@ -24,10 +24,11 @@ const Logger = require('@joplin/lib/Logger').default;
const FsDriverNode = require('@joplin/lib/fs-driver-node').default;
const shim = require('@joplin/lib/shim').default;
const { shimInit } = require('@joplin/lib/shim-init-node.js');
const bridge = require('@electron/remote').require('./bridge').default;
const EncryptionService = require('@joplin/lib/services/e2ee/EncryptionService').default;
const bridge = require('electron').remote.require('./bridge').default;
const { FileApiDriverLocal } = require('@joplin/lib/file-api-driver-local.js');
const React = require('react');
const nodeSqlite = require('sqlite3');
if (bridge().env() === 'dev') {
const newConsole = function(oldConsole) {
@ -92,7 +93,13 @@ function appVersion() {
return p.version;
}
shimInit(null, keytar, React, appVersion);
shimInit({
keytar,
React,
appVersion,
electronBridge: bridge(),
nodeSqlite,
});
// Disable drag and drop of links inside application (which would
// open it as if the whole app was a browser)

View File

@ -1,6 +1,7 @@
// This is the basic initialization for the Electron MAIN process
const electronApp = require('electron').app;
require('@electron/remote/main').initialize();
const ElectronAppWrapper = require('./ElectronAppWrapper').default;
const { initBridge } = require('./bridge');
const Logger = require('@joplin/lib/Logger').default;

File diff suppressed because it is too large Load Diff

View File

@ -104,14 +104,15 @@
"app-builder-bin": "^1.9.11",
"babel-cli": "^6.26.0",
"babel-preset-react": "^6.24.1",
"electron": "^10.1.6",
"electron-builder": "22.9.1",
"electron": "^14.1.0",
"electron-builder": "^22.11.7",
"electron-notarize": "^1.0.0",
"electron-rebuild": "^2.3.2",
"electron-rebuild": "^3.2.3",
"glob": "^7.1.6",
"gulp": "^4.0.2",
"jest": "^26.6.3",
"js-sha512": "^0.8.0",
"nan": "2.14.2",
"react-test-renderer": "^16.14.0",
"typescript": "^4.0.5"
},
@ -121,6 +122,7 @@
"7zip-bin-win": "^2.1.1"
},
"dependencies": {
"@electron/remote": "^2.0.1",
"@fortawesome/fontawesome-free": "^5.13.0",
"@joplin/lib": "~2.5",
"@joplin/renderer": "~2.5",
@ -130,8 +132,6 @@
"compare-versions": "^3.2.1",
"countable": "^3.0.1",
"debounce": "^1.2.0",
"electron-context-menu": "^0.15.0",
"electron-is-dev": "^0.3.0",
"electron-window-state": "^4.1.1",
"file-uri-to-path": "^2.0.0",
"formatcoords": "^1.1.3",
@ -157,6 +157,7 @@
"reselect": "^4.0.0",
"roboto-fontface": "^0.10.0",
"smalltalk": "^2.5.1",
"sqlite3": "^5.0.2",
"styled-components": "^5.1.1",
"styled-system": "^5.1.5",
"taboverride": "^4.0.3",

View File

@ -2,7 +2,7 @@
import { Bridge } from '../bridge';
const remoteBridge = require('electron').remote.require('./bridge').default;
const remoteBridge = require('@electron/remote').require('./bridge').default;
export default function bridge(): Bridge {
return remoteBridge();

View File

@ -0,0 +1,263 @@
'use strict';
// This is a fork of electron-context-menu@0.15.0. We need to fork it because
// the latest version only runs from the main process and we need it in the
// renderer process. It also has a dependency to electron-is-dev which also only
// runs in the main process.
//
// In fact we almost don't use any features of electron-context-menu, which is
// just a wrapper over Electron's own native context menu but with more bugs, so
// we should get rid of it, but for now this is good enough as a quick fix.
const electron = require('electron');
const electronRemote = require('@electron/remote');
const webContents = win => win.webContents || (win.getWebContents && win.getWebContents());
const decorateMenuItem = menuItem => {
return (options = {}) => {
if (options.transform && !options.click) {
menuItem.transform = options.transform;
}
return menuItem;
};
};
const removeUnusedMenuItems = menuTemplate => {
let notDeletedPreviousElement;
return menuTemplate
.filter(menuItem => menuItem !== undefined && menuItem !== false && menuItem.visible !== false)
.filter((menuItem, index, array) => {
const toDelete = menuItem.type === 'separator' && (!notDeletedPreviousElement || index === array.length - 1 || array[index + 1].type === 'separator');
notDeletedPreviousElement = toDelete ? notDeletedPreviousElement : menuItem;
return !toDelete;
});
};
const create = (win, options) => {
webContents(win).on('context-menu', (event, props) => {
if (typeof options.shouldShowMenu === 'function' && options.shouldShowMenu(event, props) === false) {
return;
}
const { editFlags } = props;
const hasText = props.selectionText.trim().length > 0;
const isLink = Boolean(props.linkURL);
const can = type => editFlags[`can${type}`] && hasText;
const defaultActions = {
separator: () => ({ type: 'separator' }),
lookUpSelection: decorateMenuItem({
id: 'lookUpSelection',
label: 'Look Up “{selection}”',
visible: process.platform === 'darwin' && hasText && !isLink,
click() {
if (process.platform === 'darwin') {
webContents(win).showDefinitionForSelection();
}
},
}),
cut: decorateMenuItem({
id: 'cut',
label: 'Cut',
enabled: can('Cut'),
visible: props.isEditable,
click(menuItem) {
props.selectionText = menuItem.transform ? menuItem.transform(props.selectionText) : props.selectionText;
electron.clipboard.writeText(props.selectionText);
webContents(win).delete();
},
}),
copy: decorateMenuItem({
id: 'copy',
label: 'Copy',
enabled: can('Copy'),
visible: props.isEditable || hasText,
click(menuItem) {
props.selectionText = menuItem.transform ? menuItem.transform(props.selectionText) : props.selectionText;
electron.clipboard.writeText(props.selectionText);
},
}),
paste: decorateMenuItem({
id: 'paste',
label: 'Paste',
enabled: editFlags.canPaste,
visible: props.isEditable,
click(menuItem) {
let clipboardContent = electron.clipboard.readText(props.selectionText);
clipboardContent = menuItem.transform ? menuItem.transform(clipboardContent) : clipboardContent;
webContents(win).insertText(clipboardContent);
},
}),
saveImage: decorateMenuItem({
id: 'saveImage',
label: 'Save Image',
visible: props.mediaType === 'image',
click(menuItem) {
props.srcURL = menuItem.transform ? menuItem.transform(props.srcURL) : props.srcURL;
// download(win, props.srcURL);
},
}),
saveImageAs: decorateMenuItem({
id: 'saveImageAs',
label: 'Save Image As…',
visible: props.mediaType === 'image',
click(menuItem) {
props.srcURL = menuItem.transform ? menuItem.transform(props.srcURL) : props.srcURL;
// download(win, props.srcURL, {saveAs: true});
},
}),
copyLink: decorateMenuItem({
id: 'copyLink',
label: 'Copy Link',
visible: props.linkURL.length !== 0 && props.mediaType === 'none',
click(menuItem) {
props.linkURL = menuItem.transform ? menuItem.transform(props.linkURL) : props.linkURL;
electron.clipboard.write({
bookmark: props.linkText,
text: props.linkURL,
});
},
}),
copyImage: decorateMenuItem({
id: 'copyImage',
label: 'Copy Image',
visible: props.mediaType === 'image',
click() {
webContents(win).copyImageAt(props.x, props.y);
},
}),
copyImageAddress: decorateMenuItem({
id: 'copyImageAddress',
label: 'Copy Image Address',
visible: props.mediaType === 'image',
click(menuItem) {
props.srcURL = menuItem.transform ? menuItem.transform(props.srcURL) : props.srcURL;
electron.clipboard.write({
bookmark: props.srcURL,
text: props.srcURL,
});
},
}),
inspect: () => ({
id: 'inspect',
label: 'Inspect Element',
click() {
win.inspectElement(props.x, props.y);
if (webContents(win).isDevToolsOpened()) {
webContents(win).devToolsWebContents.focus();
}
},
}),
services: () => ({
id: 'services',
label: 'Services',
role: 'services',
visible: process.platform === 'darwin' && (props.isEditable || hasText),
}),
};
const shouldShowInspectElement = typeof options.showInspectElement === 'boolean' ? options.showInspectElement : false;
let menuTemplate = [
defaultActions.separator(),
options.showLookUpSelection !== false && defaultActions.lookUpSelection(),
defaultActions.separator(),
defaultActions.cut(),
defaultActions.copy(),
defaultActions.paste(),
defaultActions.separator(),
defaultActions.saveImage(),
options.showSaveImageAs && defaultActions.saveImageAs(),
options.showCopyImage !== false && defaultActions.copyImage(),
options.showCopyImageAddress && defaultActions.copyImageAddress(),
defaultActions.separator(),
defaultActions.copyLink(),
defaultActions.separator(),
shouldShowInspectElement && defaultActions.inspect(),
options.showServices && defaultActions.services(),
defaultActions.separator(),
];
if (options.menu) {
menuTemplate = options.menu(defaultActions, props, win);
}
if (options.prepend) {
const result = options.prepend(defaultActions, props, win);
if (Array.isArray(result)) {
menuTemplate.unshift(...result);
}
}
if (options.append) {
const result = options.append(defaultActions, props, win);
if (Array.isArray(result)) {
menuTemplate.push(...result);
}
}
// Filter out leading/trailing separators
// TODO: https://github.com/electron/electron/issues/5869
menuTemplate = removeUnusedMenuItems(menuTemplate);
for (const menuItem of menuTemplate) {
// Apply custom labels for default menu items
if (options.labels && options.labels[menuItem.id]) {
menuItem.label = options.labels[menuItem.id];
}
// Replace placeholders in menu item labels
// if (typeof menuItem.label === 'string' && menuItem.label.includes('{selection}')) {
// const selectionString = typeof props.selectionText === 'string' ? props.selectionText.trim() : '';
// menuItem.label = menuItem.label.replace('{selection}', cliTruncate(selectionString, 25));
// }
}
if (menuTemplate.length > 0) {
const menu = (electronRemote ? electronRemote.Menu : electron.Menu).buildFromTemplate(menuTemplate);
//
// When `electronRemote` is not available, this runs in the browser process.
//
// We can safely use `win` in this case as it refers to the window the
// context-menu should open in.
//
// When this is being called from a web view, we can't use `win` as this
// would refer to the web view which is not allowed to render a popup menu.
//
menu.popup(electronRemote ? electronRemote.getCurrentWindow() : win);
}
});
};
module.exports = (options = {}) => {
if (options.window) {
const win = options.window;
// When window is a webview that has not yet finished loading webContents is not available
if (webContents(win) === undefined) {
win.addEventListener('dom-ready', () => {
create(win, options);
}, { once: true });
return;
}
return create(win, options);
}
for (const win of (electron.BrowserWindow || electronRemote.BrowserWindow).getAllWindows()) {
create(win, options);
}
(electron.app || electronRemote.app).on('browser-window-created', (event, win) => {
create(win, options);
});
};

View File

@ -103,9 +103,12 @@ export default class PluginRunner extends BasePluginRunner {
show: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
require('@electron/remote/main').enable(pluginWindow.webContents);
bridge().electronApp().registerPluginWindow(plugin.id, pluginWindow);
pluginWindow.loadURL(`${require('url').format({

View File

@ -35,13 +35,19 @@ async function main() {
process.chdir(`${__dirname}/..`);
// We need to force the ABI because Electron Builder or node-abi picks the
// wrong one. However it means it will have to be manually upgraded for each
// new Electron release. Some ABI map there:
// https://github.com/electron/node-abi/tree/master/test
const forceAbiArgs = '--force-abi 89';
if (isWindows()) {
// Cannot run this in parallel, or the 64-bit version might end up
// with 32-bit files and vice-versa
console.info(await execCommand([`"${exePath}"`, '--arch ia32'].join(' ')));
console.info(await execCommand([`"${exePath}"`, '--arch x64'].join(' ')));
console.info(await execCommand([`"${exePath}"`, forceAbiArgs, '--arch ia32'].join(' ')));
console.info(await execCommand([`"${exePath}"`, forceAbiArgs, '--arch x64'].join(' ')));
} else {
console.info(await execCommand([`"${exePath}"`].join(' ')));
console.info(await execCommand([`"${exePath}"`, forceAbiArgs].join(' ')));
}
}

View File

@ -488,7 +488,7 @@ SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
FBReactNativeSpec: 009b310a5134a345e702b4402de70b5ee2bb4832
FBReactNativeSpec: d2f54de51f69366bd1f5c1fb9270698dce678f8d
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
JoplinCommonShareExtension: 270b4f8eb4e22828eeda433a04ed689fc1fd09b5
JoplinRNShareExtension: 7137e9787374e1b0797ecbef9103d1588d90e403

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,11 @@
const sqlite3 = require('sqlite3').verbose();
const shim = require('./shim').default;
const Promise = require('promise');
class DatabaseDriverNode {
open(options) {
return new Promise((resolve, reject) => {
const sqlite3 = shim.nodeSqlite().verbose();
this.db_ = new sqlite3.Database(options.name, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, error => {
if (error) {
reject(error);

View File

@ -1,8 +1,9 @@
const { afterEachCleanUp } = require('./testing/test-utils.js');
const { shimInit } = require('./shim-init-node.js');
const sharp = require('sharp');
const nodeSqlite = require('sqlite3');
shimInit(sharp, null);
shimInit({ sharp, nodeSqlite });
global.afterEach(async () => {
await afterEachCleanUp();

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,7 @@
"es6-promise-pool": "^2.5.0",
"file-uri-to-path": "^1.0.0",
"follow-redirects": "^1.2.4",
"sqlite3": "^5.0.2",
"form-data": "^2.1.4",
"fs-extra": "^5.0.0",
"html-entities": "^1.2.1",
@ -72,7 +73,6 @@
"reselect": "^4.0.0",
"server-destroy": "^1.0.1",
"sprintf-js": "^1.1.2",
"sqlite3": "^5.0.0",
"string-padding": "^1.0.2",
"string-to-stream": "^1.1.0",
"tar": "^4.4.10",

View File

@ -2,9 +2,7 @@ import eventManager from '../eventManager';
import { Notification } from '../models/Alarm';
import shim from '../shim';
import Setting from '../models/Setting';
const notifier = require('node-notifier');
const bridge = require('electron').remote.require('./bridge').default;
interface Options {
appName: string;
@ -48,7 +46,7 @@ export default class AlarmServiceDriverNode {
const o: any = {
appID: this.appName_,
title: notification.title,
icon: `${bridge().electronApp().buildDir()}/icons/512x512.png`,
icon: `${shim.electronBridge().electronApp().buildDir()}/icons/512x512.png`,
};
if ('body' in notification) o.message = notification.body;

View File

@ -261,7 +261,7 @@ export default class ExternalEditWatcher {
if (subProcess && subProcess.pid) {
this.logger().debug(`Started editor with PID ${subProcess.pid}`);
shim.clearInterval(iid);
resolve();
resolve(null);
}
}, 100);

View File

@ -7,7 +7,6 @@ import Setting from '../../models/Setting';
import Resource from '../../models/Resource';
const EventEmitter = require('events');
const chokidar = require('chokidar');
const bridge = require('electron').remote.require('./bridge').default;
interface WatchedItem {
resourceId: string;
@ -22,6 +21,8 @@ interface WatchedItems {
[key: string]: WatchedItem;
}
type OpenItemFn = (path: string)=> void;
export default class ResourceEditWatcher {
private static instance_: ResourceEditWatcher;
@ -33,6 +34,7 @@ export default class ResourceEditWatcher {
private watchedItems_: WatchedItems = {};
private eventEmitter_: any;
private tempDir_: string = '';
private openItem_: OpenItemFn;
constructor() {
this.logger_ = new Logger();
@ -42,9 +44,10 @@ export default class ResourceEditWatcher {
this.eventEmitter_ = new EventEmitter();
}
initialize(logger: any, dispatch: Function) {
initialize(logger: any, dispatch: Function, openItem: OpenItemFn) {
this.logger_ = logger;
this.dispatch = dispatch;
this.openItem_ = openItem;
}
static instance() {
@ -253,7 +256,8 @@ export default class ResourceEditWatcher {
public async openAndWatch(resourceId: string) {
const watchedItem = await this.watch(resourceId);
bridge().openItem(watchedItem.path);
// bridge().openItem(watchedItem.path);
this.openItem_(watchedItem.path);
}
async stopWatching(resourceId: string) {

View File

@ -62,8 +62,23 @@ const gunzipFile = function(source, destination) {
});
};
function shimInit(sharp = null, keytar = null, React = null, appVersion = null) {
keytar = (shim.isWindows() || shim.isMac()) && !shim.isPortable() ? keytar : null;
// sharp = null, keytar = null, React = null, appVersion = null, electronBridge = null, nodeSqlite = null
function shimInit(options = null) {
options = {
sharp: null,
keytar: null,
React: null,
appVersion: null,
electronBridge: null,
nodeSqlite: null,
...options,
};
const sharp = options.sharp;
const keytar = (shim.isWindows() || shim.isMac()) && !shim.isPortable() ? options.keytar : null;
const appVersion = options.appVersion;
shim.setNodeSqlite(options.nodeSqlite);
shim.fsDriver = () => {
throw new Error('Not implemented');
@ -72,18 +87,23 @@ function shimInit(sharp = null, keytar = null, React = null, appVersion = null)
shim.Geolocation = GeolocationNode;
shim.FormData = require('form-data');
shim.sjclModule = require('./vendor/sjcl.js');
shim.electronBridge_ = options.electronBridge;
shim.fsDriver = () => {
if (!shim.fsDriver_) shim.fsDriver_ = new FsDriverNode();
return shim.fsDriver_;
};
if (React) {
if (options.React) {
shim.react = () => {
return React;
return options.React;
};
}
shim.electronBridge = () => {
return shim.electronBridge_;
};
shim.randomBytes = async count => {
const buffer = require('crypto').randomBytes(count);
return Array.from(buffer);
@ -123,8 +143,7 @@ function shimInit(sharp = null, keytar = null, React = null, appVersion = null)
shim.showMessageBox = (message, options = null) => {
if (shim.isElectron()) {
const bridge = require('electron').remote.require('./bridge').default;
return bridge().showMessageBox(message, options);
return shim.electronBridge().showMessageBox(message, options);
} else {
throw new Error('Not implemented');
}
@ -472,10 +491,9 @@ function shimInit(sharp = null, keytar = null, React = null, appVersion = null)
shim.Buffer = Buffer;
shim.openUrl = url => {
const bridge = require('electron').remote.require('./bridge').default;
// Returns true if it opens the file successfully; returns false if it could
// not find the file.
return bridge().openExternal(url);
return shim.electronBridge().openExternal(url);
};
shim.httpAgent_ = null;

View File

@ -21,10 +21,15 @@ let isTestingEnv_ = false;
//
// https://stackoverflow.com/a/42816077/561309
let react_: typeof React = null;
let nodeSqlite_: any = null;
const shim = {
Geolocation: null as any,
electronBridge: (): any => {
throw new Error('Not implemented');
},
msleep_: (ms: number) => {
return new Promise((resolve: Function) => {
shim.setTimeout(() => {
@ -310,6 +315,15 @@ const shim = {
throw new Error('Not implemented');
},
setNodeSqlite: (nodeSqlite: any) => {
nodeSqlite_ = nodeSqlite;
},
nodeSqlite: () => {
if (!nodeSqlite_) throw new Error('Trying to access nodeSqlite before it has been set!!!');
return nodeSqlite_;
},
setReact: (react: any) => {
react_ = react;
},

View File

@ -10,6 +10,7 @@ import * as fs from 'fs-extra';
import { setEncryptionEnabled } from '../services/synchronizer/syncInfoUtils';
const { shimInit } = require('../shim-init-node');
const sharp = require('sharp');
const nodeSqlite = require('sqlite3');
const snapshotBaseDir = `${supportDir}/syncTargetSnapshots`;
@ -108,7 +109,7 @@ export async function deploySyncTargetSnapshot(syncTargetType: string, syncVersi
}
export async function main(syncTargetType: string) {
shimInit(sharp);
shimInit({ sharp, nodeSqlite });
const validSyncTargetTypes = ['normal', 'e2ee'];
if (!validSyncTargetTypes.includes(syncTargetType)) throw new Error(`Sync target type must be: ${validSyncTargetTypes.join(', ')}`);

View File

@ -20,10 +20,11 @@ import { credentialFile } from './utils/testing/testUtils';
import apiVersionHandler from './middleware/apiVersionHandler';
import clickJackingHandler from './middleware/clickJackingHandler';
const nodeSqlite = require('sqlite3');
const cors = require('@koa/cors');
const nodeEnvFile = require('node-env-file');
const { shimInit } = require('@joplin/lib/shim-init-node.js');
shimInit();
shimInit({ nodeSqlite });
const env: Env = argv.env as Env || Env.Prod;

File diff suppressed because it is too large Load Diff