[MM-52696] Upgrade and clean up Desktop App dev dependencies (#2970)

* Upgrade to ESLint v8

* Upgrade TypeScript, api-types, react-intl

* Remove unnecessary dependencies

* Update to React 17.0.2

* npm audit fixes, remove storybook

* Lock some packages

* Remove nan patch

* Remove some deprecated dependencies

* Fix lint/type/tests

* Merge'd

* Fix bad use of spawn

* Fix notarize

* Fix afterpack, switch to tsc es2020

* Fix api types

* Use @mattermost/eslint-plugin
This commit is contained in:
Devin Binnie
2024-03-07 15:55:33 -05:00
committed by GitHub
parent 12d59cd81c
commit 9b36c25e4e
198 changed files with 4997 additions and 17374 deletions

View File

@@ -1 +0,0 @@
import '@storybook/addon-actions/register';

View File

@@ -1,10 +0,0 @@
import {configure} from '@storybook/react';
import 'bootstrap/dist/css/bootstrap.min.css';
const req = require.context('../browser/components', true, /\.stories\.jsx$/)
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);

View File

@@ -1,18 +0,0 @@
const path = require("path");
const rendererConfig = require('../../webpack.config.renderer');
// https://storybook.js.org/configurations/custom-webpack-config/#full-control-mode--default
module.exports = (storybookBaseConfig, configType) => {
// Avoid conflicting two instances of React due to two package.json structure
storybookBaseConfig.resolve.modules.unshift(path.resolve(__dirname, '../node_modules'));
// Use same rules
storybookBaseConfig.module.rules = rendererConfig.module.rules.concat({
test: /\.(ttf|woff2?|eot|svg)/,
use: {
loader: 'file-loader'
}
});
return storybookBaseConfig;
}

View File

@@ -5,12 +5,11 @@ import {MattermostServer} from 'common/servers/MattermostServer';
import ServerManager from 'common/servers/serverManager';
import {URLValidationStatus} from 'common/utils/constants';
import {getDefaultViewsForConfigServer} from 'common/views/View';
import {ServerInfo} from 'main/server/serverInfo';
import ModalManager from 'main/views/modalManager';
import {getLocalURLString, getLocalPreload} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import ModalManager from 'main/views/modalManager';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
import {ServerViewState} from './serverViewState';

View File

@@ -1,10 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {IpcMainEvent, IpcMainInvokeEvent, ipcMain} from 'electron';
import {UniqueServer, Server} from 'types/config';
import {URLValidationResult} from 'types/server';
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {ipcMain} from 'electron';
import {
CLOSE_VIEW,
@@ -22,18 +20,20 @@ import {
UPDATE_TAB_ORDER,
VALIDATE_SERVER_URL,
} from 'common/communication';
import {Logger} from 'common/log';
import ServerManager from 'common/servers/serverManager';
import {MattermostServer} from 'common/servers/MattermostServer';
import {isValidURI, isValidURL, parseURL} from 'common/utils/url';
import {URLValidationStatus} from 'common/utils/constants';
import Config from 'common/config';
import ViewManager from 'main/views/viewManager';
import ModalManager from 'main/views/modalManager';
import MainWindow from 'main/windows/mainWindow';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import {Logger} from 'common/log';
import {MattermostServer} from 'common/servers/MattermostServer';
import ServerManager from 'common/servers/serverManager';
import {URLValidationStatus} from 'common/utils/constants';
import {isValidURI, isValidURL, parseURL} from 'common/utils/url';
import {ServerInfo} from 'main/server/serverInfo';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import ModalManager from 'main/views/modalManager';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
import type {UniqueServer, Server} from 'types/config';
import type {URLValidationResult} from 'types/server';
const log = new Logger('App', 'ServerViewState');
@@ -65,7 +65,7 @@ export class ServerViewState {
this.currentServerId = orderedServers[0].id;
}
}
}
};
getCurrentServer = () => {
log.silly('getCurrentServer');
@@ -78,7 +78,7 @@ export class ServerViewState {
throw new Error('Current server does not exist');
}
return server;
}
};
switchServer = (serverId: string, waitForViewToExist = false) => {
ServerManager.getServerLog(serverId, 'WindowManager').debug('switchServer');
@@ -102,7 +102,7 @@ export class ServerViewState {
ViewManager.showById(nextView.id);
}
ipcMain.emit(UPDATE_SHORTCUT_MENU);
}
};
selectNextView = () => {
this.selectView((order) => order + 1);
@@ -115,7 +115,7 @@ export class ServerViewState {
updateCurrentView = (serverId: string, viewId: string) => {
this.currentServerId = serverId;
ServerManager.updateLastActive(viewId);
}
};
/**
* Server Modals

View File

@@ -3,17 +3,17 @@
import Joi from 'joi';
import {Args} from 'types/args';
import {AnyConfig, ConfigV0, ConfigV1, ConfigV2, ConfigV3, ConfigServer} from 'types/config';
import {DownloadedItems} from 'types/downloads';
import {SavedWindowState} from 'types/mainWindow';
import {AppState} from 'types/appState';
import {ComparableCertificate} from 'types/certificate';
import {PermissionType, TrustedOrigin} from 'types/trustedOrigin';
import {Logger} from 'common/log';
import {TAB_MESSAGING} from 'common/views/View';
import {isValidURL} from 'common/utils/url';
import {TAB_MESSAGING} from 'common/views/View';
import type {AppState} from 'types/appState';
import type {Args} from 'types/args';
import type {ComparableCertificate} from 'types/certificate';
import type {AnyConfig, ConfigV0, ConfigV1, ConfigV2, ConfigV3, ConfigServer} from 'types/config';
import type {DownloadedItems} from 'types/downloads';
import type {SavedWindowState} from 'types/mainWindow';
import type {PermissionType, TrustedOrigin} from 'types/trustedOrigin';
const log = new Logger('Validator');
const defaultOptions = {

View File

@@ -4,8 +4,8 @@
import {EventEmitter} from 'events';
import {UPDATE_APPSTATE, UPDATE_APPSTATE_TOTALS, UPDATE_APPSTATE_FOR_VIEW_ID} from 'common/communication';
import ServerManager from 'common/servers/serverManager';
import {Logger} from 'common/log';
import ServerManager from 'common/servers/serverManager';
const log = new Logger('AppState');
@@ -27,7 +27,7 @@ export class AppState extends EventEmitter {
this.expired.set(viewId, expired);
this.emitStatusForView(viewId);
}
};
updateMentions = (viewId: string, mentions: number) => {
ServerManager.getViewLog(viewId, 'AppState').silly('updateMentions', mentions);
@@ -49,7 +49,7 @@ export class AppState extends EventEmitter {
this.expired.delete(viewId);
this.mentions.delete(viewId);
this.unreads.delete(viewId);
}
};
emitStatus = () => {
log.silly('emitStatus');

View File

@@ -6,10 +6,10 @@ import {EventEmitter} from 'events';
import WindowsRegistry from 'winreg';
import WindowsRegistryUTF8 from 'winreg-utf8';
import {RegistryConfig as RegistryConfigType, Server} from 'types/config';
import {Logger} from 'common/log';
import type {RegistryConfig as RegistryConfigType, Server} from 'types/config';
const log = new Logger('RegistryConfig');
const REGISTRY_HIVE_LIST = [WindowsRegistry.HKLM, WindowsRegistry.HKCU];
const BASE_REGISTRY_KEY_PATH = '\\Software\\Policies\\Mattermost';

View File

@@ -2,7 +2,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BuildConfig} from 'types/config';
import type {BuildConfig} from 'types/config';
// For detailed guides, please refer to https://docs.mattermost.com/deployment/desktop-app-deployment.html

View File

@@ -2,15 +2,15 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import path from 'path';
import os from 'os';
import path from 'path';
/**
* Default user preferences. End-users can change these parameters by editing config.json
* @param {number} version - Scheme version. (Not application version)
*/
import {ConfigV3} from 'types/config';
import type {ConfigV3} from 'types/config';
export const getDefaultDownloadLocation = (): string | undefined => {
// eslint-disable-next-line no-undef

View File

@@ -2,13 +2,17 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import fs from 'fs';
import os from 'os';
import path from 'path';
import {EventEmitter} from 'events';
import {
import {Logger} from 'common/log';
import Utils, {copy} from 'common/utils/util';
import * as Validator from 'common/Validator';
import {getDefaultViewsForConfigServer} from 'common/views/View';
import type {
AnyConfig,
BuildConfig,
CombinedConfig,
@@ -17,16 +21,11 @@ import {
RegistryConfig as RegistryConfigType,
} from 'types/config';
import {Logger} from 'common/log';
import {getDefaultViewsForConfigServer} from 'common/views/View';
import Utils, {copy} from 'common/utils/util';
import * as Validator from 'common/Validator';
import defaultPreferences, {getDefaultDownloadLocation} from './defaultPreferences';
import upgradeConfigData from './upgradePreferences';
import buildConfig from './buildConfig';
import RegistryConfig, {REGISTRY_READ_EVENT} from './RegistryConfig';
import defaultPreferences, {getDefaultDownloadLocation} from './defaultPreferences';
import migrateConfigItems from './migrationPreferences';
import RegistryConfig, {REGISTRY_READ_EVENT} from './RegistryConfig';
import upgradeConfigData from './upgradePreferences';
const log = new Logger('Config');
@@ -67,7 +66,7 @@ export class Config extends EventEmitter {
this.canUpgradeValue = this.checkWriteableApp();
this.reload();
}
};
initRegistry = () => {
if (process.platform !== 'win32') {
@@ -82,7 +81,7 @@ export class Config extends EventEmitter {
});
this.registryConfig.init();
});
}
};
/**
* Reload all sources of config data
@@ -101,7 +100,7 @@ export class Config extends EventEmitter {
this.regenerateCombinedConfigData();
this.emit('update', this.combinedData);
}
};
/*********************
* Setters and Getters
@@ -116,11 +115,11 @@ export class Config extends EventEmitter {
set = (key: keyof ConfigType, data: ConfigType[keyof ConfigType]): void => {
log.debug('set');
this.setMultiple({[key]: data});
}
};
setConfigPath = (configPath: string) => {
this.configFilePath = configPath;
}
};
/**
* Used to save an array of config properties in one go
@@ -136,7 +135,7 @@ export class Config extends EventEmitter {
this.localConfigData = Object.assign({}, this.localConfigData, {...newData, teams: this.localConfigData?.teams});
this.regenerateCombinedConfigData();
this.saveLocalConfigData();
}
};
setServers = (servers: ConfigServer[], lastActiveServer?: number) => {
log.debug('setServers', servers, lastActiveServer);
@@ -144,7 +143,7 @@ export class Config extends EventEmitter {
this.localConfigData = Object.assign({}, this.localConfigData, {teams: servers, lastActiveTeam: lastActiveServer ?? this.localConfigData?.lastActiveTeam});
this.regenerateCombinedConfigData();
this.saveLocalConfigData();
}
};
// getters for accessing the various config data inputs
@@ -265,7 +264,7 @@ export class Config extends EventEmitter {
this._predefinedServers.push(...this.registryConfigData.servers.map((server, index) => getDefaultViewsForConfigServer({...server, order: index})));
}
this.reload();
}
};
/**
* Config file loading methods
@@ -299,7 +298,7 @@ export class Config extends EventEmitter {
} catch (error) {
this.emit('error', error);
}
}
};
/**
* Loads and returns locally stored config data from the filesystem or returns app defaults if no file is found
@@ -326,7 +325,7 @@ export class Config extends EventEmitter {
this.writeFile(this.configFilePath, configData);
}
return configData;
}
};
/**
* Determines if locally stored data needs to be updated and upgrades as needed
@@ -357,7 +356,7 @@ export class Config extends EventEmitter {
}
return configData as ConfigType;
}
};
/**
* Properly combines all sources of data into a single, manageable set of all config data
@@ -384,7 +383,7 @@ export class Config extends EventEmitter {
if (this.combinedData) {
this.combinedData.appName = this.appName;
}
}
};
// helper functions
private writeFile = (filePath: string, configData: Partial<ConfigType>, callback?: fs.NoParamCallback) => {
@@ -407,7 +406,7 @@ export class Config extends EventEmitter {
fs.writeFileSync(filePath, json, 'utf8');
}
}
};
private checkWriteableApp = () => {
if (!this.appPath) {
@@ -440,7 +439,7 @@ export class Config extends EventEmitter {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return process.platform !== 'darwin' && __CAN_UPGRADE__;
}
};
}
const config = new Config();

View File

@@ -1,13 +1,12 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Config, MigrationInfo} from 'types/config';
import JsonFileManager from 'common/JsonFileManager';
import {TAB_MESSAGING} from 'common/views/View';
import {migrationInfoPath} from 'main/constants';
import type {Config, MigrationInfo} from 'types/config';
export default function migrateConfigItems(config: Config) {
const migrationPrefs = new JsonFileManager<MigrationInfo>(migrationInfoPath);
let didMigrate = false;

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2015-2016 Yuya Ochiai
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ConfigV0, ConfigV1, ConfigV2} from 'types/config';
import type {ConfigV0, ConfigV1, ConfigV2} from 'types/config';
import defaultPreferences, {getDefaultDownloadLocation} from './defaultPreferences';

View File

@@ -1,8 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {upgradeV0toV1, upgradeV1toV2, upgradeV2toV3} from 'common/config/upgradePreferences';
import pastDefaultPreferences from 'common/config/pastDefaultPreferences';
import {upgradeV0toV1, upgradeV1toV2, upgradeV2toV3} from 'common/config/upgradePreferences';
jest.mock('common/views/View', () => ({
getDefaultViewsForConfigServer: (value) => ({

View File

@@ -2,10 +2,10 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ConfigV3, ConfigV2, ConfigV1, ConfigV0, AnyConfig} from 'types/config';
import {getDefaultViewsForConfigServer} from 'common/views/View';
import type {ConfigV3, ConfigV2, ConfigV1, ConfigV0, AnyConfig} from 'types/config';
import pastDefaultPreferences from './pastDefaultPreferences';
function deepCopy<T>(object: T): T {

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {DownloadedItem} from 'types/downloads';
import type {DownloadItemTypeEnum} from 'main/downloadsManager';
import {DownloadItemTypeEnum} from 'main/downloadsManager';
import type {DownloadedItem} from 'types/downloads';
/**
* This string includes special characters so that it's not confused with

View File

@@ -1,7 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import log, {LevelOption} from 'electron-log';
import type {LevelOption} from 'electron-log';
import log from 'electron-log';
import Util from 'common/utils/util';
@@ -37,15 +38,15 @@ export class Logger {
silly: this.prefixed(log.silly, ...prefixes),
log: this.prefixed(log.log, ...prefixes),
};
}
};
private prefixed = (func: (...args: any[]) => void, ...additionalPrefixes: string[]) => {
return (...args: any[]) => func(...this.prefixes, ...this.shortenPrefixes(...additionalPrefixes), ...args);
}
};
private shortenPrefixes = (...prefixes: string[]) => {
return prefixes.map((prefix) => `[${Util.shorten(prefix)}]`);
}
};
error = this.prefixed(log.error);
warn = this.prefixed(log.warn);

View File

@@ -3,10 +3,10 @@
import {v4 as uuid} from 'uuid';
import {UniqueServer, Server} from 'types/config';
import {parseURL} from 'common/utils/url';
import type {UniqueServer, Server} from 'types/config';
export class MattermostServer {
id: string;
name: string;
@@ -27,7 +27,7 @@ export class MattermostServer {
if (!this.url) {
throw new Error('Invalid url for creating a server');
}
}
};
toUniqueServer = (): UniqueServer => {
return {
@@ -36,5 +36,5 @@ export class MattermostServer {
id: this.id,
isPredefined: this.isPredefined,
};
}
};
}

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {TAB_MESSAGING, TAB_FOCALBOARD, TAB_PLAYBOOKS} from 'common/views/View';
import {parseURL, isInternalURL} from 'common/utils/url';
import Utils from 'common/utils/util';
import {TAB_MESSAGING, TAB_FOCALBOARD, TAB_PLAYBOOKS} from 'common/views/View';
import {ServerManager} from './serverManager';

View File

@@ -3,21 +3,22 @@
import EventEmitter from 'events';
import {Server, ConfigServer, ConfigView} from 'types/config';
import {RemoteInfo} from 'types/server';
import Config from 'common/config';
import {
SERVERS_URL_MODIFIED,
SERVERS_UPDATE,
} from 'common/communication';
import Config from 'common/config';
import {Logger, getLevel} from 'common/log';
import {MattermostServer} from 'common/servers/MattermostServer';
import {TAB_FOCALBOARD, TAB_MESSAGING, TAB_PLAYBOOKS, MattermostView, getDefaultViews} from 'common/views/View';
import MessagingView from 'common/views/MessagingView';
import FocalboardView from 'common/views/FocalboardView';
import PlaybooksView from 'common/views/PlaybooksView';
import {getFormattedPathName, isInternalURL, parseURL} from 'common/utils/url';
import FocalboardView from 'common/views/FocalboardView';
import MessagingView from 'common/views/MessagingView';
import PlaybooksView from 'common/views/PlaybooksView';
import type {MattermostView} from 'common/views/View';
import {TAB_FOCALBOARD, TAB_MESSAGING, TAB_PLAYBOOKS, getDefaultViews} from 'common/views/View';
import type {Server, ConfigServer, ConfigView} from 'types/config';
import type {RemoteInfo} from 'types/server';
const log = new Logger('ServerManager');
@@ -55,7 +56,7 @@ export class ServerManager extends EventEmitter {
}
return views;
}, [] as MattermostView[]);
}
};
getOrderedServers = () => {
log.debug('getOrderedServers');
@@ -67,7 +68,7 @@ export class ServerManager extends EventEmitter {
}
return servers;
}, [] as MattermostServer[]);
}
};
getLastActiveTabForServer = (serverId: string) => {
log.withPrefix(serverId).debug('getLastActiveTabForServer');
@@ -80,27 +81,27 @@ export class ServerManager extends EventEmitter {
}
}
return this.getFirstOpenViewForServer(serverId);
}
};
getServer = (id: string) => {
return this.servers.get(id);
}
};
getView = (id: string) => {
return this.views.get(id);
}
};
getAllServers = () => {
return [...this.servers.values()];
}
};
hasServers = () => {
return Boolean(this.servers.size);
}
};
getRemoteInfo = (serverId: string) => {
return this.remoteInfo.get(serverId);
}
};
updateRemoteInfos = (remoteInfos: Map<string, RemoteInfo>) => {
let hasUpdates = false;
@@ -112,7 +113,7 @@ export class ServerManager extends EventEmitter {
if (hasUpdates) {
this.persistServers();
}
}
};
lookupViewByURL = (inputURL: URL | string, ignoreScheme = false) => {
log.silly('lookupViewByURL', `${inputURL}`, ignoreScheme);
@@ -139,21 +140,21 @@ export class ServerManager extends EventEmitter {
}
});
return selectedView;
}
};
updateServerOrder = (serverOrder: string[]) => {
log.debug('updateServerOrder', serverOrder);
this.serverOrder = serverOrder;
this.persistServers();
}
};
updateTabOrder = (serverId: string, viewOrder: string[]) => {
log.withPrefix(serverId).debug('updateTabOrder', viewOrder);
this.viewOrder.set(serverId, viewOrder);
this.persistServers();
}
};
addServer = (server: Server) => {
const newServer = new MattermostServer(server, false);
@@ -176,7 +177,7 @@ export class ServerManager extends EventEmitter {
this.emit(SERVERS_URL_MODIFIED, [newServer.id]);
this.persistServers();
return newServer;
}
};
editServer = (serverId: string, server: Server) => {
const existingServer = this.servers.get(serverId);
@@ -203,7 +204,7 @@ export class ServerManager extends EventEmitter {
urlModified?.();
this.persistServers();
}
};
removeServer = (serverId: string) => {
this.viewOrder.get(serverId)?.forEach((viewId) => this.views.delete(viewId));
@@ -216,7 +217,7 @@ export class ServerManager extends EventEmitter {
this.servers.delete(serverId);
this.persistServers();
}
};
setViewIsOpen = (viewId: string, isOpen: boolean) => {
const view = this.views.get(viewId);
@@ -226,7 +227,7 @@ export class ServerManager extends EventEmitter {
view.isOpen = isOpen;
this.persistServers();
}
};
updateLastActive = (viewId: string) => {
const view = this.views.get(viewId);
@@ -241,7 +242,7 @@ export class ServerManager extends EventEmitter {
}
this.persistServers(serverOrder);
}
};
reloadFromConfig = () => {
const serverOrder: string[] = [];
@@ -257,7 +258,7 @@ export class ServerManager extends EventEmitter {
}
this.filterOutDuplicateServers();
this.serverOrder = serverOrder;
}
};
private filterOutDuplicateServers = () => {
const servers = [...this.servers.keys()].map((key) => ({key, value: this.servers.get(key)!}));
@@ -269,7 +270,7 @@ export class ServerManager extends EventEmitter {
uniqueServers.add(`${server.value.name}:${server.value.url}`);
}
});
}
};
private initServer = (configServer: ConfigServer, isPredefined: boolean) => {
const server = new MattermostServer(configServer, isPredefined);
@@ -290,7 +291,7 @@ export class ServerManager extends EventEmitter {
this.lastActiveView.set(server.id, viewOrder[configServer.lastActiveTab]);
}
return server.id;
}
};
private getFirstOpenViewForServer = (serverId: string) => {
const viewOrder = this.getOrderedTabsForServer(serverId);
@@ -300,7 +301,7 @@ export class ServerManager extends EventEmitter {
throw new Error(`No views open for server id ${serverId}`);
}
return firstView;
}
};
private persistServers = async (lastActiveServer?: number) => {
this.emit(SERVERS_UPDATE);
@@ -314,7 +315,7 @@ export class ServerManager extends EventEmitter {
return servers;
}, [] as ConfigServer[]);
await Config.setServers(localServers, lastActiveServer);
}
};
private getLastActiveView = (serverId: string) => {
let lastActiveView: number | undefined;
@@ -325,7 +326,7 @@ export class ServerManager extends EventEmitter {
}
}
return lastActiveView;
}
};
private toConfigServer = (server: MattermostServer): ConfigServer => {
return {
@@ -346,7 +347,7 @@ export class ServerManager extends EventEmitter {
return views;
}, [] as ConfigView[]) ?? [],
};
}
};
private getNewView = (srv: MattermostServer, viewName: string, isOpen?: boolean) => {
switch (viewName) {
@@ -359,7 +360,7 @@ export class ServerManager extends EventEmitter {
default:
throw new Error('Not implemeneted');
}
}
};
private updateServerURL = (serverId: string) => {
const server = this.servers.get(serverId);
@@ -375,7 +376,7 @@ export class ServerManager extends EventEmitter {
return true;
}
return false;
}
};
private includeId = (id: string, ...prefixes: string[]) => {
const shouldInclude = ['debug', 'silly'].includes(getLevel());

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
// Copyright (c) 2015-2016 Yuya Ochiai
import {Rectangle} from 'electron';
import type {Rectangle} from 'electron';
import {DEVELOPMENT, PRODUCTION} from './constants';

View File

@@ -3,11 +3,11 @@
import {v4 as uuid} from 'uuid';
import {UniqueView} from 'types/config';
import type {MattermostServer} from 'common/servers/MattermostServer';
import {MattermostServer} from 'common/servers/MattermostServer';
import type {UniqueView} from 'types/config';
import {ViewType, MattermostView} from './View';
import type {ViewType, MattermostView} from './View';
export default abstract class BaseView implements MattermostView {
id: string;
@@ -35,5 +35,5 @@ export default abstract class BaseView implements MattermostView {
name: this.type,
isOpen: this.isOpen,
};
}
};
}

View File

@@ -4,7 +4,8 @@
import {getFormattedPathName} from 'common/utils/url';
import BaseView from './BaseView';
import {ViewType, TAB_FOCALBOARD} from './View';
import type {ViewType} from './View';
import {TAB_FOCALBOARD} from './View';
export default class FocalboardView extends BaseView {
get url(): URL {

View File

@@ -2,7 +2,8 @@
// See LICENSE.txt for license information.
import BaseView from './BaseView';
import {ViewType, TAB_MESSAGING} from './View';
import type {ViewType} from './View';
import {TAB_MESSAGING} from './View';
export default class MessagingView extends BaseView {
get url(): URL {

View File

@@ -4,7 +4,8 @@
import {getFormattedPathName} from 'common/utils/url';
import BaseView from './BaseView';
import {ViewType, TAB_PLAYBOOKS} from './View';
import type {ViewType} from './View';
import {TAB_PLAYBOOKS} from './View';
export default class PlaybooksView extends BaseView {
get url(): URL {

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {UniqueView, Server} from 'types/config';
import type {MattermostServer} from 'common/servers/MattermostServer';
import {MattermostServer} from 'common/servers/MattermostServer';
import type {UniqueView, Server} from 'types/config';
export const TAB_MESSAGING = 'TAB_MESSAGING';
export const TAB_FOCALBOARD = 'TAB_FOCALBOARD';

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable no-undef */
jest.mock('main/constants', () => ({
configPath: 'configPath',
allowedProtocolFile: 'allowedProtocolFile',

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable no-undef */
function toContainObject(received, argument) {
const pass = this.equals(received,
expect.arrayContaining([

View File

@@ -27,7 +27,7 @@ describe('main/AppVersionManager', () => {
fs.readFileSync.mockReturnValue('some bad JSON');
Validator.validateAppState.mockReturnValue(false);
// eslint-disable-next-line no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const appVersionManager = new AppVersionManager('somefilename.txt');
expect(fs.writeFile).toBeCalledWith('somefilename.txt', '{}', expect.any(Function));

View File

@@ -4,14 +4,13 @@
import {ipcMain} from 'electron';
import {AppState} from 'types/appState';
import {UPDATE_PATHS} from 'common/communication';
import JsonFileManager from 'common/JsonFileManager';
import * as Validator from 'common/Validator';
import {appVersionJson} from 'main/constants';
import type {AppState} from 'types/appState';
export class AppVersionManager extends JsonFileManager<AppState> {
constructor(file: string) {
super(file);
@@ -24,7 +23,7 @@ export class AppVersionManager extends JsonFileManager<AppState> {
if (!validatedJSON) {
this.setJson({});
}
}
};
set lastAppVersion(version) {
this.setValue('lastAppVersion', version);

View File

@@ -3,7 +3,6 @@
'use strict';
import {spawn} from 'child_process';
import path from 'path';
import {app, dialog} from 'electron';

View File

@@ -3,14 +3,12 @@
// See LICENSE.txt for license information.
import {spawn} from 'child_process';
import fs from 'fs';
import os from 'os';
import path from 'path';
import {app, dialog} from 'electron';
import {Logger} from 'common/log';
import {localizeMessage} from 'main/i18nManager';
const log = new Logger('CriticalErrorHandler');
@@ -19,7 +17,7 @@ export class CriticalErrorHandler {
init = () => {
process.on('unhandledRejection', this.processUncaughtExceptionHandler);
process.on('uncaughtException', this.processUncaughtExceptionHandler);
}
};
private processUncaughtExceptionHandler = (err: Error) => {
if (process.env.NODE_ENV === 'test') {
@@ -33,7 +31,7 @@ export class CriticalErrorHandler {
this.showExceptionDialog(err);
});
}
}
};
private showExceptionDialog = (err: Error) => {
const file = path.join(app.getPath('userData'), `uncaughtException-${Date.now()}.txt`);
@@ -91,7 +89,7 @@ export class CriticalErrorHandler {
}
app.exit(-1);
});
}
};
private openDetachedExternal = (url: string) => {
const spawnOption = {detached: true, stdio: 'ignore' as const};
@@ -105,7 +103,7 @@ export class CriticalErrorHandler {
default:
return undefined;
}
}
};
private createErrorReport = (err: Error) => {
// eslint-disable-next-line no-undef
@@ -114,7 +112,7 @@ export class CriticalErrorHandler {
return `Application: ${app.name} ${app.getVersion()}${__HASH_VERSION__ ? ` [commit: ${__HASH_VERSION__}]` : ''}\n` +
`Platform: ${os.type()} ${os.release()} ${os.arch()}\n` +
`${err.stack}`;
}
};
}
const criticalErrorHandler = new CriticalErrorHandler();

View File

@@ -2,13 +2,14 @@
// See LICENSE.txt for license information.
import {app} from 'electron';
import {Args} from 'types/args';
import yargs from 'yargs';
import {protocols} from '../../electron-builder.json';
import * as Validator from 'common/Validator';
import type {Args} from 'types/args';
import {protocols} from '../../electron-builder.json';
export default function parse(args: string[]) {
return validateArgs(parseArgs(triageArgs(args)));
}

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {EventEmitter} from 'events';
import {app, powerMonitor} from 'electron';
import {EventEmitter} from 'events';
import {Logger} from 'common/log';

View File

@@ -6,9 +6,8 @@ import fs from 'fs';
import {shell, dialog} from 'electron';
import MainWindow from './windows/mainWindow';
import {AllowProtocolDialog} from './allowProtocolDialog';
import MainWindow from './windows/mainWindow';
jest.mock('fs', () => ({
readFile: jest.fn(),

View File

@@ -7,14 +7,13 @@ import fs from 'fs';
import {dialog, shell} from 'electron';
import {localizeMessage} from 'main/i18nManager';
import buildConfig from 'common/config/buildConfig';
import {Logger} from 'common/log';
import * as Validator from 'common/Validator';
import {localizeMessage} from 'main/i18nManager';
import MainWindow from './windows/mainWindow';
import {allowedProtocolFile} from './constants';
import MainWindow from './windows/mainWindow';
const log = new Logger('AllowProtocolDialog');
@@ -35,14 +34,14 @@ export class AllowProtocolDialog {
this.addScheme('https');
buildConfig.allowedProtocols.forEach(this.addScheme);
});
}
};
addScheme = (scheme: string) => {
const proto = `${scheme}:`;
if (!this.allowedProtocols.includes(proto)) {
this.allowedProtocols.push(proto);
}
}
};
handleDialogEvent = async (protocol: string, URL: string) => {
try {
@@ -88,7 +87,7 @@ export class AllowProtocolDialog {
} catch (error) {
log.warn('Could not open external URL', error);
}
}
};
}
const allowProtocolDialog = new AllowProtocolDialog();

View File

@@ -3,12 +3,11 @@
import {app, dialog} from 'electron';
import CertificateStore from 'main/certificateStore';
import MainWindow from 'main/windows/mainWindow';
import ViewManager from 'main/views/viewManager';
import {handleAppWillFinishLaunching, handleAppCertificateError, certificateErrorCallbacks} from 'main/app/app';
import {getDeeplinkingURL, openDeepLink} from 'main/app/utils';
import CertificateStore from 'main/certificateStore';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
jest.mock('electron', () => ({
app: {

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {app, BrowserWindow, Event, dialog, WebContents, Certificate, Details} from 'electron';
import type {BrowserWindow, Event, WebContents, Certificate, Details} from 'electron';
import {app, dialog} from 'electron';
import {Logger} from 'common/log';
import {parseURL} from 'common/utils/url';
import updateManager from 'main/autoUpdater';
import CertificateStore from 'main/certificateStore';
import {localizeMessage} from 'main/i18nManager';

View File

@@ -6,12 +6,10 @@ import {app} from 'electron';
import {RELOAD_CONFIGURATION} from 'common/communication';
import Config from 'common/config';
import {setLoggingLevel} from 'common/log';
import {handleConfigUpdate} from 'main/app/config';
import {handleMainWindowIsShown} from 'main/app/intercom';
import MainWindow from 'main/windows/mainWindow';
import AutoLauncher from 'main/AutoLauncher';
import MainWindow from 'main/windows/mainWindow';
jest.mock('electron', () => ({
app: {

View File

@@ -3,12 +3,9 @@
import {app, ipcMain, nativeTheme} from 'electron';
import {CombinedConfig, Config as ConfigType} from 'types/config';
import {DARK_MODE_CHANGE, EMIT_CONFIGURATION, RELOAD_CONFIGURATION} from 'common/communication';
import Config from 'common/config';
import {Logger, setLoggingLevel} from 'common/log';
import AutoLauncher from 'main/AutoLauncher';
import {setUnreadBadgeSetting} from 'main/badge';
import Tray from 'main/tray/tray';
@@ -16,6 +13,8 @@ import LoadingScreen from 'main/views/loadingScreen';
import MainWindow from 'main/windows/mainWindow';
import SettingsWindow from 'main/windows/settingsWindow';
import type {CombinedConfig, Config as ConfigType} from 'types/config';
import {handleMainWindowIsShown} from './intercom';
import {handleUpdateMenuEvent, updateSpellCheckerLocales} from './utils';

View File

@@ -10,10 +10,6 @@ import('main/views/serverDropdownView');
import('main/views/downloadsDropdownMenuView');
import('main/views/downloadsDropdownView');
if (process.env.NODE_ENV !== 'production' && module.hot) {
module.hot.accept();
}
// attempt to initialize the application
try {
initialize();

View File

@@ -6,7 +6,6 @@ import path from 'path';
import {app, session} from 'electron';
import Config from 'common/config';
import parseArgs from 'main/ParseArgs';
import ViewManager from 'main/views/viewManager';

View File

@@ -34,7 +34,7 @@ import {
} from 'common/communication';
import Config from 'common/config';
import {Logger} from 'common/log';
import ServerManager from 'common/servers/serverManager';
import AllowProtocolDialog from 'main/allowProtocolDialog';
import AppVersionManager from 'main/AppVersionManager';
import AuthManager from 'main/authManager';
@@ -48,15 +48,12 @@ import downloadsManager from 'main/downloadsManager';
import i18nManager from 'main/i18nManager';
import parseArgs from 'main/ParseArgs';
import PermissionsManager from 'main/permissionsManager';
import ServerManager from 'common/servers/serverManager';
import TrustedOriginsStore from 'main/trustedOrigins';
import Tray from 'main/tray/tray';
import TrustedOriginsStore from 'main/trustedOrigins';
import UserActivityMonitor from 'main/UserActivityMonitor';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
import {protocols} from '../../../electron-builder.json';
import {
handleAppBeforeQuit,
handleAppBrowserWindowCreated,
@@ -103,6 +100,8 @@ import {
handleRestore,
} from './windows';
import {protocols} from '../../../electron-builder.json';
export const mainProtocol = protocols?.[0]?.schemes?.[0];
const log = new Logger('App.Initialize');

View File

@@ -3,10 +3,10 @@
import {app} from 'electron';
import {getLocalURLString, getLocalPreload} from 'main/utils';
import ServerManager from 'common/servers/serverManager';
import MainWindow from 'main/windows/mainWindow';
import {getLocalURLString, getLocalPreload} from 'main/utils';
import ModalManager from 'main/views/modalManager';
import MainWindow from 'main/windows/mainWindow';
import {
handleWelcomeScreenModal,

View File

@@ -1,21 +1,20 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {app, IpcMainEvent, IpcMainInvokeEvent, Menu} from 'electron';
import {UniqueServer} from 'types/config';
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {app, Menu} from 'electron';
import ServerViewState from 'app/serverViewState';
import {Logger} from 'common/log';
import ServerManager from 'common/servers/serverManager';
import {ping} from 'common/utils/requests';
import NotificationManager from 'main/notifications';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import ModalManager from 'main/views/modalManager';
import MainWindow from 'main/windows/mainWindow';
import type {UniqueServer} from 'types/config';
import {handleAppBeforeQuit} from './app';
const log = new Logger('App.Intercom');

View File

@@ -1,12 +1,10 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {dialog, screen} from 'electron';
import fs from 'fs-extra';
import {dialog, screen} from 'electron';
import JsonFileManager from 'common/JsonFileManager';
import {updatePaths} from 'main/constants';
import {getDeeplinkingURL, resizeScreen, migrateMacAppStore} from './utils';

View File

@@ -3,23 +3,18 @@
import path from 'path';
import type {BrowserWindow, Rectangle, Session} from 'electron';
import {app, Menu, session, dialog, nativeImage, screen} from 'electron';
import isDev from 'electron-is-dev';
import fs from 'fs-extra';
import {app, BrowserWindow, Menu, Rectangle, Session, session, dialog, nativeImage, screen} from 'electron';
import isDev from 'electron-is-dev';
import {MigrationInfo} from 'types/config';
import {RemoteInfo} from 'types/server';
import {Boundaries} from 'types/utils';
import Config from 'common/config';
import {Logger} from 'common/log';
import JsonFileManager from 'common/JsonFileManager';
import ServerManager from 'common/servers/serverManager';
import {MattermostServer} from 'common/servers/MattermostServer';
import {APP_MENU_WILL_CLOSE} from 'common/communication';
import Config from 'common/config';
import JsonFileManager from 'common/JsonFileManager';
import {Logger} from 'common/log';
import type {MattermostServer} from 'common/servers/MattermostServer';
import ServerManager from 'common/servers/serverManager';
import {isValidURI} from 'common/utils/url';
import updateManager from 'main/autoUpdater';
import {migrationInfoPath, updatePaths} from 'main/constants';
import {localizeMessage} from 'main/i18nManager';
@@ -30,6 +25,10 @@ import Tray from 'main/tray/tray';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
import type {MigrationInfo} from 'types/config';
import type {RemoteInfo} from 'types/server';
import type {Boundaries} from 'types/utils';
import {mainProtocol} from './initialize';
const assetsDir = path.resolve(app.getAppPath(), 'assets');

View File

@@ -1,10 +1,11 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserWindow, IpcMainEvent, systemPreferences} from 'electron';
import type {IpcMainEvent} from 'electron';
import {BrowserWindow, systemPreferences} from 'electron';
import {Logger} from 'common/log';
import Config from 'common/config';
import {Logger} from 'common/log';
const log = new Logger('App.Windows');

View File

@@ -3,9 +3,9 @@
'use strict';
import {AuthManager} from 'main/authManager';
import MainWindow from 'main/windows/mainWindow';
import ModalManager from 'main/views/modalManager';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
jest.mock('common/utils/url', () => {
const actualUrl = jest.requireActual('common/utils/url');

View File

@@ -1,19 +1,18 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {AuthenticationResponseDetails, AuthInfo, WebContents, Event} from 'electron';
import {PermissionType} from 'types/trustedOrigin';
import {LoginModalData} from 'types/auth';
import type {AuthenticationResponseDetails, AuthInfo, WebContents, Event} from 'electron';
import {Logger} from 'common/log';
import {BASIC_AUTH_PERMISSION} from 'common/permissions';
import {isCustomLoginURL, isTrustedURL, parseURL} from 'common/utils/url';
import modalManager from 'main/views/modalManager';
import TrustedOriginsStore from 'main/trustedOrigins';
import {getLocalURLString, getLocalPreload} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import modalManager from 'main/views/modalManager';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
import type {LoginModalData} from 'types/auth';
import type {PermissionType} from 'types/trustedOrigin';
const log = new Logger('AuthManager');
const preload = getLocalPreload('internalAPI.js');
@@ -51,7 +50,7 @@ export class AuthManager {
} else {
this.popPermissionModal(request, authInfo, BASIC_AUTH_PERMISSION);
}
}
};
popLoginModal = (request: AuthenticationResponseDetails, authInfo: AuthInfo) => {
const mainWindow = MainWindow.get();
@@ -70,7 +69,7 @@ export class AuthManager {
this.handleCancelLoginEvent(request);
});
}
}
};
popPermissionModal = (request: AuthenticationResponseDetails, authInfo: AuthInfo, permission: PermissionType) => {
const mainWindow = MainWindow.get();
@@ -89,7 +88,7 @@ export class AuthManager {
this.handleCancelLoginEvent(request);
});
}
}
};
handleLoginCredentialsEvent = (request: AuthenticationResponseDetails, username?: string, password?: string) => {
const callback = this.loginCallbackMap.get(request.url);
@@ -101,12 +100,12 @@ export class AuthManager {
callback(username, password);
}
this.loginCallbackMap.delete(request.url);
}
};
handleCancelLoginEvent = (request: AuthenticationResponseDetails) => {
log.info(`Cancelling request for ${request ? request.url : 'unknown'}`);
this.handleLoginCredentialsEvent(request); // we use undefined to cancel the request
}
};
handlePermissionGranted(url: string, permission: PermissionType) {
const parsedURL = parseURL(url);

View File

@@ -5,7 +5,6 @@ import {ipcMain as notMockedIpcMain} from 'electron';
import {autoUpdater as notMockedAutoUpdater} from 'electron-updater';
import {CHECK_FOR_UPDATES} from 'common/communication';
import NotificationManager from 'main/notifications';
import {UpdateManager} from './autoUpdater';

View File

@@ -4,13 +4,8 @@
import path from 'path';
import {dialog, ipcMain, app, nativeImage} from 'electron';
import {autoUpdater, CancellationToken, ProgressInfo, UpdateInfo} from 'electron-updater';
import {Logger} from 'common/log';
import downloadsManager from 'main/downloadsManager';
import {localizeMessage} from 'main/i18nManager';
import NotificationManager from 'main/notifications';
import type {ProgressInfo, UpdateInfo} from 'electron-updater';
import {autoUpdater, CancellationToken} from 'electron-updater';
import {
CANCEL_UPGRADE,
@@ -24,6 +19,10 @@ import {
UPDATE_REMIND_LATER,
} from 'common/communication';
import Config from 'common/config';
import {Logger} from 'common/log';
import downloadsManager from 'main/downloadsManager';
import {localizeMessage} from 'main/i18nManager';
import NotificationManager from 'main/notifications';
const NEXT_NOTIFY = 86400000; // 24 hours
const NEXT_CHECK = 3600000; // 1 hour
@@ -109,44 +108,44 @@ export class UpdateManager {
} else if (this.versionAvailable) {
this.notifyUpgrade();
}
}
};
notifyUpgrade = (): void => {
ipcMain.emit(UPDATE_AVAILABLE, null, this.versionAvailable);
NotificationManager.displayUpgrade(this.versionAvailable || 'unknown', this.handleDownload);
}
};
notifyDownloaded = (): void => {
ipcMain.emit(UPDATE_DOWNLOADED, null, this.downloadedInfo);
NotificationManager.displayRestartToUpgrade(this.versionDownloaded || 'unknown', this.handleUpdate);
}
};
handleDownload = (): void => {
if (this.lastCheck) {
clearTimeout(this.lastCheck);
}
autoUpdater.downloadUpdate(this.cancellationToken);
}
};
handleCancelDownload = (): void => {
this.cancellationToken?.cancel();
this.cancellationToken = new CancellationToken();
}
};
handleRemindLater = (): void => {
// TODO
}
};
handleOnQuit = (): void => {
if (this.versionDownloaded) {
autoUpdater.quitAndInstall(true, false);
}
}
};
handleUpdate = (): void => {
downloadsManager.removeUpdateBeforeRestart();
autoUpdater.quitAndInstall();
}
};
displayNoUpgrade = (): void => {
const version = app.getVersion();
@@ -159,7 +158,7 @@ export class UpdateManager {
buttons: [localizeMessage('label.ok', 'OK')],
detail: localizeMessage('main.autoUpdater.noUpdate.detail', 'You are using the latest version of the {appName} Desktop App (version {version}). You\'ll be notified when a new version is available to install.', {appName: app.name, version}),
});
}
};
checkForUpdates = (manually: boolean): void => {
if (!Config.canUpgrade) {
@@ -183,7 +182,7 @@ export class UpdateManager {
});
this.lastCheck = setTimeout(() => this.checkForUpdates(false), NEXT_CHECK);
}
}
};
}
const updateManager = new UpdateManager();

View File

@@ -4,9 +4,8 @@
import {app, nativeImage} from 'electron';
import MainWindow from './windows/mainWindow';
import * as Badge from './badge';
import MainWindow from './windows/mainWindow';
jest.mock('electron', () => ({
app: {

View File

@@ -2,12 +2,12 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserWindow, app, nativeImage} from 'electron';
import type {BrowserWindow} from 'electron';
import {app, nativeImage} from 'electron';
import AppState from 'common/appState';
import {UPDATE_APPSTATE_TOTALS} from 'common/communication';
import {Logger} from 'common/log';
import {localizeMessage} from 'main/i18nManager';
import MainWindow from './windows/mainWindow';

View File

@@ -2,9 +2,9 @@
// See LICENSE.txt for license information.
'use strict';
import MainWindow from 'main/windows/mainWindow';
import ModalManager from 'main/views/modalManager';
import {CertificateManager} from 'main/certificateManager';
import ModalManager from 'main/views/modalManager';
import MainWindow from 'main/windows/mainWindow';
jest.mock('main/windows/mainWindow', () => ({
get: jest.fn().mockImplementation(() => ({})),

View File

@@ -1,14 +1,14 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Certificate, WebContents, Event} from 'electron';
import {CertificateModalData} from 'types/certificate';
import type {Certificate, WebContents, Event} from 'electron';
import {Logger} from 'common/log';
import modalManager from './views/modalManager';
import type {CertificateModalData} from 'types/certificate';
import {getLocalURLString, getLocalPreload} from './utils';
import modalManager from './views/modalManager';
import MainWindow from './windows/mainWindow';
const log = new Logger('CertificateManager');
@@ -38,7 +38,7 @@ export class CertificateManager {
} else {
log.info(`There were ${list.length} candidate certificates. Skipping certificate selection`);
}
}
};
popCertificateModal = (url: string, list: Certificate[]) => {
const mainWindow = MainWindow.get();
@@ -57,7 +57,7 @@ export class CertificateManager {
this.handleSelectedCertificate(url);
});
}
}
};
handleSelectedCertificate = (server: string, cert?: Certificate) => {
const callback = this.certificateRequestCallbackMap.get(server);
@@ -75,7 +75,7 @@ export class CertificateManager {
}
}
this.certificateRequestCallbackMap.delete(server);
}
};
}
const certificateManager = new CertificateManager();

View File

@@ -5,14 +5,15 @@
import fs from 'fs';
import {Certificate, ipcMain} from 'electron';
import {ComparableCertificate} from 'types/certificate';
import type {Certificate} from 'electron';
import {ipcMain} from 'electron';
import {UPDATE_PATHS} from 'common/communication';
import {Logger} from 'common/log';
import * as Validator from 'common/Validator';
import type {ComparableCertificate} from 'types/certificate';
import {certificateStorePath} from './constants';
function comparableCertificate(certificate: Certificate, dontTrust = false): ComparableCertificate {
@@ -83,7 +84,7 @@ export class CertificateStore {
// clicking "Don't ask again" checkbox before cancelling the connection.
const dontTrust = this.data[targetURL.origin]?.dontTrust;
return dontTrust === undefined ? false : dontTrust;
}
};
}
let certificateStore = new CertificateStore(certificateStorePath);

View File

@@ -2,8 +2,9 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, BrowserWindow, ContextMenuParams, Event} from 'electron';
import electronContextMenu, {Options} from 'electron-context-menu';
import type {BrowserView, BrowserWindow, ContextMenuParams, Event} from 'electron';
import type {Options} from 'electron-context-menu';
import electronContextMenu from 'electron-context-menu';
import {parseURL} from 'common/utils/url';
@@ -46,12 +47,12 @@ export default class ContextMenu {
this.menuDispose();
delete this.menuDispose;
}
}
};
reload = () => {
this.dispose();
const options = {window: this.view, ...this.menuOptions};
this.menuDispose = electronContextMenu(options);
}
};
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {DiagnosticsStepConstructorPayload} from 'types/diagnostics';
import type {DiagnosticsStepConstructorPayload} from 'types/diagnostics';
import {addDurationToFnReturnObject} from './steps/internal/utils';

View File

@@ -2,11 +2,12 @@
// See LICENSE.txt for license information.
import {shell} from 'electron';
import log, {ElectronLog} from 'electron-log';
import {DiagnosticsReport} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import log from 'electron-log';
import type {DiagnosticsReport} from 'types/diagnostics';
import DiagnosticsStep from './DiagnosticStep';
import Step0 from './steps/step0.logLevel';
import Step1 from './steps/step1.internetConnection';
import Step10 from './steps/step10.crashReports';
@@ -62,7 +63,7 @@ class DiagnosticsModule {
this.logger.error('Diagnostics.run Error: ', {error});
this.initializeValues(true);
}
}
};
initializeValues = (clear = false) => {
this.logger.transports.file.level = 'silly';
@@ -72,14 +73,14 @@ class DiagnosticsModule {
this.stepCurrent = 0;
this.stepTotal = clear ? 0 : this.getStepCount();
this.report = [];
}
};
getStepCount = () => {
const stepsCount = SORTED_STEPS.length;
this.logger.debug('Diagnostics.getStepCount', {stepsCount});
return stepsCount;
}
};
executeSteps = async () => {
this.logger.info('Diagnostics.executeSteps Started');
@@ -112,7 +113,7 @@ class DiagnosticsModule {
this.stepCurrent = index;
}
this.logger.info('Diagnostics.executeSteps Finished');
}
};
printReport = () => {
const totalStepsCount = this.getStepCount();
@@ -129,42 +130,42 @@ class DiagnosticsModule {
this.logger.info(`| ---${this.fillSpaces(3)}| ---${this.fillSpaces(maxStepNameLength - 3)} | ---${this.fillSpaces(7)}|`);
this.logger.info(`${successfulStepsCount} out of ${totalStepsCount} steps succeeded`);
this.printStepEnd('Report');
}
};
showLogFile = () => {
const pathToFile = this.getLoggerFilePath();
this.logger.debug('Diagnostics.showLogFile', {pathToFile});
shell.showItemInFolder(pathToFile);
}
};
sendNotificationDiagnosticsStarted = () => {
this.logger.debug('Diagnostics sendNotification DiagnosticsStarted');
}
};
isValidStep = (step: unknown) => {
return step instanceof DiagnosticsStep;
}
};
getLoggerFilePath = () => {
return this.logger.transports.file.getFile()?.path;
}
};
isRunning = () => {
return this.stepTotal > 0 && this.stepCurrent >= 0;
}
};
getSuccessfulStepsCount = () => {
return this.report.filter((step) => step.succeeded).length;
}
};
private printStepStart = (name: string) => {
this.logger.info(`${HASHTAGS} ${name} START ${HASHTAGS}`);
}
};
private printStepEnd = (name: string) => {
this.logger.info(`${HASHTAGS} ${name} END ${HASHTAGS}`);
}
};
private addToReport(data: DiagnosticsReport[number]): void {
this.report = [
@@ -178,7 +179,7 @@ class DiagnosticsModule {
return '';
}
return ' '.repeat(i);
}
};
}
const Diagnostics = new DiagnosticsModule();

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ElectronLog} from 'electron-log';
import type {ElectronLog} from 'electron-log';
import {obfuscateByType} from './obfuscators';

View File

@@ -4,12 +4,14 @@ import fs from 'fs';
import https from 'https';
import readline from 'readline';
import {BrowserWindow, Rectangle, WebContents} from 'electron';
import log, {ElectronLog, LogLevel} from 'electron-log';
import {AddDurationToFnReturnObject, LogFileLineData, LogLevelAmounts, WindowStatus} from 'types/diagnostics';
import type {BrowserWindow, Rectangle, WebContents} from 'electron';
import type {ElectronLog, LogLevel} from 'electron-log';
import log from 'electron-log';
import {IS_ONLINE_ENDPOINT, LOGS_MAX_STRING_LENGTH, REGEX_LOG_FILE_LINE} from 'common/constants';
import type {AddDurationToFnReturnObject, LogFileLineData, LogLevelAmounts, WindowStatus} from 'types/diagnostics';
export function dateTimeInFilename(date?: Date) {
const now = date ?? new Date();
return `${now.getDate()}-${now.getMonth()}-${now.getFullYear()}_${now.getHours()}-${now.getMinutes()}-${now.getSeconds()}-${now.getMilliseconds()}`;
@@ -79,7 +81,7 @@ export async function isOnline(logger: ElectronLog = log, url = IS_ONLINE_ENDPOI
return;
}
} catch (e) {
logger.error('Cannot parse response')
logger.error('Cannot parse response');
}
}
resolve(false);

View File

@@ -1,8 +1,9 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';

View File

@@ -4,15 +4,15 @@
import path from 'path';
import {app} from 'electron';
import {ElectronLog} from 'electron-log';
import type {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import loggerHooks from './internal/loggerHooks';
import {dateTimeInFilename} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-0';
const stepDescriptiveName = 'logConfig';

View File

@@ -1,13 +1,14 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import DiagnosticsStep from '../DiagnosticStep';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import {isOnline} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-1';
const stepDescriptiveName = 'internetConnection';

View File

@@ -4,8 +4,9 @@ import fs from 'fs';
import path from 'path';
import {app} from 'electron';
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';

View File

@@ -2,8 +2,9 @@
// See LICENSE.txt for license information.
import {session} from 'electron';
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';

View File

@@ -2,14 +2,14 @@
// See LICENSE.txt for license information.
import fs from 'fs';
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import Config from 'common/config';
import * as Validator from 'common/Validator';
import {configPath} from 'main/constants';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-2';

View File

@@ -1,16 +1,17 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import ServerManager from 'common/servers/serverManager';
import {parseURL} from 'common/utils/url';
import DiagnosticsStep from '../DiagnosticStep';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import {isOnline} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-3';
const stepDescriptiveName = 'serverConnectivity';

View File

@@ -2,11 +2,12 @@
// See LICENSE.txt for license information.
import {session} from 'electron';
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import {COOKIE_NAME_AUTH_TOKEN, COOKIE_NAME_CSRF, COOKIE_NAME_USER_ID} from 'common/constants';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-4';

View File

@@ -1,16 +1,16 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import MainWindow from 'main/windows/mainWindow';
import DiagnosticsStep from '../DiagnosticStep';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import {browserWindowVisibilityStatus, webContentsCheck} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-5';
const stepDescriptiveName = 'BrowserWindowsChecks';

View File

@@ -3,14 +3,17 @@
import fs from 'fs';
import {Notification, systemPreferences} from 'electron';
import log, {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import log from 'electron-log';
import DiagnosticsStep from '../DiagnosticStep';
import config from 'common/config';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import {checkPathPermissions} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-6';
const stepDescriptiveName = 'PermissionsCheck';

View File

@@ -3,13 +3,14 @@
import path from 'path';
import {app, powerMonitor} from 'electron';
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import DiagnosticsStep from '../DiagnosticStep';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import {dateTimeInFilename} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-7';
const stepDescriptiveName = 'PerformanceAndMemory';

View File

@@ -1,16 +1,17 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import log, {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import log from 'electron-log';
import {getPercentage} from 'main/utils';
import DiagnosticsStep from '../DiagnosticStep';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import {readFileLineByLine} from './internal/utils';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-8';
const stepDescriptiveName = 'LogHeuristics';

View File

@@ -1,11 +1,12 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ElectronLog} from 'electron-log';
import {DiagnosticStepResponse} from 'types/diagnostics';
import type {ElectronLog} from 'electron-log';
import config from 'common/config';
import type {DiagnosticStepResponse} from 'types/diagnostics';
import DiagnosticsStep from '../DiagnosticStep';
const stepName = 'Step-9';

View File

@@ -1,15 +1,13 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import path from 'path';
import fs from 'fs';
import path from 'path';
import {shell} from 'electron';
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import Config from 'common/config';
import {APP_UPDATE_KEY} from 'common/constants';
import {DownloadsManager} from 'main/downloadsManager';
const downloadLocationMock = '/path/to/downloads';

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import path from 'path';
import fs from 'fs';
import path from 'path';
import {DownloadItem, Event, WebContents, FileFilter, ipcMain, dialog, shell, Menu, app, IpcMainInvokeEvent} from 'electron';
import {ProgressInfo, UpdateInfo} from 'electron-updater';
import {DownloadedItem, DownloadItemDoneEventState, DownloadedItems, DownloadItemState, DownloadItemUpdatedEventState} from 'types/downloads';
import type {DownloadItem, Event, WebContents, FileFilter, IpcMainInvokeEvent} from 'electron';
import {ipcMain, dialog, shell, Menu, app} from 'electron';
import type {ProgressInfo, UpdateInfo} from 'electron-updater';
import {
CANCEL_UPDATE_DOWNLOAD,
@@ -25,16 +25,18 @@ import {
UPDATE_PROGRESS,
} from 'common/communication';
import Config from 'common/config';
import {APP_UPDATE_KEY, UPDATE_DOWNLOAD_ITEM} from 'common/constants';
import JsonFileManager from 'common/JsonFileManager';
import {Logger} from 'common/log';
import {APP_UPDATE_KEY, UPDATE_DOWNLOAD_ITEM} from 'common/constants';
import {DOWNLOADS_DROPDOWN_AUTOCLOSE_TIMEOUT, DOWNLOADS_DROPDOWN_MAX_ITEMS} from 'common/utils/constants';
import * as Validator from 'common/Validator';
import {localizeMessage} from 'main/i18nManager';
import NotificationManager from 'main/notifications';
import {doubleSecToMs, getPercentage, isStringWithLength, readFilenameFromContentDispositionHeader, shouldIncrementFilename} from 'main/utils';
import ViewManager from 'main/views/viewManager';
import MainWindow from 'main/windows/mainWindow';
import {doubleSecToMs, getPercentage, isStringWithLength, readFilenameFromContentDispositionHeader, shouldIncrementFilename} from 'main/utils';
import type {DownloadedItem, DownloadItemDoneEventState, DownloadedItems, DownloadItemState, DownloadItemUpdatedEventState} from 'types/downloads';
import appVersionManager from './AppVersionManager';
import {downloadsJson} from './constants';
@@ -189,7 +191,7 @@ export class DownloadsManager extends JsonFileManager<DownloadedItems> {
this.clearFile(file);
}
}
}
};
checkForDeletedFiles = () => {
log.debug('checkForDeletedFiles');
@@ -388,7 +390,7 @@ export class DownloadsManager extends JsonFileManager<DownloadedItems> {
startFrom,
localizeMessage('main.downloadsManager.specifyDownloadsFolder', 'Specify the folder where files will download'),
);
}
};
private selectDefaultDownloadDirectory = async (startFrom: string, message: string) => {
log.debug('handleSelectDownload', startFrom);
@@ -398,7 +400,7 @@ export class DownloadsManager extends JsonFileManager<DownloadedItems> {
properties:
['openDirectory', 'createDirectory', 'dontAddToRecent', 'promptToCreate']});
return result.filePaths[0];
}
};
private verifyMacAppStoreDownloadFolder = async (fileName: string) => {
let downloadLocation = Config.downloadLocation;
@@ -421,7 +423,7 @@ export class DownloadsManager extends JsonFileManager<DownloadedItems> {
}
return downloadLocation;
}
};
private markFileAsDeleted = (item: DownloadedItem) => {
const fileId = this.getDownloadedFileId(item);
@@ -647,7 +649,7 @@ export class DownloadsManager extends JsonFileManager<DownloadedItems> {
private getBookmark = (item: DownloadItem) => {
return this.bookmarks.get(this.getFileId(item))?.bookmark;
}
};
private getFileSize = (item: DownloadItem) => {
const itemTotalBytes = item.getTotalBytes();

View File

@@ -6,7 +6,8 @@ import {ipcMain} from 'electron';
import {GET_AVAILABLE_LANGUAGES, GET_LANGUAGE_INFORMATION} from 'common/communication';
import {Logger} from 'common/log';
import {Language, languages} from '../../i18n/i18n';
import type {Language} from '../../i18n/i18n';
import {languages} from '../../i18n/i18n';
export function localizeMessage(s: string, defaultString = '', values: any = {}) {
let str = i18nManager.currentLanguage.url[s] || defaultString;
@@ -39,23 +40,23 @@ export class I18nManager {
log.warn('Failed to set new language', locale);
return false;
}
};
getLanguages = () => {
return languages;
}
};
getAvailableLanguages = () => {
return Object.keys(languages);
}
};
isLanguageAvailable = (locale: string) => {
return Boolean(this.getLanguages()[locale]);
}
};
getCurrentLanguage = () => {
return this.currentLanguage;
}
};
}
const i18nManager = new I18nManager();

View File

@@ -5,11 +5,9 @@
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import {localizeMessage} from 'main/i18nManager';
import ServerManager from 'common/servers/serverManager';
import ServerViewState from 'app/serverViewState';
import ServerManager from 'common/servers/serverManager';
import {localizeMessage} from 'main/i18nManager';
import CallsWidgetWindow from 'main/windows/callsWidgetWindow';
import {createTemplate} from './app';

View File

@@ -3,24 +3,24 @@
// See LICENSE.txt for license information.
'use strict';
import {app, ipcMain, Menu, MenuItemConstructorOptions, MenuItem, session, shell, WebContents, clipboard} from 'electron';
import type {MenuItemConstructorOptions, MenuItem, WebContents} from 'electron';
import {app, ipcMain, Menu, session, shell, clipboard} from 'electron';
import log from 'electron-log';
import ServerViewState from 'app/serverViewState';
import {OPEN_SERVERS_DROPDOWN, SHOW_NEW_SERVER_MODAL} from 'common/communication';
import {t} from 'common/utils/util';
import {getViewDisplayName, ViewType} from 'common/views/View';
import {Config} from 'common/config';
import {localizeMessage} from 'main/i18nManager';
import type {Config} from 'common/config';
import ServerManager from 'common/servers/serverManager';
import {UpdateManager} from 'main/autoUpdater';
import downloadsManager from 'main/downloadsManager';
import {t} from 'common/utils/util';
import {getViewDisplayName} from 'common/views/View';
import type {ViewType} from 'common/views/View';
import type {UpdateManager} from 'main/autoUpdater';
import Diagnostics from 'main/diagnostics';
import downloadsManager from 'main/downloadsManager';
import {localizeMessage} from 'main/i18nManager';
import ViewManager from 'main/views/viewManager';
import SettingsWindow from 'main/windows/settingsWindow';
import CallsWidgetWindow from 'main/windows/callsWidgetWindow';
import SettingsWindow from 'main/windows/settingsWindow';
export function createTemplate(config: Config, updateManager: UpdateManager) {
const separatorItem: MenuItemConstructorOptions = {

View File

@@ -3,12 +3,11 @@
// See LICENSE.txt for license information.
'use strict';
import {Menu, MenuItem, MenuItemConstructorOptions} from 'electron';
import type {MenuItem, MenuItemConstructorOptions} from 'electron';
import {Menu} from 'electron';
import ServerViewState from 'app/serverViewState';
import ServerManager from 'common/servers/serverManager';
import {localizeMessage} from 'main/i18nManager';
import SettingsWindow from 'main/windows/settingsWindow';

View File

@@ -4,12 +4,10 @@
import os from 'os';
import path from 'path';
import {app, Notification} from 'electron';
import {v4 as uuid} from 'uuid';
import {app, Notification} from 'electron';
import Utils from 'common/utils/util';
import {localizeMessage} from 'main/i18nManager';
const assetsDir = path.resolve(app.getAppPath(), 'assets');

View File

@@ -4,16 +4,14 @@
import os from 'os';
import path from 'path';
import {app, Notification} from 'electron';
import {v4 as uuid} from 'uuid';
import {app, Notification} from 'electron';
import {MentionOptions} from 'types/notification';
import Utils from 'common/utils/util';
import {localizeMessage} from 'main/i18nManager';
import type {MentionOptions} from 'types/notification';
const assetsDir = path.resolve(app.getAppPath(), 'assets');
const appIconURL = path.resolve(assetsDir, 'appicon_48.png');
@@ -52,5 +50,5 @@ export class Mention extends Notification {
getNotificationSound = () => {
return this.customSound;
}
};
}

View File

@@ -4,18 +4,17 @@
'use strict';
import notMockedCP from 'child_process';
import {Notification as NotMockedNotification, shell, app, BrowserWindow, WebContents} from 'electron';
import {getFocusAssist as notMockedGetFocusAssist} from 'windows-focus-assist';
import type {BrowserWindow, WebContents} from 'electron';
import {Notification as NotMockedNotification, shell, app} from 'electron';
import {getDoNotDisturb as notMockedGetDarwinDoNotDisturb} from 'macos-notification-state';
import {getFocusAssist as notMockedGetFocusAssist} from 'windows-focus-assist';
import {PLAY_SOUND} from 'common/communication';
import notMockedConfig from 'common/config';
import {localizeMessage as notMockedLocalizeMessage} from 'main/i18nManager';
import notMockedPermissionsManager from 'main/permissionsManager';
import notMockedMainWindow from 'main/windows/mainWindow';
import ViewManager from 'main/views/viewManager';
import notMockedMainWindow from 'main/windows/mainWindow';
import getLinuxDoNotDisturb from './dnd-linux';
@@ -50,7 +49,7 @@ jest.mock('electron', () => {
on = (event: string, callback: () => void) => {
this.callbackMap.set(event, callback);
}
};
show = jest.fn().mockImplementation(() => {
this.callbackMap.get('show')?.();

View File

@@ -2,23 +2,22 @@
// See LICENSE.txt for license information.
import {app, shell, Notification} from 'electron';
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import Config from 'common/config';
import {PLAY_SOUND, NOTIFICATION_CLICKED} from 'common/communication';
import Config from 'common/config';
import {Logger} from 'common/log';
import getLinuxDoNotDisturb from './dnd-linux';
import getWindowsDoNotDisturb from './dnd-windows';
import {DownloadNotification} from './Download';
import {Mention} from './Mention';
import {NewVersionNotification, UpgradeNotification} from './Upgrade';
import PermissionsManager from '../permissionsManager';
import ViewManager from '../views/viewManager';
import MainWindow from '../windows/mainWindow';
import {Mention} from './Mention';
import {DownloadNotification} from './Download';
import {NewVersionNotification, UpgradeNotification} from './Upgrade';
import getLinuxDoNotDisturb from './dnd-linux';
import getWindowsDoNotDisturb from './dnd-windows';
const log = new Logger('Notifications');
class NotificationManager {

View File

@@ -4,7 +4,6 @@
import {dialog} from 'electron';
import {parseURL, isTrustedURL} from 'common/utils/url';
import ViewManager from 'main/views/viewManager';
import CallsWidgetWindow from 'main/windows/callsWidgetWindow';
import MainWindow from 'main/windows/mainWindow';

View File

@@ -1,9 +1,10 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {
import type {
PermissionRequestHandlerHandlerDetails,
WebContents,
WebContents} from 'electron';
import {
app,
dialog,
ipcMain,
@@ -12,9 +13,8 @@ import {
import {UPDATE_PATHS} from 'common/communication';
import JsonFileManager from 'common/JsonFileManager';
import {Logger} from 'common/log';
import {t} from 'common/utils/util';
import {isTrustedURL, parseURL} from 'common/utils/url';
import {t} from 'common/utils/util';
import {permissionsJson} from 'main/constants';
import {localizeMessage} from 'main/i18nManager';
import ViewManager from 'main/views/viewManager';
@@ -69,7 +69,7 @@ export class PermissionsManager extends JsonFileManager<Permissions> {
permission,
details.securityOrigin ?? details.requestingUrl,
));
}
};
doPermissionRequest = async (
webContentsId: number,
@@ -172,7 +172,7 @@ export class PermissionsManager extends JsonFileManager<Permissions> {
// We've checked everything so we're okay to grant the remaining cases
return true;
}
};
}
t('main.permissionsManager.checkPermission.dialog.message.media');

View File

@@ -1,11 +1,10 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {IpcRendererEvent, contextBridge, ipcRenderer, webFrame} from 'electron';
import type {IpcRendererEvent} from 'electron';
import {contextBridge, ipcRenderer, webFrame} from 'electron';
import {ExternalAPI} from 'types/externalAPI';
import {DesktopAPI} from '@mattermost/desktop-api';
import type {DesktopAPI} from '@mattermost/desktop-api';
import {
NOTIFY_MENTION,
@@ -42,6 +41,8 @@ import {
LEGACY_OFF,
} from 'common/communication';
import type {ExternalAPI} from 'types/externalAPI';
const createListener: ExternalAPI['createListener'] = (channel: string, listener: (...args: never[]) => void) => {
const listenerWithEvent = (_: IpcRendererEvent, ...args: unknown[]) =>
listener(...args as never[]);

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ClientConfig, RemoteInfo} from 'types/server';
import {MattermostServer} from 'common/servers/MattermostServer';
import type {MattermostServer} from 'common/servers/MattermostServer';
import {parseURL} from 'common/utils/url';
import type {ClientConfig, RemoteInfo} from 'types/server';
import {getServerAPI} from './serverAPI';
export class ServerInfo {
@@ -24,7 +24,7 @@ export class ServerInfo {
);
return this.remoteInfo;
}
};
fetchRemoteInfo = async () => {
await this.fetchConfigData();
@@ -34,7 +34,7 @@ export class ServerInfo {
);
return this.remoteInfo;
}
};
private getRemoteInfo = <T>(
callback: (data: T) => void,
@@ -54,17 +54,17 @@ export class ServerInfo {
() => reject(new Error('Aborted')),
(error: Error) => reject(error));
});
}
};
private onGetConfig = (data: ClientConfig) => {
this.remoteInfo.serverVersion = data.Version;
this.remoteInfo.siteURL = data.SiteURL;
this.remoteInfo.siteName = data.SiteName;
this.remoteInfo.hasFocalboard = this.remoteInfo.hasFocalboard || data.BuildBoards === 'true';
}
};
private onGetPlugins = (data: Array<{id: string; version: string}>) => {
this.remoteInfo.hasFocalboard = this.remoteInfo.hasFocalboard || data.some((plugin) => plugin.id === 'focalboard');
this.remoteInfo.hasPlaybooks = data.some((plugin) => plugin.id === 'playbooks');
}
};
}

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {handleConfigUpdate} from 'main/app/config';
import AutoLauncher from 'main/AutoLauncher';
import Tray from './tray';

View File

@@ -8,7 +8,6 @@ import {app, nativeImage, Tray, systemPreferences, nativeTheme} from 'electron';
import AppState from 'common/appState';
import {UPDATE_APPSTATE_TOTALS} from 'common/communication';
import {Logger} from 'common/log';
import {localizeMessage} from 'main/i18nManager';
import MainWindow from 'main/windows/mainWindow';
import SettingsWindow from 'main/windows/settingsWindow';
@@ -44,7 +43,7 @@ export class TrayIcon {
this.tray.on('click', this.onClick);
this.tray.on('right-click', () => this.tray?.popUpContextMenu());
this.tray.on('balloon-click', this.onClick);
}
};
refreshImages = (trayIconTheme: string) => {
const systemTheme = nativeTheme.shouldUseDarkColors ? 'light' : 'dark';
@@ -98,13 +97,13 @@ export class TrayIcon {
this.update(this.status, this.message);
}
return this.images;
}
};
destroy = () => {
if (process.platform === 'win32') {
this.tray?.destroy();
}
}
};
setMenu = (tMenu: Electron.Menu) => this.tray?.setContextMenu(tMenu);
@@ -117,7 +116,7 @@ export class TrayIcon {
this.message = message;
this.tray.setImage(this.images[status]);
this.tray.setToolTip(message);
}
};
// Linux note: the click event was fixed in Electron v23, but only fires when the OS supports StatusIconLinuxDbus
// There is a fallback case that will make sure the icon is displayed, but will only support the context menu
@@ -163,7 +162,7 @@ export class TrayIcon {
} else {
this.update('normal', app.name);
}
}
};
}
const tray = new TrayIcon();

View File

@@ -2,8 +2,8 @@
// See LICENSE.txt for license information.
'use strict';
import {TrustedOriginsStore} from 'main/trustedOrigins';
import {BASIC_AUTH_PERMISSION} from 'common/permissions';
import {TrustedOriginsStore} from 'main/trustedOrigins';
jest.mock('path', () => ({
resolve: jest.fn(),

View File

@@ -7,12 +7,12 @@ import fs from 'fs';
import {ipcMain} from 'electron';
import {TrustedOrigin, PermissionType} from 'types/trustedOrigin';
import {UPDATE_PATHS} from 'common/communication';
import {Logger} from 'common/log';
import * as Validator from 'common/Validator';
import type {TrustedOrigin, PermissionType} from 'types/trustedOrigin';
import {trustedOriginsStoreFile} from './constants';
const log = new Logger('TrustedOriginsStore');
@@ -34,7 +34,7 @@ export class TrustedOriginsStore {
storeData = null;
}
return storeData;
}
};
load = () => {
const storeData = this.readFromFile();
@@ -46,7 +46,7 @@ export class TrustedOriginsStore {
}
}
this.data = new Map(Object.entries(result));
}
};
// don't use this, is for ease of mocking it on testing
saveToFile(stringMap: string) {
@@ -77,11 +77,11 @@ export class TrustedOriginsStore {
// enables usage of `targetURL` for `permission`
addPermission = (targetURL: URL, permission: PermissionType) => {
this.set(targetURL, {[permission]: true});
}
};
delete = (targetURL: URL) => {
return this.data?.delete(targetURL.origin);
}
};
isExisting = (targetURL: URL) => {
return this.data?.has(targetURL.origin) || false;
@@ -95,7 +95,7 @@ export class TrustedOriginsStore {
const urlPermissions = this.data?.get(targetURL.origin);
return urlPermissions ? urlPermissions[permission] : undefined;
}
};
}
const trustedOriginsStore = new TrustedOriginsStore(trustedOriginsStoreFile);

View File

@@ -2,21 +2,20 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import path from 'path';
import fs from 'fs';
import {exec as execOriginal} from 'child_process';
import fs from 'fs';
import path from 'path';
import {promisify} from 'util';
const exec = promisify(execOriginal);
import {app, BrowserWindow} from 'electron';
import {Args} from 'types/args';
import type {BrowserWindow} from 'electron';
import {app} from 'electron';
import {BACK_BAR_HEIGHT, customLoginRegexPaths, PRODUCTION, TAB_BAR_HEIGHT} from 'common/utils/constants';
import Utils from 'common/utils/util';
import {isAdminUrl, isPluginUrl, isTeamUrl, isUrlType, parseURL} from 'common/utils/url';
import Utils from 'common/utils/util';
import type {Args} from 'types/args';
export function isInsideRectangle(container: Electron.Rectangle, rect: Electron.Rectangle) {
return container.x <= rect.x && container.y <= rect.y && container.width >= rect.width && container.height >= rect.height;

View File

@@ -8,11 +8,11 @@ import {LOAD_FAILED, TOGGLE_BACK_BUTTON, UPDATE_TARGET_URL} from 'common/communi
import {MattermostServer} from 'common/servers/MattermostServer';
import MessagingView from 'common/views/MessagingView';
import MainWindow from '../windows/mainWindow';
import {MattermostBrowserView} from './MattermostBrowserView';
import ContextMenu from '../contextMenu';
import Utils from '../utils';
import {MattermostBrowserView} from './MattermostBrowserView';
import MainWindow from '../windows/mainWindow';
jest.mock('electron', () => ({
app: {

View File

@@ -2,11 +2,9 @@
// See LICENSE.txt for license information.
import {BrowserView, app, ipcMain} from 'electron';
import {BrowserViewConstructorOptions, Event, Input} from 'electron/main';
import type {BrowserViewConstructorOptions, Event, Input} from 'electron/main';
import {EventEmitter} from 'events';
import {RELOAD_INTERVAL, MAX_SERVER_RETRIES, SECOND, MAX_LOADING_SCREEN_SECONDS} from 'common/utils/constants';
import AppState from 'common/appState';
import {
LOAD_RETRY,
@@ -21,18 +19,18 @@ import {
CLOSE_SERVERS_DROPDOWN,
CLOSE_DOWNLOADS_DROPDOWN,
} from 'common/communication';
import type {Logger} from 'common/log';
import ServerManager from 'common/servers/serverManager';
import {Logger} from 'common/log';
import {RELOAD_INTERVAL, MAX_SERVER_RETRIES, SECOND, MAX_LOADING_SCREEN_SECONDS} from 'common/utils/constants';
import {isInternalURL, parseURL} from 'common/utils/url';
import {MattermostView} from 'common/views/View';
import type {MattermostView} from 'common/views/View';
import MainWindow from 'main/windows/mainWindow';
import WebContentsEventManager from './webContentEvents';
import ContextMenu from '../contextMenu';
import {getWindowBoundaries, getLocalPreload, composeUserAgent, shouldHaveBackBar} from '../utils';
import WebContentsEventManager from './webContentEvents';
enum Status {
LOADING,
READY,
@@ -52,7 +50,7 @@ export class MattermostBrowserView extends EventEmitter {
private loggedIn: boolean;
private atRoot: boolean;
private options: BrowserViewConstructorOptions;
private removeLoading?: number;
private removeLoading?: NodeJS.Timeout;
private contextMenu: ContextMenu;
private status?: Status;
private retryLoad?: NodeJS.Timeout;
@@ -142,7 +140,7 @@ export class MattermostBrowserView extends EventEmitter {
) {
this.reload();
}
}
};
goToOffset = (offset: number) => {
if (this.browserView.webContents.canGoToOffset(offset)) {
@@ -154,7 +152,7 @@ export class MattermostBrowserView extends EventEmitter {
this.reload();
}
}
}
};
getBrowserHistoryStatus = () => {
if (this.currentURL?.toString() === this.view.url.toString()) {
@@ -168,12 +166,12 @@ export class MattermostBrowserView extends EventEmitter {
canGoBack: this.browserView.webContents.canGoBack(),
canGoForward: this.browserView.webContents.canGoForward(),
};
}
};
updateHistoryButton = () => {
const {canGoBack, canGoForward} = this.getBrowserHistoryStatus();
this.browserView.webContents.send(BROWSER_HISTORY_STATUS_UPDATED, canGoBack, canGoForward);
}
};
load = (someURL?: URL | string) => {
if (!this.browserView) {
@@ -208,7 +206,7 @@ export class MattermostBrowserView extends EventEmitter {
}
this.loadRetry(loadURL, err);
});
}
};
show = () => {
const mainWindow = MainWindow.get();
@@ -228,32 +226,32 @@ export class MattermostBrowserView extends EventEmitter {
if (this.status === Status.READY) {
this.focus();
}
}
};
hide = () => {
if (this.isVisible) {
this.isVisible = false;
MainWindow.get()?.removeBrowserView(this.browserView);
}
}
};
reload = () => {
this.resetLoadingStatus();
AppState.updateExpired(this.id, false);
this.load();
}
};
getBounds = () => {
return this.browserView.getBounds();
}
};
openFind = () => {
this.browserView.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: [process.platform === 'darwin' ? 'cmd' : 'ctrl', 'shift']});
}
};
setBounds = (boundaries: Electron.Rectangle) => {
this.browserView.setBounds(boundaries);
}
};
destroy = () => {
WebContentsEventManager.removeWebContentsListeners(this.webContentsId);
@@ -273,7 +271,7 @@ export class MattermostBrowserView extends EventEmitter {
if (this.removeLoading) {
clearTimeout(this.removeLoading);
}
}
};
/**
* Code to turn off the old method of getting unreads
@@ -282,7 +280,7 @@ export class MattermostBrowserView extends EventEmitter {
offLegacyUnreads = () => {
this.browserView.webContents.off('page-title-updated', this.handleTitleUpdate);
this.browserView.webContents.off('page-favicon-updated', this.handleFaviconUpdate);
}
};
/**
* Status hooks
@@ -294,19 +292,19 @@ export class MattermostBrowserView extends EventEmitter {
this.status = Status.LOADING;
this.maxRetries = MAX_SERVER_RETRIES;
}
}
};
isReady = () => {
return this.status === Status.READY;
}
};
isErrored = () => {
return this.status === Status.ERROR;
}
};
needsLoadingScreen = () => {
return !(this.status === Status.READY || this.status === Status.ERROR);
}
};
setInitialized = (timedout?: boolean) => {
this.status = Status.READY;
@@ -317,7 +315,7 @@ export class MattermostBrowserView extends EventEmitter {
}
clearTimeout(this.removeLoading);
delete this.removeLoading;
}
};
openDevTools = () => {
// Workaround for a bug with our Dev Tools on Mac
@@ -336,7 +334,7 @@ export class MattermostBrowserView extends EventEmitter {
}
this.browserView.webContents.openDevTools({mode: 'detach'});
}
};
/**
* WebContents hooks
@@ -344,11 +342,11 @@ export class MattermostBrowserView extends EventEmitter {
sendToRenderer = (channel: string, ...args: any[]) => {
this.browserView.webContents.send(channel, ...args);
}
};
isDestroyed = () => {
return this.browserView.webContents.isDestroyed();
}
};
focus = () => {
if (this.browserView.webContents) {
@@ -356,7 +354,7 @@ export class MattermostBrowserView extends EventEmitter {
} else {
this.log.warn('trying to focus the browserview, but it doesn\'t yet have webcontents.');
}
}
};
/**
* ALT key handling for the 3-dot menu (Windows/Linux)
@@ -386,7 +384,7 @@ export class MattermostBrowserView extends EventEmitter {
if (this.isAltKeyReleased(input)) {
MainWindow.focusThreeDotMenu();
}
}
};
/**
* Unreads/mentions handlers
@@ -398,7 +396,7 @@ export class MattermostBrowserView extends EventEmitter {
const mentions = (results && results.value && parseInt(results.value[MENTIONS_GROUP], 10)) || 0;
AppState.updateMentions(this.id, mentions);
}
};
// if favicon is null, it will affect appState, but won't be memoized
private findUnreadState = (favicon: string | null) => {
@@ -407,13 +405,13 @@ export class MattermostBrowserView extends EventEmitter {
} catch (err: any) {
this.log.error('There was an error trying to request the unread state', err);
}
}
};
private handleTitleUpdate = (e: Event, title: string) => {
this.log.debug('handleTitleUpdate', title);
this.updateMentionsFromTitle(title);
}
};
private handleFaviconUpdate = (e: Event, favicons: string[]) => {
this.log.silly('handleFaviconUpdate', favicons);
@@ -421,7 +419,7 @@ export class MattermostBrowserView extends EventEmitter {
// if unread state is stored for that favicon, retrieve value.
// if not, get related info from preload and store it for future changes
this.findUnreadState(favicons[0]);
}
};
/**
* Loading/retry logic
@@ -446,7 +444,7 @@ export class MattermostBrowserView extends EventEmitter {
}
});
};
}
};
private retryInBackground = (loadURL: string) => {
return () => {
@@ -459,13 +457,13 @@ export class MattermostBrowserView extends EventEmitter {
this.retryLoad = setTimeout(this.retryInBackground(loadURL), RELOAD_INTERVAL);
});
};
}
};
private loadRetry = (loadURL: string, err: Error) => {
this.retryLoad = setTimeout(this.retry(loadURL), RELOAD_INTERVAL);
MainWindow.sendToRenderer(LOAD_RETRY, this.id, Date.now() + RELOAD_INTERVAL, err.toString(), loadURL.toString());
this.log.info(`failed loading ${loadURL}: ${err}, retrying in ${RELOAD_INTERVAL / SECOND} seconds`);
}
};
private loadSuccess = (loadURL: string) => {
return () => {
@@ -484,7 +482,7 @@ export class MattermostBrowserView extends EventEmitter {
this.setBounds(getWindowBoundaries(mainWindow, shouldHaveBackBar(this.view.url || '', this.currentURL)));
}
};
}
};
/**
* WebContents event handlers
@@ -511,7 +509,7 @@ export class MattermostBrowserView extends EventEmitter {
MainWindow.sendToRenderer(TOGGLE_BACK_BUTTON, false);
this.log.debug('hide back button');
}
}
};
private handleUpdateTarget = (e: Event, url: string) => {
this.log.silly('handleUpdateTarget', e, url);
@@ -521,11 +519,11 @@ export class MattermostBrowserView extends EventEmitter {
} else {
this.emit(UPDATE_TARGET_URL, url);
}
}
};
private handleServerWasModified = (serverIds: string) => {
if (serverIds.includes(this.view.server.id)) {
this.reload();
}
}
};
}

View File

@@ -6,7 +6,6 @@
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import {DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT, DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH, TAB_BAR_HEIGHT} from 'common/utils/constants';
import MainWindow from 'main/windows/mainWindow';
import {DownloadsDropdownMenuView} from './downloadsDropdownMenuView';

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, ipcMain, IpcMainEvent} from 'electron';
import {CoordinatesToJsonType, DownloadedItem, DownloadsMenuOpenEventPayload} from 'types/downloads';
import type {IpcMainEvent} from 'electron';
import {BrowserView, ipcMain} from 'electron';
import {
CLOSE_DOWNLOADS_DROPDOWN_MENU,
@@ -19,18 +18,20 @@ import {
UPDATE_DOWNLOADS_DROPDOWN_MENU,
UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM,
} from 'common/communication';
import {Logger} from 'common/log';
import Config from 'common/config';
import {Logger} from 'common/log';
import {
DOWNLOADS_DROPDOWN_FULL_WIDTH,
DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT,
DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH,
TAB_BAR_HEIGHT,
} from 'common/utils/constants';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import downloadsManager from 'main/downloadsManager';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import type {CoordinatesToJsonType, DownloadedItem, DownloadsMenuOpenEventPayload} from 'types/downloads';
const log = new Logger('DownloadsDropdownMenuView');
export class DownloadsDropdownMenuView {
@@ -76,7 +77,7 @@ export class DownloadsDropdownMenuView {
}});
this.view.webContents.loadURL(getLocalURLString('downloadsDropdownMenu.html'));
MainWindow.get()?.addBrowserView(this.view);
}
};
/**
* This is called every time the "window" is resized so that we can position
@@ -88,14 +89,14 @@ export class DownloadsDropdownMenuView {
this.windowBounds = newBounds;
this.updateDownloadsDropdownMenu();
this.repositionDownloadsDropdownMenu();
}
};
private updateItem = (event: IpcMainEvent, item: DownloadedItem) => {
log.debug('updateItem', {item});
this.item = item;
this.updateDownloadsDropdownMenu();
}
};
private updateDownloadsDropdownMenu = () => {
log.silly('updateDownloadsDropdownMenu');
@@ -107,7 +108,7 @@ export class DownloadsDropdownMenuView {
);
ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, true, this.item);
this.repositionDownloadsDropdownMenu();
}
};
private handleOpen = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload = {} as DownloadsMenuOpenEventPayload) => {
log.debug('handleOpen', {bounds: this.bounds, payload});
@@ -128,7 +129,7 @@ export class DownloadsDropdownMenuView {
MainWindow.get()?.setTopBrowserView(this.view);
this.view.webContents.focus();
this.updateDownloadsDropdownMenu();
}
};
private handleClose = () => {
log.silly('handleClose');
@@ -138,7 +139,7 @@ export class DownloadsDropdownMenuView {
ipcMain.emit(UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM);
this.view?.setBounds(this.getBounds(this.windowBounds?.width ?? 0, 0, 0));
MainWindow.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN_MENU);
}
};
private handleToggle = (event: IpcMainEvent, payload: DownloadsMenuOpenEventPayload) => {
if (this.open) {
@@ -153,27 +154,27 @@ export class DownloadsDropdownMenuView {
} else {
this.handleOpen(event, payload);
}
}
};
private openFile = () => {
downloadsManager.openFile(this.item);
this.handleClose();
}
};
private showFileInFolder = (e: IpcMainEvent, item: DownloadedItem) => {
downloadsManager.showFileInFolder(item);
this.handleClose();
}
};
private clearFile = () => {
downloadsManager.clearFile(this.item);
this.handleClose();
}
};
private cancelDownload = () => {
downloadsManager.cancelDownload(this.item);
this.handleClose();
}
};
private getBounds = (windowWidth: number, width: number, height: number) => {
// MUST return integers
@@ -183,7 +184,7 @@ export class DownloadsDropdownMenuView {
width: Math.round(width),
height: Math.round(height),
};
}
};
private getX = (windowWidth: number) => {
const result = (windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH - DOWNLOADS_DROPDOWN_MENU_FULL_WIDTH) + (this.coordinates?.x || 0) + (this.coordinates?.width || 0);
@@ -191,12 +192,12 @@ export class DownloadsDropdownMenuView {
return 0;
}
return Math.round(result);
}
};
private getY = () => {
const result = TAB_BAR_HEIGHT + (this.coordinates?.y || 0) + (this.coordinates?.height || 0);
return Math.round(result);
}
};
private repositionDownloadsDropdownMenu = () => {
if (!this.windowBounds) {
@@ -207,7 +208,7 @@ export class DownloadsDropdownMenuView {
if (this.open) {
this.view?.setBounds(this.bounds);
}
}
};
}
const downloadsDropdownMenuView = new DownloadsDropdownMenuView();

View File

@@ -6,7 +6,6 @@
import {getDoNotDisturb as getDarwinDoNotDisturb} from 'macos-notification-state';
import {DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT, TAB_BAR_HEIGHT} from 'common/utils/constants';
import MainWindow from 'main/windows/mainWindow';
import {DownloadsDropdownView} from './downloadsDropdownView';

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowserView, ipcMain, IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {DownloadedItem} from 'types/downloads';
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
import {BrowserView, ipcMain} from 'electron';
import {
CLOSE_DOWNLOADS_DROPDOWN,
@@ -19,14 +18,15 @@ import {
MAIN_WINDOW_CREATED,
MAIN_WINDOW_RESIZED,
} from 'common/communication';
import {Logger} from 'common/log';
import Config from 'common/config';
import {Logger} from 'common/log';
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 downloadsManager from 'main/downloadsManager';
import {getLocalPreload, getLocalURLString} from 'main/utils';
import MainWindow from 'main/windows/mainWindow';
import type {DownloadedItem} from 'types/downloads';
const log = new Logger('DownloadsDropdownView');
export class DownloadsDropdownView {
@@ -70,7 +70,7 @@ export class DownloadsDropdownView {
this.view.webContents.loadURL(getLocalURLString('downloadsDropdown.html'));
this.view.webContents.session.webRequest.onHeadersReceived(downloadsManager.webRequestOnHeadersReceivedHandler);
MainWindow.get()?.addBrowserView(this.view);
}
};
/**
* This is called every time the "window" is resized so that we can position
@@ -82,13 +82,13 @@ export class DownloadsDropdownView {
this.windowBounds = newBounds;
this.updateDownloadsDropdown();
this.repositionDownloadsDropdown();
}
};
private updateDownloadsDropdownMenuItem = (event: IpcMainEvent, item?: DownloadedItem) => {
log.silly('updateDownloadsDropdownMenuItem', {item});
this.item = item;
this.updateDownloadsDropdown();
}
};
private updateDownloadsDropdown = () => {
log.silly('updateDownloadsDropdown');
@@ -100,7 +100,7 @@ export class DownloadsDropdownView {
MainWindow.getBounds(),
this.item,
);
}
};
private handleOpen = () => {
log.debug('handleOpen', {bounds: this.bounds});
@@ -114,7 +114,7 @@ export class DownloadsDropdownView {
this.view.webContents.focus();
downloadsManager.onOpen();
MainWindow.sendToRenderer(OPEN_DOWNLOADS_DROPDOWN);
}
};
private handleClose = () => {
log.silly('handleClose');
@@ -122,18 +122,18 @@ export class DownloadsDropdownView {
this.view?.setBounds(this.getBounds(this.windowBounds?.width ?? 0, 0, 0));
downloadsManager.onClose();
MainWindow.sendToRenderer(CLOSE_DOWNLOADS_DROPDOWN);
}
};
private clearDownloads = () => {
downloadsManager.clearDownloadsDropDown();
this.handleClose();
}
};
private openFile = (e: IpcMainEvent, item: DownloadedItem) => {
log.debug('openFile', {item});
downloadsManager.openFile(item);
}
};
private getBounds = (windowWidth: number, width: number, height: number) => {
// Must always use integers
@@ -143,7 +143,7 @@ export class DownloadsDropdownView {
width: Math.round(width),
height: Math.round(height),
};
}
};
private getX = (windowWidth: number) => {
const result = windowWidth - DOWNLOADS_DROPDOWN_FULL_WIDTH;
@@ -151,11 +151,11 @@ export class DownloadsDropdownView {
return 0;
}
return Math.round(result);
}
};
private getY = () => {
return Math.round(TAB_BAR_HEIGHT);
}
};
private repositionDownloadsDropdown = () => {
if (!(this.bounds && this.windowBounds)) {
@@ -169,7 +169,7 @@ export class DownloadsDropdownView {
if (downloadsManager.getIsOpen()) {
this.view?.setBounds(this.bounds);
}
}
};
private handleReceivedDownloadsDropdownSize = (event: IpcMainEvent, width: number, height: number) => {
log.silly('handleReceivedDownloadsDropdownSize', {width, height});
@@ -182,11 +182,11 @@ export class DownloadsDropdownView {
if (downloadsManager.getIsOpen()) {
this.view?.setBounds(this.bounds);
}
}
};
private getDownloadImageThumbnailLocation = (event: IpcMainInvokeEvent, location: string) => {
return location;
}
};
}
const downloadsDropdownView = new DownloadsDropdownView();

Some files were not shown because too many files have changed in this diff Show More