[MM-52696] Upgrade and clean up Desktop App dev dependencies (#2970)

* Upgrade to ESLint v8

* Upgrade TypeScript, api-types, react-intl

* Remove unnecessary dependencies

* Update to React 17.0.2

* npm audit fixes, remove storybook

* Lock some packages

* Remove nan patch

* Remove some deprecated dependencies

* Fix lint/type/tests

* Merge'd

* Fix bad use of spawn

* Fix notarize

* Fix afterpack, switch to tsc es2020

* Fix api types

* Use @mattermost/eslint-plugin
This commit is contained in:
Devin Binnie
2024-03-07 15:55:33 -05:00
committed by GitHub
parent 12d59cd81c
commit 9b36c25e4e
198 changed files with 4997 additions and 17374 deletions

View File

@@ -8,11 +8,11 @@ import {LOAD_FAILED, TOGGLE_BACK_BUTTON, UPDATE_TARGET_URL} from 'common/communi
import {MattermostServer} from 'common/servers/MattermostServer';
import MessagingView from 'common/views/MessagingView';
import MainWindow from '../windows/mainWindow';
import {MattermostBrowserView} from './MattermostBrowserView';
import ContextMenu from '../contextMenu';
import Utils from '../utils';
import {MattermostBrowserView} from './MattermostBrowserView';
import MainWindow from '../windows/mainWindow';
jest.mock('electron', () => ({
app: {

View File

@@ -2,11 +2,9 @@
// See LICENSE.txt for license information.
import {BrowserView, app, ipcMain} from 'electron';
import {BrowserViewConstructorOptions, Event, Input} from 'electron/main';
import type {BrowserViewConstructorOptions, Event, Input} from 'electron/main';
import {EventEmitter} from 'events';
import {RELOAD_INTERVAL, MAX_SERVER_RETRIES, SECOND, MAX_LOADING_SCREEN_SECONDS} from 'common/utils/constants';
import AppState from 'common/appState';
import {
LOAD_RETRY,
@@ -21,18 +19,18 @@ import {
CLOSE_SERVERS_DROPDOWN,
CLOSE_DOWNLOADS_DROPDOWN,
} from 'common/communication';
import type {Logger} from 'common/log';
import ServerManager from 'common/servers/serverManager';
import {Logger} from 'common/log';
import {RELOAD_INTERVAL, MAX_SERVER_RETRIES, SECOND, MAX_LOADING_SCREEN_SECONDS} from 'common/utils/constants';
import {isInternalURL, parseURL} from 'common/utils/url';
import {MattermostView} from 'common/views/View';
import type {MattermostView} from 'common/views/View';
import MainWindow from 'main/windows/mainWindow';
import WebContentsEventManager from './webContentEvents';
import ContextMenu from '../contextMenu';
import {getWindowBoundaries, getLocalPreload, composeUserAgent, shouldHaveBackBar} from '../utils';
import WebContentsEventManager from './webContentEvents';
enum Status {
LOADING,
READY,
@@ -52,7 +50,7 @@ export class MattermostBrowserView extends EventEmitter {
private loggedIn: boolean;
private atRoot: boolean;
private options: BrowserViewConstructorOptions;
private removeLoading?: number;
private removeLoading?: NodeJS.Timeout;
private contextMenu: ContextMenu;
private status?: Status;
private retryLoad?: NodeJS.Timeout;
@@ -142,7 +140,7 @@ export class MattermostBrowserView extends EventEmitter {
) {
this.reload();
}
}
};
goToOffset = (offset: number) => {
if (this.browserView.webContents.canGoToOffset(offset)) {
@@ -154,7 +152,7 @@ export class MattermostBrowserView extends EventEmitter {
this.reload();
}
}
}
};
getBrowserHistoryStatus = () => {
if (this.currentURL?.toString() === this.view.url.toString()) {
@@ -168,12 +166,12 @@ export class MattermostBrowserView extends EventEmitter {
canGoBack: this.browserView.webContents.canGoBack(),
canGoForward: this.browserView.webContents.canGoForward(),
};
}
};
updateHistoryButton = () => {
const {canGoBack, canGoForward} = this.getBrowserHistoryStatus();
this.browserView.webContents.send(BROWSER_HISTORY_STATUS_UPDATED, canGoBack, canGoForward);
}
};
load = (someURL?: URL | string) => {
if (!this.browserView) {
@@ -208,7 +206,7 @@ export class MattermostBrowserView extends EventEmitter {
}
this.loadRetry(loadURL, err);
});
}
};
show = () => {
const mainWindow = MainWindow.get();
@@ -228,32 +226,32 @@ export class MattermostBrowserView extends EventEmitter {
if (this.status === Status.READY) {
this.focus();
}
}
};
hide = () => {
if (this.isVisible) {
this.isVisible = false;
MainWindow.get()?.removeBrowserView(this.browserView);
}
}
};
reload = () => {
this.resetLoadingStatus();
AppState.updateExpired(this.id, false);
this.load();
}
};
getBounds = () => {
return this.browserView.getBounds();
}
};
openFind = () => {
this.browserView.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: [process.platform === 'darwin' ? 'cmd' : 'ctrl', 'shift']});
}
};
setBounds = (boundaries: Electron.Rectangle) => {
this.browserView.setBounds(boundaries);
}
};
destroy = () => {
WebContentsEventManager.removeWebContentsListeners(this.webContentsId);
@@ -273,7 +271,7 @@ export class MattermostBrowserView extends EventEmitter {
if (this.removeLoading) {
clearTimeout(this.removeLoading);
}
}
};
/**
* Code to turn off the old method of getting unreads
@@ -282,7 +280,7 @@ export class MattermostBrowserView extends EventEmitter {
offLegacyUnreads = () => {
this.browserView.webContents.off('page-title-updated', this.handleTitleUpdate);
this.browserView.webContents.off('page-favicon-updated', this.handleFaviconUpdate);
}
};
/**
* Status hooks
@@ -294,19 +292,19 @@ export class MattermostBrowserView extends EventEmitter {
this.status = Status.LOADING;
this.maxRetries = MAX_SERVER_RETRIES;
}
}
};
isReady = () => {
return this.status === Status.READY;
}
};
isErrored = () => {
return this.status === Status.ERROR;
}
};
needsLoadingScreen = () => {
return !(this.status === Status.READY || this.status === Status.ERROR);
}
};
setInitialized = (timedout?: boolean) => {
this.status = Status.READY;
@@ -317,7 +315,7 @@ export class MattermostBrowserView extends EventEmitter {
}
clearTimeout(this.removeLoading);
delete this.removeLoading;
}
};
openDevTools = () => {
// Workaround for a bug with our Dev Tools on Mac
@@ -336,7 +334,7 @@ export class MattermostBrowserView extends EventEmitter {
}
this.browserView.webContents.openDevTools({mode: 'detach'});
}
};
/**
* WebContents hooks
@@ -344,11 +342,11 @@ export class MattermostBrowserView extends EventEmitter {
sendToRenderer = (channel: string, ...args: any[]) => {
this.browserView.webContents.send(channel, ...args);
}
};
isDestroyed = () => {
return this.browserView.webContents.isDestroyed();
}
};
focus = () => {
if (this.browserView.webContents) {
@@ -356,7 +354,7 @@ export class MattermostBrowserView extends EventEmitter {
} else {
this.log.warn('trying to focus the browserview, but it doesn\'t yet have webcontents.');
}
}
};
/**
* ALT key handling for the 3-dot menu (Windows/Linux)
@@ -386,7 +384,7 @@ export class MattermostBrowserView extends EventEmitter {
if (this.isAltKeyReleased(input)) {
MainWindow.focusThreeDotMenu();
}
}
};
/**
* Unreads/mentions handlers
@@ -398,7 +396,7 @@ export class MattermostBrowserView extends EventEmitter {
const mentions = (results && results.value && parseInt(results.value[MENTIONS_GROUP], 10)) || 0;
AppState.updateMentions(this.id, mentions);
}
};
// if favicon is null, it will affect appState, but won't be memoized
private findUnreadState = (favicon: string | null) => {
@@ -407,13 +405,13 @@ export class MattermostBrowserView extends EventEmitter {
} catch (err: any) {
this.log.error('There was an error trying to request the unread state', err);
}
}
};
private handleTitleUpdate = (e: Event, title: string) => {
this.log.debug('handleTitleUpdate', title);
this.updateMentionsFromTitle(title);
}
};
private handleFaviconUpdate = (e: Event, favicons: string[]) => {
this.log.silly('handleFaviconUpdate', favicons);
@@ -421,7 +419,7 @@ export class MattermostBrowserView extends EventEmitter {
// if unread state is stored for that favicon, retrieve value.
// if not, get related info from preload and store it for future changes
this.findUnreadState(favicons[0]);
}
};
/**
* Loading/retry logic
@@ -446,7 +444,7 @@ export class MattermostBrowserView extends EventEmitter {
}
});
};
}
};
private retryInBackground = (loadURL: string) => {
return () => {
@@ -459,13 +457,13 @@ export class MattermostBrowserView extends EventEmitter {
this.retryLoad = setTimeout(this.retryInBackground(loadURL), RELOAD_INTERVAL);
});
};
}
};
private loadRetry = (loadURL: string, err: Error) => {
this.retryLoad = setTimeout(this.retry(loadURL), RELOAD_INTERVAL);
MainWindow.sendToRenderer(LOAD_RETRY, this.id, Date.now() + RELOAD_INTERVAL, err.toString(), loadURL.toString());
this.log.info(`failed loading ${loadURL}: ${err}, retrying in ${RELOAD_INTERVAL / SECOND} seconds`);
}
};
private loadSuccess = (loadURL: string) => {
return () => {
@@ -484,7 +482,7 @@ export class MattermostBrowserView extends EventEmitter {
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.view.url || '', this.currentURL)));
}
};
}
};
/**
* WebContents event handlers
@@ -511,7 +509,7 @@ export class MattermostBrowserView extends EventEmitter {
MainWindow.sendToRenderer(TOGGLE_BACK_BUTTON, false);
this.log.debug('hide back button');
}
}
};
private handleUpdateTarget = (e: Event, url: string) => {
this.log.silly('handleUpdateTarget', e, url);
@@ -521,11 +519,11 @@ export class MattermostBrowserView extends EventEmitter {
} else {
this.emit(UPDATE_TARGET_URL, url);
}
}
};
private handleServerWasModified = (serverIds: string) => {
if (serverIds.includes(this.view.server.id)) {
this.reload();
}
}
};
}

View File

@@ -6,7 +6,6 @@
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import {DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, TAB_BAR_HEIGHT} from 'common/utils/constants';
import MainWindow from 'main/windows/mainWindow';
import {DownloadsDropdownMenuView} from './downloadsDropdownMenuView';

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, ipcMain, IpcMainEvent} from 'electron';
import {CoordinatesToJsonType, DownloadedItem, DownloadsMenuOpenEventPayload} from 'types/downloads';
import type {IpcMainEvent} from 'electron';
import {BrowserView, ipcMain} from 'electron';
import {
CLOSE_DOWNLOADS_DROPDOWN_MENU,
@@ -19,18 +18,20 @@ import {
UPDATE_DOWNLOADS_DROPDOWN_MENU,
UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM,
} from 'common/communication';
import {Logger} from 'common/log';
import Config from 'common/config';
import {Logger} from 'common/log';
import {
DOWNLOADS_DROPDOWN_FULL_WIDTH,
DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT,
DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH,
TAB_BAR_HEIGHT,
} from 'common/utils/constants';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import downloadsManager from 'main/downloadsManager';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import type {CoordinatesToJsonType, DownloadedItem, DownloadsMenuOpenEventPayload} from 'types/downloads';
const log = new Logger('DownloadsDropdownMenuView');
export class DownloadsDropdownMenuView {
@@ -76,7 +77,7 @@ export class DownloadsDropdownMenuView {
}});
this.view.webContents.loadURL(getLocalURLString('downloadsDropdownMenu.html'));
MainWindow.get()?.addBrowserView(this.view);
}
};
/**
* This is called every time the "window" is resized so that we can position
@@ -88,14 +89,14 @@ export class DownloadsDropdownMenuView {
this.windowBounds = newBounds;
this.updateDownloadsDropdownMenu();
this.repositionDownloadsDropdownMenu();
}
};
private updateItem = (event: IpcMainEvent, item: DownloadedItem) => {
log.debug('updateItem', {item});
this.item = item;
this.updateDownloadsDropdownMenu();
}
};
private updateDownloadsDropdownMenu = () => {
log.silly('updateDownloadsDropdownMenu');
@@ -107,7 +108,7 @@ export class DownloadsDropdownMenuView {
);
ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, true, this.item);
this.repositionDownloadsDropdownMenu();
}
};
private handleOpen = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload = {} as DownloadsMenuOpenEventPayload) => {
log.debug('handleOpen', {bounds: this.bounds, payload});
@@ -128,7 +129,7 @@ export class DownloadsDropdownMenuView {
MainWindow.get()?.setTopBrowserView(this.view);
this.view.webContents.focus();
this.updateDownloadsDropdownMenu();
}
};
private handleClose = () => {
log.silly('handleClose');
@@ -138,7 +139,7 @@ export class DownloadsDropdownMenuView {
ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM);
this.view?.setBounds(this.getBounds(this.windowBounds?.width ?? 0, 0, 0));
MainWindow.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN_MENU);
}
};
private handleToggle = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload) => {
if (this.open) {
@@ -153,27 +154,27 @@ export class DownloadsDropdownMenuView {
} else {
this.handleOpen(event, payload);
}
}
};
private openFile = () => {
downloadsManager.openFile(this.item);
this.handleClose();
}
};
private showFileInFolder = (e: IpcMainEvent, item: DownloadedItem) => {
downloadsManager.showFileInFolder(item);
this.handleClose();
}
};
private clearFile = () => {
downloadsManager.clearFile(this.item);
this.handleClose();
}
};
private cancelDownload = () => {
downloadsManager.cancelDownload(this.item);
this.handleClose();
}
};
private getBounds = (windowWidth: number, width: number, height: number) => {
// MUST return integers
@@ -183,7 +184,7 @@ export class DownloadsDropdownMenuView {
width: Math.round(width),
height: Math.round(height),
};
}
};
private getX = (windowWidth: number) => {
const result = (windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH - DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH) + (this.coordinates?.x || 0) + (this.coordinates?.width || 0);
@@ -191,12 +192,12 @@ export class DownloadsDropdownMenuView {
return 0;
}
return Math.round(result);
}
};
private getY = () => {
const result = TAB_BAR_HEIGHT + (this.coordinates?.y || 0) + (this.coordinates?.height || 0);
return Math.round(result);
}
};
private repositionDownloadsDropdownMenu = () => {
if (!this.windowBounds) {
@@ -207,7 +208,7 @@ export class DownloadsDropdownMenuView {
if (this.open) {
this.view?.setBounds(this.bounds);
}
}
};
}
const downloadsDropdownMenuView = new DownloadsDropdownMenuView();

View File

@@ -6,7 +6,6 @@
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import {DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT, TAB_BAR_HEIGHT} from 'common/utils/constants';
import MainWindow from 'main/windows/mainWindow';
import {DownloadsDropdownView} from './downloadsDropdownView';

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, ipcMain, IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {DownloadedItem} from 'types/downloads';
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {BrowserView, ipcMain} from 'electron';
import {
CLOSE_DOWNLOADS_DROPDOWN,
@@ -19,14 +18,15 @@ import {
MAIN_WINDOW_CREATED,
MAIN_WINDOW_RESIZED,
} from 'common/communication';
import {Logger} from 'common/log';
import Config from 'common/config';
import {Logger} from 'common/log';
import {TAB_BAR_HEIGHT, DOWNLOADS_DROPDOWN_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT, DOWNLOADS_DROPDOWN_FULL_WIDTH} from 'common/utils/constants';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import downloadsManager from 'main/downloadsManager';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import type {DownloadedItem} from 'types/downloads';
const log = new Logger('DownloadsDropdownView');
export class DownloadsDropdownView {
@@ -70,7 +70,7 @@ export class DownloadsDropdownView {
this.view.webContents.loadURL(getLocalURLString('downloadsDropdown.html'));
this.view.webContents.session.webRequest.onHeadersReceived(downloadsManager.webRequestOnHeadersReceivedHandler);
MainWindow.get()?.addBrowserView(this.view);
}
};
/**
* This is called every time the "window" is resized so that we can position
@@ -82,13 +82,13 @@ export class DownloadsDropdownView {
this.windowBounds = newBounds;
this.updateDownloadsDropdown();
this.repositionDownloadsDropdown();
}
};
private updateDownloadsDropdownMenuItem = (event: IpcMainEvent, item?: DownloadedItem) => {
log.silly('updateDownloadsDropdownMenuItem', {item});
this.item = item;
this.updateDownloadsDropdown();
}
};
private updateDownloadsDropdown = () => {
log.silly('updateDownloadsDropdown');
@@ -100,7 +100,7 @@ export class DownloadsDropdownView {
MainWindow.getBounds(),
this.item,
);
}
};
private handleOpen = () => {
log.debug('handleOpen', {bounds: this.bounds});
@@ -114,7 +114,7 @@ export class DownloadsDropdownView {
this.view.webContents.focus();
downloadsManager.onOpen();
MainWindow.sendToRenderer(OPEN_DOWNLOADS_DROPDOWN);
}
};
private handleClose = () => {
log.silly('handleClose');
@@ -122,18 +122,18 @@ export class DownloadsDropdownView {
this.view?.setBounds(this.getBounds(this.windowBounds?.width ?? 0, 0, 0));
downloadsManager.onClose();
MainWindow.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN);
}
};
private clearDownloads = () => {
downloadsManager.clearDownloadsDropDown();
this.handleClose();
}
};
private openFile = (e: IpcMainEvent, item: DownloadedItem) => {
log.debug('openFile', {item});
downloadsManager.openFile(item);
}
};
private getBounds = (windowWidth: number, width: number, height: number) => {
// Must always use integers
@@ -143,7 +143,7 @@ export class DownloadsDropdownView {
width: Math.round(width),
height: Math.round(height),
};
}
};
private getX = (windowWidth: number) => {
const result = windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH;
@@ -151,11 +151,11 @@ export class DownloadsDropdownView {
return 0;
}
return Math.round(result);
}
};
private getY = () => {
return Math.round(TAB_BAR_HEIGHT);
}
};
private repositionDownloadsDropdown = () => {
if (!(this.bounds && this.windowBounds)) {
@@ -169,7 +169,7 @@ export class DownloadsDropdownView {
if (downloadsManager.getIsOpen()) {
this.view?.setBounds(this.bounds);
}
}
};
private handleReceivedDownloadsDropdownSize = (event: IpcMainEvent, width: number, height: number) => {
log.silly('handleReceivedDownloadsDropdownSize', {width, height});
@@ -182,11 +182,11 @@ export class DownloadsDropdownView {
if (downloadsManager.getIsOpen()) {
this.view?.setBounds(this.bounds);
}
}
};
private getDownloadImageThumbnailLocation = (event: IpcMainInvokeEvent, location: string) => {
return location;
}
};
}
const downloadsDropdownView = new DownloadsDropdownView();

View File

@@ -5,7 +5,6 @@ import {BrowserView, app, ipcMain} from 'electron';
import {DARK_MODE_CHANGE, LOADING_SCREEN_ANIMATION_FINISHED, MAIN_WINDOW_RESIZED, TOGGLE_LOADING_SCREEN_VISIBILITY} from 'common/communication';
import {Logger} from 'common/log';
import {getLocalPreload, getLocalURLString, getWindowBoundaries} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
@@ -34,11 +33,11 @@ export class LoadingScreen {
setDarkMode = (darkMode: boolean) => {
this.view?.webContents.send(DARK_MODE_CHANGE, darkMode);
}
};
isHidden = () => {
return this.state === LoadingScreenState.HIDDEN;
}
};
show = () => {
const mainWindow = MainWindow.get();
@@ -67,14 +66,14 @@ export class LoadingScreen {
}
this.setBounds();
}
};
fade = () => {
if (this.view && this.state === LoadingScreenState.VISIBLE) {
this.state = LoadingScreenState.FADING;
this.view.webContents.send(TOGGLE_LOADING_SCREEN_VISIBILITY, false);
}
}
};
private create = () => {
const preload = getLocalPreload('internalAPI.js');
@@ -88,7 +87,7 @@ export class LoadingScreen {
}});
const localURL = getLocalURLString('loadingScreen.html');
this.view.webContents.loadURL(localURL);
}
};
private handleAnimationFinished = () => {
log.debug('handleLoadingScreenAnimationFinished');
@@ -101,7 +100,7 @@ export class LoadingScreen {
if (process.env.NODE_ENV === 'test') {
app.emit('e2e-app-loaded');
}
}
};
private setBounds = () => {
if (this.view) {
@@ -111,7 +110,7 @@ export class LoadingScreen {
}
this.view.setBounds(getWindowBoundaries(mainWindow));
}
}
};
}
const loadingScreen = new LoadingScreen();

View File

@@ -1,10 +1,9 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserWindow, ipcMain} from 'electron';
import {IpcMainEvent, IpcMainInvokeEvent} from 'electron/main';
import {CombinedConfig} from 'types/config';
import type {BrowserWindow} from 'electron';
import {ipcMain} from 'electron';
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron/main';
import {
RETRIEVE_MODAL_INFO,
@@ -18,11 +17,12 @@ import {
MAIN_WINDOW_RESIZED,
} from 'common/communication';
import {Logger} from 'common/log';
import {getAdjustedWindowBoundaries} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import WebContentsEventManager from 'main/views/webContentEvents';
import ViewManager from 'main/views/viewManager';
import WebContentsEventManager from 'main/views/webContentEvents';
import MainWindow from 'main/windows/mainWindow';
import type {CombinedConfig} from 'types/config';
import {ModalView} from './modalView';
@@ -62,7 +62,7 @@ export class ModalManager {
return modalPromise;
}
return this.modalPromises.get(key) as Promise<T2>;
}
};
findModalByCaller = (event: IpcMainInvokeEvent) => {
if (this.modalQueue.length) {
@@ -72,7 +72,7 @@ export class ModalManager {
return requestModal;
}
return null;
}
};
handleInfoRequest = (event: IpcMainInvokeEvent) => {
log.debug('handleInfoRequest');
@@ -82,7 +82,7 @@ export class ModalManager {
return requestModal.handleInfoRequest();
}
return null;
}
};
showModal = () => {
const withDevTools = process.env.MM_DEBUG_MODALS || false;
@@ -96,7 +96,7 @@ export class ModalManager {
modal.hide();
}
});
}
};
handleModalFinished = (mode: 'resolve' | 'reject', event: IpcMainEvent, data: unknown) => {
log.debug('handleModalFinished', {mode, data});
@@ -117,7 +117,7 @@ export class ModalManager {
MainWindow.sendToRenderer(MODAL_CLOSE);
ViewManager.focusCurrentView();
}
}
};
handleModalResult = (event: IpcMainEvent, data: unknown) => this.handleModalFinished('resolve', event, data);
@@ -125,11 +125,11 @@ export class ModalManager {
filterActive = () => {
this.modalQueue = this.modalQueue.filter((modal) => modal.isActive());
}
};
isModalDisplayed = () => {
return this.modalQueue.some((modal) => modal.isActive());
}
};
handleResizeModal = (bounds: Electron.Rectangle) => {
log.debug('handleResizeModal', {bounds, modalQueueLength: this.modalQueue.length});
@@ -138,13 +138,13 @@ export class ModalManager {
const currentModal = this.modalQueue[0];
currentModal.view.setBounds(getAdjustedWindowBoundaries(bounds.width, bounds.height));
}
}
};
focusCurrentModal = () => {
if (this.isModalDisplayed()) {
this.modalQueue[0].view.webContents.focus();
}
}
};
handleEmitConfiguration = (event: IpcMainEvent, config: CombinedConfig) => {
if (this.modalQueue.length) {
@@ -154,12 +154,12 @@ export class ModalManager {
this.modalQueue.forEach((modal) => {
modal.view.webContents.send(DARK_MODE_CHANGE, config.darkMode);
});
}
};
handleGetModalUncloseable = (event: IpcMainInvokeEvent) => {
const modalView = this.modalQueue.find((modal) => modal.view.webContents.id === event.sender.id);
return modalView?.uncloseable;
}
};
}
const modalManager = new ModalManager();

View File

@@ -1,7 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, BrowserWindow} from 'electron';
import type {BrowserWindow} from 'electron';
import {BrowserView} from 'electron';
import {Logger} from 'common/log';
@@ -90,7 +91,7 @@ export class ModalView<T, T2> {
this.log.info(`showing dev tools for ${this.key}`);
this.view.webContents.openDevTools({mode: 'detach'});
}
}
};
hide = () => {
if (this.windowAttached) {
@@ -108,11 +109,11 @@ export class ModalView<T, T2> {
delete this.windowAttached;
this.status = Status.ACTIVE;
}
}
};
handleInfoRequest = () => {
return this.data;
}
};
reject = (data: T2) => {
if (this.onReject) {
@@ -120,7 +121,7 @@ export class ModalView<T, T2> {
}
this.hide();
this.status = Status.DONE;
}
};
resolve = (data: T2) => {
if (this.onResolve) {
@@ -128,7 +129,7 @@ export class ModalView<T, T2> {
}
this.hide();
this.status = Status.DONE;
}
};
isActive = () => this.status !== Status.DONE;
}

View File

@@ -4,7 +4,6 @@
'use strict';
import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHADOW_WIDTH} from 'common/utils/constants';
import MainWindow from 'main/windows/mainWindow';
import {ServerDropdownView} from './serverDropdownView';

View File

@@ -1,12 +1,10 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, ipcMain, IpcMainEvent} from 'electron';
import {UniqueServer} from 'types/config';
import type {IpcMainEvent} from 'electron';
import {BrowserView, ipcMain} from 'electron';
import ServerViewState from 'app/serverViewState';
import AppState from 'common/appState';
import {
CLOSE_SERVERS_DROPDOWN,
@@ -22,11 +20,12 @@ import {
} from 'common/communication';
import Config from 'common/config';
import {Logger} from 'common/log';
import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHADOW_WIDTH} from 'common/utils/constants';
import ServerManager from 'common/servers/serverManager';
import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHADOW_WIDTH} from 'common/utils/constants';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import type {UniqueServer} from 'types/config';
import MainWindow from '../windows/mainWindow';
const log = new Logger('ServerDropdownView');
@@ -71,7 +70,7 @@ export class ServerDropdownView {
private updateWindowBounds = (newBounds: Electron.Rectangle) => {
this.windowBounds = newBounds;
this.updateDropdown();
}
};
private init = () => {
log.info('init');
@@ -90,7 +89,7 @@ export class ServerDropdownView {
this.windowBounds = MainWindow.getBounds();
this.updateDropdown();
MainWindow.get()?.addBrowserView(this.view);
}
};
private updateDropdown = () => {
log.silly('updateDropdown');
@@ -107,12 +106,12 @@ export class ServerDropdownView {
this.mentions,
this.unreads,
);
}
};
private updateServers = () => {
this.setOrderedServers();
this.updateDropdown();
}
};
private updateMentions = (expired: Map<string, boolean>, mentions: Map<string, number>, unreads: Map<string, boolean>) => {
log.silly('updateMentions', {expired, mentions, unreads});
@@ -121,7 +120,7 @@ export class ServerDropdownView {
this.mentions = this.reduceNotifications(this.mentions, mentions, (base, value) => (base ?? 0) + (value ?? 0));
this.expired = this.reduceNotifications(this.expired, expired, (base, value) => base || value || false);
this.updateDropdown();
}
};
/**
* Menu open/close/size handlers
@@ -141,7 +140,7 @@ export class ServerDropdownView {
this.view.webContents.focus();
MainWindow.sendToRenderer(OPEN_SERVERS_DROPDOWN);
this.isOpen = true;
}
};
private handleClose = () => {
log.silly('handleClose');
@@ -149,7 +148,7 @@ export class ServerDropdownView {
this.view?.setBounds(this.getBounds(0, 0));
MainWindow.sendToRenderer(CLOSE_SERVERS_DROPDOWN);
this.isOpen = false;
}
};
private handleReceivedMenuSize = (event: IpcMainEvent, width: number, height: number) => {
log.silly('handleReceivedMenuSize', {width, height});
@@ -158,7 +157,7 @@ export class ServerDropdownView {
if (this.isOpen) {
this.view?.setBounds(this.bounds);
}
}
};
/**
* Helpers
@@ -171,7 +170,7 @@ export class ServerDropdownView {
width,
height,
};
}
};
private reduceNotifications = <T>(inputMap: Map<string, T>, items: Map<string, T>, modifier: (base?: T, value?: T) => T) => {
inputMap.clear();
@@ -183,12 +182,12 @@ export class ServerDropdownView {
map.set(view.server.id, modifier(map.get(view.server.id), items.get(key)));
return map;
}, inputMap);
}
};
private setOrderedServers = () => {
this.servers = ServerManager.getOrderedServers().map((server) => server.toUniqueServer());
this.hasGPOServers = this.servers.some((srv) => srv.isPredefined);
}
};
}
const serverDropdownView = new ServerDropdownView();

View File

@@ -7,17 +7,15 @@
import {dialog} from 'electron';
import ServerViewState from 'app/serverViewState';
import {BROWSER_HISTORY_PUSH, LOAD_SUCCESS, SET_ACTIVE_VIEW} from 'common/communication';
import {TAB_MESSAGING} from 'common/views/View';
import ServerManager from 'common/servers/serverManager';
import urlUtils from 'common/utils/url';
import {TAB_MESSAGING} from 'common/views/View';
import MainWindow from 'main/windows/mainWindow';
import LoadingScreen from './loadingScreen';
import {MattermostBrowserView} from './MattermostBrowserView';
import {ViewManager} from './viewManager';
import LoadingScreen from './loadingScreen';
jest.mock('electron', () => ({
app: {

View File

@@ -1,13 +1,12 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, dialog, ipcMain, IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {BrowserView, dialog, ipcMain} from 'electron';
import isDev from 'electron-is-dev';
import ServerViewState from 'app/serverViewState';
import AppState from 'common/appState';
import {SECOND, TAB_BAR_HEIGHT} from 'common/utils/constants';
import {
UPDATE_TARGET_URL,
LOAD_SUCCESS,
@@ -37,20 +36,21 @@ import {
} from 'common/communication';
import Config from 'common/config';
import {Logger} from 'common/log';
import Utils from 'common/utils/util';
import {MattermostServer} from 'common/servers/MattermostServer';
import type {MattermostServer} from 'common/servers/MattermostServer';
import ServerManager from 'common/servers/serverManager';
import {MattermostView, TAB_MESSAGING} from 'common/views/View';
import {SECOND, TAB_BAR_HEIGHT} from 'common/utils/constants';
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 {localizeMessage} from 'main/i18nManager';
import MainWindow from 'main/windows/mainWindow';
import {getLocalURLString, getLocalPreload, getAdjustedWindowBoundaries, shouldHaveBackBar} from '../utils';
import LoadingScreen from './loadingScreen';
import {MattermostBrowserView} from './MattermostBrowserView';
import modalManager from './modalManager';
import LoadingScreen from './loadingScreen';
import {getLocalURLString, getLocalPreload, getAdjustedWindowBoundaries, shouldHaveBackBar} from '../utils';
const log = new Logger('ViewManager');
const URL_VIEW_DURATION = 10 * SECOND;
@@ -93,26 +93,26 @@ export class ViewManager {
LoadingScreen.show();
ServerManager.getAllServers().forEach((server) => this.loadServer(server));
this.showInitial();
}
};
getView = (viewId: string) => {
return this.views.get(viewId);
}
};
getCurrentView = () => {
if (this.currentView) {
return this.views.get(this.currentView);
}
return undefined;
}
};
getViewByWebContentsId = (webContentsId: number) => {
return [...this.views.values()].find((view) => view.webContentsId === webContentsId);
}
};
isViewClosed = (viewId: string) => {
return this.closedViews.has(viewId);
}
};
showById = (viewId: string) => {
this.getViewLogger(viewId).debug('showById', viewId);
@@ -144,7 +144,7 @@ export class ViewManager {
this.getViewLogger(viewId).warn(`Couldn't find a view with name: ${viewId}`);
}
modalManager.showModal();
}
};
focusCurrentView = () => {
log.debug('focusCurrentView');
@@ -158,7 +158,7 @@ export class ViewManager {
if (view) {
view.focus();
}
}
};
reload = () => {
const currentView = this.getCurrentView();
@@ -166,7 +166,7 @@ export class ViewManager {
LoadingScreen.show();
currentView.reload();
}
}
};
sendToAllViews = (channel: string, ...args: unknown[]) => {
this.views.forEach((view) => {
@@ -174,11 +174,11 @@ export class ViewManager {
view.sendToRenderer(channel, ...args);
}
});
}
};
sendToFind = () => {
this.getCurrentView()?.openFind();
}
};
/**
* Deep linking
@@ -231,7 +231,7 @@ export class ViewManager {
private deeplinkFailed = (viewId: string, err: string, url: string) => {
this.getViewLogger(viewId).error(`failed to load deeplink ${url}`, err);
this.views.get(viewId)?.removeListener(LOAD_SUCCESS, this.deeplinkSuccess);
}
};
/**
* View loading helpers
@@ -240,7 +240,7 @@ export class ViewManager {
private loadServer = (server: MattermostServer) => {
const views = ServerManager.getOrderedTabsForServer(server.id);
views.forEach((view) => this.loadView(server, view));
}
};
private loadView = (srv: MattermostServer, view: MattermostView, url?: string) => {
if (!view.isOpen) {
@@ -249,7 +249,7 @@ export class ViewManager {
}
const browserView = this.makeView(srv, view, url);
this.addView(browserView);
}
};
private makeView = (srv: MattermostServer, view: MattermostView, url?: string): MattermostBrowserView => {
const mainWindow = MainWindow.get();
@@ -264,14 +264,14 @@ export class ViewManager {
browserView.on(UPDATE_TARGET_URL, this.showURLView);
browserView.load(url);
return browserView;
}
};
private addView = (view: MattermostBrowserView): void => {
this.views.set(view.id, view);
if (this.closedViews.has(view.id)) {
this.closedViews.delete(view.id);
}
}
};
private showInitial = () => {
log.verbose('showInitial');
@@ -285,7 +285,7 @@ export class ViewManager {
} else {
MainWindow.get()?.webContents.send(SET_ACTIVE_VIEW);
}
}
};
/**
* Mattermost view event handlers
@@ -297,7 +297,7 @@ export class ViewManager {
if (this.currentView === viewId) {
this.showById(this.currentView);
}
}
};
private finishLoading = (viewId: string) => {
this.getViewLogger(viewId).debug('finishLoading');
@@ -306,7 +306,7 @@ export class ViewManager {
this.showById(this.currentView);
LoadingScreen.fade();
}
}
};
private failLoading = (viewId: string) => {
this.getViewLogger(viewId).debug('failLoading');
@@ -315,7 +315,7 @@ export class ViewManager {
if (this.currentView === viewId) {
this.getCurrentView()?.hide();
}
}
};
private showURLView = (url: URL | string) => {
log.silly('showURLView', url);
@@ -390,7 +390,7 @@ export class ViewManager {
hideView();
};
}
}
};
/**
* Event Handlers
@@ -468,19 +468,19 @@ export class ViewManager {
} else {
this.showInitial();
}
}
};
private handleHistory = (event: IpcMainEvent, offset: number) => {
this.getCurrentView()?.goToOffset(offset);
}
};
private handleAppLoggedIn = (event: IpcMainEvent) => {
this.getViewByWebContentsId(event.sender.id)?.onLogin(true);
}
};
private handleAppLoggedOut = (event: IpcMainEvent) => {
this.getViewByWebContentsId(event.sender.id)?.onLogin(false);
}
};
private handleBrowserHistoryPush = (e: IpcMainEvent, pathName: string) => {
log.debug('handleBrowserHistoryPush', e.sender.id, pathName);
@@ -512,13 +512,13 @@ export class ViewManager {
redirectedView?.sendToRenderer(BROWSER_HISTORY_PUSH, cleanedPathName);
redirectedView?.updateHistoryButton();
}
}
};
private handleRequestBrowserHistoryStatus = (e: IpcMainInvokeEvent) => {
log.silly('handleRequestBrowserHistoryStatus', e.sender.id);
return this.getViewByWebContentsId(e.sender.id)?.getBrowserHistoryStatus();
}
};
private handleReactAppInitialized = (e: IpcMainEvent) => {
log.debug('handleReactAppInitialized', e.sender.id);
@@ -530,7 +530,7 @@ export class ViewManager {
LoadingScreen.fade();
}
}
}
};
private handleReloadCurrentView = () => {
log.debug('handleReloadCurrentView');
@@ -541,7 +541,7 @@ export class ViewManager {
}
view?.reload();
this.showById(view?.id);
}
};
private handleLegacyOff = (e: IpcMainEvent) => {
log.silly('handleLegacyOff', {webContentsId: e.sender.id});
@@ -551,7 +551,7 @@ export class ViewManager {
return;
}
view.offLegacyUnreads();
}
};
// if favicon is null, it means it is the initial load,
// so don't memoize as we don't have the favicons and there is no rush to find out.
@@ -563,7 +563,7 @@ export class ViewManager {
return;
}
AppState.updateUnreads(view.id, result);
}
};
private handleUnreadsAndMentionsChanged = (e: IpcMainEvent, isUnread: boolean, mentionCount: number) => {
log.silly('handleUnreadsAndMentionsChanged', {webContentsId: e.sender.id, isUnread, mentionCount});
@@ -574,7 +574,7 @@ export class ViewManager {
}
AppState.updateUnreads(view.id, isUnread);
AppState.updateMentions(view.id, mentionCount);
}
};
private handleSessionExpired = (event: IpcMainEvent, isExpired: boolean) => {
const view = this.getViewByWebContentsId(event.sender.id);
@@ -584,7 +584,7 @@ export class ViewManager {
ServerManager.getViewLog(view.id, 'ViewManager').debug('handleSessionExpired', isExpired);
AppState.updateExpired(view.id, isExpired);
}
};
private handleSetCurrentViewBounds = (newBounds: Electron.Rectangle) => {
log.debug('handleSetCurrentViewBounds', newBounds);
@@ -594,7 +594,7 @@ export class ViewManager {
const adjustedBounds = getAdjustedWindowBoundaries(newBounds.width, newBounds.height, shouldHaveBackBar(currentView.view.url, currentView.currentURL));
currentView.setBounds(adjustedBounds);
}
}
};
/**
* Helper functions
@@ -615,11 +615,11 @@ export class ViewManager {
this.showById(id);
});
ipcMain.emit(OPEN_VIEW, null, view.id);
}
};
private getViewLogger = (viewId: string) => {
return ServerManager.getViewLog(viewId, 'ViewManager');
}
};
private handleGetViewInfoForTest = (event: IpcMainInvokeEvent) => {
const view = this.getViewByWebContentsId(event.sender.id);
@@ -632,7 +632,7 @@ export class ViewManager {
serverName: view.view.server.name,
viewType: view.view.type,
};
}
};
}
const viewManager = new ViewManager();

View File

@@ -6,14 +6,13 @@
import {shell, BrowserWindow} from 'electron';
import {getLevel} from 'common/log';
import ContextMenu from 'main/contextMenu';
import ViewManager from 'main/views/viewManager';
import allowProtocolDialog from '../allowProtocolDialog';
import {WebContentsEventManager} from './webContentEvents';
import allowProtocolDialog from '../allowProtocolDialog';
jest.mock('electron', () => ({
app: {},
shell: {

View File

@@ -3,7 +3,8 @@
import path from 'path';
import {BrowserWindow, session, shell, WebContents, Event} from 'electron';
import type {WebContents, Event} from 'electron';
import {BrowserWindow, session, shell} from 'electron';
import Config from 'common/config';
import {Logger, getLevel} from 'common/log';
@@ -25,16 +26,13 @@ import {
isValidURI,
parseURL,
} from 'common/utils/url';
import {flushCookiesStore} from 'main/app/utils';
import ContextMenu from 'main/contextMenu';
import MainWindow from 'main/windows/mainWindow';
import ViewManager from 'main/views/viewManager';
import CallsWidgetWindow from 'main/windows/callsWidgetWindow';
import MainWindow from 'main/windows/mainWindow';
import {protocols} from '../../../electron-builder.json';
import allowProtocolDialog from '../allowProtocolDialog';
import {composeUserAgent} from '../utils';
@@ -73,14 +71,14 @@ export class WebContentsEventManager {
}
return ServerManager.getViewLog(view.id, 'WebContentsEventManager');
}
};
private isTrustedPopupWindow = (webContentsId: number) => {
if (!this.popupWindow) {
return false;
}
return webContentsId === this.popupWindow.win.webContents.id;
}
};
private getServerURLFromWebContentsId = (webContentsId: number) => {
if (this.popupWindow && webContentsId === this.popupWindow.win.webContents.id) {
@@ -92,7 +90,7 @@ export class WebContentsEventManager {
}
return ViewManager.getViewByWebContentsId(webContentsId)?.view.server.url;
}
};
private generateWillNavigate = (webContentsId: number) => {
return (event: Event, url: string) => {
@@ -320,7 +318,7 @@ export class WebContentsEventManager {
}
logFn(...entries);
}
};
removeWebContentsListeners = (id: number) => {
if (this.listeners[id]) {