Migrate downloads dropdown menus to singletons (#2680)
This commit is contained in:
@@ -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);
|
||||||
|
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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(),
|
||||||
|
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user