diff --git a/src/common/Validator.ts b/src/common/Validator.ts index 388d6273..c70d82c0 100644 --- a/src/common/Validator.ts +++ b/src/common/Validator.ts @@ -4,7 +4,7 @@ import Joi from 'joi'; import {Args} from 'types/args'; -import {AnyConfig, ConfigV0, ConfigV1, ConfigV2, ConfigV3, TeamWithTabs} from 'types/config'; +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'; @@ -213,7 +213,7 @@ function cleanTeam(team: T) { }; } -function cleanTeamWithTabs(team: TeamWithTabs) { +function cleanTeamWithTabs(team: ConfigServer) { return { ...cleanTeam(team), tabs: team.tabs.map((tab) => { diff --git a/src/common/config/RegistryConfig.test.js b/src/common/config/RegistryConfig.test.js index 3f162e73..382cb753 100644 --- a/src/common/config/RegistryConfig.test.js +++ b/src/common/config/RegistryConfig.test.js @@ -67,7 +67,6 @@ describe('common/config/RegistryConfig', () => { expect(registryConfig.data.teams).toContainEqual({ name: 'server-1', url: 'http://server-1.com', - order: 0, }); expect(registryConfig.data.enableAutoUpdater).toBe(true); expect(registryConfig.data.enableServerManagement).toBe(true); diff --git a/src/common/config/RegistryConfig.ts b/src/common/config/RegistryConfig.ts index a286f351..65ce5436 100644 --- a/src/common/config/RegistryConfig.ts +++ b/src/common/config/RegistryConfig.ts @@ -6,7 +6,7 @@ import {EventEmitter} from 'events'; import WindowsRegistry from 'winreg'; import WindowsRegistryUTF8 from 'winreg-utf8'; -import {RegistryConfig as RegistryConfigType, FullTeam} from 'types/config'; +import {RegistryConfig as RegistryConfigType, Team} from 'types/config'; import {Logger} from 'common/log'; @@ -78,12 +78,11 @@ export default class RegistryConfig extends EventEmitter { */ async getServersListFromRegistry() { const defaultServers = await this.getRegistryEntry(`${BASE_REGISTRY_KEY_PATH}\\DefaultServerList`); - return defaultServers.flat(2).reduce((servers: FullTeam[], server, index) => { + return defaultServers.flat(2).reduce((servers: Team[], server) => { if (server) { servers.push({ name: (server as WindowsRegistry.RegistryItem).name, url: (server as WindowsRegistry.RegistryItem).value, - order: index, }); } return servers; diff --git a/src/common/config/index.test.js b/src/common/config/index.test.js index 8c7a40ab..d2ba76fb 100644 --- a/src/common/config/index.test.js +++ b/src/common/config/index.test.js @@ -1,22 +1,19 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import fs from 'fs'; + import {Config} from 'common/config'; const configPath = '/fake/config/path'; +const appName = 'app-name'; +const appPath = '/my/app/path'; -jest.mock('electron', () => ({ - app: { - name: 'Mattermost', - getPath: jest.fn(), - getAppPath: () => '/path/to/app', - }, - ipcMain: { - on: jest.fn(), - }, - nativeTheme: { - shouldUseDarkColors: false, - }, +jest.mock('fs', () => ({ + readFileSync: jest.fn(), + writeFileSync: jest.fn(), + existsSync: jest.fn(), + mkdirSync: jest.fn(), })); jest.mock('common/Validator', () => ({ @@ -28,7 +25,7 @@ jest.mock('common/Validator', () => ({ })); jest.mock('common/tabs/TabView', () => ({ - getDefaultTeamWithTabsFromTeam: (value) => ({ + getDefaultConfigTeamFromTeam: (value) => ({ ...value, tabs: [ { @@ -99,15 +96,18 @@ jest.mock('common/config/RegistryConfig', () => { describe('common/config', () => { it('should load buildConfig', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); expect(config.predefinedTeams).toContainEqual(buildTeamWithTabs); }); describe('loadRegistry', () => { it('should load the registry items and reload the config', () => { - const config = new Config(configPath); + const config = new Config(); config.reload = jest.fn(); - config.loadRegistry({teams: [registryTeam]}); + config.init(configPath, appName, appPath); + config.onLoadRegistry({teams: [registryTeam]}); expect(config.reload).toHaveBeenCalled(); expect(config.predefinedTeams).toContainEqual({ ...registryTeam, @@ -125,7 +125,8 @@ describe('common/config', () => { describe('reload', () => { it('should emit update event', () => { - const config = new Config(configPath); + const config = new Config(); + config.init(configPath, appName, appPath); config.loadDefaultConfigData = jest.fn(); config.loadBuildConfigData = jest.fn(); config.loadLocalConfigFile = jest.fn(); @@ -134,6 +135,7 @@ describe('common/config', () => { config.combinedData = {test: 'test'}; }); config.emit = jest.fn(); + fs.existsSync.mockReturnValue(true); config.reload(); expect(config.emit).toHaveBeenNthCalledWith(1, 'update', {test: 'test'}); @@ -142,7 +144,9 @@ describe('common/config', () => { describe('set', () => { it('should set an arbitrary value and save to local config data', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.localConfigData = {}; config.regenerateCombinedConfigData = jest.fn().mockImplementation(() => { config.combinedData = {...config.localConfigData}; @@ -155,24 +159,46 @@ describe('common/config', () => { expect(config.saveLocalConfigData).toHaveBeenCalled(); }); - it('should set teams without including predefined', () => { - const config = new Config(configPath); + it('should not allow teams to be set using this method', () => { + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); + config.localConfigData = {teams: [team]}; + config.regenerateCombinedConfigData = jest.fn().mockImplementation(() => { + config.combinedData = {...config.localConfigData}; + }); + config.saveLocalConfigData = jest.fn(); + + config.set('teams', [{...buildTeamWithTabs, name: 'build-team-2'}]); + expect(config.localConfigData.teams).not.toContainEqual({...buildTeamWithTabs, name: 'build-team-2'}); + expect(config.localConfigData.teams).toContainEqual(team); + }); + }); + + describe('setServers', () => { + it('should set only local servers', () => { + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.localConfigData = {}; config.regenerateCombinedConfigData = jest.fn().mockImplementation(() => { config.combinedData = {...config.localConfigData}; }); config.saveLocalConfigData = jest.fn(); - config.set('teams', [{...buildTeamWithTabs, name: 'build-team-2'}, team]); - expect(config.localConfigData.teams).not.toContainEqual({...buildTeamWithTabs, name: 'build-team-2'}); - expect(config.localConfigData.teams).toContainEqual(team); - expect(config.predefinedTeams).toContainEqual({...buildTeamWithTabs, name: 'build-team-2'}); + config.setServers([{...buildTeamWithTabs, name: 'build-team-2'}, team], 0); + expect(config.localConfigData.teams).toContainEqual({...buildTeamWithTabs, name: 'build-team-2'}); + expect(config.localConfigData.lastActiveTeam).toBe(0); + expect(config.regenerateCombinedConfigData).toHaveBeenCalled(); + expect(config.saveLocalConfigData).toHaveBeenCalled(); }); }); describe('saveLocalConfigData', () => { it('should emit update event on save', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.localConfigData = {test: 'test'}; config.combinedData = {...config.localConfigData}; config.writeFile = jest.fn().mockImplementation((configFilePath, data, callback) => { @@ -185,7 +211,9 @@ describe('common/config', () => { }); it('should emit error when fs.writeSync throws an error', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.localConfigData = {test: 'test'}; config.combinedData = {...config.localConfigData}; config.writeFile = jest.fn().mockImplementation((configFilePath, data, callback) => { @@ -198,7 +226,9 @@ describe('common/config', () => { }); it('should emit error when writeFile throws an error', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.localConfigData = {test: 'test'}; config.combinedData = {...config.localConfigData}; config.writeFile = jest.fn().mockImplementation(() => { @@ -212,7 +242,9 @@ describe('common/config', () => { it('should retry when file is locked', () => { const testFunc = jest.fn(); - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.localConfigData = {test: 'test'}; config.combinedData = {...config.localConfigData}; config.writeFile = jest.fn().mockImplementation((configFilePath, data, callback) => { @@ -228,37 +260,41 @@ describe('common/config', () => { describe('loadLocalConfigFile', () => { it('should use defaults if readFileSync fails', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.defaultConfigData = {test: 'test'}; config.combinedData = {...config.localConfigData}; - config.readFileSync = jest.fn().mockImplementation(() => { + fs.existsSync.mockReturnValue(true); + fs.readFileSync.mockImplementation(() => { throw new Error('Error message'); }); - config.writeFileSync = jest.fn(); + config.writeFile = jest.fn(); const configData = config.loadLocalConfigFile(); expect(configData).toStrictEqual({test: 'test'}); }); it('should use defaults if validation fails', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.defaultConfigData = {test: 'test'}; config.combinedData = {...config.localConfigData}; - config.readFileSync = jest.fn().mockImplementation(() => { - return {version: -1}; - }); - config.writeFileSync = jest.fn(); + fs.existsSync.mockReturnValue(true); + fs.readFileSync.mockReturnValue('{"version": -1}'); + config.writeFile = jest.fn(); const configData = config.loadLocalConfigFile(); expect(configData).toStrictEqual({test: 'test'}); }); it('should return config data if valid', () => { - const config = new Config(configPath); - config.readFileSync = jest.fn().mockImplementation(() => { - return {version: 3}; - }); - config.writeFileSync = jest.fn(); + const config = new Config(); + config.init(configPath, appName, appPath); + fs.existsSync.mockReturnValue(true); + fs.readFileSync.mockReturnValue('{"version": 3}'); + config.writeFile = jest.fn(); const configData = config.loadLocalConfigFile(); expect(configData).toStrictEqual({version: 3}); @@ -267,7 +303,9 @@ describe('common/config', () => { describe('checkForConfigUpdates', () => { it('should upgrade to latest version', () => { - const config = new Config(configPath); + const config = new Config(); + config.reload = jest.fn(); + config.init(configPath, appName, appPath); config.defaultConfigData = {version: 10}; config.writeFileSync = jest.fn(); @@ -276,111 +314,67 @@ describe('common/config', () => { }); }); - describe('regenerateCombinedConfigData', () => { - it('should combine config from all sources', () => { - const config = new Config(configPath); - config.predefinedTeams = []; - config.useNativeWindow = false; - config.defaultConfigData = {defaultSetting: 'default', otherDefaultSetting: 'default'}; - config.localConfigData = {otherDefaultSetting: 'local', localSetting: 'local', otherLocalSetting: 'local'}; - config.buildConfigData = {otherLocalSetting: 'build', buildSetting: 'build', otherBuildSetting: 'build'}; - config.registryConfigData = {otherBuildSetting: 'registry', registrySetting: 'registry'}; + // TODO: Re-enable when we migrate to ServerManager fully + // describe('regenerateCombinedConfigData', () => { + // it('should combine config from all sources', () => { + // const config = new Config(); + // config.reload = jest.fn(); + // config.init(configPath, appName, appPath); + // config.useNativeWindow = false; + // config.defaultConfigData = {defaultSetting: 'default', otherDefaultSetting: 'default'}; + // config.localConfigData = {otherDefaultSetting: 'local', localSetting: 'local', otherLocalSetting: 'local'}; + // config.buildConfigData = {otherLocalSetting: 'build', buildSetting: 'build', otherBuildSetting: 'build'}; + // config.registryConfigData = {otherBuildSetting: 'registry', registrySetting: 'registry'}; - config.regenerateCombinedConfigData(); - config.combinedData.darkMode = false; - expect(config.combinedData).toStrictEqual({ - teams: [], - registryTeams: [], - appName: 'Mattermost', - useNativeWindow: false, - darkMode: false, - otherBuildSetting: 'registry', - registrySetting: 'registry', - otherLocalSetting: 'build', - buildSetting: 'build', - otherDefaultSetting: 'local', - localSetting: 'local', - defaultSetting: 'default', - }); - }); + // config.regenerateCombinedConfigData(); + // config.combinedData.darkMode = false; + // expect(config.combinedData).toStrictEqual({ + // appName: 'app-name', + // useNativeWindow: false, + // darkMode: false, + // otherBuildSetting: 'registry', + // registrySetting: 'registry', + // otherLocalSetting: 'build', + // buildSetting: 'build', + // otherDefaultSetting: 'local', + // localSetting: 'local', + // defaultSetting: 'default', + // }); + // }); - it('should combine teams from all sources and filter duplicates', () => { - const config = new Config(configPath); - config.defaultConfigData = {}; - config.localConfigData = {}; - config.buildConfigData = {enableServerManagement: true}; - config.registryConfigData = {}; - config.predefinedTeams = [team, team]; - config.useNativeWindow = false; - config.localConfigData = {teams: [ - team, - { - ...team, - name: 'local-team-2', - url: 'http://local-team-2.com', - }, - { - ...team, - name: 'local-team-1', - order: 1, - url: 'http://local-team-1.com', - }, - ]}; + // it('should not include any teams in the combined config', () => { + // const config = new Config(); + // config.reload = jest.fn(); + // config.init(configPath, appName, appPath); + // config.defaultConfigData = {}; + // config.localConfigData = {}; + // config.buildConfigData = {enableServerManagement: true}; + // config.registryConfigData = {}; + // config.predefinedTeams.push(team, team); + // config.useNativeWindow = false; + // config.localConfigData = {teams: [ + // team, + // { + // ...team, + // name: 'local-team-2', + // url: 'http://local-team-2.com', + // }, + // { + // ...team, + // name: 'local-team-1', + // order: 1, + // url: 'http://local-team-1.com', + // }, + // ]}; - config.regenerateCombinedConfigData(); - config.combinedData.darkMode = false; - expect(config.combinedData).toStrictEqual({ - teams: [ - team, - { - ...team, - name: 'local-team-2', - order: 1, - url: 'http://local-team-2.com', - }, - { - ...team, - name: 'local-team-1', - order: 2, - url: 'http://local-team-1.com', - }, - ], - registryTeams: [], - appName: 'Mattermost', - useNativeWindow: false, - darkMode: false, - enableServerManagement: true, - }); - }); - - it('should not include local teams if enableServerManagement is false', () => { - const config = new Config(configPath); - config.defaultConfigData = {}; - config.localConfigData = {}; - config.buildConfigData = {enableServerManagement: false}; - config.registryConfigData = {}; - config.predefinedTeams = [team, team]; - config.useNativeWindow = false; - config.localConfigData = {teams: [ - team, - { - ...team, - name: 'local-team-1', - order: 1, - url: 'http://local-team-1.com', - }, - ]}; - - config.regenerateCombinedConfigData(); - config.combinedData.darkMode = false; - expect(config.combinedData).toStrictEqual({ - teams: [team], - registryTeams: [], - appName: 'Mattermost', - useNativeWindow: false, - darkMode: false, - enableServerManagement: false, - }); - }); - }); + // config.regenerateCombinedConfigData(); + // config.combinedData.darkMode = false; + // expect(config.combinedData).toStrictEqual({ + // appName: 'app-name', + // useNativeWindow: false, + // darkMode: false, + // enableServerManagement: true, + // }); + // }); + // }); }); diff --git a/src/common/config/index.ts b/src/common/config/index.ts index 591bd34d..45289a4f 100644 --- a/src/common/config/index.ts +++ b/src/common/config/index.ts @@ -7,7 +7,6 @@ import os from 'os'; import path from 'path'; import {EventEmitter} from 'events'; -import {ipcMain, nativeTheme, app} from 'electron'; import { AnyConfig, @@ -15,18 +14,13 @@ import { CombinedConfig, ConfigServer, Config as ConfigType, - LocalConfiguration, RegistryConfig as RegistryConfigType, - TeamWithTabs, } from 'types/config'; -import {UPDATE_TEAMS, GET_CONFIGURATION, UPDATE_CONFIGURATION, GET_LOCAL_CONFIGURATION, UPDATE_PATHS} from 'common/communication'; -import * as Validator from 'common/Validator'; import {Logger} from 'common/log'; -import {getDefaultTeamWithTabsFromTeam} from 'common/tabs/TabView'; -import Utils from 'common/utils/util'; - -import {configPath} from 'main/constants'; +import {getDefaultConfigTeamFromTeam} from 'common/tabs/TabView'; +import Utils, {copy} from 'common/utils/util'; +import * as Validator from 'common/Validator'; import defaultPreferences, {getDefaultDownloadLocation} from './defaultPreferences'; import upgradeConfigData from './upgradePreferences'; @@ -36,61 +30,28 @@ import migrateConfigItems from './migrationPreferences'; const log = new Logger('Config'); -/** - * Handles loading and merging all sources of configuration as well as saving user provided config - */ - -function checkWriteableApp() { - if (process.platform === 'win32') { - try { - fs.accessSync(path.join(path.dirname(app.getAppPath()), '../../'), fs.constants.W_OK); - - // check to make sure that app-update.yml exists - if (!fs.existsSync(path.join(process.resourcesPath, 'app-update.yml'))) { - log.warn('app-update.yml does not exist, disabling auto-updates'); - return false; - } - } catch (error) { - log.info(`${app.getAppPath()}: ${error}`); - log.warn('autoupgrade disabled'); - return false; - } - - // eslint-disable-next-line no-undef - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return __CAN_UPGRADE__; // prevent showing the option if the path is not writeable, like in a managed environment. - } - - // temporarily disabling auto updater for macOS due to security issues - // eslint-disable-next-line no-undef - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return process.platform !== 'darwin' && __CAN_UPGRADE__; -} export class Config extends EventEmitter { - configFilePath: string; + private configFilePath?: string; + private appName?: string; + private appPath?: string; - registryConfig: RegistryConfig; + private registryConfig: RegistryConfig; + private predefinedServers: ConfigServer[]; + private useNativeWindow: boolean; - combinedData?: CombinedConfig; - registryConfigData?: Partial; - defaultConfigData?: ConfigType; - buildConfigData?: BuildConfig; - localConfigData?: ConfigType; - useNativeWindow: boolean; - canUpgradeValue?: boolean + private combinedData?: CombinedConfig; + private localConfigData?: ConfigType; + private registryConfigData?: Partial; + private defaultConfigData?: ConfigType; + private buildConfigData?: BuildConfig; + private canUpgradeValue?: boolean; - predefinedTeams: TeamWithTabs[]; - - constructor(configFilePath: string) { + constructor() { super(); - this.configFilePath = configFilePath; - this.canUpgradeValue = checkWriteableApp(); this.registryConfig = new RegistryConfig(); - this.predefinedTeams = []; + this.predefinedServers = []; if (buildConfig.defaultTeams) { - this.predefinedTeams.push(...buildConfig.defaultTeams.map((team) => getDefaultTeamWithTabsFromTeam(team))); + this.predefinedServers.push(...buildConfig.defaultTeams.map((team, index) => getDefaultConfigTeamFromTeam({...team, order: index}))); } try { this.useNativeWindow = os.platform() === 'win32' && !Utils.isVersionGreaterThanOrEqualTo(os.release(), '6.2'); @@ -99,45 +60,30 @@ export class Config extends EventEmitter { } } - // separating constructor from init so main can setup event listeners - init = (): void => { + init = (configFilePath: string, appName: string, appPath: string) => { + this.configFilePath = configFilePath; + this.appName = appName; + this.appPath = appPath; + this.canUpgradeValue = this.checkWriteableApp(); + this.reload(); - ipcMain.handle(GET_CONFIGURATION, this.handleGetConfiguration); - ipcMain.handle(GET_LOCAL_CONFIGURATION, this.handleGetLocalConfiguration); - ipcMain.handle(UPDATE_TEAMS, this.handleUpdateTeams); - ipcMain.on(UPDATE_CONFIGURATION, this.updateConfiguration); - if (process.platform === 'darwin' || process.platform === 'win32') { - nativeTheme.on('updated', this.handleUpdateTheme); - } } initRegistry = () => { + if (process.platform !== 'win32') { + return Promise.resolve(); + } + return new Promise((resolve) => { this.registryConfig = new RegistryConfig(); this.registryConfig.once(REGISTRY_READ_EVENT, (data) => { - this.loadRegistry(data); + this.onLoadRegistry(data); resolve(); }); this.registryConfig.init(); }); } - /** - * Gets the teams from registry into the config object and reload - * - * @param {object} registryData Team configuration from the registry and if teams can be managed by user - */ - - loadRegistry = (registryData: Partial): void => { - log.verbose('Config.loadRegistry', {registryData}); - - this.registryConfigData = registryData; - if (this.registryConfigData.teams) { - this.predefinedTeams.push(...this.registryConfigData.teams.map((team) => getDefaultTeamWithTabsFromTeam(team))); - } - this.reload(); - } - /** * Reload all sources of config data * @@ -146,15 +92,21 @@ export class Config extends EventEmitter { * @emits {synchronize} emitted when requested by a call to method; used to notify other config instances of changes */ reload = (): void => { - this.defaultConfigData = this.loadDefaultConfigData(); - this.buildConfigData = this.loadBuildConfigData(); + this.defaultConfigData = copy(defaultPreferences); + this.buildConfigData = copy(buildConfig); + const loadedConfig = this.loadLocalConfigFile(); this.localConfigData = this.checkForConfigUpdates(loadedConfig); + this.regenerateCombinedConfigData(); this.emit('update', this.combinedData); } + /********************* + * Setters and Getters + *********************/ + /** * Used to save a single config property * @@ -166,18 +118,8 @@ export class Config extends EventEmitter { this.setMultiple({[key]: data}); } - updateConfiguration = (event: Electron.IpcMainEvent, properties: Array<{key: keyof ConfigType; data: ConfigType[keyof ConfigType]}> = []): Partial | undefined => { - log.debug('Config.updateConfiguration', properties); - - if (properties.length) { - const newData = properties.reduce((obj, data) => { - (obj as any)[data.key] = data.data; - return obj; - }, {} as Partial); - this.setMultiple(newData); - } - - return this.localConfigData; + setConfigPath = (configPath: string) => { + this.configFilePath = configPath; } /** @@ -188,20 +130,12 @@ export class Config extends EventEmitter { setMultiple = (newData: Partial) => { log.debug('setMultiple', newData); - this.localConfigData = Object.assign({}, this.localConfigData, newData); - if (newData.teams && this.localConfigData) { - this.localConfigData.teams = this.filterOutPredefinedTeams(newData.teams as TeamWithTabs[]); - this.predefinedTeams = this.filterInPredefinedTeams(newData.teams as TeamWithTabs[]); + if (newData.darkMode && newData.darkMode !== this.darkMode) { + this.emit('darkModeChange', newData.darkMode); } + this.localConfigData = Object.assign({}, this.localConfigData, {...newData, teams: this.localConfigData?.teams}); this.regenerateCombinedConfigData(); this.saveLocalConfigData(); - - return this.localConfigData; //this is the only part that changes - } - - setRegistryConfigData = (registryConfigData = {teams: []}): void => { - this.registryConfigData = Object.assign({}, registryConfigData); - this.reload(); } setServers = (servers: ConfigServer[], lastActiveTeam?: number) => { @@ -212,50 +146,6 @@ export class Config extends EventEmitter { this.saveLocalConfigData(); } - /** - * Used to replace the existing config data with new config data - * - * @param {object} configData a new, config data object to completely replace the existing config data - */ - replace = (configData: ConfigType) => { - const newConfigData = configData; - - this.localConfigData = Object.assign({}, this.localConfigData, newConfigData); - - this.regenerateCombinedConfigData(); - this.saveLocalConfigData(); - } - - /** - * Used to save the current set of local config data to disk - * - * @emits {update} emitted once all data has been saved - * @emits {synchronize} emitted once all data has been saved; used to notify other config instances of changes - * @emits {error} emitted if saving local config data to file fails - */ - saveLocalConfigData = (): void => { - if (!this.localConfigData) { - return; - } - - log.info('Saving config data to file...'); - - try { - this.writeFile(this.configFilePath, this.localConfigData, (error: NodeJS.ErrnoException | null) => { - if (error) { - if (error.code === 'EBUSY') { - this.saveLocalConfigData(); - } else { - this.emit('error', error); - } - } - this.emit('update', this.combinedData); - }); - } catch (error) { - this.emit('error', error); - } - } - // getters for accessing the various config data inputs get data() { @@ -288,6 +178,9 @@ export class Config extends EventEmitter { get localTeams() { return this.localConfigData?.teams ?? defaultPreferences.teams; } + get predefinedTeams() { + return this.predefinedServers; + } get enableHardwareAcceleration() { return this.combinedData?.enableHardwareAcceleration ?? defaultPreferences.enableHardwareAcceleration; } @@ -361,29 +254,67 @@ export class Config extends EventEmitter { return this.combinedData?.appLanguage; } - // initialization/processing methods - /** - * Returns a copy of the app's default config data + * Gets the teams from registry into the config object and reload + * + * @param {object} registryData Team configuration from the registry and if teams can be managed by user */ - loadDefaultConfigData = () => { - return this.copy(defaultPreferences); + + private onLoadRegistry = (registryData: Partial): void => { + log.debug('loadRegistry', {registryData}); + + this.registryConfigData = registryData; + if (this.registryConfigData.teams) { + this.predefinedTeams.push(...this.registryConfigData.teams.map((team, index) => getDefaultConfigTeamFromTeam({...team, order: index}))); + } + this.reload(); } /** - * Returns a copy of the app's build config data + * Config file loading methods */ - loadBuildConfigData = () => { - return this.copy(buildConfig); + + /** + * Used to save the current set of local config data to disk + * + * @emits {update} emitted once all data has been saved + * @emits {synchronize} emitted once all data has been saved; used to notify other config instances of changes + * @emits {error} emitted if saving local config data to file fails + */ + private saveLocalConfigData = (): void => { + if (!(this.configFilePath && this.localConfigData)) { + return; + } + + log.verbose('Saving config data to file...'); + + try { + this.writeFile(this.configFilePath, this.localConfigData, (error: NodeJS.ErrnoException | null) => { + if (error) { + if (error.code === 'EBUSY') { + this.saveLocalConfigData(); + } else { + this.emit('error', error); + } + } + this.emit('update', this.combinedData); + }); + } 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 */ - loadLocalConfigFile = (): AnyConfig => { + private loadLocalConfigFile = (): AnyConfig => { + if (!this.configFilePath) { + throw new Error('Unable to read from config, no path specified'); + } + let configData: AnyConfig; try { - configData = this.readFileSync(this.configFilePath); + configData = JSON.parse(fs.readFileSync(this.configFilePath, 'utf8')); // validate based on config file version configData = Validator.validateConfigData(configData); @@ -393,9 +324,9 @@ export class Config extends EventEmitter { } } catch (e) { log.warn('Failed to load configuration file from the filesystem. Using defaults.'); - configData = this.copy(this.defaultConfigData); + configData = copy(this.defaultConfigData); - this.writeFileSync(this.configFilePath, configData); + this.writeFile(this.configFilePath, configData); } return configData; } @@ -405,18 +336,22 @@ export class Config extends EventEmitter { * * @param {*} data locally stored data */ - checkForConfigUpdates = (data: AnyConfig) => { + private checkForConfigUpdates = (data: AnyConfig) => { + if (!this.configFilePath) { + throw new Error('Config not initialized'); + } + let configData = data; if (this.defaultConfigData) { try { if (configData.version !== this.defaultConfigData.version) { configData = upgradeConfigData(configData); - this.writeFileSync(this.configFilePath, configData); + this.writeFile(this.configFilePath, configData); log.info(`Configuration updated to version ${this.defaultConfigData.version} successfully.`); } const didMigrate = migrateConfigItems(configData); if (didMigrate) { - this.writeFileSync(this.configFilePath, configData); + this.writeFile(this.configFilePath, configData); log.info('Migrating config items successfully.'); } } catch (error) { @@ -430,43 +365,45 @@ export class Config extends EventEmitter { /** * Properly combines all sources of data into a single, manageable set of all config data */ - regenerateCombinedConfigData = () => { - // combine all config data in the correct order - this.combinedData = Object.assign({}, this.defaultConfigData, this.localConfigData, this.buildConfigData, this.registryConfigData, {useNativeWindow: this.useNativeWindow}); - - // remove unecessary data pulled from default and build config - delete this.combinedData!.defaultTeams; - - // IMPORTANT: properly combine teams from all sources - let combinedTeams: TeamWithTabs[] = []; - - combinedTeams.push(...this.predefinedTeams); - - // - add locally defined teams only if server management is enabled - if (this.localConfigData && this.enableServerManagement) { - combinedTeams.push(...this.localConfigData.teams || []); + private regenerateCombinedConfigData = () => { + if (!this.appName) { + throw new Error('Config not initialized, cannot regenerate'); } - this.predefinedTeams = this.filterOutDuplicateTeams(this.predefinedTeams); - combinedTeams = this.filterOutDuplicateTeams(combinedTeams); - combinedTeams = this.sortUnorderedTeams(combinedTeams); + // combine all config data in the correct order + this.combinedData = Object.assign({}, + this.defaultConfigData, + this.localConfigData, + this.buildConfigData, + this.registryConfigData, + {useNativeWindow: this.useNativeWindow}, + ); + + // We don't want to include the servers in the combined config, they should only be accesible via the ServerManager + //delete (this.combinedData as any).teams; + delete (this.combinedData as any).defaultTeams; if (this.combinedData) { - this.combinedData.teams = combinedTeams; - this.combinedData.registryTeams = this.registryConfigData?.teams || []; - if (process.platform === 'darwin' || process.platform === 'win32') { - this.combinedData.darkMode = nativeTheme.shouldUseDarkColors; + // TODO: This can be removed after we fully migrate to ServerManager + let combinedTeams: ConfigServer[] = []; + combinedTeams.push(...this.predefinedTeams); + if (this.localConfigData && this.enableServerManagement) { + combinedTeams.push(...this.localConfigData.teams || []); } - this.combinedData.appName = app.name; + combinedTeams = this.filterOutDuplicateTeams(combinedTeams); + combinedTeams = this.sortUnorderedTeams(combinedTeams); + this.combinedData.teams = combinedTeams; + + this.combinedData.appName = this.appName; } } /** * Returns the provided list of teams with duplicates filtered out - * + * TODO: This can be removed after we fully migrate to ServerManager * @param {array} teams array of teams to check for duplicates */ - filterOutDuplicateTeams = (teams: TeamWithTabs[]) => { + private filterOutDuplicateTeams = (teams: ConfigServer[]) => { let newTeams = teams; const uniqueURLs = new Set(); newTeams = newTeams.filter((team) => { @@ -475,41 +412,12 @@ export class Config extends EventEmitter { return newTeams; } - /** - * Returns the provided array fo teams with existing teams filtered out - * @param {array} teams array of teams to check for already defined teams - */ - filterOutPredefinedTeams = (teams: TeamWithTabs[]) => { - let newTeams = teams; - - // filter out predefined teams - newTeams = newTeams.filter((newTeam) => { - return this.predefinedTeams.findIndex((existingTeam) => newTeam.url === existingTeam.url) === -1; // eslint-disable-line max-nested-callbacks - }); - - return newTeams; - } - - /** - * Returns the provided array fo teams with existing teams includes - * @param {array} teams array of teams to check for already defined teams - */ - filterInPredefinedTeams = (teams: TeamWithTabs[]) => { - let newTeams = teams; - - // filter out predefined teams - newTeams = newTeams.filter((newTeam) => { - return this.predefinedTeams.findIndex((existingTeam) => newTeam.url === existingTeam.url) >= 0; // eslint-disable-line max-nested-callbacks - }); - - return newTeams; - } - /** * Apply a default sort order to the team list, if no order is specified. * @param {array} teams to sort + * TODO: This can be removed after we fully migrate to ServerManager */ - sortUnorderedTeams = (teams: TeamWithTabs[]) => { + private sortUnorderedTeams = (teams: ConfigServer[]) => { // We want to preserve the array order of teams in the config, otherwise a lot of bugs will occur const mappedTeams = teams.map((team, index) => ({team, originalOrder: index})); @@ -538,12 +446,7 @@ export class Config extends EventEmitter { } // helper functions - - readFileSync = (filePath: string) => { - return JSON.parse(fs.readFileSync(filePath, 'utf8')); - } - - writeFile = (filePath: string, configData: Partial, callback: fs.NoParamCallback) => { + private writeFile = (filePath: string, configData: Partial, callback?: fs.NoParamCallback) => { if (!this.defaultConfigData) { return; } @@ -552,101 +455,52 @@ export class Config extends EventEmitter { throw new Error('version ' + configData.version + ' is not equal to ' + this.defaultConfigData.version); } const json = JSON.stringify(configData, null, ' '); - fs.writeFile(filePath, json, 'utf8', callback); - } - writeFileSync = (filePath: string, config: Partial) => { - if (!this.defaultConfigData) { - return; - } + if (callback) { + fs.writeFile(filePath, json, 'utf8', callback); + } else { + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } - if (config.version !== this.defaultConfigData.version) { - throw new Error('version ' + config.version + ' is not equal to ' + this.defaultConfigData.version); - } - - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir); - } - - const json = JSON.stringify(config, null, ' '); - fs.writeFileSync(filePath, json, 'utf8'); - } - - merge = (base: T, target: T2) => { - return Object.assign({}, base, target); - } - - copy = (data: T) => { - return Object.assign({}, data); - } - - handleGetConfiguration = (event: Electron.IpcMainInvokeEvent, option: keyof CombinedConfig) => { - log.debug('Config.handleGetConfiguration', option); - - const config = {...this.combinedData}; - if (option) { - return config[option]; - } - return config; - } - - handleGetLocalConfiguration = (event: Electron.IpcMainInvokeEvent, option: keyof ConfigType) => { - log.debug('Config.handleGetLocalConfiguration', option); - - const config: Partial = {...this.localConfigData}; - config.appName = app.name; - config.enableServerManagement = this.combinedData?.enableServerManagement; - config.canUpgrade = this.canUpgrade; - if (option) { - return config[option]; - } - return config; - } - - handleUpdateTeams = (event: Electron.IpcMainInvokeEvent, newTeams: TeamWithTabs[]) => { - log.debug('Config.handleUpdateTeams'); - log.silly('Config.handleUpdateTeams', newTeams); - - this.set('teams', newTeams); - return this.combinedData!.teams; - } - - /** - * Detects changes in darkmode if it is windows or osx, updates the config and propagates the changes - * @emits 'darkModeChange' - */ - handleUpdateTheme = () => { - log.debug('Config.handleUpdateTheme'); - - if (this.combinedData && this.combinedData.darkMode !== nativeTheme.shouldUseDarkColors) { - this.combinedData.darkMode = nativeTheme.shouldUseDarkColors; - this.emit('darkModeChange', this.combinedData.darkMode); + fs.writeFileSync(filePath, json, 'utf8'); } } - /** - * Manually toggles dark mode for OSes that don't have a native dark mode setting - * @emits 'darkModeChange' - */ - toggleDarkModeManually = () => { - if (!this.combinedData) { - return; + private checkWriteableApp = () => { + if (!this.appPath) { + throw new Error('Config not initialized, cannot regenerate'); } - this.set('darkMode', !this.combinedData.darkMode); - this.emit('darkModeChange', this.combinedData.darkMode); + if (process.platform === 'win32') { + try { + fs.accessSync(path.join(path.dirname(this.appPath), '../../'), fs.constants.W_OK); + + // check to make sure that app-update.yml exists + if (!fs.existsSync(path.join(process.resourcesPath, 'app-update.yml'))) { + log.warn('app-update.yml does not exist, disabling auto-updates'); + return false; + } + } catch (error) { + log.info(`${this.appPath}: ${error}`); + log.warn('autoupgrade disabled'); + return false; + } + + // eslint-disable-next-line no-undef + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return __CAN_UPGRADE__; // prevent showing the option if the path is not writeable, like in a managed environment. + } + + // temporarily disabling auto updater for macOS due to security issues + // eslint-disable-next-line no-undef + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return process.platform !== 'darwin' && __CAN_UPGRADE__; } } -const config = new Config(configPath); +const config = new Config(); export default config; - -ipcMain.on(UPDATE_PATHS, () => { - log.debug('Config.UPDATE_PATHS'); - - config.configFilePath = configPath; - if (config.combinedData) { - config.reload(); - } -}); diff --git a/src/common/config/upgradePreferences.test.js b/src/common/config/upgradePreferences.test.js index 3dc8d378..e1cdc786 100644 --- a/src/common/config/upgradePreferences.test.js +++ b/src/common/config/upgradePreferences.test.js @@ -5,7 +5,7 @@ import {upgradeV0toV1, upgradeV1toV2, upgradeV2toV3} from 'common/config/upgrade import pastDefaultPreferences from 'common/config/pastDefaultPreferences'; jest.mock('common/tabs/TabView', () => ({ - getDefaultTeamWithTabsFromTeam: (value) => ({ + getDefaultConfigTeamFromTeam: (value) => ({ ...value, tabs: [ { @@ -122,7 +122,6 @@ describe('common/config/upgradePreferences', () => { name: 'tab2', }, ], - lastActiveTab: 0, }, { name: 'Secondary team', url: 'http://server-2.com', @@ -135,7 +134,6 @@ describe('common/config/upgradePreferences', () => { name: 'tab2', }, ], - lastActiveTab: 0, }], }); }); diff --git a/src/common/config/upgradePreferences.ts b/src/common/config/upgradePreferences.ts index a51260c4..860f8a27 100644 --- a/src/common/config/upgradePreferences.ts +++ b/src/common/config/upgradePreferences.ts @@ -4,7 +4,7 @@ import {ConfigV3, ConfigV2, ConfigV1, ConfigV0, AnyConfig} from 'types/config'; -import {getDefaultTeamWithTabsFromTeam} from 'common/tabs/TabView'; +import {getDefaultConfigTeamFromTeam} from 'common/tabs/TabView'; import pastDefaultPreferences from './pastDefaultPreferences'; @@ -37,10 +37,7 @@ export function upgradeV2toV3(configV2: ConfigV2) { const config: ConfigV3 = Object.assign({}, deepCopy(pastDefaultPreferences[3]), configV2); config.version = 3; config.teams = configV2.teams.map((value) => { - return { - ...getDefaultTeamWithTabsFromTeam(value), - lastActiveTab: 0, - }; + return getDefaultConfigTeamFromTeam(value); }); config.lastActiveTeam = 0; config.spellCheckerLocales = []; diff --git a/src/common/tabs/TabView.ts b/src/common/tabs/TabView.ts index e83ec067..060fc118 100644 --- a/src/common/tabs/TabView.ts +++ b/src/common/tabs/TabView.ts @@ -1,7 +1,7 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {FullTeam} from 'types/config'; +import {Team} from 'types/config'; import {MattermostServer} from 'common/servers/MattermostServer'; @@ -21,7 +21,7 @@ export interface TabView { get shouldNotify(): boolean; } -export function getDefaultTeamWithTabsFromTeam(team: FullTeam) { +export function getDefaultConfigTeamFromTeam(team: Team & {order: number; lastActiveTab?: number}) { return { ...team, tabs: getDefaultTabs(), diff --git a/src/common/utils/util.ts b/src/common/utils/util.ts index 39287bad..83c055bb 100644 --- a/src/common/utils/util.ts +++ b/src/common/utils/util.ts @@ -66,6 +66,10 @@ export const escapeRegex = (s?: string) => { return s.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); }; +export function copy(data: T) { + return Object.assign({}, data); +} + export default { runMode, shorten, diff --git a/src/main/app/config.ts b/src/main/app/config.ts index dad25f6f..f8a33929 100644 --- a/src/main/app/config.ts +++ b/src/main/app/config.ts @@ -1,9 +1,9 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {app, ipcMain} from 'electron'; +import {app, ipcMain, nativeTheme} from 'electron'; -import {CombinedConfig} from 'types/config'; +import {CombinedConfig, ConfigServer, Config as ConfigType} from 'types/config'; import {DARK_MODE_CHANGE, EMIT_CONFIGURATION, RELOAD_CONFIGURATION} from 'common/communication'; import Config from 'common/config'; @@ -25,6 +25,49 @@ const log = new Logger('App.Config'); // config event handlers // +export function handleGetConfiguration() { + log.debug('handleGetConfiguration'); + + return Config.data; +} + +export function handleGetLocalConfiguration() { + log.debug('handleGetLocalConfiguration'); + + return { + ...Config.localData, + appName: app.name, + enableServerManagement: Config.enableServerManagement, + canUpgrade: Config.canUpgrade, + }; +} + +export function updateConfiguration(event: Electron.IpcMainEvent, properties: Array<{key: keyof ConfigType; data: ConfigType[keyof ConfigType]}> = []) { + log.debug('updateConfiguration', properties); + + if (properties.length) { + const newData = properties.reduce((obj, data) => { + (obj as any)[data.key] = data.data; + return obj; + }, {} as Partial); + Config.setMultiple(newData); + } +} + +export function handleUpdateTheme() { + log.debug('Config.handleUpdateTheme'); + + Config.set('darkMode', nativeTheme.shouldUseDarkColors); +} + +export function handleUpdateTeams(event: Electron.IpcMainInvokeEvent, newTeams: ConfigServer[]) { + log.debug('Config.handleUpdateTeams'); + log.silly('Config.handleUpdateTeams', newTeams); + + Config.setServers(newTeams); + return Config.teams; +} + export function handleConfigUpdate(newConfig: CombinedConfig) { if (newConfig.logLevel) { setLoggingLevel(newConfig.logLevel); diff --git a/src/main/app/initialize.test.js b/src/main/app/initialize.test.js index 7ab03ca2..2578ff13 100644 --- a/src/main/app/initialize.test.js +++ b/src/main/app/initialize.test.js @@ -59,6 +59,9 @@ jest.mock('electron', () => ({ removeHandler: jest.fn(), removeListener: jest.fn(), }, + nativeTheme: { + on: jest.fn(), + }, screen: { on: jest.fn(), }, @@ -111,6 +114,7 @@ jest.mock('main/allowProtocolDialog', () => ({ jest.mock('main/app/app', () => ({})); jest.mock('main/app/config', () => ({ handleConfigUpdate: jest.fn(), + handleUpdateTheme: jest.fn(), })); jest.mock('main/app/intercom', () => ({ handleMainWindowIsShown: jest.fn(), diff --git a/src/main/app/initialize.ts b/src/main/app/initialize.ts index 5ef7ce65..c150957b 100644 --- a/src/main/app/initialize.ts +++ b/src/main/app/initialize.ts @@ -3,7 +3,7 @@ import path from 'path'; -import {app, ipcMain, session} from 'electron'; +import {app, ipcMain, nativeTheme, session} from 'electron'; import installExtension, {REACT_DEVELOPER_TOOLS} from 'electron-devtools-installer'; import isDev from 'electron-is-dev'; @@ -35,6 +35,11 @@ import { PING_DOMAIN, MAIN_WINDOW_SHOWN, OPEN_APP_MENU, + GET_CONFIGURATION, + GET_LOCAL_CONFIGURATION, + UPDATE_CONFIGURATION, + UPDATE_PATHS, + UPDATE_TEAMS, } from 'common/communication'; import Config from 'common/config'; import {Logger} from 'common/log'; @@ -47,7 +52,7 @@ import AutoLauncher from 'main/AutoLauncher'; import updateManager from 'main/autoUpdater'; import {setupBadge} from 'main/badge'; import CertificateManager from 'main/certificateManager'; -import {updatePaths} from 'main/constants'; +import {configPath, updatePaths} from 'main/constants'; import CriticalErrorHandler from 'main/CriticalErrorHandler'; import downloadsManager from 'main/downloadsManager'; import i18nManager from 'main/i18nManager'; @@ -71,7 +76,15 @@ import { handleAppWindowAllClosed, handleChildProcessGone, } from './app'; -import {handleConfigUpdate, handleDarkModeChange} from './config'; +import { + handleConfigUpdate, + handleDarkModeChange, + handleGetConfiguration, + handleGetLocalConfiguration, + handleUpdateTheme, + updateConfiguration, + handleUpdateTeams, +} from './config'; import { handleMainWindowIsShown, handleAppVersion, @@ -177,7 +190,15 @@ async function initializeConfig() { resolve(); }); - Config.init(); + Config.init(configPath, app.name, app.getAppPath()); + ipcMain.on(UPDATE_PATHS, () => { + log.debug('Config.UPDATE_PATHS'); + + Config.setConfigPath(configPath); + if (Config.data) { + Config.reload(); + } + }); }); } @@ -232,6 +253,11 @@ function initializeBeforeAppReady() { } else if (mainProtocol) { app.setAsDefaultProtocolClient(mainProtocol); } + + if (process.platform === 'darwin' || process.platform === 'win32') { + nativeTheme.on('updated', handleUpdateTheme); + handleUpdateTheme(); + } } function initializeInterCommunicationEventListeners() { @@ -269,6 +295,10 @@ function initializeInterCommunicationEventListeners() { ipcMain.on(START_UPDATE_DOWNLOAD, handleStartDownload); ipcMain.on(START_UPGRADE, handleStartUpgrade); ipcMain.handle(PING_DOMAIN, handlePingDomain); + ipcMain.handle(GET_CONFIGURATION, handleGetConfiguration); + ipcMain.handle(GET_LOCAL_CONFIGURATION, handleGetLocalConfiguration); + ipcMain.handle(UPDATE_TEAMS, handleUpdateTeams); + ipcMain.on(UPDATE_CONFIGURATION, updateConfiguration); } function initializeAfterAppReady() { diff --git a/src/main/app/intercom.test.js b/src/main/app/intercom.test.js index b82f087c..31f9e9a2 100644 --- a/src/main/app/intercom.test.js +++ b/src/main/app/intercom.test.js @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import Config from 'common/config'; -import {getDefaultTeamWithTabsFromTeam} from 'common/tabs/TabView'; +import {getDefaultConfigTeamFromTeam} from 'common/tabs/TabView'; import {getLocalURLString, getLocalPreload} from 'main/utils'; import MainWindow from 'main/windows/mainWindow'; @@ -20,10 +20,10 @@ import { } from './intercom'; jest.mock('common/config', () => ({ - set: jest.fn(), + setServers: jest.fn(), })); jest.mock('common/tabs/TabView', () => ({ - getDefaultTeamWithTabsFromTeam: jest.fn(), + getDefaultConfigTeamFromTeam: jest.fn(), })); jest.mock('main/notifications', () => ({})); jest.mock('main/utils', () => ({ @@ -75,8 +75,8 @@ const teams = [ describe('main/app/intercom', () => { describe('handleCloseTab', () => { beforeEach(() => { - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.teams = JSON.parse(JSON.stringify(teams)); }); @@ -94,8 +94,8 @@ describe('main/app/intercom', () => { describe('handleOpenTab', () => { beforeEach(() => { - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.teams = JSON.parse(JSON.stringify(teams)); }); @@ -117,12 +117,12 @@ describe('main/app/intercom', () => { getLocalPreload.mockReturnValue('/some/preload.js'); MainWindow.get.mockReturnValue({}); - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.teams = JSON.parse(JSON.stringify(teams)); - getDefaultTeamWithTabsFromTeam.mockImplementation((team) => ({ + getDefaultConfigTeamFromTeam.mockImplementation((team) => ({ ...team, tabs, })); @@ -156,8 +156,8 @@ describe('main/app/intercom', () => { getLocalPreload.mockReturnValue('/some/preload.js'); MainWindow.get.mockReturnValue({}); - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.teams = JSON.parse(JSON.stringify(teams)); }); @@ -199,8 +199,8 @@ describe('main/app/intercom', () => { getLocalPreload.mockReturnValue('/some/preload.js'); MainWindow.get.mockReturnValue({}); - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.teams = JSON.parse(JSON.stringify(teams)); }); @@ -248,8 +248,8 @@ describe('main/app/intercom', () => { getLocalPreload.mockReturnValue('/some/preload.js'); MainWindow.get.mockReturnValue({}); - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.teams = JSON.parse(JSON.stringify([])); }); @@ -271,8 +271,8 @@ describe('main/app/intercom', () => { isVisible: () => true, }); - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); Config.registryConfigData = { teams: JSON.parse(JSON.stringify([{ @@ -281,6 +281,7 @@ describe('main/app/intercom', () => { url: 'https://someurl.here', }])), }; + Config.teams = JSON.parse(JSON.stringify(teams)); handleMainWindowIsShown(); expect(ModalManager.addModal).not.toHaveBeenCalled(); diff --git a/src/main/app/intercom.ts b/src/main/app/intercom.ts index 8bbaebcf..cca8f9bf 100644 --- a/src/main/app/intercom.ts +++ b/src/main/app/intercom.ts @@ -8,7 +8,7 @@ import {MentionData} from 'types/notification'; import Config from 'common/config'; import {Logger} from 'common/log'; -import {getDefaultTeamWithTabsFromTeam} from 'common/tabs/TabView'; +import {getDefaultConfigTeamFromTeam} from 'common/tabs/TabView'; import {ping} from 'common/utils/requests'; import {displayMention} from 'main/notifications'; @@ -69,7 +69,7 @@ export function handleCloseTab(event: IpcMainEvent, serverName: string, tabName: }); const nextTab = teams.find((team) => team.name === serverName)!.tabs.filter((tab) => tab.isOpen)[0].name; WindowManager.switchTab(serverName, nextTab); - Config.set('teams', teams); + Config.setServers(teams); } export function handleOpenTab(event: IpcMainEvent, serverName: string, tabName: string) { @@ -86,7 +86,7 @@ export function handleOpenTab(event: IpcMainEvent, serverName: string, tabName: } }); WindowManager.switchTab(serverName, tabName); - Config.set('teams', teams); + Config.setServers(teams); } export function handleShowOnboardingScreens(showWelcomeScreen: boolean, showNewServerModal: boolean, mainWindowIsVisible: boolean) { @@ -153,9 +153,9 @@ export function handleNewServerModal() { modalPromise.then((data) => { const teams = Config.teams; const order = teams.length; - const newTeam = getDefaultTeamWithTabsFromTeam({...data, order}); + const newTeam = getDefaultConfigTeamFromTeam({...data, order}); teams.push(newTeam); - Config.set('teams', teams); + Config.setServers(teams); updateServerInfos([newTeam]); WindowManager.switchServer(newTeam.name, true); }).catch((e) => { @@ -198,7 +198,7 @@ export function handleEditServerModal(e: IpcMainEvent, name: string) { const teams = Config.teams; teams[serverIndex].name = data.name; teams[serverIndex].url = data.url; - Config.set('teams', teams); + Config.setServers(teams); updateServerInfos([teams[serverIndex]]); }).catch((e) => { // e is undefined for user cancellation @@ -238,7 +238,7 @@ export function handleRemoveServerModal(e: IpcMainEvent, name: string) { value.order--; } }); - Config.set('teams', teams); + Config.setServers(teams); } }).catch((e) => { // e is undefined for user cancellation @@ -267,9 +267,9 @@ export function handleWelcomeScreenModal() { modalPromise.then((data) => { const teams = Config.teams; const order = teams.length; - const newTeam = getDefaultTeamWithTabsFromTeam({...data, order}); + const newTeam = getDefaultConfigTeamFromTeam({...data, order}); teams.push(newTeam); - Config.set('teams', teams); + Config.setServers(teams); updateServerInfos([newTeam]); WindowManager.switchServer(newTeam.name, true); }).catch((e) => { @@ -324,10 +324,7 @@ export function handleUpdateLastActive(event: IpcMainEvent, serverName: string, team.lastActiveTab = viewOrder; } }); - Config.setMultiple({ - teams, - lastActiveTeam: teams.find((team) => team.name === serverName)?.order || 0, - }); + Config.setServers(teams, teams.find((team) => team.name === serverName)?.order || 0); } export function handlePingDomain(event: IpcMainInvokeEvent, url: string): Promise { diff --git a/src/main/app/utils.test.js b/src/main/app/utils.test.js index c321a40d..c3f50fc1 100644 --- a/src/main/app/utils.test.js +++ b/src/main/app/utils.test.js @@ -40,7 +40,7 @@ jest.mock('electron', () => ({ })); jest.mock('common/config', () => ({ - set: jest.fn(), + setServers: jest.fn(), })); jest.mock('common/JsonFileManager'); jest.mock('common/utils/util', () => ({ @@ -95,8 +95,8 @@ describe('main/app/utils', () => { beforeEach(() => { Utils.isVersionGreaterThanOrEqualTo.mockImplementation((version) => version === '6.0.0'); - Config.set.mockImplementation((name, value) => { - Config[name] = value; + Config.setServers.mockImplementation((value) => { + Config.teams = value; }); const teamsCopy = JSON.parse(JSON.stringify(teams)); Config.teams = teamsCopy; diff --git a/src/main/app/utils.ts b/src/main/app/utils.ts index b2482fae..cee3dc1d 100644 --- a/src/main/app/utils.ts +++ b/src/main/app/utils.ts @@ -47,8 +47,8 @@ export function openDeepLink(deeplinkingUrl: string) { } export function updateSpellCheckerLocales() { - if (Config.data?.spellCheckerLocales.length && app.isReady()) { - session.defaultSession.setSpellCheckerLanguages(Config.data?.spellCheckerLocales); + if (Config.spellCheckerLocales.length && app.isReady()) { + session.defaultSession.setSpellCheckerLanguages(Config.spellCheckerLocales); } } @@ -67,7 +67,7 @@ export function updateServerInfos(teams: TeamWithTabs[]) { hasUpdates = hasUpdates || openExtraTabs(data, team); }); if (hasUpdates) { - Config.set('teams', teams); + Config.setServers(teams); } }).catch((reason: any) => { log.error('Error getting server infos', reason); diff --git a/src/main/diagnostics/steps/step2.configValidation.ts b/src/main/diagnostics/steps/step2.configValidation.ts index 2eccb5fc..e16f8054 100644 --- a/src/main/diagnostics/steps/step2.configValidation.ts +++ b/src/main/diagnostics/steps/step2.configValidation.ts @@ -8,6 +8,8 @@ import {DiagnosticStepResponse} from 'types/diagnostics'; import Config from 'common/config'; import * as Validator from 'common/Validator'; +import {configPath} from 'main/constants'; + import DiagnosticsStep from '../DiagnosticStep'; const stepName = 'Step-2'; @@ -15,13 +17,13 @@ const stepDescriptiveName = 'configValidation'; const run = async (logger: ElectronLog): Promise => { try { - const configData = JSON.parse(fs.readFileSync(Config.configFilePath, 'utf8')); + const configData = JSON.parse(fs.readFileSync(configPath, 'utf8')); // validate based on config file version const validData = Validator.validateConfigData(configData); if (!validData) { - throw new Error(`Config validation failed. Config: ${JSON.stringify(Config.combinedData, null, 4)}`); + throw new Error(`Config validation failed. Config: ${JSON.stringify(Config.data, null, 4)}`); } return { diff --git a/src/main/diagnostics/steps/step3.serverConnectivity.ts b/src/main/diagnostics/steps/step3.serverConnectivity.ts index 1c7fb40d..f4a17387 100644 --- a/src/main/diagnostics/steps/step3.serverConnectivity.ts +++ b/src/main/diagnostics/steps/step3.serverConnectivity.ts @@ -15,7 +15,7 @@ const stepDescriptiveName = 'serverConnectivity'; const run = async (logger: ElectronLog): Promise => { try { - const teams = Config.combinedData?.teams || []; + const teams = Config.teams || []; await Promise.all(teams.map(async (team) => { logger.debug('Pinging server: ', team.url); diff --git a/src/main/menus/app.test.js b/src/main/menus/app.test.js index 87a4004c..1b988db3 100644 --- a/src/main/menus/app.test.js +++ b/src/main/menus/app.test.js @@ -65,55 +65,53 @@ jest.mock('common/tabs/TabView', () => ({ describe('main/menus/app', () => { const config = { - data: { - enableServerManagement: true, - teams: [{ - name: 'example', - url: 'http://example.com', - order: 0, - tabs: [ - { - name: 'TAB_MESSAGING', - order: 0, - isOpen: true, - }, - { - name: 'TAB_FOCALBOARD', - order: 1, - isOpen: true, - }, - { - name: 'TAB_PLAYBOOKS', - order: 2, - isOpen: true, - }, - ], - lastActiveTab: 0, - }, { - name: 'github', - url: 'https://github.com/', - order: 1, - tabs: [ - { - name: 'TAB_MESSAGING', - order: 0, - isOpen: true, - }, - { - name: 'TAB_FOCALBOARD', - order: 1, - isOpen: true, - }, - { - name: 'TAB_PLAYBOOKS', - order: 2, - isOpen: true, - }, - ], - lastActiveTab: 0, - }], - helpLink: 'http://link-to-help.site.com', - }, + enableServerManagement: true, + teams: [{ + name: 'example', + url: 'http://example.com', + order: 0, + tabs: [ + { + name: 'TAB_MESSAGING', + order: 0, + isOpen: true, + }, + { + name: 'TAB_FOCALBOARD', + order: 1, + isOpen: true, + }, + { + name: 'TAB_PLAYBOOKS', + order: 2, + isOpen: true, + }, + ], + lastActiveTab: 0, + }, { + name: 'github', + url: 'https://github.com/', + order: 1, + tabs: [ + { + name: 'TAB_MESSAGING', + order: 0, + isOpen: true, + }, + { + name: 'TAB_FOCALBOARD', + order: 1, + isOpen: true, + }, + { + name: 'TAB_PLAYBOOKS', + order: 2, + isOpen: true, + }, + ], + lastActiveTab: 0, + }], + helpLink: 'http://link-to-help.site.com', }; beforeEach(() => { getDarwinDoNotDisturb.mockReturnValue(false); @@ -218,7 +216,7 @@ describe('main/menus/app', () => { const menu = createTemplate(modifiedConfig); const fileMenu = menu.find((item) => item.label === '&AppName' || item.label === '&File'); const signInOption = fileMenu.submenu.find((item) => item.label === 'Sign in to Another Server'); - expect(signInOption).not.toBe(undefined); + expect(signInOption).toBe(undefined); }); it('should not show `Sign in to Another Server` if no teams are configured', () => { @@ -239,7 +237,7 @@ describe('main/menus/app', () => { const menu = createTemplate(modifiedConfig); const fileMenu = menu.find((item) => item.label === '&AppName' || item.label === '&File'); const signInOption = fileMenu.submenu.find((item) => item.label === 'Sign in to Another Server'); - expect(signInOption).not.toBe(undefined); + expect(signInOption).toBe(undefined); }); it('should show the first 9 servers (using order) in the Window menu', () => { @@ -250,21 +248,18 @@ describe('main/menus/app', () => { return id; }); const modifiedConfig = { - data: { - ...config.data, - teams: [...Array(15).keys()].map((key) => ({ - name: `server-${key}`, - url: `http://server-${key}.com`, - order: (key + 5) % 15, - lastActiveTab: 0, - tab: [ - { - name: 'TAB_MESSAGING', - isOpen: true, - }, - ], - })), - }, + teams: [...Array(15).keys()].map((key) => ({ + name: `server-${key}`, + url: `http://server-${key}.com`, + order: (key + 5) % 15, + lastActiveTab: 0, + tab: [ + { + name: 'TAB_MESSAGING', + isOpen: true, + }, + ], + })), }; const menu = createTemplate(modifiedConfig); const windowMenu = menu.find((item) => item.label === '&Window'); @@ -292,22 +287,19 @@ describe('main/menus/app', () => { } return id; }); - WindowManager.getCurrentTeamName.mockImplementation(() => config.data.teams[0].name); + WindowManager.getCurrentTeamName.mockImplementation(() => config.teams[0].name); const modifiedConfig = { - data: { - ...config.data, - teams: [ - { - ...config.data.teams[0], - tabs: [...Array(15).keys()].map((key) => ({ - name: `tab-${key}`, - isOpen: true, - order: (key + 5) % 15, - })), - }, - ], - }, + teams: [ + { + ...config.teams[0], + tabs: [...Array(15).keys()].map((key) => ({ + name: `tab-${key}`, + isOpen: true, + order: (key + 5) % 15, + })), + }, + ], }; const menu = createTemplate(modifiedConfig); const windowMenu = menu.find((item) => item.label === '&Window'); diff --git a/src/main/menus/app.ts b/src/main/menus/app.ts index c6c6fa30..8e3b192d 100644 --- a/src/main/menus/app.ts +++ b/src/main/menus/app.ts @@ -49,7 +49,7 @@ export function createTemplate(config: Config, updateManager: UpdateManager) { }, }); - if (config.data?.enableServerManagement === true && config.data?.teams.length > 0) { + if (config.enableServerManagement === true && config.teams.length > 0) { platformAppMenu.push({ label: localizeMessage('main.menus.app.file.signInToAnotherServer', 'Sign in to Another Server'), click() { @@ -203,7 +203,7 @@ export function createTemplate(config: Config, updateManager: UpdateManager) { viewSubMenu.push({ label: localizeMessage('main.menus.app.view.toggleDarkMode', 'Toggle Dark Mode'), click() { - config.toggleDarkModeManually(); + config.set('darkMode', !config.darkMode); }, }); } @@ -231,7 +231,7 @@ export function createTemplate(config: Config, updateManager: UpdateManager) { }], }); - const teams = config.data?.teams || []; + const teams = config.teams || []; const windowMenu = { id: 'window', label: localizeMessage('main.menus.app.window', '&Window'), @@ -251,7 +251,7 @@ export function createTemplate(config: Config, updateManager: UpdateManager) { label: isMac ? localizeMessage('main.menus.app.window.closeWindow', 'Close Window') : localizeMessage('main.menus.app.window.close', 'Close'), accelerator: 'CmdOrCtrl+W', }, separatorItem, - ...(config.data?.teams.length ? [{ + ...(config.teams.length ? [{ label: localizeMessage('main.menus.app.window.showServers', 'Show Servers'), accelerator: `${process.platform === 'darwin' ? 'Cmd+Ctrl' : 'Ctrl+Shift'}+S`, click() { @@ -325,11 +325,11 @@ export function createTemplate(config: Config, updateManager: UpdateManager) { }); } } - if (config.data?.helpLink) { + if (config.helpLink) { submenu.push({ label: localizeMessage('main.menus.app.help.learnMore', 'Learn More...'), click() { - shell.openExternal(config.data!.helpLink); + shell.openExternal(config.helpLink!); }, }); submenu.push(separatorItem); diff --git a/src/main/views/downloadsDropdownMenuView.ts b/src/main/views/downloadsDropdownMenuView.ts index db03f1ad..15d74a6c 100644 --- a/src/main/views/downloadsDropdownMenuView.ts +++ b/src/main/views/downloadsDropdownMenuView.ts @@ -19,6 +19,7 @@ import { UPDATE_DOWNLOADS_DROPDOWN_MENU_ITEM, } from 'common/communication'; import {Logger} from 'common/log'; +import Config from 'common/config'; import { DOWNLOADS_DROPDOWN_FULL_WIDTH, DOWNLOADS_DROPDOWN_MENU_FULL_HEIGHT, @@ -26,10 +27,9 @@ import { TAB_BAR_HEIGHT, } from 'common/utils/constants'; import {getLocalPreload, getLocalURLString} from 'main/utils'; - -import WindowManager from '../windows/windowManager'; import downloadsManager from 'main/downloadsManager'; import MainWindow from 'main/windows/mainWindow'; +import WindowManager from 'main/windows/windowManager'; const log = new Logger('DownloadsDropdownMenuView'); @@ -42,11 +42,11 @@ export default class DownloadsDropdownMenuView { darkMode: boolean; windowBounds: Electron.Rectangle; - constructor(darkMode: boolean) { + constructor() { this.open = false; this.item = undefined; this.coordinates = undefined; - this.darkMode = darkMode; + this.darkMode = Config.darkMode; ipcMain.on(OPEN_DOWNLOADS_DROPDOWN_MENU, this.handleOpen); ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN_MENU, this.handleClose); diff --git a/src/main/views/downloadsDropdownView.test.js b/src/main/views/downloadsDropdownView.test.js index b5d445ac..15c00a5b 100644 --- a/src/main/views/downloadsDropdownView.test.js +++ b/src/main/views/downloadsDropdownView.test.js @@ -62,6 +62,7 @@ jest.mock('electron', () => { }; }); jest.mock('main/downloadsManager', () => ({ + getDownloads: jest.fn(), onOpen: jest.fn(), onClose: jest.fn(), })); diff --git a/src/main/views/downloadsDropdownView.ts b/src/main/views/downloadsDropdownView.ts index f7bb7999..e57c1b0b 100644 --- a/src/main/views/downloadsDropdownView.ts +++ b/src/main/views/downloadsDropdownView.ts @@ -19,11 +19,12 @@ import { DOWNLOADS_DROPDOWN_OPEN_FILE, } from 'common/communication'; import {Logger} from 'common/log'; +import Config from 'common/config'; import {TAB_BAR_HEIGHT, DOWNLOADS_DROPDOWN_WIDTH, DOWNLOADS_DROPDOWN_HEIGHT, DOWNLOADS_DROPDOWN_FULL_WIDTH} from 'common/utils/constants'; -import {getLocalPreload, getLocalURLString} from 'main/utils'; -import WindowManager from '../windows/windowManager'; +import {getLocalPreload, getLocalURLString} from 'main/utils'; import downloadsManager from 'main/downloadsManager'; +import WindowManager from 'main/windows/windowManager'; import MainWindow from 'main/windows/mainWindow'; const log = new Logger('DownloadsDropdownView'); @@ -36,9 +37,9 @@ export default class DownloadsDropdownView { view: BrowserView; windowBounds: Electron.Rectangle; - constructor(downloads: DownloadedItems, darkMode: boolean) { - this.downloads = downloads; - this.darkMode = darkMode; + constructor() { + this.downloads = downloadsManager.getDownloads(); + this.darkMode = Config.darkMode; ipcMain.on(OPEN_DOWNLOADS_DROPDOWN, this.handleOpen); ipcMain.on(CLOSE_DOWNLOADS_DROPDOWN, this.handleClose); diff --git a/src/main/views/modalManager.test.js b/src/main/views/modalManager.test.js index 02443315..c2dfde0c 100644 --- a/src/main/views/modalManager.test.js +++ b/src/main/views/modalManager.test.js @@ -15,10 +15,6 @@ jest.mock('electron', () => ({ }, })); -jest.mock('common/config', () => ({ - teams: [], -})); - jest.mock('main/views/webContentEvents', () => ({ addWebContentsEventListeners: jest.fn(), })); diff --git a/src/main/views/modalManager.ts b/src/main/views/modalManager.ts index 64d20564..a361a3c6 100644 --- a/src/main/views/modalManager.ts +++ b/src/main/views/modalManager.ts @@ -17,7 +17,6 @@ import { GET_MODAL_UNCLOSEABLE, RESIZE_MODAL, } from 'common/communication'; -import Config from 'common/config'; import {Logger} from 'common/log'; import {getAdjustedWindowBoundaries} from 'main/utils'; @@ -91,7 +90,7 @@ export class ModalManager { if (index === 0) { WindowManager.sendToRenderer(MODAL_OPEN); modal.show(undefined, Boolean(withDevTools)); - WebContentsEventManager.addWebContentsEventListeners(modal.view.webContents, () => Config.teams.concat()); + WebContentsEventManager.addWebContentsEventListeners(modal.view.webContents); } else { WindowManager.sendToRenderer(MODAL_CLOSE); modal.hide(); diff --git a/src/main/views/teamDropdownView.test.js b/src/main/views/teamDropdownView.test.js index dd8b3f1d..a81bd581 100644 --- a/src/main/views/teamDropdownView.test.js +++ b/src/main/views/teamDropdownView.test.js @@ -42,7 +42,7 @@ describe('main/views/teamDropdownView', () => { MainWindow.getBounds.mockReturnValue({width: 500, height: 400, x: 0, y: 0}); }); - const teamDropdownView = new TeamDropdownView([], false, true); + const teamDropdownView = new TeamDropdownView(); if (process.platform === 'darwin') { it('should account for three dot menu, tab bar and shadow', () => { expect(teamDropdownView.getBounds(400, 300)).toStrictEqual({x: THREE_DOT_MENU_WIDTH_MAC - MENU_SHADOW_WIDTH, y: TAB_BAR_HEIGHT - MENU_SHADOW_WIDTH, width: 400, height: 300}); @@ -55,7 +55,7 @@ describe('main/views/teamDropdownView', () => { }); it('should change the view bounds based on open/closed state', () => { - const teamDropdownView = new TeamDropdownView([], false, true); + const teamDropdownView = new TeamDropdownView(); teamDropdownView.bounds = {width: 400, height: 300}; teamDropdownView.handleOpen(); expect(teamDropdownView.view.setBounds).toBeCalledWith(teamDropdownView.bounds); @@ -65,7 +65,7 @@ describe('main/views/teamDropdownView', () => { describe('addGpoToTeams', () => { it('should return teams with "isGPO": false when no config.registryTeams exist', () => { - const teamDropdownView = new TeamDropdownView([], false, true); + const teamDropdownView = new TeamDropdownView(); const teams = [{ name: 'team-1', url: 'https://mattermost.team-1.com', @@ -86,7 +86,7 @@ describe('main/views/teamDropdownView', () => { }]); }); it('should return teams with "isGPO": true if they exist in config.registryTeams', () => { - const teamDropdownView = new TeamDropdownView([], false, true); + const teamDropdownView = new TeamDropdownView(); const teams = [{ name: 'team-1', url: 'https://mattermost.team-1.com', diff --git a/src/main/views/teamDropdownView.ts b/src/main/views/teamDropdownView.ts index c26b9bc1..0668f49d 100644 --- a/src/main/views/teamDropdownView.ts +++ b/src/main/views/teamDropdownView.ts @@ -15,9 +15,12 @@ import { RECEIVE_DROPDOWN_MENU_SIZE, SET_ACTIVE_VIEW, } from 'common/communication'; +import Config from 'common/config'; import {Logger} from 'common/log'; import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHADOW_WIDTH} from 'common/utils/constants'; + import {getLocalPreload, getLocalURLString} from 'main/utils'; + import * as AppState from '../appState'; import WindowManager from '../windows/windowManager'; import MainWindow from '../windows/mainWindow'; @@ -38,10 +41,10 @@ export default class TeamDropdownView { windowBounds?: Electron.Rectangle; isOpen: boolean; - constructor(teams: TeamWithTabs[], darkMode: boolean, enableServerManagement: boolean) { - this.teams = this.addGpoToTeams(teams, []); - this.darkMode = darkMode; - this.enableServerManagement = enableServerManagement; + constructor() { + this.teams = this.addGpoToTeams(Config.teams, []); + this.darkMode = Config.darkMode; + this.enableServerManagement = Config.enableServerManagement; this.isOpen = false; this.windowBounds = MainWindow.getBounds(); diff --git a/src/main/windows/windowManager.ts b/src/main/windows/windowManager.ts index 297d0e32..ef2311c1 100644 --- a/src/main/windows/windowManager.ts +++ b/src/main/windows/windowManager.ts @@ -36,7 +36,6 @@ import {SECOND} from 'common/utils/constants'; import Config from 'common/config'; import {getTabViewName} from 'common/tabs/TabView'; -import downloadsManager from 'main/downloadsManager'; import {MattermostView} from 'main/views/MattermostView'; import { @@ -208,9 +207,9 @@ export class WindowManager { mainWindow.on('enter-full-screen', () => this.sendToRenderer('enter-full-screen')); mainWindow.on('leave-full-screen', () => this.sendToRenderer('leave-full-screen')); - this.teamDropdown = new TeamDropdownView(Config.teams, Config.darkMode, Config.enableServerManagement); - this.downloadsDropdown = new DownloadsDropdownView(downloadsManager.getDownloads(), Config.darkMode); - this.downloadsDropdownMenu = new DownloadsDropdownMenuView(Config.darkMode); + this.teamDropdown = new TeamDropdownView(); + this.downloadsDropdown = new DownloadsDropdownView(); + this.downloadsDropdownMenu = new DownloadsDropdownMenuView(); this.initializeViewManager(); } diff --git a/src/types/config.ts b/src/types/config.ts index 574f01c9..089d4e32 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -120,7 +120,7 @@ export type BuildConfig = { } export type RegistryConfig = { - teams: FullTeam[]; + teams: Team[]; enableServerManagement: boolean; enableAutoUpdater: boolean; }