Merge pull request #633 from yuya-oc/simplify-two-config

Simplify and split config.json into defaultPreferences and buildConfig
This commit is contained in:
Yuya Ochiai 2017-11-09 00:49:09 +09:00 committed by GitHub
commit 3a865f3d24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 212 additions and 177 deletions

View file

@ -1734,36 +1734,3 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
## deepmerge
This product contains 'deepmerge', a library for deep merging of JavaScript objects, by Kyle Mathews.
* HOMEPAGE:
* https://github.com/KyleAMathews/deepmerge
* LICENSE:
The MIT License (MIT)
Copyright (c) 2012 Nicholas Fisher
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -27,15 +27,15 @@
"serve": "gulp watch",
"test": "npm-run-all test:* lint:*",
"test:app": "npm run build && mocha --reporter mocha-circleci-reporter --recursive test/specs",
"package:all": "npm-run-all package:windows package:mac package:linux",
"package:windows": "npm run build && build --win --x64 --ia32 --em.name=mattermost --publish=never && npm run manipulate-windows-zip",
"package:mac": "npm run build && build --mac --publish=never",
"package:linux": "npm run build && build --linux --x64 --ia32 --em.name=mattermost-desktop --publish=never",
"package:all": "npm-run-all check-build-config package:windows package:mac package:linux",
"package:windows": "npm-run-all check-build-config build && build --win --x64 --ia32 --em.name=mattermost --publish=never && npm run manipulate-windows-zip",
"package:mac": "npm-run-all check-build-config build && build --mac --publish=never",
"package:linux": "npm-run-all check-build-config build && build --linux --x64 --ia32 --em.name=mattermost-desktop --publish=never",
"manipulate-windows-zip": "node scripts/manipulate_windows_zip.js",
"lint:js": "eslint --ext .js --ext .jsx ."
"lint:js": "eslint --ext .js --ext .jsx .",
"check-build-config": "node scripts/check_build_config.js"
},
"devDependencies": {
"deepmerge": "^1.5.2",
"7zip-bin": "^2.0.4",
"babel-core": "^6.24.1",
"babel-eslint": "^7.2.3",

View file

@ -0,0 +1,16 @@
const buildConfig = require('../src/common/config/buildConfig');
function validateBuildConfig(config) {
if (config.enableServerManagement === false && config.defaultTeams && config.defaultTeams.length === 0) {
return {
result: false,
message: `Specify at least one server for "defaultTeams" in buildConfig.js when "enableServerManagement is set to false.\n${JSON.stringify(config, null, 2)}`
};
}
return {result: true};
}
const ret = validateBuildConfig(buildConfig);
if (ret.result === false) {
throw new Error(ret.message);
}

View file

@ -8,6 +8,7 @@ const {ipcRenderer, remote} = require('electron');
const AutoLaunch = require('auto-launch');
const {debounce} = require('underscore');
const buildConfig = require('../../common/config/buildConfig');
const settings = require('../../common/settings');
const TeamList = require('./TeamList.jsx');
@ -29,7 +30,8 @@ const CONFIG_TYPE_APP_OPTIONS = 'appOptions';
const SettingsPage = createReactClass({
propTypes: {
configFile: PropTypes.string
configFile: PropTypes.string,
enableServerManagement: PropTypes.bool
},
getInitialState() {
@ -301,8 +303,10 @@ const SettingsPage = createReactClass({
onTeamsChange={this.handleTeamsChange}
updateTeam={this.updateTeam}
addServer={this.addServer}
onTeamClick={backToIndex}
allowTeamEdit={this.state.enableTeamModification}
onTeamClick={(index) => {
backToIndex(index + buildConfig.defaultTeams.length);
}}
/>
</Col>
</Row>
@ -339,7 +343,7 @@ const SettingsPage = createReactClass({
);
var srvMgmt;
if (this.state.enableServerManagement || this.state.teams.length === 0) {
if (this.props.enableServerManagement === true) {
srvMgmt = (
<div>
{serversRow}
@ -514,7 +518,7 @@ const SettingsPage = createReactClass({
bsStyle='link'
style={settingsPage.close}
onClick={this.handleCancel}
disabled={this.state.teams.length === 0}
disabled={settings.mergeDefaultTeams(this.state.teams).length === 0}
>
<span>{'×'}</span>
</Button>

View file

@ -12,14 +12,18 @@ const {remote, ipcRenderer} = require('electron');
const MainPage = require('./components/MainPage.jsx');
const AppConfig = require('./config/AppConfig.js');
const buildConfig = require('../common/config/buildConfig');
const settings = require('../common/settings');
const url = require('url');
const badge = require('./js/badge');
const utils = require('../utils/util');
const teams = settings.mergeDefaultTeams(AppConfig.data.teams);
remote.getCurrentWindow().removeAllListeners('focus');
if (AppConfig.data.teams.length === 0) {
if (teams.length === 0) {
window.location = 'settings.html';
}
@ -90,8 +94,9 @@ function showUnreadBadge(unreadCount, mentionCount) {
const permissionRequestQueue = [];
const requestingPermission = new Array(AppConfig.data.teams.length);
function teamConfigChange(teams) {
AppConfig.set('teams', teams);
function teamConfigChange(updatedTeams) {
AppConfig.set('teams', updatedTeams.slice(buildConfig.defaultTeams.length));
teams.splice(0, teams.length, ...updatedTeams);
requestingPermission.length = teams.length;
ipcRenderer.send('update-menu', AppConfig.data);
ipcRenderer.send('update-config');
@ -157,14 +162,14 @@ if (!parsedURL.query.index || parsedURL.query.index === null) {
ReactDOM.render(
<MainPage
teams={AppConfig.data.teams}
teams={teams}
initialIndex={initialIndex}
onUnreadCountChange={showUnreadBadge}
onTeamConfigChange={teamConfigChange}
useSpellChecker={AppConfig.data.useSpellChecker}
onSelectSpellCheckerLocale={handleSelectSpellCheckerLocale}
deeplinkingUrl={deeplinkingUrl}
showAddServerButton={AppConfig.data.enableServerManagement}
showAddServerButton={buildConfig.enableServerManagement}
requestingPermission={requestingPermission}
onClickPermissionDialog={handleClickPermissionDialog}
/>,

View file

@ -9,13 +9,17 @@ const React = require('react');
const ReactDOM = require('react-dom');
const SettingsPage = require('./components/SettingsPage.jsx');
const contextMenu = require('./js/contextMenu');
const buildConfig = require('../common/config/buildConfig');
const configFile = remote.app.getPath('userData') + '/config.json';
contextMenu.setup(remote.getCurrentWindow());
ReactDOM.render(
<SettingsPage configFile={configFile}/>,
<SettingsPage
configFile={configFile}
enableServerManagement={buildConfig.enableServerManagement}
/>,
document.getElementById('content')
);

View file

@ -1,32 +0,0 @@
{
"default": {
"teams": [],
"showTrayIcon": false,
"trayIconTheme": "light",
"minimizeToTray": false,
"version": 1,
"notifications": {
"flashWindow": 0
},
"showUnreadBadge": true,
"useSpellChecker": true,
"spellCheckerLocale": "en-US",
"helpLink": "https://docs.mattermost.com/help/apps/desktop-guide.html",
"enableServerManagement": true
},
"1": {
"teams": [],
"showTrayIcon": false,
"trayIconTheme": "light",
"minimizeToTray": false,
"version": 1,
"notifications": {
"flashWindow": 0
},
"showUnreadBadge": true,
"useSpellChecker": true,
"spellCheckerLocale": "en-US",
"helpLink": "https://docs.mattermost.com/help/apps/desktop-guide.html",
"enableServerManagement": true
}
}

View file

@ -0,0 +1,23 @@
/**
* Build-time configuration. End-users can't change these parameters.
* @prop {Object[]} defaultTeams
* @prop {string} defaultTeams[].name - The tab name for default team.
* @prop {string} defaultTeams[].url - The URL for default team.
* @prop {string} helpLink - The URL for "Help->Learn More..." menu item.
* If null is specified, the menu disappears.
* @prop {boolean} enableServerManagement - Whether users can edit servers configuration.
* Specify at least one server for "defaultTeams"
* when "enableServerManagement is set to false
*/
const buildConfig = {
defaultTeams: [/*
{
name: 'example',
url: 'https://example.com'
}*/
],
helpLink: 'https://docs.mattermost.com/help/apps/desktop-guide.html',
enableServerManagement: true
};
module.exports = buildConfig;

View file

@ -0,0 +1,19 @@
/**
* Default user preferences. End-users can change these parameters by editing config.json
* @param {number} version - Scheme version. (Not application version)
*/
const defaultPreferences = {
version: 1,
teams: [],
showTrayIcon: false,
trayIconTheme: 'light',
minimizeToTray: false,
notifications: {
flashWindow: 0
},
showUnreadBadge: true,
useSpellChecker: true,
spellCheckerLocale: 'en-US'
};
module.exports = defaultPreferences;

View file

@ -1,3 +0,0 @@
{
}

View file

@ -0,0 +1,11 @@
const defaultPreferences = require('./defaultPreferences');
const pastDefaultPreferences = {
0: {
url: ''
}
};
pastDefaultPreferences[`${defaultPreferences.version}`] = defaultPreferences;
module.exports = pastDefaultPreferences;

View file

@ -0,0 +1,29 @@
const pastDefaultPreferences = require('./pastDefaultPreferences');
function deepCopy(object) {
return JSON.parse(JSON.stringify(object));
}
function upgradeV0toV1(configV0) {
const config = deepCopy(pastDefaultPreferences['1']);
if (config.version !== 1) {
throw new Error('pastDefaultPreferences[\'1\'].version is not equal to 1');
}
config.teams.push({
name: 'Primary team',
url: configV0.url
});
return config;
}
function upgradeToLatest(config) {
var configVersion = config.version ? config.version : 0;
switch (configVersion) {
case 0:
return upgradeToLatest(upgradeV0toV1(config));
default:
return config;
}
}
module.exports = upgradeToLatest;

View file

@ -1,90 +1,56 @@
'use strict';
const fs = require('fs');
const path = require('path');
const deepmerge = require('./deepmerge');
const settingsVersion = 1;
const baseConfig = require('./config/base.json');
const overrideConfig = require('./config/override.json');
const buildConfig = require('./config/buildConfig');
function merge(base, target) {
return Object.assign({}, base, target);
}
function deepMergeArray(source, dest) {
return dest;
}
const defaultPreferences = require('./config/defaultPreferences');
const upgradePreferences = require('./config/upgradePreferences');
function loadDefault(version, spellCheckerLocale) {
var ver = version;
if (version == null) {
ver = settingsVersion;
}
const base = baseConfig[ver] || baseConfig.default;
const override = overrideConfig[ver] || {};
const defaults = deepmerge(base, override, {arrayMerge: deepMergeArray});
return Object.assign(defaults, {
spellCheckerLocale: spellCheckerLocale || defaults.spellCheckerLocale || 'en-US'
function loadDefault(spellCheckerLocale) {
const config = JSON.parse(JSON.stringify(defaultPreferences));
return Object.assign({}, config, {
spellCheckerLocale: spellCheckerLocale || defaultPreferences.pellCheckerLocale || 'en-US'
});
}
function upgradeV0toV1(configV0) {
var config = loadDefault(1);
config.teams.push({
name: 'Primary team',
url: configV0.url
});
return config;
function hasBuildConfigDefaultTeams(config) {
return config.defaultTeams.length > 0;
}
function upgrade(config, newAppVersion) {
var configVersion = config.version ? config.version : 0;
if (newAppVersion) {
config.lastMattermostVersion = newAppVersion;
}
switch (configVersion) {
case 0:
return upgrade(upgradeV0toV1(config));
default:
return config;
}
function upgrade(config) {
return upgradePreferences(config);
}
module.exports = {
version: settingsVersion,
version: defaultPreferences.version,
upgrade,
readFileSync(configFile) {
var config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
// need to be able to compare 1 to '1'
if (config.version == settingsVersion) { // eslint-disable-line
var defaultConfig = this.loadDefault();
config = merge(defaultConfig, config);
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
if (config.version === defaultPreferences.version) {
const defaultConfig = loadDefault();
return merge(defaultConfig, config);
}
return config;
},
writeFile(configFile, config, callback) {
// need to be able to compare 1 to '1'
if (config.version != settingsVersion) { // eslint-disable-line
throw new Error('version ' + config.version + ' is not equal to ' + settingsVersion);
if (config.version !== defaultPreferences.version) {
throw new Error('version ' + config.version + ' is not equal to ' + defaultPreferences.version);
}
var data = JSON.stringify(config, null, ' ');
fs.writeFile(configFile, data, 'utf8', callback);
},
writeFileSync(configFile, config) {
// need to be able to compare 1 to '1'
if (config.version != settingsVersion) { // eslint-disable-line
throw new Error('version ' + config.version + ' is not equal to ' + settingsVersion);
if (config.version !== defaultPreferences.version) {
throw new Error('version ' + config.version + ' is not equal to ' + defaultPreferences.version);
}
const dir = path.dirname(configFile);
@ -96,5 +62,16 @@ module.exports = {
fs.writeFileSync(configFile, data, 'utf8');
},
loadDefault
loadDefault,
mergeDefaultTeams(teams) {
const newTeams = [];
if (hasBuildConfigDefaultTeams(buildConfig)) {
newTeams.push(...JSON.parse(JSON.stringify(buildConfig.defaultTeams)));
}
if (buildConfig.enableServerManagement) {
newTeams.push(...JSON.parse(JSON.stringify(teams)));
}
return newTeams;
}
};

View file

@ -69,7 +69,7 @@ try {
config = settings.readFileSync(configFile);
if (config.version !== settings.version || wasUpdated()) {
clearAppCache();
config = settings.upgrade(config, app.getVersion());
config = settings.upgrade(config);
settings.writeFileSync(configFile, config);
}
} catch (e) {

View file

@ -1,6 +1,8 @@
'use strict';
const electron = require('electron');
const settings = require('../../common/settings');
const buildConfig = require('../../common/config/buildConfig');
const Menu = electron.Menu;
@ -38,7 +40,7 @@ function createTemplate(mainWindow, config, isDev) {
}
}];
if (config.enableServerManagement === true || config.teams.length === 0) {
if (buildConfig.enableServerManagement === true) {
platformAppMenu.push({
label: 'Sign in to Another Server',
click() {
@ -173,13 +175,14 @@ function createTemplate(mainWindow, config, isDev) {
}]
});
const teams = settings.mergeDefaultTeams(config.teams);
const windowMenu = {
label: '&Window',
submenu: [{
role: 'minimize'
}, {
role: 'close'
}, separatorItem, ...config.teams.slice(0, 9).map((team, i) => {
}, separatorItem, ...teams.slice(0, 9).map((team, i) => {
return {
label: team.name,
accelerator: `CmdOrCtrl+${i + 1}`,
@ -194,23 +197,23 @@ function createTemplate(mainWindow, config, isDev) {
click() {
mainWindow.webContents.send('select-next-tab');
},
enabled: (config.teams.length > 1)
enabled: (teams.length > 1)
}, {
label: 'Select Previous Server',
accelerator: 'Ctrl+Shift+Tab',
click() {
mainWindow.webContents.send('select-previous-tab');
},
enabled: (config.teams.length > 1)
enabled: (teams.length > 1)
}]
};
template.push(windowMenu);
var submenu = [];
if (config.helpLink) {
if (buildConfig.helpLink) {
submenu.push({
label: 'Learn More...',
click() {
electron.shell.openExternal(config.helpLink);
electron.shell.openExternal(buildConfig.helpLink);
}
});
submenu.push(separatorItem);

View file

@ -4,11 +4,13 @@ const {
app,
Menu
} = require('electron');
const settings = require('../../common/settings');
function createTemplate(mainWindow, config, isDev) {
const settingsURL = isDev ? 'http://localhost:8080/browser/settings.html' : `file://${app.getAppPath()}/browser/settings.html`;
const teams = settings.mergeDefaultTeams(config.teams);
var template = [
...config.teams.slice(0, 9).map((team, i) => {
...teams.slice(0, 9).map((team, i) => {
return {
label: team.name,
click: () => {

View file

@ -12,7 +12,6 @@
"auto-launch": "^5.0.1",
"bootstrap": "^3.3.7",
"create-react-class": "^15.6.2",
"deepmerge": "^1.5.2",
"electron-context-menu": "^0.9.0",
"electron-devtools-installer": "^2.2.1",
"electron-is-dev": "^0.3.0",

View file

@ -141,10 +141,6 @@ decamelize@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
deepmerge@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
dom-helpers@^3.2.0, dom-helpers@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"

View file

@ -39,6 +39,12 @@ module.exports = {
});
},
createTestUserDataDir() {
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir);
}
},
getSpectronApp() {
return new Application({
path: electronBinaryPath,

View file

@ -8,17 +8,15 @@ describe('application', function desc() {
this.timeout(30000);
beforeEach(() => {
env.createTestUserDataDir();
env.cleanTestConfig();
this.app = env.getSpectronApp();
});
afterEach(() => {
if (this.app && this.app.isRunning()) {
return this.app.stop().then(() => {
env.cleanTestConfig();
});
return this.app.stop();
}
env.cleanTestConfig();
return true;
});

View file

@ -1,15 +1,9 @@
const settings = require('../../src/common/settings');
const deepmerge = require('deepmerge');
const buildConfig = require('../../src/common/config/buildConfig');
const defaultPreferences = require('../../src/common/config/defaultPreferences');
const pastDefaultPreferences = require('../../src/common/config/pastDefaultPreferences');
describe('common/settings.js', () => {
before(() => {
process.env.TEST = 1;
});
after(() => {
delete process.env.TEST;
});
it('should upgrade v0 config file', () => {
const v0Config = {
url: 'https://example.com/team'
@ -20,13 +14,34 @@ describe('common/settings.js', () => {
config.version.should.equal(settings.version);
});
it('should loadDefault config for version 1', () => {
const baseConfig = require('../../src/common/config/base.json');
const overrideConfig = require('../../src/common/config/override.json');
const expectedDefaults = deepmerge(
baseConfig[1], overrideConfig[1] || {}, {clone: true, arrayMerge: settings.deepMergeArray}
);
const defaultConfig = settings.loadDefault();
defaultConfig.should.eql(expectedDefaults);
it('should merge teams with buildConfig.defaultTeams', () => {
const teams = [
{
name: 'test',
url: 'https://example.com'
}
];
const mergedTeams = settings.mergeDefaultTeams(teams);
mergedTeams.should.deep.equal([
{
name: 'test',
url: 'https://example.com'
},
...buildConfig.defaultTeams
]);
});
});
describe('common/config/', () => {
it('pastDefaultPreferences should have each past version of defaultPreferences', () => {
for (let version = 0; version <= defaultPreferences.version; version++) {
pastDefaultPreferences[`${version}`].should.exist; // eslint-disable-line no-unused-expressions
}
});
it('defaultPreferences equal to one of pastDefaultPreferences', () => {
const pastPreferences = pastDefaultPreferences[`${defaultPreferences.version}`];
pastPreferences.should.deep.equal(defaultPreferences);
});
});

View file

@ -1352,10 +1352,6 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
deepmerge@~1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.3.2.tgz#1663691629d4dbfe364fa12a2a4f0aa86aa3a050"