1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-04-18 19:42:23 +02:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Laurent Cozic 42a63d7386 update 2021-10-01 19:34:03 +01:00
Laurent Cozic 4ad3f5bd57 sqlite3 2021-10-01 18:43:56 +01:00
Laurent Cozic eac36fe934 fix build 2021-10-01 18:43:12 +01:00
Laurent Cozic 979ac87e9a electron 14 2021-10-01 16:33:33 +01:00
Laurent Cozic 83941520c9 update 2021-10-01 11:59:28 +01:00
Laurent Cozic e6c299bb7b Merge branch 'dev' into electron_12 2021-10-01 11:01:44 +01:00
Laurent Cozic cee951051a Electron 12 upgrade
Doesn't work because Electron context menu needs to be moved to main
process, but spellchecker service is in renderer thread. Solution
might be to patch Electron context menu package to allow it to run
in renderer process again.
2021-06-04 12:31:16 +02:00
58 changed files with 67261 additions and 60756 deletions
+1
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
+2 -1
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();
+2 -1
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();
+956 -418
View File
File diff suppressed because it is too large Load Diff
+1
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",
@@ -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;
+14 -6
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);
+16 -5
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();
+50
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();
}
@@ -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 = {
@@ -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',
@@ -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');
+1 -1
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');
@@ -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();
@@ -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',
@@ -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',
@@ -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',
@@ -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',
@@ -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',
@@ -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;
+1 -1
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) {
@@ -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 = [
@@ -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';
@@ -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';
@@ -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';
@@ -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');
@@ -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 {
@@ -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');
+1 -1
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';
+1 -1
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;
@@ -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() {
+1 -1
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');
+1 -1
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() {
@@ -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';
+9 -2
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)
+1
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;
+4266 -7200
View File
File diff suppressed because it is too large Load Diff
+6 -5
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",
+1 -1
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();
@@ -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);
});
};
@@ -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({
@@ -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(' ')));
}
}
+1 -1
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
+8239 -137
View File
File diff suppressed because it is too large Load Diff
+11684 -11674
View File
File diff suppressed because it is too large Load Diff
+18368 -18368
View File
File diff suppressed because it is too large Load Diff
+3 -1
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);
+2 -1
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();
+4292 -3899
View File
File diff suppressed because it is too large Load Diff
+1 -1
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",
@@ -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;
+1 -1
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);
@@ -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) {
+26 -8
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;
+14
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;
},
+2 -1
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(', ')}`);
+2 -1
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;
+18990 -18990
View File
File diff suppressed because it is too large Load Diff