Files
mattermostest/src/main/views/downloadsDropdownView.ts
Devin Binnie 635a41f998 [MM-47801][MM-45980] Added support for security-scoped bookmarks to allow the MAS build to save files wherever needed (#2315)
* First pass

* [MM-47801] Added support for security-scoped bookmarks to allow the MAS build to save files wherever needed
2022-10-25 15:02:00 +03:00

217 lines
7.3 KiB
TypeScript

// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import path from 'path';
import {app, BrowserView, BrowserWindow, ipcMain, IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import log from 'electron-log';
import {CombinedConfig} from 'types/config';
import {DownloadedItem, DownloadedItems} from 'types/downloads';
import {
CLOSE_DOWNLOADS_DROPDOWN,
DOWNLOADS_DROPDOWN_SHOW_FILE_IN_FOLDER,
EMIT_CONFIGURATION,
OPEN_DOWNLOADS_DROPDOWN,
RECEIVE_DOWNLOADS_DROPDOWN_SIZE,
REQUEST_CLEAR_DOWNLOADS_DROPDOWN,
REQUEST_DOWNLOADS_DROPDOWN_INFO,
UPDATE_DOWNLOADS_DROPDOWN,
UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM,
GET_DOWNLOADED_IMAGE_THUMBNAIL_LOCATION,
} from 'common/communication';
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 WindowManager from '../windows/windowManager';
import downloadsManager from 'main/downloadsManager';
export default class DownloadsDropdownView {
bounds?: Electron.Rectangle;
darkMode: boolean;
downloads: DownloadedItems;
item: DownloadedItem | undefined;
view: BrowserView;
window: BrowserWindow;
windowBounds: Electron.Rectangle;
constructor(window: BrowserWindow, downloads: DownloadedItems, darkMode: boolean) {
this.downloads = downloads;
this.window = window;
this.darkMode = darkMode;
this.item = undefined;
this.windowBounds = this.window.getContentBounds();
this.bounds = this.getBounds(DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT);
const preload = getLocalPreload('downloadsDropdown.js');
this.view = new BrowserView({webPreferences: {
preload,
// Workaround for this issue: https://github.com/electron/electron/issues/30993
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
transparent: true,
}});
this.view.webContents.loadURL(getLocalURLString('downloadsDropdown.html'));
this.window.addBrowserView(this.view);
this.view.webContents.session.webRequest.onHeadersReceived(downloadsManager.webRequestOnHeadersReceivedHandler);
ipcMain.on(OPEN_DOWNLOADS_DROPDOWN, this.handleOpen);
ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN, this.handleClose);
ipcMain.on(EMIT_CONFIGURATION, this.updateConfig);
ipcMain.on(REQUEST_DOWNLOADS_DROPDOWN_INFO, this.updateDownloadsDropdown);
ipcMain.on(REQUEST_CLEAR_DOWNLOADS_DROPDOWN, this.clearDownloads);
ipcMain.on(RECEIVE_DOWNLOADS_DROPDOWN_SIZE, this.handleReceivedDownloadsDropdownSize);
ipcMain.on(DOWNLOADS_DROPDOWN_SHOW_FILE_IN_FOLDER, this.showFileInFolder);
ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN, this.updateDownloads);
ipcMain.on(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, this.updateDownloadsDropdownMenuItem);
ipcMain.handle(GET_DOWNLOADED_IMAGE_THUMBNAIL_LOCATION, this.getDownloadImageThumbnailLocation);
}
updateDownloads = (event: IpcMainEvent, downloads: DownloadedItems) => {
log.debug('DownloadsDropdownView.updateDownloads', {downloads});
this.downloads = downloads;
this.updateDownloadsDropdown();
}
updateDownloadsDropdownMenuItem = (event: IpcMainEvent, item?: DownloadedItem) => {
log.debug('DownloadsDropdownView.updateDownloadsDropdownMenuItem', {item});
this.item = item;
this.updateDownloadsDropdown();
}
updateConfig = (event: IpcMainEvent, config: CombinedConfig) => {
log.debug('DownloadsDropdownView.updateConfig');
this.darkMode = config.darkMode;
this.updateDownloadsDropdown();
}
/**
* This is called every time the "window" is resized so that we can position
* the downloads dropdown at the correct position
*/
updateWindowBounds = () => {
log.debug('DownloadsDropdownView.updateWindowBounds');
this.windowBounds = this.window.getContentBounds();
this.updateDownloadsDropdown();
this.repositionDownloadsDropdown();
}
updateDownloadsDropdown = () => {
log.debug('DownloadsDropdownView.updateDownloadsDropdown');
this.view.webContents.send(
UPDATE_DOWNLOADS_DROPDOWN,
this.downloads,
this.darkMode,
this.windowBounds,
this.item,
);
}
handleOpen = () => {
log.debug('DownloadsDropdownView.handleOpen', {bounds: this.bounds});
if (!this.bounds) {
return;
}
this.view.setBounds(this.bounds);
this.window.setTopBrowserView(this.view);
this.view.webContents.focus();
downloadsManager.onOpen();
WindowManager.sendToRenderer(OPEN_DOWNLOADS_DROPDOWN);
}
handleClose = () => {
log.debug('DownloadsDropdownView.handleClose');
this.view.setBounds(this.getBounds(0, 0));
downloadsManager.onClose();
WindowManager.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN);
}
clearDownloads = () => {
downloadsManager.clearDownloadsDropDown();
this.handleClose();
}
showFileInFolder = (e: IpcMainEvent, item: DownloadedItem) => {
log.debug('DownloadsDropdownView.showFileInFolder', {item});
downloadsManager.showFileInFolder(item);
}
getBounds = (width: number, height: number) => {
// Must always use integers
return {
x: this.getX(this.windowBounds.width),
y: this.getY(),
width: Math.round(width),
height: Math.round(height),
};
}
getX = (windowWidth: number) => {
const result = windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH;
if (result <= DOWNLOADS_DROPDOWN_WIDTH) {
return 0;
}
return Math.round(result);
}
getY = () => {
return Math.round(TAB_BAR_HEIGHT);
}
repositionDownloadsDropdown = () => {
if (!this.bounds) {
return;
}
this.bounds = {
...this.bounds,
x: this.getX(this.windowBounds.width),
y: this.getY(),
};
if (downloadsManager.getIsOpen()) {
this.view.setBounds(this.bounds);
}
}
handleReceivedDownloadsDropdownSize = (event: IpcMainEvent, width: number, height: number) => {
log.silly('DownloadsDropdownView.handleReceivedDownloadsDropdownSize', {width, height});
this.bounds = this.getBounds(width, height);
if (downloadsManager.getIsOpen()) {
this.view.setBounds(this.bounds);
}
}
destroy = () => {
// 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) => {
// eslint-disable-next-line no-undef
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (!__IS_MAC_APP_STORE__) {
return location;
}
return path.resolve(app.getPath('temp'), path.basename(location));
}
}