Protocol handler for deep linking in desktop app

This commit is contained in:
David Meza
2017-10-09 11:12:21 -05:00
parent fb9a005f39
commit 40892e0430
5 changed files with 52 additions and 9 deletions

View File

@@ -20,6 +20,12 @@
] ]
} }
], ],
"protocols": [
{
"name": "Mattermost",
"schemes": ["mattermost"]
}
],
"deb": { "deb": {
"synopsis": "Mattermost" "synopsis": "Mattermost"
}, },

View File

@@ -254,8 +254,7 @@ const MainPage = createReactClass({
withTab={this.props.teams.length > 1} withTab={this.props.teams.length > 1}
useSpellChecker={this.props.useSpellChecker} useSpellChecker={this.props.useSpellChecker}
onSelectSpellCheckerLocale={this.props.onSelectSpellCheckerLocale} onSelectSpellCheckerLocale={this.props.onSelectSpellCheckerLocale}
src={team.url} team={team}
name={team.name}
onTargetURLChange={self.handleTargetURLChange} onTargetURLChange={self.handleTargetURLChange}
onUnreadCountChange={handleUnreadCountChange} onUnreadCountChange={handleUnreadCountChange}
onNotificationClick={handleNotificationClick} onNotificationClick={handleNotificationClick}

View File

@@ -15,11 +15,10 @@ const preloadJS = `file://${remote.app.getAppPath()}/browser/webview/mattermost_
const MattermostView = createReactClass({ const MattermostView = createReactClass({
propTypes: { propTypes: {
name: PropTypes.string,
id: PropTypes.string, id: PropTypes.string,
onTargetURLChange: PropTypes.func, onTargetURLChange: PropTypes.func,
onUnreadCountChange: PropTypes.func, onUnreadCountChange: PropTypes.func,
src: PropTypes.string, team: PropTypes.object,
active: PropTypes.bool, active: PropTypes.bool,
withTab: PropTypes.bool, withTab: PropTypes.bool,
useSpellChecker: PropTypes.bool, useSpellChecker: PropTypes.bool,
@@ -44,8 +43,18 @@ const MattermostView = createReactClass({
var self = this; var self = this;
var webview = findDOMNode(this.refs.webview); 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) => { 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). if (e.errorCode === -3) { // An operation was aborted (due to user action).
return; return;
} }
@@ -139,7 +148,7 @@ const MattermostView = createReactClass({
}); });
webview.addEventListener('console-message', (e) => { webview.addEventListener('console-message', (e) => {
const message = `[${this.props.name}] ${e.message}`; const message = `[${this.props.team.name}] ${e.message}`;
switch (e.level) { switch (e.level) {
case 0: case 0:
console.log(message); console.log(message);
@@ -223,6 +232,10 @@ const MattermostView = createReactClass({
if (!this.props.active) { if (!this.props.active) {
classNames.push('mattermostView-hidden'); classNames.push('mattermostView-hidden');
} }
const deeplinkingUrl = remote.getCurrentWindow().deeplinkingUrl;
const lastUrl = (deeplinkingUrl === null ? this.props.team.url : deeplinkingUrl);
return ( return (
<div> <div>
{ errorView } { errorView }
@@ -230,7 +243,7 @@ const MattermostView = createReactClass({
id={this.props.id} id={this.props.id}
className={classNames.join(' ')} className={classNames.join(' ')}
preload={preloadJS} preload={preloadJS}
src={this.props.src} src={lastUrl}
ref='webview' ref='webview'
/> />
</div>); </div>);

View File

@@ -44,6 +44,7 @@ const assetsDir = path.resolve(app.getAppPath(), 'assets');
// be closed automatically when the JavaScript object is garbage collected. // be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null; var mainWindow = null;
let spellChecker = null; let spellChecker = null;
let deeplinkingUrl = null;
var argv = require('yargs').parse(process.argv.slice(1)); 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 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 instances (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. // Someone tried to run a second instance, we should focus our window.
if (mainWindow) { if (mainWindow) {
if (mainWindow.isMinimized()) { 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 // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
app.on('ready', () => { app.on('ready', () => {
@@ -331,9 +348,16 @@ app.on('ready', () => {
catch((err) => console.log('An error occurred: ', err)); 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, { mainWindow = createMainWindow(config, {
hideOnStartup, hideOnStartup,
linuxAppIcon: path.join(assetsDir, 'appicon.png') linuxAppIcon: path.join(assetsDir, 'appicon.png'),
deeplinkingUrl
}); });
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows // Dereference the window object, usually you would store windows

View File

@@ -42,6 +42,7 @@ function createMainWindow(config, options) {
}); });
const mainWindow = new BrowserWindow(windowOptions); 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`; const indexURL = global.isDev ? 'http://localhost:8080/browser/index.html' : `file://${app.getAppPath()}/browser/index.html`;
mainWindow.loadURL(indexURL); mainWindow.loadURL(indexURL);