You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-29 07:46:13 +02:00
Compare commits
24 Commits
android-v1
...
mac_notari
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87045a7b60 | ||
|
|
35242b9735 | ||
|
|
1851b0e7d1 | ||
|
|
0fcb6441de | ||
|
|
76c4d99b87 | ||
|
|
849ef418a6 | ||
|
|
8fbd1ae21a | ||
|
|
d733c0e010 | ||
|
|
a48e5cd4e8 | ||
|
|
03942a0073 | ||
|
|
0bc53dc063 | ||
|
|
56605beea2 | ||
|
|
8059d3fbd1 | ||
|
|
46c38ce0e0 | ||
|
|
dfa928c1f7 | ||
|
|
cb696276da | ||
|
|
5f80628a4d | ||
|
|
b77f868fc8 | ||
|
|
6ad9931e43 | ||
|
|
011a65f73b | ||
|
|
72ccc90ea0 | ||
|
|
f3e6c0da32 | ||
|
|
9308c3f38c | ||
|
|
c8a7c70838 |
@@ -1,6 +1,7 @@
|
||||
document.addEventListener('click', event => {
|
||||
const element = event.target;
|
||||
if (element.className === 'toc-item-link') {
|
||||
console.debug('TOC Plugin Webview: Sending scrollToHash message', element.dataset.slug);
|
||||
webviewApi.postMessage({
|
||||
name: 'scrollToHash',
|
||||
hash: element.dataset.slug,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -30,6 +30,7 @@ import { themeStyle } from '@joplin/lib/theme';
|
||||
import validateLayout from '../ResizableLayout/utils/validateLayout';
|
||||
import iterateItems from '../ResizableLayout/utils/iterateItems';
|
||||
import removeItem from '../ResizableLayout/utils/removeItem';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { PromptDialog } = require('../PromptDialog.min.js');
|
||||
@@ -38,6 +39,8 @@ const PluginManager = require('@joplin/lib/services/PluginManager');
|
||||
const EncryptionService = require('@joplin/lib/services/EncryptionService');
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
const logger = Logger.create('MainScreen');
|
||||
|
||||
interface LayerModalState {
|
||||
visible: boolean;
|
||||
message: string;
|
||||
@@ -564,6 +567,7 @@ class MainScreenComponent extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
userWebview_message(event: any) {
|
||||
logger.debug('Got message (WebView => Plugin) (2)', event);
|
||||
PluginService.instance().pluginById(event.pluginId).viewController(event.viewId).emitMessage(event);
|
||||
}
|
||||
|
||||
|
||||
116
packages/app-desktop/package-lock.json
generated
116
packages/app-desktop/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.4.12",
|
||||
"version": "1.4.18",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -5956,6 +5956,75 @@
|
||||
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.3.0.tgz",
|
||||
"integrity": "sha1-FOb9pcaOnk7L7/nM8DfL18BcWv4="
|
||||
},
|
||||
"electron-notarize": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.0.0.tgz",
|
||||
"integrity": "sha512-dsib1IAquMn0onCrNMJ6gtEIZn/azG8hZMCYOuZIMVMUeRMgBYHK1s5TK9P8xAcrAjh/2aN5WYHzgVSWX314og==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"fs-extra": "^9.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
|
||||
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-publish": {
|
||||
"version": "22.9.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.9.1.tgz",
|
||||
@@ -8828,9 +8897,7 @@
|
||||
"is-docker": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
|
||||
"integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw=="
|
||||
},
|
||||
"is-dotfile": {
|
||||
"version": "1.0.3",
|
||||
@@ -9021,9 +9088,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"is-wsl": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz",
|
||||
"integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog=="
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
|
||||
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
|
||||
"requires": {
|
||||
"is-docker": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-yarn-global": {
|
||||
"version": "0.3.0",
|
||||
@@ -11707,21 +11777,30 @@
|
||||
"dev": true
|
||||
},
|
||||
"node-notifier": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz",
|
||||
"integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz",
|
||||
"integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==",
|
||||
"requires": {
|
||||
"growly": "^1.3.0",
|
||||
"is-wsl": "^2.1.1",
|
||||
"semver": "^6.3.0",
|
||||
"is-wsl": "^2.2.0",
|
||||
"semver": "^7.3.2",
|
||||
"shellwords": "^0.1.1",
|
||||
"which": "^1.3.1"
|
||||
"uuid": "^8.3.0",
|
||||
"which": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -14984,9 +15063,7 @@
|
||||
"uuid": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
|
||||
},
|
||||
"v8-to-istanbul": {
|
||||
"version": "7.0.0",
|
||||
@@ -15202,6 +15279,7 @@
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.4.12",
|
||||
"version": "1.4.19",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
@@ -27,6 +27,7 @@
|
||||
"appId": "net.cozic.joplin-desktop",
|
||||
"productName": "Joplin",
|
||||
"npmRebuild": false,
|
||||
"afterSign": "./tools/notarizeMacApp.js",
|
||||
"extraResources": [
|
||||
"build/icons/*",
|
||||
"build/images/*"
|
||||
@@ -72,7 +73,10 @@
|
||||
"artifactName": "${productName}Portable.${ext}"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "../../Assets/macOs.icns"
|
||||
"icon": "../../Assets/macOs.icns",
|
||||
"target": "dmg",
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "./build-mac/entitlements.mac.inherit.plist"
|
||||
},
|
||||
"linux": {
|
||||
"icon": "../../Assets/LinuxIcons",
|
||||
@@ -100,6 +104,7 @@
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"electron": "^10.1.6",
|
||||
"electron-builder": "22.9.1",
|
||||
"electron-notarize": "^1.0.0",
|
||||
"electron-rebuild": "^2.3.2",
|
||||
"glob": "^7.1.6",
|
||||
"gulp": "^4.0.2",
|
||||
@@ -136,7 +141,7 @@
|
||||
"md5": "^2.2.1",
|
||||
"moment": "^2.22.2",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-notifier": "^6.0.0",
|
||||
"node-notifier": "^8.0.0",
|
||||
"pretty-bytes": "^5.3.0",
|
||||
"re-resizable": "^6.5.4",
|
||||
"react": "16.13.1",
|
||||
|
||||
@@ -6,8 +6,11 @@ import bridge from '../bridge';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { EventHandlers } from '@joplin/lib/services/plugins/utils/mapEventHandlersToIds';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
const logger = Logger.create('PluginRunner');
|
||||
|
||||
enum PluginMessageTarget {
|
||||
MainWindow = 'mainWindow',
|
||||
Plugin = 'plugin',
|
||||
@@ -127,7 +130,9 @@ export default class PluginRunner extends BasePluginRunner {
|
||||
const mappedArgs = mapEventIdsToHandlers(plugin.id, message.args);
|
||||
const fullPath = `joplin.${message.path}`;
|
||||
|
||||
this.logger().debug(`PluginRunner: execute call: ${fullPath}: ${mappedArgs}`);
|
||||
// Don't log complete HTML code, which can be long, for setHtml calls
|
||||
const debugMappedArgs = fullPath.includes('setHtml') ? '<hidden>' : mappedArgs;
|
||||
logger.debug(`Got message (3): ${fullPath}: ${debugMappedArgs}`);
|
||||
|
||||
let result: any = null;
|
||||
let error: any = null;
|
||||
|
||||
@@ -7,7 +7,10 @@ import useSubmitHandler from './hooks/useSubmitHandler';
|
||||
import useHtmlLoader from './hooks/useHtmlLoader';
|
||||
import useWebviewToPluginMessages from './hooks/useWebviewToPluginMessages';
|
||||
import useScriptLoader from './hooks/useScriptLoader';
|
||||
const styled = require('styled-components').default;
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const logger = Logger.create('UserWebview');
|
||||
|
||||
export interface Props {
|
||||
html: string;
|
||||
@@ -72,6 +75,9 @@ function UserWebview(props: Props, ref: any) {
|
||||
function postMessage(name: string, args: any = null) {
|
||||
const win = frameWindow();
|
||||
if (!win) return;
|
||||
|
||||
logger.debug('Got message', name, args);
|
||||
|
||||
win.postMessage({ target: 'webview', name, args }, '*');
|
||||
}
|
||||
|
||||
@@ -112,6 +118,7 @@ function UserWebview(props: Props, ref: any) {
|
||||
|
||||
useWebviewToPluginMessages(
|
||||
frameWindow(),
|
||||
isReady,
|
||||
props.onMessage,
|
||||
props.pluginId,
|
||||
props.viewId
|
||||
|
||||
@@ -58,10 +58,10 @@ const webviewApi = {
|
||||
setHtml: (args) => {
|
||||
contentElement.innerHTML = args.html;
|
||||
|
||||
console.debug('UserWebView frame: setting html to', args.html);
|
||||
// console.debug('UserWebviewIndex: setting html to', args.html);
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
console.debug('UserWebView frame: setting html callback', args.hash);
|
||||
console.debug('UserWebviewIndex: setting html callback', args.hash);
|
||||
window.postMessage({ target: 'UserWebview', message: 'htmlIsSet', hash: args.hash }, '*');
|
||||
});
|
||||
},
|
||||
@@ -105,6 +105,7 @@ const webviewApi = {
|
||||
if (!ipc[callName]) {
|
||||
console.warn('Missing IPC function:', event.data);
|
||||
} else {
|
||||
console.debug('UserWebviewIndex: Got message', callName, args);
|
||||
ipc[callName](args);
|
||||
}
|
||||
}));
|
||||
@@ -115,7 +116,7 @@ const webviewApi = {
|
||||
// Need to send it with a delay to make sure all listeners are
|
||||
// ready when the message is sent.
|
||||
window.requestAnimationFrame(() => {
|
||||
console.debug('UserWebView frame: calling isReady');
|
||||
console.debug('UserWebViewIndex: calling isReady');
|
||||
window.postMessage({ target: 'UserWebview', message: 'ready' }, '*');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function(frameWindow: any, isReady: boolean, postMessage: Functio
|
||||
|
||||
if (!isReady) return;
|
||||
|
||||
console.info('useHtmlLoader: setHtml', htmlHash, html);
|
||||
console.info('useHtmlLoader: setHtml', htmlHash);
|
||||
|
||||
postMessage('setHtml', {
|
||||
hash: htmlHash,
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function(frameWindow: any, onMessage: Function, pluginId: string, viewId: string) {
|
||||
const logger = Logger.create('useWebviewToPluginMessages');
|
||||
|
||||
export default function(frameWindow: any, isReady: boolean, onMessage: Function, pluginId: string, viewId: string) {
|
||||
useEffect(() => {
|
||||
if (!frameWindow) return () => {};
|
||||
|
||||
function onMessage(event: any) {
|
||||
function onMessage_(event: any) {
|
||||
if (!event.data || event.data.target !== 'plugin') return;
|
||||
|
||||
// The message is passed from one component or service to the next
|
||||
// till it reaches its destination, so if something doesn't work
|
||||
// follow the chain of messages searching for the string "Got message"
|
||||
logger.debug('Got message (WebView => Plugin) (1)', pluginId, viewId, event.data.message);
|
||||
|
||||
onMessage({
|
||||
pluginId: pluginId,
|
||||
viewId: viewId,
|
||||
@@ -13,10 +22,10 @@ export default function(frameWindow: any, onMessage: Function, pluginId: string,
|
||||
});
|
||||
}
|
||||
|
||||
frameWindow.addEventListener('message', onMessage);
|
||||
frameWindow.addEventListener('message', onMessage_);
|
||||
|
||||
return () => {
|
||||
frameWindow.removeEventListener('message', onMessage);
|
||||
frameWindow.removeEventListener('message', onMessage_);
|
||||
};
|
||||
}, [onMessage, pluginId, viewId]);
|
||||
}, [frameWindow, onMessage, isReady, pluginId, viewId]);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
import SpellCheckerServiceDriverBase from '@joplin/lib/services/spellChecker/SpellCheckerServiceDriverBase';
|
||||
import bridge from '../bridge';
|
||||
import { languageCodeOnly, localesFromLanguageCode } from '@joplin/lib/locale';
|
||||
import Logger from '@joplin/lib/Logger';
|
||||
|
||||
const logger = Logger.create('SpellCheckerServiceDriverNative');
|
||||
|
||||
export default class SpellCheckerServiceDriverNative extends SpellCheckerServiceDriverBase {
|
||||
|
||||
@@ -17,7 +21,31 @@ export default class SpellCheckerServiceDriverNative extends SpellCheckerService
|
||||
public setLanguage(v: string) {
|
||||
// If we pass an empty array, it disables spell checking
|
||||
// https://github.com/electron/electron/issues/25228
|
||||
this.session().setSpellCheckerLanguages(v ? [v] : []);
|
||||
if (!v) {
|
||||
this.session().setSpellCheckerLanguages([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// The below function will throw an error if the provided language is
|
||||
// not supported, so we provide fallbacks.
|
||||
// https://github.com/laurent22/joplin/issues/4146
|
||||
const languagesToTry = [
|
||||
v,
|
||||
languageCodeOnly(v),
|
||||
].concat(localesFromLanguageCode(languageCodeOnly(v), this.availableLanguages));
|
||||
|
||||
for (const toTry of languagesToTry) {
|
||||
try {
|
||||
this.session().setSpellCheckerLanguages([toTry]);
|
||||
logger.info(`Set effective language from "${v}" to "${toTry}"`);
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to set language to "${toTry}". Will try the next one in this list: ${JSON.stringify(languagesToTry)}`);
|
||||
logger.warn('Error was:', error);
|
||||
}
|
||||
}
|
||||
|
||||
logger.error(`Could not set language to: ${v}`);
|
||||
}
|
||||
|
||||
public get language(): string {
|
||||
|
||||
45
packages/app-desktop/tools/notarizeMacApp.js
Normal file
45
packages/app-desktop/tools/notarizeMacApp.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const electron_notarize = require('electron-notarize');
|
||||
|
||||
module.exports = async function(params) {
|
||||
if (process.platform !== 'darwin') return;
|
||||
|
||||
if (!process.env.APPLE_ID || !process.env.APPLE_ID_PASSWORD) {
|
||||
console.warn('Environment variables APPLE_ID and APPLE_ID_PASSWORD not found - notarization will NOT be done.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Same appId in electron-builder.
|
||||
const appId = 'net.cozic.joplin-desktop';
|
||||
|
||||
const appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
|
||||
if (!fs.existsSync(appPath)) {
|
||||
throw new Error(`Cannot find application at: ${appPath}`);
|
||||
}
|
||||
|
||||
console.log(`Notarizing ${appId} found at ${appPath}`);
|
||||
|
||||
await electron_notarize.notarize({
|
||||
appBundleId: appId,
|
||||
appPath: appPath,
|
||||
|
||||
// Apple Developer email address
|
||||
appleId: process.env.APPLE_ID,
|
||||
|
||||
// App-specific password: https://support.apple.com/en-us/HT204397
|
||||
appleIdPassword: process.env.APPLE_ID_PASSWORD,
|
||||
|
||||
// When Apple ID is attached to multiple providers (eg if the
|
||||
// account has been used to build multiple apps for different
|
||||
// companies), in that case the provider "Team Short Name" (also
|
||||
// known as "ProviderShortname") must be provided.
|
||||
//
|
||||
// Use this to get it:
|
||||
//
|
||||
// xcrun altool --list-providers -u APPLE_ID -p APPLE_ID_PASSWORD
|
||||
ascProvider: process.env.APPLE_ASC_PROVIDER,
|
||||
});
|
||||
|
||||
console.log(`Done notarizing ${appId}`);
|
||||
};
|
||||
@@ -6,5 +6,6 @@
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules",
|
||||
"**/dist",
|
||||
],
|
||||
}
|
||||
@@ -338,13 +338,13 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 56;
|
||||
CURRENT_PROJECT_VERSION = 57;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 10.4.0;
|
||||
MARKETING_VERSION = 10.4.1;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -365,12 +365,12 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 56;
|
||||
CURRENT_PROJECT_VERSION = 57;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 10.4.0;
|
||||
MARKETING_VERSION = 10.4.1;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
|
||||
@@ -255,14 +255,6 @@ class BaseModel {
|
||||
if (!options) options = {};
|
||||
|
||||
if (options.order && options.order.length) {
|
||||
// const items = [];
|
||||
// for (let i = 0; i < options.order.length; i++) {
|
||||
// const o = options.order[i];
|
||||
// let item = `\`${o.by}\``;
|
||||
// if (options.caseInsensitive === true) item += ' COLLATE NOCASE';
|
||||
// if (o.dir) item += ` ${o.dir}`;
|
||||
// items.push(item);
|
||||
// }
|
||||
sql += ` ORDER BY ${paginationToSql(options)}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -572,6 +572,12 @@ function languageCode() {
|
||||
return languageCodeOnly(currentLocale_);
|
||||
}
|
||||
|
||||
function localesFromLanguageCode(languageCode: string, locales: string[]): string[] {
|
||||
return locales.filter((l: string) => {
|
||||
return languageCodeOnly(l) === languageCode;
|
||||
});
|
||||
}
|
||||
|
||||
function _(s: string, ...args: any[]) {
|
||||
const strings = localeStrings(currentLocale_);
|
||||
let result = strings[s];
|
||||
@@ -588,4 +594,4 @@ function _n(singular: string, plural: string, n: number, ...args: any[]) {
|
||||
return _(singular, ...args);
|
||||
}
|
||||
|
||||
export { _, _n, supportedLocales, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
|
||||
export { _, _n, supportedLocales, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
|
||||
|
||||
@@ -446,6 +446,12 @@ class Setting extends BaseModel {
|
||||
options: () => themeOptions(),
|
||||
},
|
||||
|
||||
notificationPermission: {
|
||||
value: '',
|
||||
type: SettingItemType.String,
|
||||
public: false,
|
||||
},
|
||||
|
||||
showNoteCounts: { value: true, type: SettingItemType.Bool, public: false, advanced: true, appTypes: ['desktop'], label: () => _('Show note counts') },
|
||||
|
||||
layoutButtonSequence: {
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function(pagination: Pagination): string {
|
||||
for (let i = 0; i < pagination.order.length; i++) {
|
||||
const o = pagination.order[i];
|
||||
let item = `\`${o.by}\``;
|
||||
if (o.caseInsensitive === true) item += ' COLLATE NOCASE';
|
||||
if (!!o.caseInsensitive || !!pagination.caseInsensitive) item += ' COLLATE NOCASE';
|
||||
item += ` ${o.dir}`;
|
||||
sql.push(item);
|
||||
}
|
||||
|
||||
@@ -13,4 +13,5 @@ export interface Pagination {
|
||||
limit: number;
|
||||
order: PaginationOrder[];
|
||||
page: number;
|
||||
caseInsensitive?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,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;
|
||||
@@ -43,7 +44,91 @@ export default class AlarmServiceDriverNode {
|
||||
delete this.notifications_[id];
|
||||
}
|
||||
|
||||
scheduleNotification(notification: Notification) {
|
||||
private displayDefaultNotification(notification: Notification) {
|
||||
const o: any = {
|
||||
appID: this.appName_,
|
||||
title: notification.title,
|
||||
icon: `${bridge().electronApp().buildDir()}/icons/512x512.png`,
|
||||
};
|
||||
if ('body' in notification) o.message = notification.body;
|
||||
|
||||
// Message is required on Windows 7 however we don't want to repeat the title so
|
||||
// make it an empty string.
|
||||
// https://github.com/laurent22/joplin/issues/2144
|
||||
if (!o.message) o.message = '-';
|
||||
|
||||
this.logger().info('AlarmServiceDriverNode::scheduleNotification: Triggering notification (default):', o);
|
||||
|
||||
notifier.notify(o, (error: any, response: any) => {
|
||||
this.logger().info('AlarmServiceDriverNode::scheduleNotification: node-notifier response:', error, response);
|
||||
});
|
||||
}
|
||||
|
||||
private displayMacNotification(notification: Notification) {
|
||||
// On macOS, node-notifier is broken:
|
||||
//
|
||||
// https://github.com/mikaelbr/node-notifier/issues/352
|
||||
//
|
||||
// However we can use the native browser notification as described
|
||||
// there:
|
||||
//
|
||||
// https://www.electronjs.org/docs/tutorial/notifications
|
||||
//
|
||||
// In fact it's likely that we could use this on other platforms too
|
||||
try {
|
||||
const options: any = {
|
||||
body: notification.body ? notification.body : '-',
|
||||
onerror: (error: any) => {
|
||||
this.logger().error('AlarmServiceDriverNode::displayMacNotification', error);
|
||||
},
|
||||
};
|
||||
|
||||
this.logger().info('AlarmServiceDriverNode::displayMacNotification: Triggering notification (macOS):', notification.title, options);
|
||||
|
||||
new Notification(notification.title, options);
|
||||
} catch (error) {
|
||||
this.logger().error('AlarmServiceDriverNode::displayMacNotification', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async checkPermission() {
|
||||
if (shim.isMac() && shim.isElectron()) {
|
||||
this.logger().info(`AlarmServiceDriverNode::checkPermission: Permission in settings is "${Setting.value('notificationPermission')}"`);
|
||||
|
||||
if (Setting.value('notificationPermission') !== '') return Setting.value('notificationPermission');
|
||||
|
||||
// In theory `Notification.requestPermission()` should be used to
|
||||
// ask for permission but in practice this API is unreliable. In
|
||||
// particular, it returns "granted" immediately even when
|
||||
// notifications definitely aren't allowed (and creating a new
|
||||
// notification would fail).
|
||||
//
|
||||
// Because of that, our approach is to trigger a notification, which
|
||||
// should prompt macOS to ask for permission. Once this is done we
|
||||
// manually save the result in the settings. Of course it means that
|
||||
// if permission is changed afterwards, for example from the
|
||||
// notification center, we won't know it and notifications will
|
||||
// fail.
|
||||
//
|
||||
// All this means that for now this checkPermission function always
|
||||
// returns "granted" and the setting has only two values: "granted"
|
||||
// or "" (which means we need to do the check permission trick).
|
||||
//
|
||||
// The lack of "denied" value is acceptable in our context because
|
||||
// if a user doesn't want notifications, they can simply not set
|
||||
// alarms.
|
||||
|
||||
new Notification('Checking permissions...', {
|
||||
body: 'Permission has been granted',
|
||||
});
|
||||
|
||||
Setting.setValue('notificationPermission', 'granted');
|
||||
}
|
||||
|
||||
return 'granted';
|
||||
}
|
||||
|
||||
async scheduleNotification(notification: Notification) {
|
||||
const now = Date.now();
|
||||
const interval = notification.date.getTime() - now;
|
||||
if (interval < 0) return;
|
||||
@@ -52,6 +137,12 @@ export default class AlarmServiceDriverNode {
|
||||
throw new Error(`Trying to create a notification from an invalid object: ${JSON.stringify(notification)}`);
|
||||
}
|
||||
|
||||
const permission = await this.checkPermission();
|
||||
if (permission !== 'granted') {
|
||||
this.logger().info(`AlarmServiceDriverNode::scheduleNotification: Notification ${notification.id}: Cancelled because permission was not granted.`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger().info(`AlarmServiceDriverNode::scheduleNotification: Notification ${notification.id} with interval: ${interval}ms`);
|
||||
|
||||
if (this.notifications_[notification.id]) shim.clearTimeout(this.notifications_[notification.id].timeoutId);
|
||||
@@ -72,27 +163,15 @@ export default class AlarmServiceDriverNode {
|
||||
this.logger().info(`AlarmServiceDriverNode::scheduleNotification: Notification ${notification.id} has been deleted - not rescheduling it`);
|
||||
return;
|
||||
}
|
||||
this.scheduleNotification(this.notifications_[notification.id]);
|
||||
void this.scheduleNotification(this.notifications_[notification.id]);
|
||||
}, maxInterval);
|
||||
} else {
|
||||
timeoutId = shim.setTimeout(() => {
|
||||
const o: any = {
|
||||
appID: this.appName_,
|
||||
title: notification.title,
|
||||
icon: `${bridge().electronApp().buildDir()}/icons/512x512.png`,
|
||||
};
|
||||
if ('body' in notification) o.message = notification.body;
|
||||
|
||||
// Message is required on Windows 7 however we don't want to repeat the title so
|
||||
// make it an empty string.
|
||||
// https://github.com/laurent22/joplin/issues/2144
|
||||
if (!o.message) o.message = '-';
|
||||
|
||||
this.logger().info('AlarmServiceDriverNode::scheduleNotification: Triggering notification:', o);
|
||||
|
||||
notifier.notify(o, (error: any, response: any) => {
|
||||
this.logger().info('AlarmServiceDriverNode::scheduleNotification: node-notifier response:', error, response);
|
||||
});
|
||||
if (shim.isMac() && shim.isElectron()) {
|
||||
this.displayMacNotification(notification);
|
||||
} else {
|
||||
this.displayDefaultNotification(notification);
|
||||
}
|
||||
|
||||
this.clearNotification(notification.id);
|
||||
|
||||
|
||||
@@ -81,7 +81,10 @@ export default class ExternalEditWatcher {
|
||||
if (!this.chokidar_) return;
|
||||
|
||||
if (!this.watcher_) {
|
||||
this.watcher_ = this.chokidar_.watch(fileToWatch);
|
||||
this.watcher_ = this.chokidar_.watch(fileToWatch, {
|
||||
useFsEvents: false,
|
||||
});
|
||||
|
||||
this.watcher_.on('all', async (event: string, path: string) => {
|
||||
this.logger().debug(`ExternalEditWatcher: Event: ${event}: ${path}`);
|
||||
|
||||
|
||||
@@ -156,9 +156,14 @@ export default class ResourceEditWatcher {
|
||||
};
|
||||
|
||||
if (!this.watcher_) {
|
||||
this.watcher_ = this.chokidar_.watch(fileToWatch);
|
||||
this.watcher_ = this.chokidar_.watch(fileToWatch, {
|
||||
// Need to turn off fs-events because when it's on Chokidar
|
||||
// keeps emitting "modified" events (on "raw" handler), several
|
||||
// times per seconds, even when nothing is changed.
|
||||
useFsEvents: false,
|
||||
});
|
||||
this.watcher_.on('all', (event: any, path: string) => {
|
||||
path = toSystemSlashes(path, 'linux');
|
||||
path = path ? toSystemSlashes(path, 'linux') : '';
|
||||
|
||||
this.logger().info(`ResourceEditWatcher: Event: ${event}: ${path}`);
|
||||
|
||||
@@ -186,7 +191,7 @@ export default class ResourceEditWatcher {
|
||||
//
|
||||
// @ts-ignore Leave unused path variable
|
||||
this.watcher_.on('raw', (event: string, path: string, options: any) => {
|
||||
const watchedPath = toSystemSlashes(options.watchedPath, 'linux');
|
||||
const watchedPath = options.watchedPath ? toSystemSlashes(options.watchedPath, 'linux') : '';
|
||||
|
||||
this.logger().debug(`ResourceEditWatcher: Raw event: ${event}: ${watchedPath}`);
|
||||
if (event === 'rename') {
|
||||
|
||||
Reference in New Issue
Block a user