1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Merge branch 'desktop-protocol' of https://github.com/roman-r-m/joplin into dev

This commit is contained in:
Laurent Cozic 2021-09-03 15:00:31 +01:00
commit 7c85889c1f
9 changed files with 138 additions and 6 deletions

View File

@ -864,6 +864,9 @@ packages/lib/Logger.js.map
packages/lib/PoorManIntervals.d.ts
packages/lib/PoorManIntervals.js
packages/lib/PoorManIntervals.js.map
packages/lib/ProtocolUtils.d.ts
packages/lib/ProtocolUtils.js
packages/lib/ProtocolUtils.js.map
packages/lib/SyncTargetJoplinCloud.d.ts
packages/lib/SyncTargetJoplinCloud.js
packages/lib/SyncTargetJoplinCloud.js.map

3
.gitignore vendored
View File

@ -850,6 +850,9 @@ packages/lib/Logger.js.map
packages/lib/PoorManIntervals.d.ts
packages/lib/PoorManIntervals.js
packages/lib/PoorManIntervals.js.map
packages/lib/ProtocolUtils.d.ts
packages/lib/ProtocolUtils.js
packages/lib/ProtocolUtils.js.map
packages/lib/SyncTargetJoplinCloud.d.ts
packages/lib/SyncTargetJoplinCloud.js
packages/lib/SyncTargetJoplinCloud.js.map

View File

@ -1,6 +1,7 @@
import Logger from '@joplin/lib/Logger';
import { PluginMessage } from './services/plugins/PluginRunner';
import shim from '@joplin/lib/shim';
import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils';
const { BrowserWindow, Tray, screen } = require('electron');
const url = require('url');
@ -30,12 +31,14 @@ export default class ElectronAppWrapper {
private buildDir_: string = null;
private rendererProcessQuitReply_: RendererProcessQuitReply = null;
private pluginWindows_: PluginWindows = {};
private initialCallbackUrl_: string = null;
constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean) {
constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) {
this.electronApp_ = electronApp;
this.env_ = env;
this.isDebugMode_ = isDebugMode;
this.profilePath_ = profilePath;
this.initialCallbackUrl_ = initialCallbackUrl;
}
electronApp() {
@ -58,6 +61,10 @@ export default class ElectronAppWrapper {
return this.env_;
}
initialCallbackUrl() {
return this.initialCallbackUrl_;
}
createWindow() {
// Set to true to view errors if the application does not start
const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_;
@ -320,12 +327,18 @@ export default class ElectronAppWrapper {
}
// Someone tried to open a second instance - focus our window instead
this.electronApp_.on('second-instance', () => {
this.electronApp_.on('second-instance', (_e: any, argv: string[]) => {
const win = this.window();
if (!win) return;
if (win.isMinimized()) win.restore();
win.show();
win.focus();
if (process.platform !== 'darwin') {
const url = argv.find((arg) => isCallbackUrl(arg));
if (url) {
void this.openCallbackUrl(url);
}
}
});
return false;
@ -352,6 +365,16 @@ export default class ElectronAppWrapper {
this.electronApp_.on('activate', () => {
this.win_.show();
});
this.electronApp_.on('open-url', (_event: any, url: string) => {
void this.openCallbackUrl(url);
});
}
async openCallbackUrl(url: string) {
this.win_.webContents.send('asynchronous-message', 'openCallbackUrl', {
url: url,
});
}
}

View File

@ -36,6 +36,8 @@ import ShareService from '@joplin/lib/services/share/ShareService';
import { reg } from '@joplin/lib/registry';
import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems';
import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { parseCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import ElectronAppWrapper from '../../ElectronAppWrapper';
import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils';
const { connect } = require('react-redux');
@ -187,6 +189,23 @@ class MainScreenComponent extends React.Component<Props, State> {
this.layoutModeListenerKeyDown = this.layoutModeListenerKeyDown.bind(this);
window.addEventListener('resize', this.window_resize);
ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => {
if (message === 'openCallbackUrl') {
this.openCallbackUrl(args.url);
}
});
const initialCallbackUrl = (bridge().electronApp() as ElectronAppWrapper).initialCallbackUrl();
if (initialCallbackUrl) {
this.openCallbackUrl(initialCallbackUrl);
}
}
private openCallbackUrl(url: string) {
console.log(`openUrl ${url}`);
const { command, params } = parseCallbackUrl(url);
void CommandService.instance().execute(command.toString(), params.id);
}
private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) {

View File

@ -20,6 +20,7 @@ import Logger from '@joplin/lib/Logger';
import { FolderEntity } from '@joplin/lib/services/database/types';
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
import { store } from '@joplin/lib/reducer';
import { getFolderCallbackUrl, getTagCallbackUrl } from '@joplin/lib/callbackUrlUtils';
const { connect } = require('react-redux');
const shared = require('@joplin/lib/components/shared/side-menu-shared.js');
const { themeStyle } = require('@joplin/lib/theme');
@ -28,6 +29,7 @@ const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
const { substrWithEllipsis } = require('@joplin/lib/string-utils');
const { ALL_NOTES_FILTER_ID } = require('@joplin/lib/reserved-ids');
const { clipboard } = require('electron');
const logger = Logger.create('Sidebar');
@ -326,10 +328,29 @@ class SidebarComponent extends React.Component<Props, State> {
);
}
if (itemType === BaseModel.TYPE_FOLDER) {
menu.append(
new MenuItem({
label: _('Copy external link'),
click: () => {
clipboard.writeText(getFolderCallbackUrl(itemId));
},
})
);
}
if (itemType === BaseModel.TYPE_TAG) {
menu.append(new MenuItem(
menuUtils.commandToStatefulMenuItem('renameTag', itemId)
));
menu.append(
new MenuItem({
label: _('Copy external link'),
click: () => {
clipboard.writeText(getTagCallbackUrl(itemId));
},
})
);
}
const pluginViews = pluginUtils.viewsByType(this.pluginsRef.current, 'menuItem');

View File

@ -6,6 +6,7 @@ import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import InteropServiceHelper from '../../InteropServiceHelper';
import { _ } from '@joplin/lib/locale';
import { MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
import { getNoteCallbackUrl } from '@joplin/lib/callbackUrlUtils';
import BaseModel from '@joplin/lib/BaseModel';
const bridge = require('electron').remote.require('./bridge').default;
@ -14,6 +15,7 @@ const MenuItem = bridge().MenuItem;
import Note from '@joplin/lib/models/Note';
import Setting from '@joplin/lib/models/Setting';
const { substrWithEllipsis } = require('@joplin/lib/string-utils');
const { clipboard } = require('electron');
interface ContextMenuProps {
notes: any[];
@ -122,7 +124,6 @@ export default class NoteListUtils {
new MenuItem({
label: _('Copy Markdown link'),
click: async () => {
const { clipboard } = require('electron');
const links = [];
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
@ -133,6 +134,17 @@ export default class NoteListUtils {
})
);
if (noteIds.length == 1) {
menu.append(
new MenuItem({
label: _('Copy external link'),
click: () => {
clipboard.writeText(getNoteCallbackUrl(noteIds[0]));
},
})
);
}
if ([9, 10].includes(Setting.value('sync.target'))) {
menu.append(
new MenuItem(

View File

@ -7,6 +7,7 @@ const Logger = require('@joplin/lib/Logger').default;
const FsDriverNode = require('@joplin/lib/fs-driver-node').default;
const envFromArgs = require('@joplin/lib/envFromArgs');
const packageInfo = require('./packageInfo.js');
const { isCallbackUrl } = require('@joplin/lib/ProtocolUtils');
// Electron takes the application name from package.json `name` and
// displays this in the tray icon toolip and message box titles, however in
@ -36,7 +37,11 @@ const env = envFromArgs(process.argv);
const profilePath = profileFromArgs(process.argv);
const isDebugMode = !!process.argv && process.argv.indexOf('--debug') >= 0;
const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode);
electronApp.setAsDefaultProtocolClient('joplin');
const initialCallbackUrl = process.argv.find((arg) => isCallbackUrl(arg));
const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode, initialCallbackUrl);
initBridge(wrapper);

View File

@ -77,13 +77,22 @@
"icon": "../../Assets/macOs.icns",
"target": "dmg",
"hardenedRuntime": true,
"entitlements": "./build-mac/entitlements.mac.inherit.plist"
"entitlements": "./build-mac/entitlements.mac.inherit.plist",
"extendInfo": {
"CFBundleURLTypes": [
{
"CFBundleURLSchemes": ["joplin"],
"CFBundleTypeRole": "Editor"
}
]
}
},
"linux": {
"icon": "../../Assets/LinuxIcons",
"category": "Office",
"desktop": {
"Icon": "joplin"
"Icon": "joplin",
"MimeType": "x-scheme-handler/joplin;"
},
"target": "AppImage"
},

View File

@ -0,0 +1,37 @@
const URL = require('url-parse');
export function isCallbackUrl(s: string) {
return s.startsWith('joplin://x-callback-url/');
}
export function getNoteCallbackUrl(noteId: string) {
return `joplin://x-callback-url/openNote?id=${encodeURIComponent(noteId)}`;
}
export function getFolderCallbackUrl(folderId: string) {
return `joplin://x-callback-url/openFolder?id=${encodeURIComponent(folderId)}`;
}
export function getTagCallbackUrl(tagId: string) {
return `joplin://x-callback-url/openTag?id=${encodeURIComponent(tagId)}`;
}
export const enum CallbackUrlCommand {
OpenNote = 'openNote',
OpenFolder = 'openFolder',
OpenTag = 'openTag',
}
export interface CallbackUrlInfo {
command: CallbackUrlCommand;
params: Record<string, string>;
}
export function parseCallbackUrl(s: string): CallbackUrlInfo {
if (!isCallbackUrl(s)) throw new Error(`Invalid callback url ${s}`);
const url = new URL(s, true);
return {
command: url.pathname.substring(url.pathname.lastIndexOf('/') + 1) as CallbackUrlCommand,
params: url.query,
};
}