-
Notifications
You must be signed in to change notification settings - Fork 847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protocol deep linking #616
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! @csduarte @dmeza I'm getting the following errors when trying to launch the test build
Wondering if you've seen that error before when working on this branch? For reference, the server URL on the above screenshot was https://pre-release.mattermost.com
PS: downloaded the win64.exe file from https://circleci.com/gh/mattermost/desktop/1060#artifacts
@jasonblais For now, please restart the app. It seems that the problem happens while installing the app via Squirrel installer. Because it adds command line options, so the deeplink feature is confused at the first time. |
Updated the first comment for our readability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should handle which tab should load the deeplink. Otherwise, non-appropriate tabs may also open the link.
Please add an test case: http://pre-release.mattermost.com is registered as second or later server. And another server is registered as the first server.
src/main.js
Outdated
// Protocol handler for win32 | ||
if (process.platform === 'win32') { | ||
// Keep only command line / deep linked arguments | ||
if (Array.isArray(process.argv.slice(1)) && process.argv.slice(1).length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also handle the case, command line includes --squirrel-*
options. In that case, the app should run as an installer/uninstaller. So please do not anything.
@@ -44,8 +43,18 @@ const MattermostView = createReactClass({ | |||
var self = this; | |||
var webview = findDOMNode(this.refs.webview); | |||
|
|||
ipcRenderer.on('protocol-deeplink', (event, lastUrl) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should handle which tab should load the deeplink. Otherwise, non-appropriate tabs may also open the link.
@@ -223,14 +232,18 @@ const MattermostView = createReactClass({ | |||
if (!this.props.active) { | |||
classNames.push('mattermostView-hidden'); | |||
} | |||
|
|||
const deeplinkingUrl = remote.getCurrentWindow().deeplinkingUrl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should handle which tab should load the deeplink. Otherwise, non-appropriate tabs may also open the link.
Also, any security concerns we should be mindful of? |
@jasonblais @yuya-oc no server work is required just to use the protocol and deep linking, but it's good practice to have a server side landing page that can show download instructions in case the native app is not installed. I have a prototype version of it ready and will create a PR for it once we have it. |
ee59c35
to
ba6e8c1
Compare
^(I'd prefer to avoid adding a config setting whenever possible) |
@dmeza Please use Possibly this is not security, but we should take care that users might take mistake if the tab opens incorrect server by deeplinking. As I wrote in review comments, the link should be opened in the appropriate tab. |
@jasonblais Possibly we can support Linux by adding |
75ad1bf
to
faf649c
Compare
@yuya-oc @jasonblais added logic for:
Not really sure on how to solve and test the squirrel comment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, the feature correctly works on macOS. Please review my comments.
src/browser/components/MainPage.jsx
Outdated
if (deeplinkingUrl !== null) { | ||
for (var i = 0; i < this.props.teams.length; i++) { | ||
if (deeplinkingUrl.includes(this.props.teams[i].url)) { | ||
key = i; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mainWindow.deeplinkingUrl
should be deleted here. In current implementation, when a user opens then closes the settings page, the main window always comes back to the deeplinking URL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test case:
- Register
https://pre-release.mattermost.com
as the second server. - Quit the app.
- Open
mattermost://pre-release.mattermost.com/core/channels/apiv4
- The app should show the second tab and the channel. [OK]
- Select another channel.
- Open the settings page (Ctrl/Cmd+,) then select the first server.
- The app should open the first tab. [NG]
- The second tab should keep the last channel. [NG]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case was weird and hard to handle, but it's done and tested.
// argv: An array of the second instance’s (command line / deep linked) arguments | ||
if (process.platform === 'win32') { | ||
// Keep only command line / deep linked arguments | ||
if (Array.isArray(commandLine.slice(1)) && commandLine.slice(1).length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The app needs to do not anything other than installation or related works when --squirrel-*
command line flags exist. They are --squirrel-install
, --squirrel-uninstall
, --squirrel-updated
and --squirrel-obsolete
. https://github.com/electron/windows-installer#handling-squirrel-events
src/main.js
Outdated
@@ -313,6 +326,27 @@ ipcMain.on('download-url', (event, URL) => { | |||
}); | |||
}); | |||
|
|||
let scheme; | |||
if (protocols && protocols[0] && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider the case where multiple protocols are listed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yuya-oc I tried adding multiple protocols but after a while I realized that it's not possible because: app.setAsDefaultProtocolClient(protocol)
just registers the last one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dmeza Got it, thanks.
src/utils/util.js
Outdated
const REGEXP_DOMAIN = /(?:[^/]*\/){3}/; | ||
|
||
export function getDomain(url) { | ||
const matched = url.match(REGEXP_DOMAIN); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, why not using Node.js URL module?
And I expected the return value of this function is example.com
when url is https://example.com/test
, but it seems that the actual return value is https://example.com/
. Would you declare more right name? (It's called origin
in Node.js URL module https://nodejs.org/dist/latest/docs/api/url.html)
src/main.js
Outdated
app.on('open-url', (event, url) => { | ||
event.preventDefault(); | ||
setDeeplinkingUrl(url); | ||
mainWindow.webContents.send('protocol-deeplink', deeplinkingUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also call mainWindow.show()
to bring up hidden/minimized window.
src/browser/components/MainPage.jsx
Outdated
@@ -108,6 +121,20 @@ const MainPage = createReactClass({ | |||
ipcRenderer.on('focus-on-webview', () => { | |||
this.focusOnWebView(); | |||
}); | |||
|
|||
ipcRenderer.on('protocol-deeplink', (event, lastUrl) => { | |||
const mattermostViews = document.getElementsByClassName('mattermostView mattermostView-with-tab'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is equal to props.teams.length
. And you can access to the view via refs[mattermostView${i}]
. So you don't have to search around DOM.
By adding MattermostView.getSrc()
, MainPage
doesn't have to know about internal <webview>
. It should be the role of MattermostView
.
Works on Windows as well. @dmeza Curious why we didn't use a protocol handler that was more consistent with other apps, in the form:
Is this something we should consider? Created two follow-up tickets:
|
@jasonblais @yuya-oc the protocol handler is this part For the IOs and Android apps we might need to add the other parameters at the end. For example: |
@dmeza Why would we need the additional parameters for mobile? Makes sense that we need the server URL to know which tab to switch to (I realize now it wouldn't work without it). The existing implementation sounds good. @dmeza Please help review Yuya's earlier comments from code review, and this should be good to merge. Also adding @MusikPolice as a second code reviewer. |
@jasonblais the additional parameters are needed for mobile because they don't use urls like platform or desktop. We discussed with @jarredwitt that they make APIv4 calls directly. I have the changes for @yuya-oc's comment almost ready. I'll push later today. |
@dmeza Got it, thanks! And that's great, thanks for your continued work on this PR |
@yuya-oc @jasonblais except for the multi protocol that's not possible, all other comments are done. Tested on mac and windows that tabs display properly based on domain and that it goes to the right channel. Also tested that it works with the app closed. |
Tested https://circleci.com/gh/mattermost/desktop/1080#artifacts Almost okay. And a permalink as deeplinking also works #622 (comment).
But when the application was launched by deeplinking, it keep the link even when after using the settings page. #616 (comment) |
… to whitelabel in an easy manner.
… windows squirrel installer.
9170f6c
to
ce08b73
Compare
@yuya-oc fixed this case: #616 (comment) |
In that case, electron.exe is registered as the protocol client. The app would not work because app dir is not set when launching.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified a little.
- Removed a global variable.
- Disabled the feature in development mode.
https://circleci.com/gh/mattermost/desktop/1084#artifacts The test case has been resolved 👍 |
src/main.js
Outdated
@@ -46,7 +46,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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yuya-oc @jasonblais removing the global variable deeplinkingUrl
breaks the logic to be able to deep link when the application is closed. Test case would be to have multiple tabs, close the app completely and deep link to a channel in the second tab. It stays on the first tab.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dmeza I believed there are no cases which must refer the global variable though, I’ll check again. Thanks.
@yuya-oc a question just to educate me, why is it a good idea to disable deep linking in dev mode? |
@dmeza It seems that electron.exe is registered as the protocol client in dev mode. In such case, the application couldn’t launch correctly because the second arg to specify runtime code is missing. |
As mentioned by @dmeza #616 (comment), the global variable is needed for mac at |
Description
Adds protocol mattermost to fire up the desktop application from links. Also adds deep linking to be able to go to the specific channel on the link, this was done without the need for a hard refresh if the application is already running.
A use case are links on emails that would allow the user to access the desktop application directly. This was added for mac and windows.
Based on this feature proposal:
read and understood our Contributing Guidelines
completed Mattermost Contributor Agreement
executed npm run lint:js for proper code formatting
Test Cases
Tested with OSX El Capitan 10.11 and Windows 8.1.
Package and run the application and run example links in a browser:
There are two cases, when the application is already running. Or after the protocol has registered in the OS, then close the app and try the protocol from a restarted browser and it should start the application correctly and go to the right channel. (edited)