[MM-22555] Auto-fill server URLs when deep linking into the Desktop App and the server isn't configured (#3200)

* Allow deep linking to non-configured servers by auto filling the modal

* Fall back to base URL if URL with path name does not work

* Allow deep linking directly into a new server with a permalink

* Support welcome screen/no server case

* Some cleanup
This commit is contained in:
Devin Binnie
2024-11-18 14:08:00 -05:00
committed by GitHub
parent 21487e2496
commit 8aa0b86c7a
14 changed files with 107 additions and 38 deletions

View File

@@ -65,6 +65,18 @@ export class ModalManager {
return this.modalPromises.get(key) as Promise<T2>;
};
removeModal = (key: string) => {
const modalView = this.modalQueue.find((modal) => modal.key === key);
if (!modalView) {
return;
}
modalView.hide();
modalView.resolve(null);
this.modalPromises.delete(key);
this.filterActive();
};
findModalByCaller = (event: IpcMainInvokeEvent) => {
if (this.modalQueue.length) {
const requestModal = this.modalQueue.find((modal) => {

View File

@@ -1,8 +1,6 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {dialog} from 'electron';
import ServerViewState from 'app/serverViewState';
import {BROWSER_HISTORY_PUSH, LOAD_SUCCESS, SET_ACTIVE_VIEW} from 'common/communication';
import ServerManager from 'common/servers/serverManager';
@@ -20,9 +18,6 @@ jest.mock('electron', () => ({
getAppPath: () => '/path/to/app',
getPath: jest.fn(() => '/valid/downloads/path'),
},
dialog: {
showErrorBox: jest.fn(),
},
ipcMain: {
emit: jest.fn(),
on: jest.fn(),
@@ -33,6 +28,7 @@ jest.mock('app/serverViewState', () => ({
getCurrentServer: jest.fn(),
updateCurrentView: jest.fn(),
init: jest.fn(),
showNewServerModal: jest.fn(),
}));
jest.mock('common/views/View', () => ({
getViewName: jest.fn((a, b) => `${a}-${b}`),
@@ -62,6 +58,10 @@ jest.mock('main/app/utils', () => ({
flushCookiesStore: jest.fn(),
}));
jest.mock('main/app/intercom', () => ({
handleWelcomeScreenModal: jest.fn(),
}));
jest.mock('main/i18nManager', () => ({
localizeMessage: jest.fn(),
}));
@@ -116,8 +116,9 @@ jest.mock('./MattermostWebContentsView', () => ({
MattermostWebContentsView: jest.fn(),
}));
jest.mock('./modalManager', () => ({
jest.mock('main/views/modalManager', () => ({
showModal: jest.fn(),
removeModal: jest.fn(),
isModalDisplayed: jest.fn(),
}));
jest.mock('./webContentEvents', () => ({}));
@@ -321,6 +322,7 @@ describe('main/views/viewManager', () => {
isOpen: true,
url: new URL('http://server1.com/view'),
},
undefined,
);
makeSpy.mockRestore();
});
@@ -692,11 +694,12 @@ describe('main/views/viewManager', () => {
expect(view.load).not.toHaveBeenCalled();
});
it('should throw dialog when cannot find the view', () => {
it('should open new server modal when using a server that does not exist', () => {
ServerManager.hasServers.mockReturnValue(true);
const view = {...baseView};
viewManager.handleDeepLink('mattermost://server-1.com/deep/link?thing=yes');
viewManager.handleDeepLink('mattermost://server-2.com/deep/link?thing=yes');
expect(view.load).not.toHaveBeenCalled();
expect(dialog.showErrorBox).toHaveBeenCalled();
expect(ServerViewState.showNewServerModal).toHaveBeenCalled();
});
it('should reopen closed view if called upon', () => {

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {WebContentsView, dialog, ipcMain} from 'electron';
import {WebContentsView, ipcMain} from 'electron';
import isDev from 'electron-is-dev';
import ServerViewState from 'app/serverViewState';
@@ -41,18 +41,18 @@ import {getFormattedPathName, parseURL} from 'common/utils/url';
import Utils from 'common/utils/util';
import type {MattermostView} from 'common/views/View';
import {TAB_MESSAGING} from 'common/views/View';
import {handleWelcomeScreenModal} from 'main/app/intercom';
import {flushCookiesStore} from 'main/app/utils';
import DeveloperMode from 'main/developerMode';
import {localizeMessage} from 'main/i18nManager';
import performanceMonitor from 'main/performanceMonitor';
import PermissionsManager from 'main/permissionsManager';
import ModalManager from 'main/views/modalManager';
import MainWindow from 'main/windows/mainWindow';
import type {DeveloperSettings} from 'types/settings';
import LoadingScreen from './loadingScreen';
import {MattermostWebContentsView} from './MattermostWebContentsView';
import modalManager from './modalManager';
import {getLocalPreload, getAdjustedWindowBoundaries} from '../utils';
@@ -158,14 +158,14 @@ export class ViewManager {
} else {
this.getViewLogger(viewId).warn(`Couldn't find a view with name: ${viewId}`);
}
modalManager.showModal();
ModalManager.showModal();
};
focusCurrentView = () => {
log.debug('focusCurrentView');
if (modalManager.isModalDisplayed()) {
modalManager.focusCurrentModal();
if (ModalManager.isModalDisplayed()) {
ModalManager.focusCurrentModal();
return;
}
@@ -227,11 +227,11 @@ export class ViewManager {
webContentsView.once(LOAD_FAILED, this.deeplinkFailed);
}
}
} else if (ServerManager.hasServers()) {
ServerViewState.showNewServerModal(`${parsedURL.host}${getFormattedPathName(parsedURL.pathname)}${parsedURL.search}`);
} else {
dialog.showErrorBox(
localizeMessage('main.views.viewManager.handleDeepLink.error.title', 'No matching server'),
localizeMessage('main.views.viewManager.handleDeepLink.error.body', 'There is no configured server in the app that matches the requested url: {url}', {url: parsedURL.toString()}),
);
ModalManager.removeModal('welcomeScreen');
handleWelcomeScreenModal(`${parsedURL.host}${getFormattedPathName(parsedURL.pathname)}${parsedURL.search}`);
}
}
};
@@ -439,7 +439,7 @@ export class ViewManager {
} else if (recycle) {
views.set(view.id, recycle);
} else {
views.set(view.id, this.makeView(srv, view));
views.set(view.id, this.makeView(srv, view, srv.initialLoadURL?.toString()));
}
}