Refactor config, move ipc calls to app module, some cleanup (#2669)
This commit is contained in:
@@ -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<RegistryConfigType>;
|
||||
defaultConfigData?: ConfigType;
|
||||
buildConfigData?: BuildConfig;
|
||||
localConfigData?: ConfigType;
|
||||
useNativeWindow: boolean;
|
||||
canUpgradeValue?: boolean
|
||||
private combinedData?: CombinedConfig;
|
||||
private localConfigData?: ConfigType;
|
||||
private registryConfigData?: Partial<RegistryConfigType>;
|
||||
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<void>((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<RegistryConfigType>): 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<ConfigType> | 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<ConfigType>);
|
||||
this.setMultiple(newData);
|
||||
}
|
||||
|
||||
return this.localConfigData;
|
||||
setConfigPath = (configPath: string) => {
|
||||
this.configFilePath = configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,20 +130,12 @@ export class Config extends EventEmitter {
|
||||
setMultiple = (newData: Partial<ConfigType>) => {
|
||||
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<RegistryConfigType>): 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<ConfigType>, callback: fs.NoParamCallback) => {
|
||||
private writeFile = (filePath: string, configData: Partial<ConfigType>, 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<ConfigType>) => {
|
||||
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 = <T, T2>(base: T, target: T2) => {
|
||||
return Object.assign({}, base, target);
|
||||
}
|
||||
|
||||
copy = <T>(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<LocalConfiguration> = {...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();
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user