[MM-55503] Update Calls Desktop API (#2937)

* Update Calls Desktop API

* Update tests

* Handle Calls links internally

---------

Co-authored-by: Devin Binnie <52460000+devinbinnie@users.noreply.github.com>
This commit is contained in:
Claudio Costa
2024-01-24 09:54:19 -06:00
committed by GitHub
parent 37829f11d2
commit 1613eb17bc
7 changed files with 146 additions and 72 deletions

View File

@@ -81,25 +81,30 @@ const desktopAPI: DesktopAPI = {
onBrowserHistoryPush: (listener) => createListener(BROWSER_HISTORY_PUSH, listener),
sendBrowserHistoryPush: (path) => ipcRenderer.send(BROWSER_HISTORY_PUSH, path),
// Calls widget
openLinkFromCallsWidget: (url) => ipcRenderer.send(CALLS_LINK_CLICK, url),
openScreenShareModal: () => ipcRenderer.send(DESKTOP_SOURCES_MODAL_REQUEST),
onScreenShared: (listener) => createListener(CALLS_WIDGET_SHARE_SCREEN, listener),
callsWidgetConnected: (callID, sessionID) => ipcRenderer.send(CALLS_JOINED_CALL, callID, sessionID),
onJoinCallRequest: (listener) => createListener(CALLS_JOIN_REQUEST, listener),
resizeCallsWidget: (width, height) => ipcRenderer.send(CALLS_WIDGET_RESIZE, width, height),
focusPopout: () => ipcRenderer.send(CALLS_POPOUT_FOCUS),
leaveCall: () => ipcRenderer.send(CALLS_LEAVE_CALL),
sendCallsError: (error) => ipcRenderer.send(CALLS_ERROR, error),
// Calls plugin
getDesktopSources: (opts) => ipcRenderer.invoke(GET_DESKTOP_SOURCES, opts),
onOpenScreenShareModal: (listener) => createListener(DESKTOP_SOURCES_MODAL_REQUEST, listener),
shareScreen: (sourceID, withAudio) => ipcRenderer.send(CALLS_WIDGET_SHARE_SCREEN, sourceID, withAudio),
// Calls
joinCall: (opts) => ipcRenderer.invoke(CALLS_JOIN_CALL, opts),
sendJoinCallRequest: (callId) => ipcRenderer.send(CALLS_JOIN_REQUEST, callId),
leaveCall: () => ipcRenderer.send(CALLS_LEAVE_CALL),
callsWidgetConnected: (callID, sessionID) => ipcRenderer.send(CALLS_JOINED_CALL, callID, sessionID),
resizeCallsWidget: (width, height) => ipcRenderer.send(CALLS_WIDGET_RESIZE, width, height),
sendCallsError: (err, callID, errMsg) => ipcRenderer.send(CALLS_ERROR, err, callID, errMsg),
onCallsError: (listener) => createListener(CALLS_ERROR, listener),
getDesktopSources: (opts) => ipcRenderer.invoke(GET_DESKTOP_SOURCES, opts),
openScreenShareModal: () => ipcRenderer.send(DESKTOP_SOURCES_MODAL_REQUEST),
onOpenScreenShareModal: (listener) => createListener(DESKTOP_SOURCES_MODAL_REQUEST, listener),
shareScreen: (sourceID, withAudio) => ipcRenderer.send(CALLS_WIDGET_SHARE_SCREEN, sourceID, withAudio),
onScreenShared: (listener) => createListener(CALLS_WIDGET_SHARE_SCREEN, listener),
sendJoinCallRequest: (callId) => ipcRenderer.send(CALLS_JOIN_REQUEST, callId),
onJoinCallRequest: (listener) => createListener(CALLS_JOIN_REQUEST, listener),
openLinkFromCalls: (url) => ipcRenderer.send(CALLS_LINK_CLICK, url),
focusPopout: () => ipcRenderer.send(CALLS_POPOUT_FOCUS),
// Utility
unregister: (channel) => ipcRenderer.removeAllListeners(channel),
};

View File

@@ -7,7 +7,7 @@ import {BrowserWindow, desktopCapturer, systemPreferences, ipcMain} from 'electr
import ServerViewState from 'app/serverViewState';
import {CALLS_WIDGET_SHARE_SCREEN, UPDATE_SHORTCUT_MENU} from 'common/communication';
import {CALLS_WIDGET_SHARE_SCREEN, BROWSER_HISTORY_PUSH, UPDATE_SHORTCUT_MENU} from 'common/communication';
import {
MINIMUM_CALLS_WIDGET_WIDTH,
MINIMUM_CALLS_WIDGET_HEIGHT,
@@ -534,6 +534,7 @@ describe('main/windows/callsWidgetWindow', () => {
describe('handleGetDesktopSources', () => {
const callsWidgetWindow = new CallsWidgetWindow();
callsWidgetWindow.options = {callID: 'callID'};
callsWidgetWindow.win = {
webContents: {
send: jest.fn(),
@@ -626,12 +627,8 @@ describe('main/windows/callsWidgetWindow', () => {
it('should send error with no sources', async () => {
jest.spyOn(desktopCapturer, 'getSources').mockResolvedValue([]);
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledTimes(1);
});
@@ -649,12 +646,8 @@ describe('main/windows/callsWidgetWindow', () => {
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
expect(systemPreferences.getMediaAccessStatus).toHaveBeenCalledWith('screen');
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledTimes(1);
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledTimes(1);
});
@@ -680,12 +673,8 @@ describe('main/windows/callsWidgetWindow', () => {
expect(callsWidgetWindow.missingScreensharePermissions).toBe(true);
expect(resetScreensharePermissionsMacOS).toHaveBeenCalledTimes(1);
expect(openScreensharePermissionsSettingsMacOS).toHaveBeenCalledTimes(0);
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', {
err: 'screen-permissions',
});
expect(callsWidgetWindow.win.webContents.send).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
expect(views.get('server-1_view-1').sendToRenderer).toHaveBeenCalledWith('calls-error', 'screen-permissions', 'callID');
await callsWidgetWindow.handleGetDesktopSources({sender: {id: 1}}, null);
@@ -799,6 +788,49 @@ describe('main/windows/callsWidgetWindow', () => {
});
});
describe('handleCallsLinkClick', () => {
const view = {
view: {
server: {
id: 'server-1',
},
},
sendToRenderer: jest.fn(),
};
const callsWidgetWindow = new CallsWidgetWindow();
callsWidgetWindow.mainView = view;
callsWidgetWindow.win = {webContents: {id: 1}};
const focus = jest.fn();
beforeEach(() => {
urlUtils.parseURL.mockImplementation((url) => {
try {
return new URL(url);
} catch (e) {
return undefined;
}
});
MainWindow.get.mockReturnValue({focus});
ViewManager.getView.mockReturnValue(view);
ViewManager.handleDeepLink = jest.fn();
});
it('should switch server, focus and send history push event', () => {
const url = '/team/channel';
callsWidgetWindow.handleCallsLinkClick({sender: {id: 1}}, url);
expect(ServerViewState.switchServer).toHaveBeenCalledWith('server-1');
expect(focus).toHaveBeenCalled();
expect(view.sendToRenderer).toBeCalledWith(BROWSER_HISTORY_PUSH, url);
});
it('should call ViewManager.handleDeepLink for parseable urls', () => {
const url = 'http://localhost:8065/team/channel';
callsWidgetWindow.handleCallsLinkClick({sender: {id: 1}}, url);
expect(ViewManager.handleDeepLink).toHaveBeenCalledWith(new URL(url));
});
});
describe('isOpen', () => {
const callsWidgetWindow = new CallsWidgetWindow();

View File

@@ -69,7 +69,7 @@ export class CallsWidgetWindow {
// forwards to the main app
ipcMain.on(DESKTOP_SOURCES_MODAL_REQUEST, this.forwardToMainApp(DESKTOP_SOURCES_MODAL_REQUEST));
ipcMain.on(CALLS_ERROR, this.forwardToMainApp(CALLS_ERROR));
ipcMain.on(CALLS_LINK_CLICK, this.forwardToMainApp(CALLS_LINK_CLICK));
ipcMain.on(CALLS_LINK_CLICK, this.handleCallsLinkClick);
ipcMain.on(CALLS_JOIN_REQUEST, this.forwardToMainApp(CALLS_JOIN_REQUEST));
// deprecated in favour of CALLS_LINK_CLICK
@@ -394,7 +394,7 @@ export class CallsWidgetWindow {
}
}
const screenPermissionsErrMsg = {err: 'screen-permissions'};
const screenPermissionsErrArgs = ['screen-permissions', this.callID];
return desktopCapturer.getSources(opts).then((sources) => {
let hasScreenPermissions = true;
@@ -409,8 +409,8 @@ export class CallsWidgetWindow {
if (!hasScreenPermissions || !sources.length) {
log.info('missing screen permissions');
view.sendToRenderer(CALLS_ERROR, screenPermissionsErrMsg);
this.win?.webContents.send(CALLS_ERROR, screenPermissionsErrMsg);
view.sendToRenderer(CALLS_ERROR, ...screenPermissionsErrArgs);
this.win?.webContents.send(CALLS_ERROR, ...screenPermissionsErrArgs);
return [];
}
@@ -426,8 +426,8 @@ export class CallsWidgetWindow {
}).catch((err) => {
log.error('desktopCapturer.getSources failed', err);
view.sendToRenderer(CALLS_ERROR, screenPermissionsErrMsg);
this.win?.webContents.send(CALLS_ERROR, screenPermissionsErrMsg);
view.sendToRenderer(CALLS_ERROR, ...screenPermissionsErrArgs);
this.win?.webContents.send(CALLS_ERROR, ...screenPermissionsErrArgs);
return [];
});
@@ -505,6 +505,31 @@ export class CallsWidgetWindow {
};
}
private handleCallsLinkClick = (event: IpcMainEvent, url: string) => {
log.debug('handleCallsLinkClick', url);
if (!this.isCallsWidget(event.sender.id)) {
return;
}
if (!this.serverID) {
return;
}
const parsedURL = parseURL(url);
if (parsedURL) {
ViewManager.handleDeepLink(parsedURL);
return;
}
// If parsing above fails it means it's a relative path (e.g.
// pointing to a channel).
ServerViewState.switchServer(this.serverID);
MainWindow.get()?.focus();
this.mainView?.sendToRenderer(BROWSER_HISTORY_PUSH, url);
}
/**
* @deprecated
*/