-
Notifications
You must be signed in to change notification settings - Fork 829
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
[MM-55152] Add new Desktop API endpoints, improve preload script, some clean-up #2900
Conversation
@@ -68,7 +68,7 @@ export class ServerViewState { | |||
} | |||
|
|||
getCurrentServer = () => { | |||
log.debug('getCurrentServer'); | |||
log.silly('getCurrentServer'); |
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.
These logs kept just getting in the way as some of these methods are called very frequently (ie. on every click) so I changed the logging level on them.
src/main/app/intercom.ts
Outdated
@@ -114,7 +113,7 @@ export function handleWelcomeScreenModal() { | |||
} | |||
} | |||
|
|||
export function handleMentionNotification(event: IpcMainEvent, title: string, body: string, channel: {id: string}, teamId: string, url: string, silent: boolean, data: MentionData) { | |||
export function handleMentionNotification(event: IpcMainEvent, title: string, body: string, channel: {id: string}, teamId: string, url: string, silent: boolean, data: {soundName: string}) { |
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.
Some types, like MentionData
and some of the calls types weren't as necessary since we have more control over what is sent up and down through the API, so I've removed most of the extra ones.
@@ -1,94 +0,0 @@ | |||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. |
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.
There was no need for a separate calls widget script since we can assume that everything that is exposed is accessible remotely, and it was easier to just manage one for all use cases.
@@ -89,6 +91,12 @@ export class MattermostBrowserView extends EventEmitter { | |||
if (process.platform !== 'darwin') { | |||
this.browserView.webContents.on('before-input-event', this.handleInputEvents); | |||
} | |||
this.browserView.webContents.on('input-event', (_, inputEvent) => { |
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 was transferred over from the preload script, that checks when the user clicks on the main view to close any of the dropdowns.
|
||
if (!this.isAllowedEvent(ev)) { | ||
log.warn('Disallowed calls event'); | ||
if (this.mainView?.webContentsId !== ev.sender.id) { |
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.
I decided that it made sense to be more deliberately restrictive on some of these calls methods, so I've removed and reworked all the checks for one-way message passing between the two. It also was necessary to do to merge the preload scripts as the message passing code was getting confused.
@@ -25,7 +25,7 @@ export class AppState extends EventEmitter { | |||
updateExpired = (viewId: string, expired: boolean) => { | |||
ServerManager.getViewLog(viewId, 'AppState').silly('updateExpired', expired); | |||
|
|||
this.unreads.set(viewId, expired); | |||
this.expired.set(viewId, expired); |
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 was just wrong :P
export function handleMentionNotification(event: IpcMainEvent, title: string, body: string, channel: {id: string}, teamId: string, url: string, silent: boolean, data: MentionData) { | ||
log.debug('handleMentionNotification', {title, body, channel, teamId, url, silent, data}); | ||
NotificationManager.displayMention(title, body, channel, teamId, url, silent, event.sender, data); | ||
export function handleMentionNotification(event: IpcMainEvent, title: string, body: string, channelId: string, teamId: string, url: string, silent: boolean, soundName: string) { |
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.
Simplified this since we should be more specific about what these calls expect.
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.
Does changing this cause any issues for old web apps, or is it this just called from the desktop app itself?
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 called from the Desktop itself, I do some "magic" in the preload script to account for the legacy case so that we could fix it properly here.
@@ -75,7 +75,7 @@ export class ServerDropdownView { | |||
|
|||
private init = () => { | |||
log.info('init'); | |||
const preload = getLocalPreload('desktopAPI.js'); | |||
const preload = getLocalPreload('internalAPI.js'); |
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.
I didn't realize all the BrowserViews that make up the desktop app also needed this preload script. I guess it makes sense since it's also how they access the desktop API
@streamer45 Gentle ping to review :) |
@devinbinnie Could you confirm whether this is supposed to be a backwards compatible change? What I mean is, is the expectation that running this Desktop branch against a non-updated webapp (or plugin) should work as before? |
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.
Gave this a quick pass, mostly focusing on Calls related changes. Overall it looks great, left a couple of comments.
|
||
if (!this.isCallsWidget(ev.sender.id)) { | ||
log.debug('onJoinedCall', 'blocked on wrong webContentsId'); | ||
return; |
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.
I suppose that case shouldn't really happen in a sane environment but no objections with properly rejecting.
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.
Great stuff, thank you for being patient with me 🤝
@cpoile Would be good if you could do some smoketesting purely for Calls ringing functionality on top of this PR. Not blocking, just something we may want to cover in case I missed anything.
I did some preliminary testing, made sure that all the calls functionality worked as I saw, but yeah feel free to do another pass :) |
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.
@devinbinnie
Tested this on Mac and windows (5.7 version from the artifacts)
- inviting user, license upload, custom emoji, slash command, desktop notification on mac and windows, link preview, creating GM, converting gm to private, draft counts, GM Unread message count, creating teams.
@@ -143,6 +143,7 @@ | |||
"@electron/fuses": "1.6.0", | |||
"@electron/universal": "1.3.1", | |||
"@mattermost/compass-icons": "0.1.32", | |||
"@mattermost/desktop-api": "*", |
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.
Can we depend on a version number here? Not a big deal, but would make it easier to see which version we were actually using in a specific release, if we ever need to figure that out in the future. The lockfile has all that info too, but it's not exactly easy to read.
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.
We actually do this same thing here for the webapp, I believe we do that to make sure we use the local package instead of the remote one while developing.
We want the web app to depend on a specific version so we know how the compatibility works, but for the Desktop App it should always use whatever types are available within the same commit as the app was built.
LEGACY_OFF, | ||
} from 'common/communication'; | ||
|
||
const createListener: ExternalAPI['createListener'] = (channel: string, listener: (...args: never[]) => void) => { |
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 really nicely handled. Something to be extremely careful with is accidentally returning privileged objects to the webapp context. IpcRendererEvent
is one such object and this covers that case well.
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.
FWIW, I believe Electron will not send anything that isn't serializable, so eg. we can't send a function callback by accident.
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.
It'll serialize all objects and proxy all functions. Prototypes chains will be ignored though.
Summary
This PR introduces the concept of a new Desktop App/Web App API, provided by the preload script in the form of methods exposed on the
window.desktopAPI
object. These are exposed securely by thecontextBridge
module of Electron that allows us to share some functionality directly to the renderer process, without exposing any modules directly. This way, we can avoid relying onwindow.postMessage
listeners and work with a clean, typed API in the Web App when Desktop App communication is necessary.In this change we have:
contextBridge
API and all the current functionality in the form of functions that the web app can callWe'll be waiting to merge this PR until after it has been tested and integrated properly with the Web App. There are also some concerns about running the new API alongside the old one, so we'll have to see what challenges that presents.
Related PR
mattermost/mattermost#25438: Web App PR utilizing the new API
Ticket Link
https://mattermost.atlassian.net/browse/MM-55152