[MM-43941] Optimize app resizing on Windows/Linux by waiting for the viewport to resize (#2204)

* [MM-43941] Optimize app resizing on Windows/Linux by waiting for the viewport to resize

* Some mitigations for Windows

* Make the logs sillier

* Fixed the no servers case

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Devin Binnie
2022-07-26 10:25:13 -04:00
committed by GitHub
parent 4914aef2a3
commit 829b49571f
5 changed files with 132 additions and 45 deletions

View File

@@ -126,3 +126,5 @@ export const PING_DOMAIN_RESPONSE = 'ping-domain-response';
export const GET_LANGUAGE_INFORMATION = 'get-language-information';
export const RETRIEVED_LANGUAGE_INFORMATION = 'retrieved-language-information';
export const GET_AVAILABLE_LANGUAGES = 'get-available-languages';
export const VIEW_FINISHED_RESIZING = 'view-finished-resizing';

View File

@@ -29,6 +29,7 @@ import {
GET_VIEW_WEBCONTENTS_ID,
DISPATCH_GET_DESKTOP_SOURCES,
DESKTOP_SOURCES_RESULT,
VIEW_FINISHED_RESIZING,
} from 'common/communication';
const UNREAD_COUNT_INTERVAL = 1000;
@@ -292,3 +293,6 @@ ipcRenderer.on(DESKTOP_SOURCES_RESULT, (event, sources) => {
/* eslint-enable no-magic-numbers */
window.addEventListener('resize', () => {
ipcRenderer.send(VIEW_FINISHED_RESIZING);
});

View File

@@ -40,7 +40,7 @@ import WebContentsEventManager from './webContentEvents';
const URL_VIEW_DURATION = 10 * SECOND;
const URL_VIEW_HEIGHT = 20;
enum LoadingScreenState {
export enum LoadingScreenState {
VISIBLE = 1,
FADING = 2,
HIDDEN = 3,

View File

@@ -58,6 +58,9 @@ jest.mock('../utils', () => ({
}));
jest.mock('../views/viewManager', () => ({
ViewManager: jest.fn(),
LoadingScreenState: {
HIDDEN: 3,
},
}));
jest.mock('../CriticalErrorHandler', () => jest.fn());
jest.mock('../views/teamDropdownView', () => jest.fn());
@@ -203,6 +206,7 @@ describe('main/windows/windowManager', () => {
});
afterEach(() => {
jest.runAllTimers();
jest.resetAllMocks();
});
@@ -212,33 +216,71 @@ describe('main/windows/windowManager', () => {
expect(windowManager.teamDropdown.updateWindowBounds).toHaveBeenCalled();
});
it('should use getContentBounds when the platform is not linux', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'win32',
});
windowManager.handleResizeMainWindow();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(view.setBounds).toHaveBeenCalledWith({width: 800, height: 600});
});
it('should use getSize when the platform is linux', () => {
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', {
value: 'linux',
});
windowManager.handleResizeMainWindow();
Object.defineProperty(process, 'platform', {
value: originalPlatform,
});
expect(view.setBounds).not.toHaveBeenCalled();
jest.runAllTimers();
expect(view.setBounds).toHaveBeenCalledWith({width: 1000, height: 900});
});
});
describe('handleWillResizeMainWindow', () => {
const windowManager = new WindowManager();
const view = {
setBounds: jest.fn(),
tab: {
url: 'http://server-1.com',
},
view: {
webContents: {
getURL: jest.fn(),
},
},
};
windowManager.viewManager = {
getCurrentView: () => view,
setLoadingScreenBounds: jest.fn(),
loadingScreenState: 3,
};
windowManager.mainWindow = {
getContentBounds: () => ({width: 800, height: 600}),
getSize: () => [1000, 900],
};
windowManager.teamDropdown = {
updateWindowBounds: jest.fn(),
};
beforeEach(() => {
getAdjustedWindowBoundaries.mockImplementation((width, height) => ({width, height}));
});
afterEach(() => {
windowManager.isResizing = false;
jest.resetAllMocks();
});
it('should update loading screen and team dropdown bounds', () => {
const event = {preventDefault: jest.fn()};
windowManager.handleWillResizeMainWindow(event, {width: 800, height: 600});
expect(windowManager.viewManager.setLoadingScreenBounds).toHaveBeenCalled();
expect(windowManager.teamDropdown.updateWindowBounds).toHaveBeenCalled();
});
it('should not resize if the app is already resizing', () => {
windowManager.isResizing = true;
const event = {preventDefault: jest.fn()};
windowManager.handleWillResizeMainWindow(event, {width: 800, height: 600});
expect(view.setBounds).not.toHaveBeenCalled();
});
it('should use provided bounds', () => {
const event = {preventDefault: jest.fn()};
windowManager.handleWillResizeMainWindow(event, {width: 800, height: 600});
expect(windowManager.isResizing).toBe(true);
expect(view.setBounds).toHaveBeenCalledWith({width: 800, height: 600});
});
});
describe('restoreMain', () => {
const windowManager = new WindowManager();
windowManager.mainWindow = {

View File

@@ -26,15 +26,18 @@ import {
DISPATCH_GET_DESKTOP_SOURCES,
DESKTOP_SOURCES_RESULT,
RELOAD_CURRENT_VIEW,
VIEW_FINISHED_RESIZING,
} from 'common/communication';
import urlUtils from 'common/utils/url';
import {SECOND} from 'common/utils/constants';
import Config from 'common/config';
import {getTabViewName, TAB_MESSAGING} from 'common/tabs/TabView';
import {MattermostView} from 'main/views/MattermostView';
import {getAdjustedWindowBoundaries, shouldHaveBackBar} from '../utils';
import {ViewManager} from '../views/viewManager';
import {ViewManager, LoadingScreenState} from '../views/viewManager';
import CriticalErrorHandler from '../CriticalErrorHandler';
import TeamDropdownView from '../views/teamDropdownView';
@@ -71,6 +74,7 @@ export class WindowManager {
ipcMain.handle(GET_VIEW_WEBCONTENTS_ID, this.handleGetWebContentsId);
ipcMain.on(DISPATCH_GET_DESKTOP_SOURCES, this.handleGetDesktopSources);
ipcMain.on(RELOAD_CURRENT_VIEW, this.handleReloadCurrentView);
ipcMain.on(VIEW_FINISHED_RESIZING, this.handleViewFinishedResizing);
}
handleUpdateConfig = () => {
@@ -134,7 +138,12 @@ export class WindowManager {
});
this.mainWindow.on('maximize', this.handleMaximizeMainWindow);
this.mainWindow.on('unmaximize', this.handleUnmaximizeMainWindow);
if (process.platform === 'linux') {
this.mainWindow.on('resize', this.handleResizeMainWindow);
} else {
this.mainWindow.on('will-resize', this.handleWillResizeMainWindow);
this.mainWindow.on('resized', this.handleResizedMainWindow);
}
this.mainWindow.on('focus', this.focusBrowserView);
this.mainWindow.on('enter-full-screen', () => this.sendToRenderer('enter-full-screen'));
this.mainWindow.on('leave-full-screen', () => this.sendToRenderer('leave-full-screen'));
@@ -173,42 +182,72 @@ export class WindowManager {
this.sendToRenderer(MAXIMIZE_CHANGE, false);
}
handleResizeMainWindow = () => {
log.debug('WindowManager.handleResizeMainWindow');
isResizing = false;
handleWillResizeMainWindow = (event: Event, newBounds: Electron.Rectangle) => {
log.silly('WindowManager.handleWillResizeMainWindow');
if (!(this.viewManager && this.mainWindow)) {
return;
}
const currentView = this.viewManager.getCurrentView();
let bounds: Partial<Electron.Rectangle>;
// Workaround for linux maximizing/minimizing, which doesn't work properly because of these bugs:
// https://github.com/electron/electron/issues/28699
// https://github.com/electron/electron/issues/28106
if (process.platform === 'linux') {
if (this.isResizing && this.viewManager.loadingScreenState === LoadingScreenState.HIDDEN && this.viewManager.getCurrentView()) {
log.silly('prevented resize');
event.preventDefault();
return;
}
this.throttledWillResize(newBounds);
this.viewManager?.setLoadingScreenBounds();
this.teamDropdown?.updateWindowBounds();
ipcMain.emit(RESIZE_MODAL, null, newBounds);
}
handleResizedMainWindow = () => {
if (this.mainWindow) {
this.throttledWillResize(this.mainWindow?.getContentBounds());
}
this.isResizing = false;
}
handleViewFinishedResizing = () => {
this.isResizing = false;
}
private throttledWillResize = (newBounds: Electron.Rectangle) => {
this.isResizing = true;
this.setCurrentViewBounds(newBounds);
}
handleResizeMainWindow = () => {
log.silly('WindowManager.handleResizeMainWindow');
if (!(this.viewManager && this.mainWindow)) {
return;
}
const size = this.mainWindow.getSize();
bounds = {width: size[0], height: size[1]};
} else {
bounds = this.mainWindow.getContentBounds();
}
const bounds = {width: size[0], height: size[1]};
const setBoundsFunction = () => {
if (currentView) {
currentView.setBounds(getAdjustedWindowBoundaries(bounds.width!, bounds.height!, shouldHaveBackBar(currentView.tab.url, currentView.view.webContents.getURL())));
}
};
// Another workaround since the window doesn't update properly under Linux for some reason
// Another workaround since the window doesn't update p roperly under Linux for some reason
// See above comment
if (process.platform === 'linux') {
setTimeout(setBoundsFunction, 10);
} else {
setBoundsFunction();
}
setTimeout(this.setCurrentViewBounds, 10, bounds);
this.viewManager.setLoadingScreenBounds();
this.teamDropdown?.updateWindowBounds();
ipcMain.emit(RESIZE_MODAL, null, bounds);
};
setCurrentViewBounds = (bounds: {width: number; height: number}) => {
const currentView = this.viewManager?.getCurrentView();
if (currentView) {
const adjustedBounds = getAdjustedWindowBoundaries(bounds.width, bounds.height, shouldHaveBackBar(currentView.tab.url, currentView.view.webContents.getURL()));
this.setBoundsFunction(currentView, adjustedBounds);
}
}
private setBoundsFunction = (currentView: MattermostView, bounds: Electron.Rectangle) => {
log.silly('setBoundsFunction', bounds.width, bounds.height);
currentView.setBounds(bounds);
};
// max retries allows the message to get to the renderer even if it is sent while the app is starting up.
sendToRendererWithRetry = (maxRetries: number, channel: string, ...args: any[]) => {