Migrate downloads dropdown menus to singletons (#2680)

This commit is contained in:
Devin Binnie
2023-04-18 09:14:18 -04:00
committed by GitHub
parent b71322a682
commit a141d3cde4
6 changed files with 129 additions and 173 deletions

View File

@@ -9,7 +9,7 @@ import {DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT, DOWN
import MainWindow from 'main/windows/mainWindow'; import MainWindow from 'main/windows/mainWindow';
import DownloadsDropdownMenuView from './downloadsDropdownMenuView'; import {DownloadsDropdownMenuView} from './downloadsDropdownMenuView';
jest.mock('main/utils', () => ({ jest.mock('main/utils', () => ({
getLocalPreload: (file) => file, getLocalPreload: (file) => file,
@@ -76,12 +76,13 @@ describe('main/views/DownloadsDropdownMenuView', () => {
describe('getBounds', () => { describe('getBounds', () => {
it('should be placed top-left inside the downloads dropdown if coordinates not used', () => { it('should be placed top-left inside the downloads dropdown if coordinates not used', () => {
const downloadsDropdownMenuView = new DownloadsDropdownMenuView(); const downloadsDropdownMenuView = new DownloadsDropdownMenuView();
expect(downloadsDropdownMenuView.getBounds(DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT)).toStrictEqual({x: 800 - DOWNLOADS_DROPDOWN_FULL_WIDTH - DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, y: TAB_BAR_HEIGHT, width: DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, height: DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT}); expect(downloadsDropdownMenuView.getBounds(800, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT)).toStrictEqual({x: 800 - DOWNLOADS_DROPDOWN_FULL_WIDTH - DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, y: TAB_BAR_HEIGHT, width: DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, height: DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT});
}); });
}); });
it('should change the view bounds based on open/closed state', () => { it('should change the view bounds based on open/closed state', () => {
const downloadsDropdownMenuView = new DownloadsDropdownMenuView(); const downloadsDropdownMenuView = new DownloadsDropdownMenuView();
downloadsDropdownMenuView.init();
downloadsDropdownMenuView.bounds = {width: 400, height: 300}; downloadsDropdownMenuView.bounds = {width: 400, height: 300};
downloadsDropdownMenuView.handleOpen(); downloadsDropdownMenuView.handleOpen();
expect(downloadsDropdownMenuView.view.setBounds).toBeCalledWith(downloadsDropdownMenuView.bounds); expect(downloadsDropdownMenuView.view.setBounds).toBeCalledWith(downloadsDropdownMenuView.bounds);

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {BrowserView, ipcMain, IpcMainEvent} from 'electron'; import {BrowserView, ipcMain, IpcMainEvent} from 'electron';
import {CombinedConfig} from 'types/config';
import {CoordinatesToJsonType, DownloadedItem, DownloadsMenuOpenEventPayload} from 'types/downloads'; import {CoordinatesToJsonType, DownloadedItem, DownloadsMenuOpenEventPayload} from 'types/downloads';
import { import {
@@ -33,40 +32,35 @@ import WindowManager from 'main/windows/windowManager';
const log = new Logger('DownloadsDropdownMenuView'); const log = new Logger('DownloadsDropdownMenuView');
export default class DownloadsDropdownMenuView { export class DownloadsDropdownMenuView {
open: boolean; private open: boolean;
view: BrowserView; private view?: BrowserView;
bounds: Electron.Rectangle; private bounds?: Electron.Rectangle;
item?: DownloadedItem; private item?: DownloadedItem;
coordinates?: CoordinatesToJsonType; private coordinates?: CoordinatesToJsonType;
darkMode: boolean; private windowBounds?: Electron.Rectangle;
windowBounds: Electron.Rectangle;
constructor() { constructor() {
this.open = false; this.open = false;
this.item = undefined;
this.coordinates = undefined;
this.darkMode = Config.darkMode;
ipcMain.on(OPEN_DOWNLOADS_DROPDOWN_MENU, this.handleOpen); ipcMain.on(OPEN_DOWNLOADS_DROPDOWN_MENU, this.handleOpen);
ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN_MENU, this.handleClose); ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN_MENU, this.handleClose);
ipcMain.on(TOGGLE_DOWNLOADS_DROPDOWN_MENU, this.handleToggle); ipcMain.on(TOGGLE_DOWNLOADS_DROPDOWN_MENU, this.handleToggle);
ipcMain.on(EMIT_CONFIGURATION, this.updateConfig); ipcMain.on(EMIT_CONFIGURATION, this.updateDownloadsDropdownMenu);
ipcMain.on(REQUEST_DOWNLOADS_DROPDOWN_MENU_INFO, this.updateDownloadsDropdownMenu); ipcMain.on(REQUEST_DOWNLOADS_DROPDOWN_MENU_INFO, this.updateDownloadsDropdownMenu);
ipcMain.on(DOWNLOADS_DROPDOWN_MENU_OPEN_FILE, this.openFile); ipcMain.on(DOWNLOADS_DROPDOWN_MENU_OPEN_FILE, this.openFile);
ipcMain.on(DOWNLOADS_DROPDOWN_MENU_SHOW_FILE_IN_FOLDER, this.showFileInFolder); ipcMain.on(DOWNLOADS_DROPDOWN_MENU_SHOW_FILE_IN_FOLDER, this.showFileInFolder);
ipcMain.on(DOWNLOADS_DROPDOWN_MENU_CANCEL_DOWNLOAD, this.cancelDownload); ipcMain.on(DOWNLOADS_DROPDOWN_MENU_CANCEL_DOWNLOAD, this.cancelDownload);
ipcMain.on(DOWNLOADS_DROPDOWN_MENU_CLEAR_FILE, this.clearFile); ipcMain.on(DOWNLOADS_DROPDOWN_MENU_CLEAR_FILE, this.clearFile);
ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN_MENU, this.updateItem); ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN_MENU, this.updateItem);
const mainWindow = MainWindow.get();
const windowBounds = MainWindow.getBounds();
if (!(mainWindow && windowBounds)) {
throw new Error('Cannot initialize downloadsDropdownMenuView, missing MainWindow');
} }
this.windowBounds = windowBounds; init = () => {
this.bounds = this.getBounds(DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT); this.windowBounds = MainWindow.getBounds();
if (!this.windowBounds) {
throw new Error('Cannot initialize downloadsDropdownMenuView, missing MainWindow');
}
this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT);
const preload = getLocalPreload('desktopAPI.js'); const preload = getLocalPreload('desktopAPI.js');
this.view = new BrowserView({webPreferences: { this.view = new BrowserView({webPreferences: {
@@ -78,22 +72,7 @@ export default class DownloadsDropdownMenuView {
transparent: true, transparent: true,
}}); }});
this.view.webContents.loadURL(getLocalURLString('downloadsDropdownMenu.html')); this.view.webContents.loadURL(getLocalURLString('downloadsDropdownMenu.html'));
mainWindow.addBrowserView(this.view); MainWindow.get()?.addBrowserView(this.view);
}
updateItem = (event: IpcMainEvent, item: DownloadedItem) => {
log.debug('updateItem', {item});
this.item = item;
this.updateDownloadsDropdownMenu();
}
updateConfig = (event: IpcMainEvent, config: CombinedConfig) => {
log.debug('updateConfig');
this.darkMode = config.darkMode;
this.updateDownloadsDropdownMenu();
} }
/** /**
@@ -103,30 +82,34 @@ export default class DownloadsDropdownMenuView {
updateWindowBounds = () => { updateWindowBounds = () => {
log.debug('updateWindowBounds'); log.debug('updateWindowBounds');
const mainWindow = MainWindow.get(); this.windowBounds = MainWindow.getBounds();
if (mainWindow) {
this.windowBounds = mainWindow.getContentBounds();
this.updateDownloadsDropdownMenu(); this.updateDownloadsDropdownMenu();
this.repositionDownloadsDropdownMenu(); this.repositionDownloadsDropdownMenu();
} }
private updateItem = (event: IpcMainEvent, item: DownloadedItem) => {
log.debug('updateItem', {item});
this.item = item;
this.updateDownloadsDropdownMenu();
} }
updateDownloadsDropdownMenu = () => { private updateDownloadsDropdownMenu = () => {
log.debug('updateDownloadsDropdownMenu'); log.debug('updateDownloadsDropdownMenu');
this.view.webContents.send( this.view?.webContents.send(
UPDATE_DOWNLOADS_DROPDOWN_MENU, UPDATE_DOWNLOADS_DROPDOWN_MENU,
this.item, this.item,
this.darkMode, Config.darkMode,
); );
ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, true, this.item); ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, true, this.item);
this.repositionDownloadsDropdownMenu(); this.repositionDownloadsDropdownMenu();
} }
handleOpen = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload = {} as DownloadsMenuOpenEventPayload) => { private handleOpen = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload = {} as DownloadsMenuOpenEventPayload) => {
log.debug('handleOpen', {bounds: this.bounds, payload}); log.debug('handleOpen', {bounds: this.bounds, payload});
if (!this.bounds) { if (!(this.bounds && this.view && this.windowBounds)) {
return; return;
} }
@@ -137,24 +120,24 @@ export default class DownloadsDropdownMenuView {
this.open = true; this.open = true;
this.coordinates = coordinates; this.coordinates = coordinates;
this.item = item; this.item = item;
this.bounds = this.getBounds(DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT); this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT);
this.view.setBounds(this.bounds); this.view.setBounds(this.bounds);
MainWindow.get()?.setTopBrowserView(this.view); MainWindow.get()?.setTopBrowserView(this.view);
this.view.webContents.focus(); this.view.webContents.focus();
this.updateDownloadsDropdownMenu(); this.updateDownloadsDropdownMenu();
} }
handleClose = () => { private handleClose = () => {
log.debug('handleClose'); log.debug('handleClose');
this.open = false; this.open = false;
this.item = undefined; this.item = undefined;
ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM); ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM);
this.view.setBounds(this.getBounds(0, 0)); this.view?.setBounds(this.getBounds(this.windowBounds?.width ?? 0, 0, 0));
WindowManager.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN_MENU); WindowManager.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN_MENU);
} }
handleToggle = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload) => { private handleToggle = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload) => {
if (this.open) { if (this.open) {
if (this.item?.location === payload.item.location) { if (this.item?.location === payload.item.location) {
// clicking 3-dot in the same item // clicking 3-dot in the same item
@@ -169,61 +152,60 @@ export default class DownloadsDropdownMenuView {
} }
} }
openFile = () => { private openFile = () => {
downloadsManager.openFile(this.item); downloadsManager.openFile(this.item);
this.handleClose(); this.handleClose();
} }
showFileInFolder = (e: IpcMainEvent, item: DownloadedItem) => { private showFileInFolder = (e: IpcMainEvent, item: DownloadedItem) => {
downloadsManager.showFileInFolder(item); downloadsManager.showFileInFolder(item);
this.handleClose(); this.handleClose();
} }
clearFile = () => { private clearFile = () => {
downloadsManager.clearFile(this.item); downloadsManager.clearFile(this.item);
this.handleClose(); this.handleClose();
} }
cancelDownload = () => { private cancelDownload = () => {
downloadsManager.cancelDownload(this.item); downloadsManager.cancelDownload(this.item);
this.handleClose(); this.handleClose();
} }
getBounds = (width: number, height: number) => { private getBounds = (windowWidth: number, width: number, height: number) => {
// MUST return integers // MUST return integers
return { return {
x: this.getX(), x: this.getX(windowWidth),
y: this.getY(), y: this.getY(),
width: Math.round(width), width: Math.round(width),
height: Math.round(height), height: Math.round(height),
}; };
} }
getX = () => { private getX = (windowWidth: number) => {
const result = (this.windowBounds.width - DOWNLOADS_DROPDOWN_FULL_WIDTH - DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH) + (this.coordinates?.x || 0) + (this.coordinates?.width || 0); const result = (windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH - DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH) + (this.coordinates?.x || 0) + (this.coordinates?.width || 0);
if (result <= DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH) { if (result <= DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH) {
return 0; return 0;
} }
return Math.round(result); return Math.round(result);
} }
getY = () => { private getY = () => {
const result = TAB_BAR_HEIGHT + (this.coordinates?.y || 0) + (this.coordinates?.height || 0); const result = TAB_BAR_HEIGHT + (this.coordinates?.y || 0) + (this.coordinates?.height || 0);
return Math.round(result); return Math.round(result);
} }
repositionDownloadsDropdownMenu = () => { private repositionDownloadsDropdownMenu = () => {
this.bounds = this.getBounds(DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT); if (!this.windowBounds) {
return;
}
this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT);
if (this.open) { if (this.open) {
this.view.setBounds(this.bounds); this.view?.setBounds(this.bounds);
}
} }
} }
destroy = () => { const downloadsDropdownMenuView = new DownloadsDropdownMenuView();
// workaround to eliminate zombie processes export default downloadsDropdownMenuView;
// https://github.com/mattermost/desktop/pull/1519
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.view.webContents.destroy();
}
}

View File

@@ -9,7 +9,7 @@ import {DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT, TAB_BAR_HEIGHT
import MainWindow from 'main/windows/mainWindow'; import MainWindow from 'main/windows/mainWindow';
import DownloadsDropdownView from './downloadsDropdownView'; import {DownloadsDropdownView} from './downloadsDropdownView';
jest.mock('main/utils', () => ({ jest.mock('main/utils', () => ({
getLocalPreload: (file) => file, getLocalPreload: (file) => file,
@@ -81,20 +81,19 @@ describe('main/views/DownloadsDropdownView', () => {
}); });
describe('getBounds', () => { describe('getBounds', () => {
it('should be placed far right when window is large enough', () => { it('should be placed far right when window is large enough', () => {
MainWindow.getBounds.mockReturnValue({width: 800, height: 600, x: 0, y: 0});
const downloadsDropdownView = new DownloadsDropdownView(); const downloadsDropdownView = new DownloadsDropdownView();
expect(downloadsDropdownView.getBounds(DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT)).toStrictEqual({x: 800 - DOWNLOADS_DROPDOWN_FULL_WIDTH, y: TAB_BAR_HEIGHT, width: DOWNLOADS_DROPDOWN_FULL_WIDTH, height: DOWNLOADS_DROPDOWN_HEIGHT}); expect(downloadsDropdownView.getBounds(800, DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT)).toStrictEqual({x: 800 - DOWNLOADS_DROPDOWN_FULL_WIDTH, y: TAB_BAR_HEIGHT, width: DOWNLOADS_DROPDOWN_FULL_WIDTH, height: DOWNLOADS_DROPDOWN_HEIGHT});
}); });
it('should be placed left if window is very small', () => { it('should be placed left if window is very small', () => {
MainWindow.getBounds.mockReturnValue({width: 500, height: 400, x: 0, y: 0});
const downloadsDropdownView = new DownloadsDropdownView(); const downloadsDropdownView = new DownloadsDropdownView();
expect(downloadsDropdownView.getBounds(DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT)).toStrictEqual({x: 0, y: TAB_BAR_HEIGHT, width: DOWNLOADS_DROPDOWN_FULL_WIDTH, height: DOWNLOADS_DROPDOWN_HEIGHT}); expect(downloadsDropdownView.getBounds(500, DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT)).toStrictEqual({x: 0, y: TAB_BAR_HEIGHT, width: DOWNLOADS_DROPDOWN_FULL_WIDTH, height: DOWNLOADS_DROPDOWN_HEIGHT});
}); });
}); });
it('should change the view bounds based on open/closed state', () => { it('should change the view bounds based on open/closed state', () => {
MainWindow.getBounds.mockReturnValue({width: 800, height: 600, x: 0, y: 0}); MainWindow.getBounds.mockReturnValue({width: 800, height: 600, x: 0, y: 0});
const downloadsDropdownView = new DownloadsDropdownView(); const downloadsDropdownView = new DownloadsDropdownView();
downloadsDropdownView.init();
downloadsDropdownView.bounds = {width: 400, height: 300}; downloadsDropdownView.bounds = {width: 400, height: 300};
downloadsDropdownView.handleOpen(); downloadsDropdownView.handleOpen();
expect(downloadsDropdownView.view.setBounds).toBeCalledWith(downloadsDropdownView.bounds); expect(downloadsDropdownView.view.setBounds).toBeCalledWith(downloadsDropdownView.bounds);

View File

@@ -3,8 +3,7 @@
import {BrowserView, ipcMain, IpcMainEvent, IpcMainInvokeEvent} from 'electron'; import {BrowserView, ipcMain, IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {CombinedConfig} from 'types/config'; import {DownloadedItem} from 'types/downloads';
import {DownloadedItem, DownloadedItems} from 'types/downloads';
import { import {
CLOSE_DOWNLOADS_DROPDOWN, CLOSE_DOWNLOADS_DROPDOWN,
@@ -29,37 +28,31 @@ import MainWindow from 'main/windows/mainWindow';
const log = new Logger('DownloadsDropdownView'); const log = new Logger('DownloadsDropdownView');
export default class DownloadsDropdownView { export class DownloadsDropdownView {
bounds?: Electron.Rectangle; private bounds?: Electron.Rectangle;
darkMode: boolean; private windowBounds?: Electron.Rectangle;
downloads: DownloadedItems; private item?: DownloadedItem;
item?: DownloadedItem; private view?: BrowserView;
view: BrowserView;
windowBounds: Electron.Rectangle;
constructor() { constructor() {
this.downloads = downloadsManager.getDownloads();
this.darkMode = Config.darkMode;
ipcMain.on(OPEN_DOWNLOADS_DROPDOWN, this.handleOpen); ipcMain.on(OPEN_DOWNLOADS_DROPDOWN, this.handleOpen);
ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN, this.handleClose); ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN, this.handleClose);
ipcMain.on(EMIT_CONFIGURATION, this.updateConfig); ipcMain.on(EMIT_CONFIGURATION, this.updateDownloadsDropdown);
ipcMain.on(REQUEST_DOWNLOADS_DROPDOWN_INFO, this.updateDownloadsDropdown); ipcMain.on(REQUEST_DOWNLOADS_DROPDOWN_INFO, this.updateDownloadsDropdown);
ipcMain.on(REQUEST_CLEAR_DOWNLOADS_DROPDOWN, this.clearDownloads); ipcMain.on(REQUEST_CLEAR_DOWNLOADS_DROPDOWN, this.clearDownloads);
ipcMain.on(RECEIVE_DOWNLOADS_DROPDOWN_SIZE, this.handleReceivedDownloadsDropdownSize); ipcMain.on(RECEIVE_DOWNLOADS_DROPDOWN_SIZE, this.handleReceivedDownloadsDropdownSize);
ipcMain.on(DOWNLOADS_DROPDOWN_OPEN_FILE, this.openFile); ipcMain.on(DOWNLOADS_DROPDOWN_OPEN_FILE, this.openFile);
ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN, this.updateDownloads); ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN, this.updateDownloadsDropdown);
ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, this.updateDownloadsDropdownMenuItem); ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, this.updateDownloadsDropdownMenuItem);
ipcMain.handle(GET_DOWNLOADED_IMAGE_THUMBNAIL_LOCATION, this.getDownloadImageThumbnailLocation); ipcMain.handle(GET_DOWNLOADED_IMAGE_THUMBNAIL_LOCATION, this.getDownloadImageThumbnailLocation);
const mainWindow = MainWindow.get();
const windowBounds = MainWindow.getBounds();
if (!(mainWindow && windowBounds)) {
throw new Error('Cannot initialize downloadsDropdownView, missing MainWindow');
} }
this.windowBounds = windowBounds; init = () => {
this.bounds = this.getBounds(DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT); this.windowBounds = MainWindow.getBounds();
if (!this.windowBounds) {
throw new Error('Cannot initialize, no main window');
}
this.bounds = this.getBounds(this.windowBounds.width, DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT);
const preload = getLocalPreload('desktopAPI.js'); const preload = getLocalPreload('desktopAPI.js');
this.view = new BrowserView({webPreferences: { this.view = new BrowserView({webPreferences: {
@@ -73,28 +66,7 @@ export default class DownloadsDropdownView {
this.view.webContents.loadURL(getLocalURLString('downloadsDropdown.html')); this.view.webContents.loadURL(getLocalURLString('downloadsDropdown.html'));
this.view.webContents.session.webRequest.onHeadersReceived(downloadsManager.webRequestOnHeadersReceivedHandler); this.view.webContents.session.webRequest.onHeadersReceived(downloadsManager.webRequestOnHeadersReceivedHandler);
mainWindow.addBrowserView(this.view); MainWindow.get()?.addBrowserView(this.view);
}
updateDownloads = (event: IpcMainEvent, downloads: DownloadedItems) => {
log.debug('updateDownloads', {downloads});
this.downloads = downloads;
this.updateDownloadsDropdown();
}
updateDownloadsDropdownMenuItem = (event: IpcMainEvent, item?: DownloadedItem) => {
log.debug('updateDownloadsDropdownMenuItem', {item});
this.item = item;
this.updateDownloadsDropdown();
}
updateConfig = (event: IpcMainEvent, config: CombinedConfig) => {
log.debug('updateConfig');
this.darkMode = config.darkMode;
this.updateDownloadsDropdown();
} }
/** /**
@@ -104,30 +76,33 @@ export default class DownloadsDropdownView {
updateWindowBounds = () => { updateWindowBounds = () => {
log.debug('updateWindowBounds'); log.debug('updateWindowBounds');
const mainWindow = MainWindow.get(); this.windowBounds = MainWindow.getBounds();
if (mainWindow) {
this.windowBounds = mainWindow.getContentBounds();
this.updateDownloadsDropdown(); this.updateDownloadsDropdown();
this.repositionDownloadsDropdown(); this.repositionDownloadsDropdown();
} }
private updateDownloadsDropdownMenuItem = (event: IpcMainEvent, item?: DownloadedItem) => {
log.debug('updateDownloadsDropdownMenuItem', {item});
this.item = item;
this.updateDownloadsDropdown();
} }
updateDownloadsDropdown = () => { private updateDownloadsDropdown = () => {
log.debug('updateDownloadsDropdown'); log.debug('updateDownloadsDropdown');
this.view.webContents.send( this.view?.webContents.send(
UPDATE_DOWNLOADS_DROPDOWN, UPDATE_DOWNLOADS_DROPDOWN,
this.downloads, downloadsManager.getDownloads(),
this.darkMode, Config.darkMode,
this.windowBounds, MainWindow.getBounds(),
this.item, this.item,
); );
} }
handleOpen = () => { private handleOpen = () => {
log.debug('handleOpen', {bounds: this.bounds}); log.debug('handleOpen', {bounds: this.bounds});
if (!this.bounds) { if (!(this.bounds && this.view)) {
return; return;
} }
@@ -138,36 +113,36 @@ export default class DownloadsDropdownView {
WindowManager.sendToRenderer(OPEN_DOWNLOADS_DROPDOWN); WindowManager.sendToRenderer(OPEN_DOWNLOADS_DROPDOWN);
} }
handleClose = () => { private handleClose = () => {
log.debug('handleClose'); log.debug('handleClose');
this.view.setBounds(this.getBounds(0, 0)); this.view?.setBounds(this.getBounds(this.windowBounds?.width ?? 0, 0, 0));
downloadsManager.onClose(); downloadsManager.onClose();
WindowManager.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN); WindowManager.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN);
} }
clearDownloads = () => { private clearDownloads = () => {
downloadsManager.clearDownloadsDropDown(); downloadsManager.clearDownloadsDropDown();
this.handleClose(); this.handleClose();
} }
openFile = (e: IpcMainEvent, item: DownloadedItem) => { private openFile = (e: IpcMainEvent, item: DownloadedItem) => {
log.debug('openFile', {item}); log.debug('openFile', {item});
downloadsManager.openFile(item); downloadsManager.openFile(item);
} }
getBounds = (width: number, height: number) => { private getBounds = (windowWidth: number, width: number, height: number) => {
// Must always use integers // Must always use integers
return { return {
x: this.getX(this.windowBounds.width), x: this.getX(windowWidth),
y: this.getY(), y: this.getY(),
width: Math.round(width), width: Math.round(width),
height: Math.round(height), height: Math.round(height),
}; };
} }
getX = (windowWidth: number) => { private getX = (windowWidth: number) => {
const result = windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH; const result = windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH;
if (result <= DOWNLOADS_DROPDOWN_WIDTH) { if (result <= DOWNLOADS_DROPDOWN_WIDTH) {
return 0; return 0;
@@ -175,11 +150,11 @@ export default class DownloadsDropdownView {
return Math.round(result); return Math.round(result);
} }
getY = () => { private getY = () => {
return Math.round(TAB_BAR_HEIGHT); return Math.round(TAB_BAR_HEIGHT);
} }
repositionDownloadsDropdown = () => { private repositionDownloadsDropdown = () => {
if (!(this.bounds && this.windowBounds)) { if (!(this.bounds && this.windowBounds)) {
return; return;
} }
@@ -189,28 +164,27 @@ export default class DownloadsDropdownView {
y: this.getY(), y: this.getY(),
}; };
if (downloadsManager.getIsOpen()) { if (downloadsManager.getIsOpen()) {
this.view.setBounds(this.bounds); this.view?.setBounds(this.bounds);
} }
} }
handleReceivedDownloadsDropdownSize = (event: IpcMainEvent, width: number, height: number) => { private handleReceivedDownloadsDropdownSize = (event: IpcMainEvent, width: number, height: number) => {
log.silly('handleReceivedDownloadsDropdownSize', {width, height}); log.silly('handleReceivedDownloadsDropdownSize', {width, height});
this.bounds = this.getBounds(width, height); if (!this.windowBounds) {
return;
}
this.bounds = this.getBounds(this.windowBounds.width, width, height);
if (downloadsManager.getIsOpen()) { if (downloadsManager.getIsOpen()) {
this.view.setBounds(this.bounds); this.view?.setBounds(this.bounds);
} }
} }
destroy = () => { private getDownloadImageThumbnailLocation = (event: IpcMainInvokeEvent, location: string) => {
// workaround to eliminate zombie processes
// https://github.com/mattermost/desktop/pull/1519
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.view.webContents.destroy();
}
getDownloadImageThumbnailLocation = (event: IpcMainInvokeEvent, location: string) => {
return location; return location;
} }
} }
const downloadsDropdownView = new DownloadsDropdownView();
export default downloadsDropdownView;

View File

@@ -76,8 +76,12 @@ jest.mock('../views/loadingScreen', () => ({
jest.mock('../views/teamDropdownView', () => ({ jest.mock('../views/teamDropdownView', () => ({
updateWindowBounds: jest.fn(), updateWindowBounds: jest.fn(),
})); }));
jest.mock('../views/downloadsDropdownView', () => jest.fn()); jest.mock('../views/downloadsDropdownView', () => ({
jest.mock('../views/downloadsDropdownMenuView', () => jest.fn()); updateWindowBounds: jest.fn(),
}));
jest.mock('../views/downloadsDropdownMenuView', () => ({
updateWindowBounds: jest.fn(),
}));
jest.mock('./settingsWindow', () => ({ jest.mock('./settingsWindow', () => ({
show: jest.fn(), show: jest.fn(),
get: jest.fn(), get: jest.fn(),

View File

@@ -44,9 +44,6 @@ import SettingsWindow from './settingsWindow';
const log = new Logger('WindowManager'); const log = new Logger('WindowManager');
export class WindowManager { export class WindowManager {
private downloadsDropdown?: DownloadsDropdownView;
private downloadsDropdownMenu?: DownloadsDropdownMenuView;
private isResizing: boolean; private isResizing: boolean;
constructor() { constructor() {
@@ -98,11 +95,10 @@ export class WindowManager {
mainWindow.on('enter-full-screen', () => this.sendToRenderer('enter-full-screen')); mainWindow.on('enter-full-screen', () => this.sendToRenderer('enter-full-screen'));
mainWindow.on('leave-full-screen', () => this.sendToRenderer('leave-full-screen')); mainWindow.on('leave-full-screen', () => this.sendToRenderer('leave-full-screen'));
this.downloadsDropdown = new DownloadsDropdownView();
this.downloadsDropdownMenu = new DownloadsDropdownMenuView();
this.initializeViewManager(); this.initializeViewManager();
TeamDropdownView.init(); TeamDropdownView.init();
DownloadsDropdownView.init();
DownloadsDropdownMenuView.init();
} }
// max retries allows the message to get to the renderer even if it is sent while the app is starting up. // max retries allows the message to get to the renderer even if it is sent while the app is starting up.
@@ -240,14 +236,14 @@ export class WindowManager {
*****************/ *****************/
private handleMaximizeMainWindow = () => { private handleMaximizeMainWindow = () => {
this.downloadsDropdown?.updateWindowBounds(); DownloadsDropdownView.updateWindowBounds();
this.downloadsDropdownMenu?.updateWindowBounds(); DownloadsDropdownMenuView.updateWindowBounds();
this.sendToRenderer(MAXIMIZE_CHANGE, true); this.sendToRenderer(MAXIMIZE_CHANGE, true);
} }
private handleUnmaximizeMainWindow = () => { private handleUnmaximizeMainWindow = () => {
this.downloadsDropdown?.updateWindowBounds(); DownloadsDropdownView.updateWindowBounds();
this.downloadsDropdownMenu?.updateWindowBounds(); DownloadsDropdownMenuView.updateWindowBounds();
this.sendToRenderer(MAXIMIZE_CHANGE, false); this.sendToRenderer(MAXIMIZE_CHANGE, false);
} }
@@ -276,8 +272,8 @@ export class WindowManager {
this.throttledWillResize(newBounds); this.throttledWillResize(newBounds);
LoadingScreen.setBounds(); LoadingScreen.setBounds();
TeamDropdownView.updateWindowBounds(); TeamDropdownView.updateWindowBounds();
this.downloadsDropdown?.updateWindowBounds(); DownloadsDropdownView.updateWindowBounds();
this.downloadsDropdownMenu?.updateWindowBounds(); DownloadsDropdownMenuView.updateWindowBounds();
ipcMain.emit(RESIZE_MODAL, null, newBounds); ipcMain.emit(RESIZE_MODAL, null, newBounds);
} }
@@ -288,8 +284,8 @@ export class WindowManager {
this.throttledWillResize(bounds); this.throttledWillResize(bounds);
ipcMain.emit(RESIZE_MODAL, null, bounds); ipcMain.emit(RESIZE_MODAL, null, bounds);
TeamDropdownView.updateWindowBounds(); TeamDropdownView.updateWindowBounds();
this.downloadsDropdown?.updateWindowBounds(); DownloadsDropdownView.updateWindowBounds();
this.downloadsDropdownMenu?.updateWindowBounds(); DownloadsDropdownMenuView.updateWindowBounds();
this.isResizing = false; this.isResizing = false;
} }
@@ -318,8 +314,8 @@ export class WindowManager {
LoadingScreen.setBounds(); LoadingScreen.setBounds();
TeamDropdownView.updateWindowBounds(); TeamDropdownView.updateWindowBounds();
this.downloadsDropdown?.updateWindowBounds(); DownloadsDropdownView.updateWindowBounds();
this.downloadsDropdownMenu?.updateWindowBounds(); DownloadsDropdownMenuView.updateWindowBounds();
ipcMain.emit(RESIZE_MODAL, null, bounds); ipcMain.emit(RESIZE_MODAL, null, bounds);
}; };