From 40892e04305f1a6524693c373b1ba73f29e4c356 Mon Sep 17 00:00:00 2001 From: David Meza Date: Mon, 9 Oct 2017 11:12:21 -0500 Subject: [PATCH] Protocol handler for deep linking in desktop app --- electron-builder.json | 6 +++++ src/browser/components/MainPage.jsx | 3 +-- src/browser/components/MattermostView.jsx | 23 +++++++++++++++---- src/main.js | 28 +++++++++++++++++++++-- src/main/mainWindow.js | 1 + 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/electron-builder.json b/electron-builder.json index 6ab6896c..899d1a4e 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -20,6 +20,12 @@ ] } ], + "protocols": [ + { + "name": "Mattermost", + "schemes": ["mattermost"] + } + ], "deb": { "synopsis": "Mattermost" }, diff --git a/src/browser/components/MainPage.jsx b/src/browser/components/MainPage.jsx index 965066a5..623e288c 100644 --- a/src/browser/components/MainPage.jsx +++ b/src/browser/components/MainPage.jsx @@ -254,8 +254,7 @@ const MainPage = createReactClass({ withTab={this.props.teams.length > 1} useSpellChecker={this.props.useSpellChecker} onSelectSpellCheckerLocale={this.props.onSelectSpellCheckerLocale} - src={team.url} - name={team.name} + team={team} onTargetURLChange={self.handleTargetURLChange} onUnreadCountChange={handleUnreadCountChange} onNotificationClick={handleNotificationClick} diff --git a/src/browser/components/MattermostView.jsx b/src/browser/components/MattermostView.jsx index 22d55b14..f8d363f0 100644 --- a/src/browser/components/MattermostView.jsx +++ b/src/browser/components/MattermostView.jsx @@ -15,11 +15,10 @@ const preloadJS = `file://${remote.app.getAppPath()}/browser/webview/mattermost_ const MattermostView = createReactClass({ propTypes: { - name: PropTypes.string, id: PropTypes.string, onTargetURLChange: PropTypes.func, onUnreadCountChange: PropTypes.func, - src: PropTypes.string, + team: PropTypes.object, active: PropTypes.bool, withTab: PropTypes.bool, useSpellChecker: PropTypes.bool, @@ -44,8 +43,18 @@ const MattermostView = createReactClass({ var self = this; var webview = findDOMNode(this.refs.webview); + ipcRenderer.on('protocol-deeplink', (event, lastUrl) => { + webview.executeJavaScript( + 'history.pushState(null, null, "/' + + lastUrl.replace(lastUrl.match(/(?:[^/]*\/){3}/), '') + '");' + ); + webview.executeJavaScript( + 'dispatchEvent(new PopStateEvent("popstate", null));' + ); + }); + webview.addEventListener('did-fail-load', (e) => { - console.log(self.props.name, 'webview did-fail-load', e); + console.log(self.props.team.name, 'webview did-fail-load', e); if (e.errorCode === -3) { // An operation was aborted (due to user action). return; } @@ -139,7 +148,7 @@ const MattermostView = createReactClass({ }); webview.addEventListener('console-message', (e) => { - const message = `[${this.props.name}] ${e.message}`; + const message = `[${this.props.team.name}] ${e.message}`; switch (e.level) { case 0: console.log(message); @@ -223,6 +232,10 @@ const MattermostView = createReactClass({ if (!this.props.active) { classNames.push('mattermostView-hidden'); } + + const deeplinkingUrl = remote.getCurrentWindow().deeplinkingUrl; + const lastUrl = (deeplinkingUrl === null ? this.props.team.url : deeplinkingUrl); + return (
{ errorView } @@ -230,7 +243,7 @@ const MattermostView = createReactClass({ id={this.props.id} className={classNames.join(' ')} preload={preloadJS} - src={this.props.src} + src={lastUrl} ref='webview' />
); diff --git a/src/main.js b/src/main.js index 65d3d31e..e35c5f1f 100644 --- a/src/main.js +++ b/src/main.js @@ -44,6 +44,7 @@ const assetsDir = path.resolve(app.getAppPath(), 'assets'); // be closed automatically when the JavaScript object is garbage collected. var mainWindow = null; let spellChecker = null; +let deeplinkingUrl = null; var argv = require('yargs').parse(process.argv.slice(1)); @@ -148,7 +149,14 @@ const trayImages = (() => { })(); // If there is already an instance, activate the window in the existing instace and quit this one -if (app.makeSingleInstance((/*commandLine, workingDirectory*/) => { +if (app.makeSingleInstance((commandLine/*, workingDirectory*/) => { + // Protocol handler for win32 + // argv: An array of the second instance’s (command line / deep linked) arguments + if (process.platform === 'win32') { + // Keep only command line / deep linked arguments + deeplinkingUrl = commandLine.slice(1); + } + // Someone tried to run a second instance, we should focus our window. if (mainWindow) { if (mainWindow.isMinimized()) { @@ -319,6 +327,15 @@ ipcMain.on('download-url', (event, URL) => { }); }); +app.setAsDefaultProtocolClient('Mattermost'); + +// Protocol handler for osx +app.on('open-url', function(event, url) { + event.preventDefault(); + deeplinkingUrl = url.replace('Mattermost', 'https'); + mainWindow.webContents.send('protocol-deeplink', deeplinkingUrl); +}); + // This method will be called when Electron has finished // initialization and is ready to create browser windows. app.on('ready', () => { @@ -331,9 +348,16 @@ app.on('ready', () => { catch((err) => console.log('An error occurred: ', err)); } + // Protocol handler for win32 + if (process.platform === 'win32') { + // Keep only command line / deep linked arguments + deeplinkingUrl = process.argv.slice(1); + } + mainWindow = createMainWindow(config, { hideOnStartup, - linuxAppIcon: path.join(assetsDir, 'appicon.png') + linuxAppIcon: path.join(assetsDir, 'appicon.png'), + deeplinkingUrl }); mainWindow.on('closed', () => { // Dereference the window object, usually you would store windows diff --git a/src/main/mainWindow.js b/src/main/mainWindow.js index 89bb6234..ae62f3a6 100644 --- a/src/main/mainWindow.js +++ b/src/main/mainWindow.js @@ -42,6 +42,7 @@ function createMainWindow(config, options) { }); const mainWindow = new BrowserWindow(windowOptions); + mainWindow.deeplinkingUrl = options.deeplinkingUrl; const indexURL = global.isDev ? 'http://localhost:8080/browser/index.html' : `file://${app.getAppPath()}/browser/index.html`; mainWindow.loadURL(indexURL);