[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:
@@ -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';
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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,
|
||||
|
@@ -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 = {
|
||||
|
@@ -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[]) => {
|
||||
|
Reference in New Issue
Block a user