From b17d8bc53393132296f229e48929175cf4402772 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 12 Aug 2021 21:51:03 +0100 Subject: [PATCH 01/24] Register to handle joplin:// links --- packages/app-desktop/ElectronAppWrapper.ts | 14 +++++++++++++- packages/app-desktop/main.js | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index cbb26217b..97fb81f33 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -320,12 +320,16 @@ 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) => arg.startsWith('joplin://')); + if (!!url) this.onUrl(url) + } }); return false; @@ -352,6 +356,14 @@ export default class ElectronAppWrapper { this.electronApp_.on('activate', () => { this.win_.show(); }); + + this.electronApp_.on('open-url', (_event: any, url: string) => { + this.onUrl(url); + }); + } + + async onUrl(url: string) { + console.log(`on url: ${url}`); } } diff --git a/packages/app-desktop/main.js b/packages/app-desktop/main.js index e0d4159dc..de13b5050 100644 --- a/packages/app-desktop/main.js +++ b/packages/app-desktop/main.js @@ -36,6 +36,14 @@ const env = envFromArgs(process.argv); const profilePath = profileFromArgs(process.argv); const isDebugMode = !!process.argv && process.argv.indexOf('--debug') >= 0; +if (env === 'dev' && process.platform === 'win32') { + electronApp.setAsDefaultProtocolClient('joplin', process.execPath, [ + resolve(process.argv[1]) + ]); +} else { + electronApp.setAsDefaultProtocolClient('joplin'); +} + const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode); initBridge(wrapper); From f909fe6670eeadcff1efa9f10cd9b2e17a77134b Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 13 Aug 2021 22:06:01 +0100 Subject: [PATCH 02/24] Trying to get it to work on Linux --- packages/app-desktop/ElectronAppWrapper.ts | 4 ++-- packages/app-desktop/package.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index 97fb81f33..06c2149da 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -328,8 +328,8 @@ export default class ElectronAppWrapper { win.focus(); if (process.platform !== 'darwin') { const url = argv.find((arg) => arg.startsWith('joplin://')); - if (!!url) this.onUrl(url) - } + if (url) this.onUrl(url); + } }); return false; diff --git a/packages/app-desktop/package.json b/packages/app-desktop/package.json index 4e8e9c9ed..5f48b62a4 100644 --- a/packages/app-desktop/package.json +++ b/packages/app-desktop/package.json @@ -83,7 +83,8 @@ "icon": "../../Assets/LinuxIcons", "category": "Office", "desktop": { - "Icon": "joplin" + "Icon": "joplin", + "MimeType": "x-scheme-handler/joplin;" }, "target": "AppImage" }, From 0a6390ed967f418c64dc91466212cf6f99ab4566 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 13 Aug 2021 22:45:13 +0100 Subject: [PATCH 03/24] Actually open the note --- packages/app-desktop/ElectronAppWrapper.ts | 4 +++- packages/app-desktop/app.ts | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index 06c2149da..1b9475239 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -363,7 +363,9 @@ export default class ElectronAppWrapper { } async onUrl(url: string) { - console.log(`on url: ${url}`); + this.win_.webContents.send('asynchronous-message', 'openUrl', { + url: url, + }); } } diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index 81a7deed8..1d270a71b 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -41,6 +41,7 @@ import RevisionService from '@joplin/lib/services/RevisionService'; import MigrationService from '@joplin/lib/services/MigrationService'; import { loadCustomCss, injectCustomStyles } from '@joplin/lib/CssUtils'; // import populateDatabase from '@joplin/lib/services/debug/populateDatabase'; +const ipcRenderer = require('electron').ipcRenderer; const commands = [ require('./gui/MainScreen/commands/editAlarm'), @@ -161,6 +162,27 @@ class Application extends BaseApplication { super(); this.bridge_nativeThemeUpdated = this.bridge_nativeThemeUpdated.bind(this); + + ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { + if (message === 'openUrl') { + const noteId = (args.url as string).substring('joplin://'.length); + console.log(`open note ${noteId}`); + + CommandService.instance().execute('openNote', noteId); + // const item = Note + // this.store().dispatch({ + // type: 'FOLDER_AND_NOTE_SELECT', + // folderId: item.parent_id, + // noteId: item.id, + // }); + // this.store().dispatch({ + // type: 'NAV_GO', + // routeName: 'Note', + // noteId: noteId, + // }); + } + }); + } hasGui() { From 00504898f28a5b105fd95f8a42830f8115a4e60e Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 13 Aug 2021 22:50:49 +0100 Subject: [PATCH 04/24] Cleanup --- packages/app-desktop/app.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index 1d270a71b..c457642c6 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -166,20 +166,7 @@ class Application extends BaseApplication { ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { if (message === 'openUrl') { const noteId = (args.url as string).substring('joplin://'.length); - console.log(`open note ${noteId}`); - CommandService.instance().execute('openNote', noteId); - // const item = Note - // this.store().dispatch({ - // type: 'FOLDER_AND_NOTE_SELECT', - // folderId: item.parent_id, - // noteId: item.id, - // }); - // this.store().dispatch({ - // type: 'NAV_GO', - // routeName: 'Note', - // noteId: noteId, - // }); } }); From 61161039c8f096e6e8abf9cc77f78929f52cf788 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 13 Aug 2021 23:21:14 +0100 Subject: [PATCH 05/24] Open the note from URL even if Joplin isn't running --- packages/app-desktop/ElectronAppWrapper.ts | 14 +++++++++----- packages/app-desktop/app.ts | 9 --------- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 9 +++++++++ packages/app-desktop/main.js | 4 +++- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index 1b9475239..d99b23c04 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -30,12 +30,14 @@ export default class ElectronAppWrapper { private buildDir_: string = null; private rendererProcessQuitReply_: RendererProcessQuitReply = null; private pluginWindows_: PluginWindows = {}; + private initialUrl_: string = null; - constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean) { + constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialUrl: string) { this.electronApp_ = electronApp; this.env_ = env; this.isDebugMode_ = isDebugMode; this.profilePath_ = profilePath; + this.initialUrl_ = initialUrl; } electronApp() { @@ -183,6 +185,8 @@ export default class ElectronAppWrapper { // save the response and try quit again. this.rendererProcessQuitReply_ = args; this.electronApp_.quit(); + } else if (message === 'getInitialUrl' && this.initialUrl_) { + this.openUrl(this.initialUrl_); } }); @@ -328,7 +332,7 @@ export default class ElectronAppWrapper { win.focus(); if (process.platform !== 'darwin') { const url = argv.find((arg) => arg.startsWith('joplin://')); - if (url) this.onUrl(url); + if (url) this.openUrl(url); } }); @@ -358,11 +362,11 @@ export default class ElectronAppWrapper { }); this.electronApp_.on('open-url', (_event: any, url: string) => { - this.onUrl(url); - }); + this.openUrl(url); + }); } - async onUrl(url: string) { + async openUrl(url: string) { this.win_.webContents.send('asynchronous-message', 'openUrl', { url: url, }); diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index c457642c6..81a7deed8 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -41,7 +41,6 @@ import RevisionService from '@joplin/lib/services/RevisionService'; import MigrationService from '@joplin/lib/services/MigrationService'; import { loadCustomCss, injectCustomStyles } from '@joplin/lib/CssUtils'; // import populateDatabase from '@joplin/lib/services/debug/populateDatabase'; -const ipcRenderer = require('electron').ipcRenderer; const commands = [ require('./gui/MainScreen/commands/editAlarm'), @@ -162,14 +161,6 @@ class Application extends BaseApplication { super(); this.bridge_nativeThemeUpdated = this.bridge_nativeThemeUpdated.bind(this); - - ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { - if (message === 'openUrl') { - const noteId = (args.url as string).substring('joplin://'.length); - CommandService.instance().execute('openNote', noteId); - } - }); - } hasGui() { diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index d82125dce..a5951bdfb 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -185,6 +185,15 @@ class MainScreenComponent extends React.Component { this.layoutModeListenerKeyDown = this.layoutModeListenerKeyDown.bind(this); window.addEventListener('resize', this.window_resize); + + ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { + if (message === 'openUrl') { + console.log(`openUrl ${args.url}`); + const noteId = (args.url as string).substring('joplin://'.length); + CommandService.instance().execute('openNote', noteId); + } + }); + ipcRenderer.send('asynchronous-message', 'getInitialUrl'); } private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) { diff --git a/packages/app-desktop/main.js b/packages/app-desktop/main.js index de13b5050..679243437 100644 --- a/packages/app-desktop/main.js +++ b/packages/app-desktop/main.js @@ -44,7 +44,9 @@ if (env === 'dev' && process.platform === 'win32') { electronApp.setAsDefaultProtocolClient('joplin'); } -const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode); +const initialUrl = process.argv.find((arg) => arg.startsWith('joplin://')); + +const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode, initialUrl); initBridge(wrapper); From f118f5250f9e633d43c22dce126ff558a7373703 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 13:33:45 +0100 Subject: [PATCH 06/24] Handle openFolder and openTag too; change the URL format; extract ULR functions to a separate file --- packages/app-desktop/ElectronAppWrapper.ts | 12 ++++--- .../app-desktop/gui/MainScreen/MainScreen.tsx | 9 ++--- packages/app-desktop/gui/Sidebar/Sidebar.tsx | 22 +++++++++++++ .../app-desktop/gui/utils/NoteListUtils.ts | 13 ++++++++ packages/lib/ProtocolUtils.ts | 33 +++++++++++++++++++ 5 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 packages/lib/ProtocolUtils.ts diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index d99b23c04..c4b2eb00f 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -185,8 +185,8 @@ export default class ElectronAppWrapper { // save the response and try quit again. this.rendererProcessQuitReply_ = args; this.electronApp_.quit(); - } else if (message === 'getInitialUrl' && this.initialUrl_) { - this.openUrl(this.initialUrl_); + } else if (message === 'mainScreenReady' && this.initialUrl_) { + void this.openUrl(this.initialUrl_); } }); @@ -332,7 +332,9 @@ export default class ElectronAppWrapper { win.focus(); if (process.platform !== 'darwin') { const url = argv.find((arg) => arg.startsWith('joplin://')); - if (url) this.openUrl(url); + if (url) { + void this.openUrl(url); + } } }); @@ -362,8 +364,8 @@ export default class ElectronAppWrapper { }); this.electronApp_.on('open-url', (_event: any, url: string) => { - this.openUrl(url); - }); + void this.openUrl(url); + }); } async openUrl(url: string) { diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index a5951bdfb..51d818ef9 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -36,6 +36,7 @@ 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 { parseUrl } from '@joplin/lib/ProtocolUtils'; const { connect } = require('react-redux'); const { PromptDialog } = require('../PromptDialog.min.js'); @@ -189,11 +190,11 @@ class MainScreenComponent extends React.Component { ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { if (message === 'openUrl') { console.log(`openUrl ${args.url}`); - const noteId = (args.url as string).substring('joplin://'.length); - CommandService.instance().execute('openNote', noteId); + const { command, params } = parseUrl(args.url); + void CommandService.instance().execute(command, params.id); } - }); - ipcRenderer.send('asynchronous-message', 'getInitialUrl'); + }); + ipcRenderer.send('asynchronous-message', 'mainScreenReady'); } private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) { diff --git a/packages/app-desktop/gui/Sidebar/Sidebar.tsx b/packages/app-desktop/gui/Sidebar/Sidebar.tsx index 5b5eee00e..6653dd465 100644 --- a/packages/app-desktop/gui/Sidebar/Sidebar.tsx +++ b/packages/app-desktop/gui/Sidebar/Sidebar.tsx @@ -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 { getFolderUrl, getTagUrl } from '@joplin/lib/ProtocolUtils'; const { connect } = require('react-redux'); const shared = require('@joplin/lib/components/shared/side-menu-shared.js'); const { themeStyle } = require('@joplin/lib/theme'); @@ -326,10 +327,31 @@ class SidebarComponent extends React.Component { ); } + if (itemType === BaseModel.TYPE_FOLDER) { + menu.append( + new MenuItem({ + label: _('Copy folder URL'), + click: async () => { + const { clipboard } = require('electron'); + clipboard.writeText(getFolderUrl(itemId)); + }, + }) + ); + } + if (itemType === BaseModel.TYPE_TAG) { menu.append(new MenuItem( menuUtils.commandToStatefulMenuItem('renameTag', itemId) )); + menu.append( + new MenuItem({ + label: _('Copy tag URL'), + click: async () => { + const { clipboard } = require('electron'); + clipboard.writeText(getTagUrl(itemId)); + }, + }) + ); } const pluginViews = pluginUtils.viewsByType(this.pluginsRef.current, 'menuItem'); diff --git a/packages/app-desktop/gui/utils/NoteListUtils.ts b/packages/app-desktop/gui/utils/NoteListUtils.ts index 81c9dbfea..306e9468c 100644 --- a/packages/app-desktop/gui/utils/NoteListUtils.ts +++ b/packages/app-desktop/gui/utils/NoteListUtils.ts @@ -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 { getNoteUrl } from '@joplin/lib/ProtocolUtils'; import BaseModel from '@joplin/lib/BaseModel'; const bridge = require('electron').remote.require('./bridge').default; @@ -133,6 +134,18 @@ export default class NoteListUtils { }) ); + if (noteIds.length == 1) { + menu.append( + new MenuItem({ + label: _('Copy note URL'), + click: async () => { + const { clipboard } = require('electron'); + clipboard.writeText(getNoteUrl(noteIds[0])); + }, + }) + ); + } + if ([9, 10].includes(Setting.value('sync.target'))) { menu.append( new MenuItem( diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts new file mode 100644 index 000000000..eab28fed4 --- /dev/null +++ b/packages/lib/ProtocolUtils.ts @@ -0,0 +1,33 @@ +export function getNoteUrl(noteId: string) { + return `joplin://x-callback-url/openNote?id=${noteId}`; +} + +export function getFolderUrl(folderId: string) { + return `joplin://x-callback-url/openFolder?id=${folderId}`; +} + +export function getTagUrl(tagId: string) { + return `joplin://x-callback-url/openTag?id=${tagId}`; +} + +export type Command = 'openNote' | 'openFolder' | 'openTag'; + +export interface UlrInfo { + command: Command; + params: any; +} + +export function parseUrl(s: string): UlrInfo { + if (!s.startsWith('joplin://')) return null; + const url = new URL(s); + + const params: any = {}; + for (const [key, value] of url.searchParams) { + params[key] = value; + } + + return { + command: url.pathname.substring(url.pathname.lastIndexOf('/') + 1) as Command, + params, + }; +} From 047883bd27ca9d2d89b2ef199f6dcc275f3c476d Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 20:12:14 +0100 Subject: [PATCH 07/24] Read initial url from a field --- packages/app-desktop/ElectronAppWrapper.ts | 6 ++++-- .../app-desktop/gui/MainScreen/MainScreen.tsx | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index c4b2eb00f..8d7d2633c 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -60,6 +60,10 @@ export default class ElectronAppWrapper { return this.env_; } + initialUrl() { + return this.initialUrl_; + } + createWindow() { // Set to true to view errors if the application does not start const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_; @@ -185,8 +189,6 @@ export default class ElectronAppWrapper { // save the response and try quit again. this.rendererProcessQuitReply_ = args; this.electronApp_.quit(); - } else if (message === 'mainScreenReady' && this.initialUrl_) { - void this.openUrl(this.initialUrl_); } }); diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index 51d818ef9..576f829d4 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -37,6 +37,7 @@ import { reg } from '@joplin/lib/registry'; import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems'; import { localSyncInfoFromState } from '@joplin/lib/services/synchronizer/syncInfoUtils'; import { parseUrl } from '@joplin/lib/ProtocolUtils'; +import ElectronAppWrapper from '../../ElectronAppWrapper'; const { connect } = require('react-redux'); const { PromptDialog } = require('../PromptDialog.min.js'); @@ -189,12 +190,20 @@ class MainScreenComponent extends React.Component { ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { if (message === 'openUrl') { - console.log(`openUrl ${args.url}`); - const { command, params } = parseUrl(args.url); - void CommandService.instance().execute(command, params.id); + this.openUrl(args.url); } }); - ipcRenderer.send('asynchronous-message', 'mainScreenReady'); + + const initialUrl = (bridge().electronApp() as ElectronAppWrapper).initialUrl(); + if (initialUrl) { + this.openUrl(initialUrl); + } + } + + private openUrl(url: string) { + console.log(`openUrl ${url}`); + const { command, params } = parseUrl(url); + void CommandService.instance().execute(command, params.id); } private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) { From f454c4e33b77baec3314551cf50c1eb26eb11e91 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 20:20:16 +0100 Subject: [PATCH 08/24] Add a function to check for valid callback url --- packages/app-desktop/main.js | 3 ++- packages/lib/ProtocolUtils.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/app-desktop/main.js b/packages/app-desktop/main.js index 679243437..d3e0b1bca 100644 --- a/packages/app-desktop/main.js +++ b/packages/app-desktop/main.js @@ -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 @@ -44,7 +45,7 @@ if (env === 'dev' && process.platform === 'win32') { electronApp.setAsDefaultProtocolClient('joplin'); } -const initialUrl = process.argv.find((arg) => arg.startsWith('joplin://')); +const initialUrl = process.argv.find((arg) => isCallbackUrl(arg)); const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode, initialUrl); diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index eab28fed4..415d86bac 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -1,3 +1,7 @@ +export function isCallbackUrl(s: string) { + return s.startsWith('joplin://x-callback-url/'); +} + export function getNoteUrl(noteId: string) { return `joplin://x-callback-url/openNote?id=${noteId}`; } From 305d0ffc491ba0fd4e6e36f80ad18dcc5514b6aa Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 20:57:01 +0100 Subject: [PATCH 09/24] Rename copy folder URL -> copy notebook URL --- packages/app-desktop/gui/Sidebar/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app-desktop/gui/Sidebar/Sidebar.tsx b/packages/app-desktop/gui/Sidebar/Sidebar.tsx index 6653dd465..910ebc8bb 100644 --- a/packages/app-desktop/gui/Sidebar/Sidebar.tsx +++ b/packages/app-desktop/gui/Sidebar/Sidebar.tsx @@ -330,7 +330,7 @@ class SidebarComponent extends React.Component { if (itemType === BaseModel.TYPE_FOLDER) { menu.append( new MenuItem({ - label: _('Copy folder URL'), + label: _('Copy notebook URL'), click: async () => { const { clipboard } = require('electron'); clipboard.writeText(getFolderUrl(itemId)); From ecf718005d22c60dea73053a144553792066543c Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 21:01:03 +0100 Subject: [PATCH 10/24] Code review comments --- packages/app-desktop/gui/Sidebar/Sidebar.tsx | 7 +++---- packages/app-desktop/gui/utils/NoteListUtils.ts | 5 ++--- packages/lib/ProtocolUtils.ts | 8 ++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/app-desktop/gui/Sidebar/Sidebar.tsx b/packages/app-desktop/gui/Sidebar/Sidebar.tsx index 910ebc8bb..06759f59b 100644 --- a/packages/app-desktop/gui/Sidebar/Sidebar.tsx +++ b/packages/app-desktop/gui/Sidebar/Sidebar.tsx @@ -29,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'); @@ -331,8 +332,7 @@ class SidebarComponent extends React.Component { menu.append( new MenuItem({ label: _('Copy notebook URL'), - click: async () => { - const { clipboard } = require('electron'); + click: () => { clipboard.writeText(getFolderUrl(itemId)); }, }) @@ -346,8 +346,7 @@ class SidebarComponent extends React.Component { menu.append( new MenuItem({ label: _('Copy tag URL'), - click: async () => { - const { clipboard } = require('electron'); + click: () => { clipboard.writeText(getTagUrl(itemId)); }, }) diff --git a/packages/app-desktop/gui/utils/NoteListUtils.ts b/packages/app-desktop/gui/utils/NoteListUtils.ts index 306e9468c..ec8dd06b5 100644 --- a/packages/app-desktop/gui/utils/NoteListUtils.ts +++ b/packages/app-desktop/gui/utils/NoteListUtils.ts @@ -15,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[]; @@ -123,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]); @@ -138,8 +138,7 @@ export default class NoteListUtils { menu.append( new MenuItem({ label: _('Copy note URL'), - click: async () => { - const { clipboard } = require('electron'); + click: () => { clipboard.writeText(getNoteUrl(noteIds[0])); }, }) diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 415d86bac..2c70a5732 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -16,16 +16,16 @@ export function getTagUrl(tagId: string) { export type Command = 'openNote' | 'openFolder' | 'openTag'; -export interface UlrInfo { +export interface CallbackUrlInfo { command: Command; - params: any; + params: Record; } -export function parseUrl(s: string): UlrInfo { +export function parseUrl(s: string): CallbackUrlInfo { if (!s.startsWith('joplin://')) return null; const url = new URL(s); - const params: any = {}; + const params: Record = {}; for (const [key, value] of url.searchParams) { params[key] = value; } From f0361bf80d3a1cd56f37d9c1cfd1b115a6df82ea Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 21:53:52 +0100 Subject: [PATCH 11/24] Review comments - escape vars in url --- packages/lib/ProtocolUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 2c70a5732..65549e8c1 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -3,15 +3,15 @@ export function isCallbackUrl(s: string) { } export function getNoteUrl(noteId: string) { - return `joplin://x-callback-url/openNote?id=${noteId}`; + return `joplin://x-callback-url/openNote?id=${encodeURIComponent(noteId)}`; } export function getFolderUrl(folderId: string) { - return `joplin://x-callback-url/openFolder?id=${folderId}`; + return `joplin://x-callback-url/openFolder?id=${encodeURIComponent(folderId)}`; } export function getTagUrl(tagId: string) { - return `joplin://x-callback-url/openTag?id=${tagId}`; + return `joplin://x-callback-url/openTag?id=${encodeURIComponent(tagId)}`; } export type Command = 'openNote' | 'openFolder' | 'openTag'; From 2b6b4dd916888734e5125158f7fcf783b0e58519 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 21:55:47 +0100 Subject: [PATCH 12/24] Rename initialUrl -> initialCallbackUrl --- packages/app-desktop/ElectronAppWrapper.ts | 10 +++++----- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 6 +++--- packages/app-desktop/main.js | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index 8d7d2633c..ed27453ba 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -30,14 +30,14 @@ export default class ElectronAppWrapper { private buildDir_: string = null; private rendererProcessQuitReply_: RendererProcessQuitReply = null; private pluginWindows_: PluginWindows = {}; - private initialUrl_: string = null; + private initialCallbackUrl_: string = null; - constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialUrl: string) { + constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) { this.electronApp_ = electronApp; this.env_ = env; this.isDebugMode_ = isDebugMode; this.profilePath_ = profilePath; - this.initialUrl_ = initialUrl; + this.initialCallbackUrl_ = initialCallbackUrl; } electronApp() { @@ -60,8 +60,8 @@ export default class ElectronAppWrapper { return this.env_; } - initialUrl() { - return this.initialUrl_; + initialCallbackUrl() { + return this.initialCallbackUrl_; } createWindow() { diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index 576f829d4..bb58812da 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -194,9 +194,9 @@ class MainScreenComponent extends React.Component { } }); - const initialUrl = (bridge().electronApp() as ElectronAppWrapper).initialUrl(); - if (initialUrl) { - this.openUrl(initialUrl); + const initialCallbackUrl = (bridge().electronApp() as ElectronAppWrapper).initialCallbackUrl(); + if (initialCallbackUrl) { + this.openUrl(initialCallbackUrl); } } diff --git a/packages/app-desktop/main.js b/packages/app-desktop/main.js index d3e0b1bca..6e530023c 100644 --- a/packages/app-desktop/main.js +++ b/packages/app-desktop/main.js @@ -45,9 +45,9 @@ if (env === 'dev' && process.platform === 'win32') { electronApp.setAsDefaultProtocolClient('joplin'); } -const initialUrl = process.argv.find((arg) => isCallbackUrl(arg)); +const initialCallbackUrl = process.argv.find((arg) => isCallbackUrl(arg)); -const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode, initialUrl); +const wrapper = new ElectronAppWrapper(electronApp, env, profilePath, isDebugMode, initialCallbackUrl); initBridge(wrapper); From ee46978389fa7d070dca476b3d737137b2486b6f Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 21:59:54 +0100 Subject: [PATCH 13/24] Review comments - throw an error if callback url is not valid --- packages/lib/ProtocolUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 65549e8c1..4e361d211 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -22,7 +22,7 @@ export interface CallbackUrlInfo { } export function parseUrl(s: string): CallbackUrlInfo { - if (!s.startsWith('joplin://')) return null; + if (!isCallbackUrl(s)) throw new Error(`Invalid callback url ${s}`); const url = new URL(s); const params: Record = {}; From b02baa6891e21cde92f6b1cdfc59305f6cfd8718 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 22:34:47 +0100 Subject: [PATCH 14/24] Update ignore files --- .eslintignore | 3 +++ .gitignore | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.eslintignore b/.eslintignore index b3341f001..87394a6a4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -861,6 +861,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 diff --git a/.gitignore b/.gitignore index 387ffae34..383fb29b3 100644 --- a/.gitignore +++ b/.gitignore @@ -846,6 +846,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 From 90621a841781653e87d2bd52b7b5fa01301cc2c7 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 23:07:22 +0100 Subject: [PATCH 15/24] Fix linter errors --- packages/app-desktop/main.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/app-desktop/main.js b/packages/app-desktop/main.js index 6e530023c..cd2295b09 100644 --- a/packages/app-desktop/main.js +++ b/packages/app-desktop/main.js @@ -38,9 +38,7 @@ const profilePath = profileFromArgs(process.argv); const isDebugMode = !!process.argv && process.argv.indexOf('--debug') >= 0; if (env === 'dev' && process.platform === 'win32') { - electronApp.setAsDefaultProtocolClient('joplin', process.execPath, [ - resolve(process.argv[1]) - ]); + electronApp.setAsDefaultProtocolClient('joplin', process.execPath, [process.argv[1]]); } else { electronApp.setAsDefaultProtocolClient('joplin'); } From 2386abea3ed21580e2b4db02c30f2aa3d3c58384 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 23:18:58 +0100 Subject: [PATCH 16/24] Rename parseUrl -> parseCallbackUrl --- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 4 ++-- packages/lib/ProtocolUtils.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index bb58812da..fd805dea5 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -36,7 +36,7 @@ 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 { parseUrl } from '@joplin/lib/ProtocolUtils'; +import { parseCallbackUrl } from '@joplin/lib/ProtocolUtils'; import ElectronAppWrapper from '../../ElectronAppWrapper'; const { connect } = require('react-redux'); @@ -202,7 +202,7 @@ class MainScreenComponent extends React.Component { private openUrl(url: string) { console.log(`openUrl ${url}`); - const { command, params } = parseUrl(url); + const { command, params } = parseCallbackUrl(url); void CommandService.instance().execute(command, params.id); } diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 4e361d211..f36548c3b 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -21,7 +21,7 @@ export interface CallbackUrlInfo { params: Record; } -export function parseUrl(s: string): CallbackUrlInfo { +export function parseCallbackUrl(s: string): CallbackUrlInfo { if (!isCallbackUrl(s)) throw new Error(`Invalid callback url ${s}`); const url = new URL(s); From 20d1f74ee44a87624e56be538f72aea84203d335 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 14 Aug 2021 23:30:19 +0100 Subject: [PATCH 17/24] Use enum --- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 4 ++-- packages/lib/ProtocolUtils.ts | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index fd805dea5..bfc757527 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -36,7 +36,7 @@ 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/ProtocolUtils'; +import { Command, parseCallbackUrl } from '@joplin/lib/ProtocolUtils'; import ElectronAppWrapper from '../../ElectronAppWrapper'; const { connect } = require('react-redux'); @@ -203,7 +203,7 @@ class MainScreenComponent extends React.Component { private openUrl(url: string) { console.log(`openUrl ${url}`); const { command, params } = parseCallbackUrl(url); - void CommandService.instance().execute(command, params.id); + void CommandService.instance().execute(Command[command], params.id); } private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) { diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index f36548c3b..9eda60e27 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -14,7 +14,11 @@ export function getTagUrl(tagId: string) { return `joplin://x-callback-url/openTag?id=${encodeURIComponent(tagId)}`; } -export type Command = 'openNote' | 'openFolder' | 'openTag'; +export enum Command { + openNote = 'openNote', + openFolder = 'openFolder', + openTag = 'openTag', +} export interface CallbackUrlInfo { command: Command; From 6c18c6ddc7a397a0f1778fca2a7b9098a5c33f46 Mon Sep 17 00:00:00 2001 From: Roman Date: Sun, 15 Aug 2021 13:48:32 +0100 Subject: [PATCH 18/24] Use url-parse --- packages/lib/ProtocolUtils.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 9eda60e27..5d8cf2ad5 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -1,3 +1,5 @@ +const URL = require('url-parse'); + export function isCallbackUrl(s: string) { return s.startsWith('joplin://x-callback-url/'); } @@ -27,15 +29,9 @@ export interface CallbackUrlInfo { export function parseCallbackUrl(s: string): CallbackUrlInfo { if (!isCallbackUrl(s)) throw new Error(`Invalid callback url ${s}`); - const url = new URL(s); - - const params: Record = {}; - for (const [key, value] of url.searchParams) { - params[key] = value; - } - + const url = new URL(s, true); return { command: url.pathname.substring(url.pathname.lastIndexOf('/') + 1) as Command, - params, + params: url.query, }; } From 62c5f433d76b3871bd49016be302c5b67bac34e9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 20 Aug 2021 21:19:21 +0100 Subject: [PATCH 19/24] Rename enum values --- packages/lib/ProtocolUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 5d8cf2ad5..6562fef91 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -17,9 +17,9 @@ export function getTagUrl(tagId: string) { } export enum Command { - openNote = 'openNote', - openFolder = 'openFolder', - openTag = 'openTag', + OpenNote = 'openNote', + OpenFolder = 'openFolder', + OpenTag = 'openTag', } export interface CallbackUrlInfo { From f42fd0ecceb9bc0724d1e573fffa1128999b0f41 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 20 Aug 2021 21:43:37 +0100 Subject: [PATCH 20/24] Fix enum usage --- packages/app-desktop/ElectronAppWrapper.ts | 3 ++- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 4 ++-- packages/lib/ProtocolUtils.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index ed27453ba..861706f0b 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -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/ProtocolUtils'; const { BrowserWindow, Tray, screen } = require('electron'); const url = require('url'); @@ -333,7 +334,7 @@ export default class ElectronAppWrapper { win.show(); win.focus(); if (process.platform !== 'darwin') { - const url = argv.find((arg) => arg.startsWith('joplin://')); + const url = argv.find((arg) => isCallbackUrl(arg)); if (url) { void this.openUrl(url); } diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index 4efa29655..2baa72884 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -36,7 +36,7 @@ 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 { Command, parseCallbackUrl } from '@joplin/lib/ProtocolUtils'; +import { parseCallbackUrl } from '@joplin/lib/ProtocolUtils'; import ElectronAppWrapper from '../../ElectronAppWrapper'; import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils'; @@ -205,7 +205,7 @@ class MainScreenComponent extends React.Component { private openUrl(url: string) { console.log(`openUrl ${url}`); const { command, params } = parseCallbackUrl(url); - void CommandService.instance().execute(Command[command], params.id); + void CommandService.instance().execute(command.toString(), params.id); } private updateLayoutPluginViews(layout: LayoutItem, plugins: PluginStates) { diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 6562fef91..112d97a63 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -16,7 +16,7 @@ export function getTagUrl(tagId: string) { return `joplin://x-callback-url/openTag?id=${encodeURIComponent(tagId)}`; } -export enum Command { +export const enum Command { OpenNote = 'openNote', OpenFolder = 'openFolder', OpenTag = 'openTag', From b269c2fdb9075db38e31320259cf39641a813e83 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 28 Aug 2021 14:26:54 +0100 Subject: [PATCH 21/24] Simplify protocol setup --- packages/app-desktop/main.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/app-desktop/main.js b/packages/app-desktop/main.js index cd2295b09..2d8dc44a8 100644 --- a/packages/app-desktop/main.js +++ b/packages/app-desktop/main.js @@ -37,11 +37,7 @@ const env = envFromArgs(process.argv); const profilePath = profileFromArgs(process.argv); const isDebugMode = !!process.argv && process.argv.indexOf('--debug') >= 0; -if (env === 'dev' && process.platform === 'win32') { - electronApp.setAsDefaultProtocolClient('joplin', process.execPath, [process.argv[1]]); -} else { - electronApp.setAsDefaultProtocolClient('joplin'); -} +electronApp.setAsDefaultProtocolClient('joplin'); const initialCallbackUrl = process.argv.find((arg) => isCallbackUrl(arg)); From 1126899769299eeb78670be62b03d23337256dc3 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Sep 2021 22:27:24 +0100 Subject: [PATCH 22/24] Code review changes --- packages/app-desktop/ElectronAppWrapper.ts | 8 ++++---- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 8 ++++---- packages/app-desktop/gui/Sidebar/Sidebar.tsx | 10 +++++----- packages/app-desktop/gui/utils/NoteListUtils.ts | 6 +++--- packages/lib/ProtocolUtils.ts | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index 861706f0b..e25758c41 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -336,7 +336,7 @@ export default class ElectronAppWrapper { if (process.platform !== 'darwin') { const url = argv.find((arg) => isCallbackUrl(arg)); if (url) { - void this.openUrl(url); + void this.openCallbackUrl(url); } } }); @@ -367,12 +367,12 @@ export default class ElectronAppWrapper { }); this.electronApp_.on('open-url', (_event: any, url: string) => { - void this.openUrl(url); + void this.openCallbackUrl(url); }); } - async openUrl(url: string) { - this.win_.webContents.send('asynchronous-message', 'openUrl', { + async openCallbackUrl(url: string) { + this.win_.webContents.send('asynchronous-message', 'openCallbackUrl', { url: url, }); } diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index 2baa72884..b03dd5bf5 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -191,18 +191,18 @@ class MainScreenComponent extends React.Component { window.addEventListener('resize', this.window_resize); ipcRenderer.on('asynchronous-message', (_event: any, message: string, args: any) => { - if (message === 'openUrl') { - this.openUrl(args.url); + if (message === 'openCallbackUrl') { + this.openCallbackUrl(args.url); } }); const initialCallbackUrl = (bridge().electronApp() as ElectronAppWrapper).initialCallbackUrl(); if (initialCallbackUrl) { - this.openUrl(initialCallbackUrl); + this.openCallbackUrl(initialCallbackUrl); } } - private openUrl(url: string) { + private openCallbackUrl(url: string) { console.log(`openUrl ${url}`); const { command, params } = parseCallbackUrl(url); void CommandService.instance().execute(command.toString(), params.id); diff --git a/packages/app-desktop/gui/Sidebar/Sidebar.tsx b/packages/app-desktop/gui/Sidebar/Sidebar.tsx index 06759f59b..209fa26b0 100644 --- a/packages/app-desktop/gui/Sidebar/Sidebar.tsx +++ b/packages/app-desktop/gui/Sidebar/Sidebar.tsx @@ -20,7 +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 { getFolderUrl, getTagUrl } from '@joplin/lib/ProtocolUtils'; +import { getFolderCallbackUrl, getTagCallbackUrl } from '@joplin/lib/ProtocolUtils'; const { connect } = require('react-redux'); const shared = require('@joplin/lib/components/shared/side-menu-shared.js'); const { themeStyle } = require('@joplin/lib/theme'); @@ -331,9 +331,9 @@ class SidebarComponent extends React.Component { if (itemType === BaseModel.TYPE_FOLDER) { menu.append( new MenuItem({ - label: _('Copy notebook URL'), + label: _('Copy external link'), click: () => { - clipboard.writeText(getFolderUrl(itemId)); + clipboard.writeText(getFolderCallbackUrl(itemId)); }, }) ); @@ -345,9 +345,9 @@ class SidebarComponent extends React.Component { )); menu.append( new MenuItem({ - label: _('Copy tag URL'), + label: _('Copy external link'), click: () => { - clipboard.writeText(getTagUrl(itemId)); + clipboard.writeText(getTagCallbackUrl(itemId)); }, }) ); diff --git a/packages/app-desktop/gui/utils/NoteListUtils.ts b/packages/app-desktop/gui/utils/NoteListUtils.ts index ec8dd06b5..4007098e3 100644 --- a/packages/app-desktop/gui/utils/NoteListUtils.ts +++ b/packages/app-desktop/gui/utils/NoteListUtils.ts @@ -6,7 +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 { getNoteUrl } from '@joplin/lib/ProtocolUtils'; +import { getNoteCallbackUrl } from '@joplin/lib/ProtocolUtils'; import BaseModel from '@joplin/lib/BaseModel'; const bridge = require('electron').remote.require('./bridge').default; @@ -137,9 +137,9 @@ export default class NoteListUtils { if (noteIds.length == 1) { menu.append( new MenuItem({ - label: _('Copy note URL'), + label: _('Copy external link'), click: () => { - clipboard.writeText(getNoteUrl(noteIds[0])); + clipboard.writeText(getNoteCallbackUrl(noteIds[0])); }, }) ); diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/ProtocolUtils.ts index 112d97a63..2018ead98 100644 --- a/packages/lib/ProtocolUtils.ts +++ b/packages/lib/ProtocolUtils.ts @@ -4,26 +4,26 @@ export function isCallbackUrl(s: string) { return s.startsWith('joplin://x-callback-url/'); } -export function getNoteUrl(noteId: string) { +export function getNoteCallbackUrl(noteId: string) { return `joplin://x-callback-url/openNote?id=${encodeURIComponent(noteId)}`; } -export function getFolderUrl(folderId: string) { +export function getFolderCallbackUrl(folderId: string) { return `joplin://x-callback-url/openFolder?id=${encodeURIComponent(folderId)}`; } -export function getTagUrl(tagId: string) { +export function getTagCallbackUrl(tagId: string) { return `joplin://x-callback-url/openTag?id=${encodeURIComponent(tagId)}`; } -export const enum Command { +export const enum CallbackUrlCommand { OpenNote = 'openNote', OpenFolder = 'openFolder', OpenTag = 'openTag', } export interface CallbackUrlInfo { - command: Command; + command: CallbackUrlCommand; params: Record; } @@ -31,7 +31,7 @@ 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 Command, + command: url.pathname.substring(url.pathname.lastIndexOf('/') + 1) as CallbackUrlCommand, params: url.query, }; } From 1a703c4ecdce41b1cd4e74baae025f7c433e05b6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Sep 2021 22:28:33 +0100 Subject: [PATCH 23/24] Rename ProtocolUtils -> callbackUrlUtils --- packages/app-desktop/ElectronAppWrapper.ts | 2 +- packages/app-desktop/gui/MainScreen/MainScreen.tsx | 2 +- packages/app-desktop/gui/Sidebar/Sidebar.tsx | 2 +- packages/app-desktop/gui/utils/NoteListUtils.ts | 2 +- packages/lib/{ProtocolUtils.ts => callbackUrlUtils.ts} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename packages/lib/{ProtocolUtils.ts => callbackUrlUtils.ts} (100%) diff --git a/packages/app-desktop/ElectronAppWrapper.ts b/packages/app-desktop/ElectronAppWrapper.ts index e25758c41..61939a44e 100644 --- a/packages/app-desktop/ElectronAppWrapper.ts +++ b/packages/app-desktop/ElectronAppWrapper.ts @@ -1,7 +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/ProtocolUtils'; +import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils'; const { BrowserWindow, Tray, screen } = require('electron'); const url = require('url'); diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index b03dd5bf5..87f086612 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -36,7 +36,7 @@ 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/ProtocolUtils'; +import { parseCallbackUrl } from '@joplin/lib/callbackUrlUtils'; import ElectronAppWrapper from '../../ElectronAppWrapper'; import { showMissingMasterKeyMessage } from '@joplin/lib/services/e2ee/utils'; diff --git a/packages/app-desktop/gui/Sidebar/Sidebar.tsx b/packages/app-desktop/gui/Sidebar/Sidebar.tsx index 209fa26b0..314944cdb 100644 --- a/packages/app-desktop/gui/Sidebar/Sidebar.tsx +++ b/packages/app-desktop/gui/Sidebar/Sidebar.tsx @@ -20,7 +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/ProtocolUtils'; +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'); diff --git a/packages/app-desktop/gui/utils/NoteListUtils.ts b/packages/app-desktop/gui/utils/NoteListUtils.ts index 4007098e3..666955948 100644 --- a/packages/app-desktop/gui/utils/NoteListUtils.ts +++ b/packages/app-desktop/gui/utils/NoteListUtils.ts @@ -6,7 +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/ProtocolUtils'; +import { getNoteCallbackUrl } from '@joplin/lib/callbackUrlUtils'; import BaseModel from '@joplin/lib/BaseModel'; const bridge = require('electron').remote.require('./bridge').default; diff --git a/packages/lib/ProtocolUtils.ts b/packages/lib/callbackUrlUtils.ts similarity index 100% rename from packages/lib/ProtocolUtils.ts rename to packages/lib/callbackUrlUtils.ts From 886b6d11264983e25fd15ce9d8515bf4246cc092 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 1 Sep 2021 22:46:44 +0100 Subject: [PATCH 24/24] Add stuff to maybe get it to work on MacOS --- packages/app-desktop/package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/app-desktop/package.json b/packages/app-desktop/package.json index ffd863621..c43febb34 100644 --- a/packages/app-desktop/package.json +++ b/packages/app-desktop/package.json @@ -77,7 +77,15 @@ "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",