commit
b27c2dda36
4
.eslintignore
Normal file
4
.eslintignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
release/
|
||||||
|
src/node_modules/
|
240
.eslintrc-platform-v3.4.0.json
Normal file
240
.eslintrc-platform-v3.4.0.json
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
{
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true,
|
||||||
|
"impliedStrict": true,
|
||||||
|
"modules": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parser": "babel-eslint",
|
||||||
|
"plugins": [
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"jquery": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"jest": true,
|
||||||
|
"describe": true,
|
||||||
|
"it": true,
|
||||||
|
"expect": true,
|
||||||
|
"before": true,
|
||||||
|
"after": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"array-bracket-spacing": [2, "never"],
|
||||||
|
"array-callback-return": 2,
|
||||||
|
"arrow-body-style": 0,
|
||||||
|
"arrow-parens": [2, "always"],
|
||||||
|
"arrow-spacing": [2, { "before": true, "after": true }],
|
||||||
|
"block-scoped-var": 2,
|
||||||
|
"brace-style": [2, "1tbs", { "allowSingleLine": false }],
|
||||||
|
"camelcase": [2, {"properties": "never"}],
|
||||||
|
"comma-dangle": [2, "never"],
|
||||||
|
"comma-spacing": [2, {"before": false, "after": true}],
|
||||||
|
"comma-style": [2, "last"],
|
||||||
|
"complexity": [1, 10],
|
||||||
|
"computed-property-spacing": [2, "never"],
|
||||||
|
"consistent-return": 2,
|
||||||
|
"consistent-this": [2, "self"],
|
||||||
|
"constructor-super": 2,
|
||||||
|
"curly": [2, "all"],
|
||||||
|
"dot-location": [2, "object"],
|
||||||
|
"dot-notation": 2,
|
||||||
|
"eqeqeq": [2, "smart"],
|
||||||
|
"func-names": 2,
|
||||||
|
"func-style": [2, "declaration"],
|
||||||
|
"generator-star-spacing": [2, {"before": false, "after": true}],
|
||||||
|
"global-require": 2,
|
||||||
|
"guard-for-in": 2,
|
||||||
|
"id-blacklist": 0,
|
||||||
|
"indent": [2, 4, {"SwitchCase": 0}],
|
||||||
|
"jsx-quotes": [2, "prefer-single"],
|
||||||
|
"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
|
||||||
|
"keyword-spacing": [2, {"before": true, "after": true, "overrides": {}}],
|
||||||
|
"linebreak-style": 2,
|
||||||
|
"lines-around-comment": [2, { "beforeBlockComment": true, "beforeLineComment": true, "allowBlockStart": true, "allowBlockEnd": true }],
|
||||||
|
"max-lines": [1, {"max": 450, "skipBlankLines": true, "skipComments": false}],
|
||||||
|
"max-nested-callbacks": [1, {"max":1}],
|
||||||
|
"max-nested-callbacks": [2, {"max":2}],
|
||||||
|
"max-statements-per-line": [2, {"max": 1}],
|
||||||
|
"new-cap": 2,
|
||||||
|
"new-parens": 2,
|
||||||
|
"newline-before-return": 0,
|
||||||
|
"newline-per-chained-call": 0,
|
||||||
|
"no-alert": 2,
|
||||||
|
"no-array-constructor": 2,
|
||||||
|
"no-caller": 2,
|
||||||
|
"no-case-declarations": 2,
|
||||||
|
"no-class-assign": 2,
|
||||||
|
"no-cond-assign": [2, "except-parens"],
|
||||||
|
"no-confusing-arrow": 2,
|
||||||
|
"no-console": 2,
|
||||||
|
"no-const-assign": 2,
|
||||||
|
"no-constant-condition": 2,
|
||||||
|
"no-debugger": 2,
|
||||||
|
"no-div-regex": 2,
|
||||||
|
"no-dupe-args": 2,
|
||||||
|
"no-dupe-class-members": 2,
|
||||||
|
"no-dupe-keys": 2,
|
||||||
|
"no-duplicate-case": 2,
|
||||||
|
"no-duplicate-imports": [2, {"includeExports": true}],
|
||||||
|
"no-else-return": 2,
|
||||||
|
"no-empty": 2,
|
||||||
|
"no-empty-function": 2,
|
||||||
|
"no-empty-pattern": 2,
|
||||||
|
"no-eval": 2,
|
||||||
|
"no-ex-assign": 2,
|
||||||
|
"no-extend-native": 2,
|
||||||
|
"no-extra-bind": 2,
|
||||||
|
"no-extra-label": 2,
|
||||||
|
"no-extra-parens": 0,
|
||||||
|
"no-extra-semi": 2,
|
||||||
|
"no-fallthrough": 2,
|
||||||
|
"no-floating-decimal": 2,
|
||||||
|
"no-func-assign": 2,
|
||||||
|
"no-implicit-globals": 0,
|
||||||
|
"no-implied-eval": 2,
|
||||||
|
"no-inner-declarations": 0,
|
||||||
|
"no-invalid-regexp": 2,
|
||||||
|
"no-irregular-whitespace": 2,
|
||||||
|
"no-iterator": 2,
|
||||||
|
"no-labels": 2,
|
||||||
|
"no-lone-blocks": 2,
|
||||||
|
"no-lonely-if": 2,
|
||||||
|
"no-loop-func": 2,
|
||||||
|
"no-magic-numbers": [1, { "ignore": [-1, 0, 1, 2], "enforceConst": true, "detectObjects": true } ],
|
||||||
|
"no-mixed-operators": [2, {"allowSamePrecedence": false}],
|
||||||
|
"no-mixed-spaces-and-tabs": 2,
|
||||||
|
"no-multi-spaces": [2, { "exceptions": { "Property": false } }],
|
||||||
|
"no-multi-str": 0,
|
||||||
|
"no-multiple-empty-lines": [2, {"max": 1}],
|
||||||
|
"no-native-reassign": 2,
|
||||||
|
"no-negated-condition": 2,
|
||||||
|
"no-nested-ternary": 2,
|
||||||
|
"no-new": 2,
|
||||||
|
"no-new-func": 2,
|
||||||
|
"no-new-object": 2,
|
||||||
|
"no-new-symbol": 2,
|
||||||
|
"no-new-wrappers": 2,
|
||||||
|
"no-octal-escape": 2,
|
||||||
|
"no-param-reassign": 2,
|
||||||
|
"no-process-env": 2,
|
||||||
|
"no-process-exit": 2,
|
||||||
|
"no-proto": 2,
|
||||||
|
"no-redeclare": 2,
|
||||||
|
"no-return-assign": [2, "always"],
|
||||||
|
"no-script-url": 2,
|
||||||
|
"no-self-assign": 2,
|
||||||
|
"no-self-compare": 2,
|
||||||
|
"no-sequences": 2,
|
||||||
|
"no-shadow": [2, {"hoist": "functions"}],
|
||||||
|
"no-shadow-restricted-names": 2,
|
||||||
|
"no-spaced-func": 2,
|
||||||
|
"no-ternary": 0,
|
||||||
|
"no-this-before-super": 2,
|
||||||
|
"no-throw-literal": 2,
|
||||||
|
"no-trailing-spaces": [2, { "skipBlankLines": false }],
|
||||||
|
"no-undef-init": 2,
|
||||||
|
"no-undefined": 2,
|
||||||
|
"no-underscore-dangle": 2,
|
||||||
|
"no-unexpected-multiline": 2,
|
||||||
|
"no-unmodified-loop-condition": 2,
|
||||||
|
"no-unneeded-ternary": [2, {"defaultAssignment": false}],
|
||||||
|
"no-unreachable": 2,
|
||||||
|
"no-unsafe-finally": 2,
|
||||||
|
"no-unused-expressions": 2,
|
||||||
|
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
|
||||||
|
"no-use-before-define": [2, "nofunc"],
|
||||||
|
"no-useless-computed-key": 2,
|
||||||
|
"no-useless-concat": 2,
|
||||||
|
"no-useless-constructor": 2,
|
||||||
|
"no-useless-escape": 2,
|
||||||
|
"no-useless-rename": 2,
|
||||||
|
"no-var": 0,
|
||||||
|
"no-void": 2,
|
||||||
|
"no-warning-comments": 1,
|
||||||
|
"no-whitespace-before-property": 2,
|
||||||
|
"no-with": 2,
|
||||||
|
"object-curly-newline": 0,
|
||||||
|
"object-curly-spacing": [2, "never"],
|
||||||
|
"object-property-newline": [2, {"allowMultiplePropertiesPerLine": true}],
|
||||||
|
"object-shorthand": [2, "always"],
|
||||||
|
"one-var": [2, "never"],
|
||||||
|
"one-var-declaration-per-line": 0,
|
||||||
|
"operator-linebreak": [2, "after"],
|
||||||
|
"padded-blocks": [2, "never"],
|
||||||
|
"prefer-arrow-callback": 2,
|
||||||
|
"prefer-const": 2,
|
||||||
|
"prefer-reflect": 2,
|
||||||
|
"prefer-rest-params": 2,
|
||||||
|
"prefer-spread": 2,
|
||||||
|
"prefer-template": 0,
|
||||||
|
"quote-props": [2, "as-needed"],
|
||||||
|
"quotes": [2, "single", "avoid-escape"],
|
||||||
|
"radix": 2,
|
||||||
|
"react/display-name": [2, { "ignoreTranspilerName": false }],
|
||||||
|
"react/jsx-boolean-value": [2, "always"],
|
||||||
|
"react/jsx-closing-bracket-location": [2, { "location": "tag-aligned" }],
|
||||||
|
"react/jsx-curly-spacing": [2, "never"],
|
||||||
|
"react/jsx-equals-spacing": [2, "never"],
|
||||||
|
"react/jsx-first-prop-new-line": [2, "multiline"],
|
||||||
|
"react/jsx-handler-names": 0,
|
||||||
|
"react/jsx-indent": [2, 4],
|
||||||
|
"react/jsx-indent-props": [2, 4],
|
||||||
|
"react/jsx-key": 2,
|
||||||
|
"react/jsx-max-props-per-line": [2, { "maximum": 1 }],
|
||||||
|
"react/jsx-no-bind": 0,
|
||||||
|
"react/jsx-no-duplicate-props": [2, { "ignoreCase": false }],
|
||||||
|
"react/jsx-no-literals": 2,
|
||||||
|
"react/jsx-no-target-blank": 2,
|
||||||
|
"react/jsx-no-undef": 2,
|
||||||
|
"react/jsx-pascal-case": 2,
|
||||||
|
"react/jsx-filename-extension": 2,
|
||||||
|
"react/jsx-space-before-closing": [2, "never"],
|
||||||
|
"react/jsx-uses-react": 2,
|
||||||
|
"react/jsx-uses-vars": 2,
|
||||||
|
"react/no-comment-textnodes": 2,
|
||||||
|
"react/no-danger": 0,
|
||||||
|
"react/no-deprecated": 2,
|
||||||
|
"react/no-did-mount-set-state": 2,
|
||||||
|
"react/no-did-update-set-state": 2,
|
||||||
|
"react/no-direct-mutation-state": 2,
|
||||||
|
"react/no-is-mounted": 2,
|
||||||
|
"react/no-multi-comp": [2, { "ignoreStateless": true }],
|
||||||
|
"react/no-render-return-value": 2,
|
||||||
|
"react/no-set-state": 0,
|
||||||
|
"react/no-string-refs": 0,
|
||||||
|
"react/no-unknown-property": 2,
|
||||||
|
"react/prefer-es6-class": 2,
|
||||||
|
"react/prefer-stateless-function": 0,
|
||||||
|
"react/prop-types": 2,
|
||||||
|
"react/require-optimization": 1,
|
||||||
|
"react/require-render-return": 2,
|
||||||
|
"react/self-closing-comp": 2,
|
||||||
|
"react/sort-comp": 0,
|
||||||
|
"react/wrap-multilines": 2,
|
||||||
|
"require-yield": 2,
|
||||||
|
"rest-spread-spacing": [2, "never"],
|
||||||
|
"semi": [2, "always"],
|
||||||
|
"semi-spacing": [2, {"before": false, "after": true}],
|
||||||
|
"sort-imports": 0,
|
||||||
|
"space-before-blocks": [2, "always"],
|
||||||
|
"space-before-function-paren": [2, "never"],
|
||||||
|
"space-in-parens": [2, "never"],
|
||||||
|
"space-infix-ops": 2,
|
||||||
|
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||||
|
"template-curly-spacing": [2, "never"],
|
||||||
|
"valid-typeof": 2,
|
||||||
|
"vars-on-top": 0,
|
||||||
|
"wrap-iife": [2, "outside"],
|
||||||
|
"wrap-regex": 2,
|
||||||
|
"yoda": [2, "never", {"exceptRange": false, "onlyEquality": false}]
|
||||||
|
}
|
||||||
|
}
|
18
.eslintrc.json
Normal file
18
.eslintrc.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"extends": "./.eslintrc-platform-v3.4.0.json",
|
||||||
|
"rules": {
|
||||||
|
"global-require": 1,
|
||||||
|
"indent": [2, 2, {"SwitchCase": 0}],
|
||||||
|
"no-console": 0,
|
||||||
|
"no-eval": 1,
|
||||||
|
"no-process-env": 0,
|
||||||
|
"no-native-reassign": ["error", {"exceptions": ["Notification"]}],
|
||||||
|
"no-underscore-dangle": 1,
|
||||||
|
"react/jsx-boolean-value": [1, "always"],
|
||||||
|
"react/jsx-indent": [2, 2],
|
||||||
|
"react/jsx-indent-props": [2, 2],
|
||||||
|
"react/no-multi-comp": 1,
|
||||||
|
"react/prefer-es6-class": 1,
|
||||||
|
"react/prop-types": 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ Please see http://www.mattermost.org/feature-requests/ .
|
||||||
## Pull request
|
## Pull request
|
||||||
Pull requests are welcome. Thank you for your great work!
|
Pull requests are welcome. Thank you for your great work!
|
||||||
|
|
||||||
1. When you edit the code, please run `npm run prettify` to format your code before `git commit`.
|
1. When you edit the code, please confirm `npm test` successfully finishes.
|
||||||
2. Please update `CHANGELOG.md` and `docs/*.md` if it's necessary.
|
2. Please update `CHANGELOG.md` and `docs/*.md` if it's necessary.
|
||||||
3. In the description of your pull request, please include:
|
3. In the description of your pull request, please include:
|
||||||
* Operating System version on which you tested
|
* Operating System version on which you tested
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Before submitting, please confirm you've
|
Before submitting, please confirm you've
|
||||||
- [ ] read and understood our [Contributing Guidelines](https://github.com/mattermost/desktop/blob/master/CONTRIBUTING.md)
|
- [ ] read and understood our [Contributing Guidelines](https://github.com/mattermost/desktop/blob/master/CONTRIBUTING.md)
|
||||||
- [ ] completed [Mattermost Contributor Agreement](http://www.mattermost.org/mattermost-contributor-agreement/)
|
- [ ] completed [Mattermost Contributor Agreement](http://www.mattermost.org/mattermost-contributor-agreement/)
|
||||||
- [ ] executed `npm run prettify` for proper code formatting
|
- [ ] executed `npm run prettify` for proper code formatting
|
||||||
|
|
|
@ -62,7 +62,7 @@ $ npm run test:app
|
||||||
Test coding style:
|
Test coding style:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ npm run test:code
|
$ npm run lint:js
|
||||||
```
|
```
|
||||||
|
|
||||||
### Helper commmands
|
### Helper commmands
|
||||||
|
|
94
gulpfile.js
94
gulpfile.js
|
@ -3,9 +3,6 @@
|
||||||
var gulp = require('gulp');
|
var gulp = require('gulp');
|
||||||
var prettify = require('gulp-jsbeautifier');
|
var prettify = require('gulp-jsbeautifier');
|
||||||
var diff = require('gulp-diff');
|
var diff = require('gulp-diff');
|
||||||
var esformatter = require('gulp-esformatter');
|
|
||||||
var esformatter_origin = require('esformatter');
|
|
||||||
var through = require('through2');
|
|
||||||
var electron = require('electron-connect').server.create({
|
var electron = require('electron-connect').server.create({
|
||||||
path: './dist'
|
path: './dist'
|
||||||
});
|
});
|
||||||
|
@ -18,10 +15,10 @@ const distPackageAuthor = 'Mattermost, Inc.';
|
||||||
|
|
||||||
var sources = ['**/*.js', '**/*.json', '**/*.css', '**/*.html', '!**/node_modules/**', '!dist/**', '!release/**', '!**/test_config.json'];
|
var sources = ['**/*.js', '**/*.json', '**/*.css', '**/*.html', '!**/node_modules/**', '!dist/**', '!release/**', '!**/test_config.json'];
|
||||||
|
|
||||||
gulp.task('prettify', ['prettify:sources', 'prettify:jsx']);
|
gulp.task('prettify', ['prettify:sources']);
|
||||||
gulp.task('prettify:verify', ['prettify:sources:verify', 'prettify:jsx:verify']);
|
gulp.task('prettify:verify', ['prettify:sources:verify']);
|
||||||
|
|
||||||
var prettify_options = {
|
var prettifyOptions = {
|
||||||
html: {
|
html: {
|
||||||
indent_size: 2,
|
indent_size: 2,
|
||||||
end_with_newline: true
|
end_with_newline: true
|
||||||
|
@ -32,62 +29,35 @@ var prettify_options = {
|
||||||
},
|
},
|
||||||
js: {
|
js: {
|
||||||
indent_size: 2,
|
indent_size: 2,
|
||||||
brace_style: "end-expand",
|
brace_style: 'end-expand',
|
||||||
end_with_newline: true
|
end_with_newline: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
gulp.task('prettify:sources', ['sync-meta'], function() {
|
gulp.task('prettify:sources', ['sync-meta'], () => {
|
||||||
return gulp.src(sources)
|
return gulp.src(sources).
|
||||||
.pipe(prettify(prettify_options))
|
pipe(prettify(prettifyOptions)).
|
||||||
.pipe(prettify.reporter())
|
pipe(prettify.reporter()).
|
||||||
.pipe(diff())
|
pipe(diff()).
|
||||||
.pipe(diff.reporter({
|
pipe(diff.reporter({
|
||||||
quiet: true,
|
quiet: true,
|
||||||
fail: false
|
fail: false
|
||||||
}))
|
})).
|
||||||
.pipe(gulp.dest('.'));
|
pipe(gulp.dest('.'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('prettify:sources:verify', function() {
|
gulp.task('prettify:sources:verify', () => {
|
||||||
return gulp.src(sources)
|
return gulp.src(sources).
|
||||||
.pipe(prettify(prettify_options))
|
pipe(prettify(prettifyOptions)).
|
||||||
.pipe(prettify.reporter())
|
pipe(prettify.reporter()).
|
||||||
.pipe(diff())
|
pipe(diff()).
|
||||||
.pipe(diff.reporter({
|
pipe(diff.reporter({
|
||||||
quiet: true,
|
quiet: true,
|
||||||
fail: true
|
fail: true
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gulp.task('build', ['sync-meta', 'copy'], (cb) => {
|
||||||
var esformatter_jsx_options = {
|
|
||||||
indent: {
|
|
||||||
value: ' '
|
|
||||||
},
|
|
||||||
plugins: ['esformatter-jsx']
|
|
||||||
};
|
|
||||||
|
|
||||||
gulp.task('prettify:jsx', function() {
|
|
||||||
return gulp.src('src/browser/**/*.jsx')
|
|
||||||
.pipe(esformatter(esformatter_jsx_options))
|
|
||||||
.pipe(gulp.dest('src/browser'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('prettify:jsx:verify', function() {
|
|
||||||
return gulp.src('src/browser/**/*.jsx')
|
|
||||||
.pipe(through.obj(function(file, enc, cb) {
|
|
||||||
var result = esformatter_origin.diff.unified(file.contents.toString(), esformatter_origin.rc(file.path, esformatter_jsx_options));
|
|
||||||
if (result !== "") {
|
|
||||||
console.log('Error: ' + file.path + ' must be formatted');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
gulp.task('build', ['sync-meta', 'copy'], function(cb) {
|
|
||||||
const appPackageJson = require('./src/package.json');
|
const appPackageJson = require('./src/package.json');
|
||||||
const distPackageJson = Object.assign({}, appPackageJson, {
|
const distPackageJson = Object.assign({}, appPackageJson, {
|
||||||
author: {
|
author: {
|
||||||
|
@ -100,23 +70,23 @@ gulp.task('build', ['sync-meta', 'copy'], function(cb) {
|
||||||
|
|
||||||
gulp.task('copy', ['copy:resources', 'copy:html/css', 'copy:modules']);
|
gulp.task('copy', ['copy:resources', 'copy:html/css', 'copy:modules']);
|
||||||
|
|
||||||
gulp.task('copy:resources', function() {
|
gulp.task('copy:resources', () => {
|
||||||
return gulp.src('src/resources/**')
|
return gulp.src('src/resources/**').
|
||||||
.pipe(gulp.dest('dist/resources'));
|
pipe(gulp.dest('dist/resources'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('copy:html/css', function() {
|
gulp.task('copy:html/css', () => {
|
||||||
return gulp.src(['src/browser/**/*.html', 'src/browser/**/*.css'])
|
return gulp.src(['src/browser/**/*.html', 'src/browser/**/*.css']).
|
||||||
.pipe(gulp.dest('dist/browser'));
|
pipe(gulp.dest('dist/browser'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('copy:modules', function() {
|
gulp.task('copy:modules', () => {
|
||||||
return gulp.src(['src/node_modules/bootstrap/dist/**'])
|
return gulp.src(['src/node_modules/bootstrap/dist/**']).
|
||||||
.pipe(gulp.dest('dist/browser/modules/bootstrap'));
|
pipe(gulp.dest('dist/browser/modules/bootstrap'));
|
||||||
});
|
});
|
||||||
|
|
||||||
function spawnWebpack(config, cb) {
|
function spawnWebpack(config, cb) {
|
||||||
const ext = process.platform === 'win32' ? '.cmd' : ''
|
const ext = process.platform === 'win32' ? '.cmd' : '';
|
||||||
spawn(path.resolve(`./node_modules/.bin/webpack${ext}`), ['--config', config], {
|
spawn(path.resolve(`./node_modules/.bin/webpack${ext}`), ['--config', config], {
|
||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
}).on('exit', (code) => {
|
}).on('exit', (code) => {
|
||||||
|
@ -132,7 +102,7 @@ gulp.task('webpack:renderer', (cb) => {
|
||||||
spawnWebpack('webpack.config.renderer.js', cb);
|
spawnWebpack('webpack.config.renderer.js', cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('watch', ['build', 'webpack:main', 'webpack:renderer'], function() {
|
gulp.task('watch', ['build', 'webpack:main', 'webpack:renderer'], () => {
|
||||||
var options = ['--livereload'];
|
var options = ['--livereload'];
|
||||||
electron.start(options);
|
electron.start(options);
|
||||||
|
|
||||||
|
@ -140,13 +110,13 @@ gulp.task('watch', ['build', 'webpack:main', 'webpack:renderer'], function() {
|
||||||
gulp.watch(['src/browser/**/*.js', 'src/browser/**/*.jsx'], ['webpack:renderer']);
|
gulp.watch(['src/browser/**/*.js', 'src/browser/**/*.jsx'], ['webpack:renderer']);
|
||||||
gulp.watch(['src/browser/**/*.css', 'src/browser/**/*.html', 'src/resources/**/*.png'], ['copy']);
|
gulp.watch(['src/browser/**/*.css', 'src/browser/**/*.html', 'src/resources/**/*.png'], ['copy']);
|
||||||
|
|
||||||
gulp.watch(['dist/main.js', 'dist/resources/**'], function() {
|
gulp.watch(['dist/main.js', 'dist/resources/**'], () => {
|
||||||
electron.restart(options);
|
electron.restart(options);
|
||||||
});
|
});
|
||||||
gulp.watch(['dist/browser/*.js'], electron.reload);
|
gulp.watch(['dist/browser/*.js'], electron.reload);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('sync-meta', function() {
|
gulp.task('sync-meta', () => {
|
||||||
var appPackageJson = require('./src/package.json');
|
var appPackageJson = require('./src/package.json');
|
||||||
var packageJson = require('./package.json');
|
var packageJson = require('./package.json');
|
||||||
appPackageJson.name = packageJson.name;
|
appPackageJson.name = packageJson.name;
|
||||||
|
|
11
package.json
11
package.json
|
@ -25,17 +25,19 @@
|
||||||
"start": "electron dist",
|
"start": "electron dist",
|
||||||
"watch": "gulp watch",
|
"watch": "gulp watch",
|
||||||
"serve": "gulp watch",
|
"serve": "gulp watch",
|
||||||
"test": "npm run build && mocha --reporter mocha-circleci-reporter --recursive test/specs && gulp prettify:verify",
|
"test": "npm run build && npm run test:app && npm run lint:js",
|
||||||
|
"test:app": "mocha --reporter mocha-circleci-reporter --recursive test/specs",
|
||||||
"package:all": "npm run package:windows && npm run package:mac && npm run package:linux",
|
"package:all": "npm run package:windows && npm run package:mac && npm run package:linux",
|
||||||
"package:windows": "build --win --x64 --ia32 --em.name=mattermost && npm run manipulate-windows-zip",
|
"package:windows": "build --win --x64 --ia32 --em.name=mattermost && npm run manipulate-windows-zip",
|
||||||
"package:mac": "build --mac --x64 --ia32",
|
"package:mac": "build --mac --x64 --ia32",
|
||||||
"package:linux": "build --linux --x64 --ia32 --em.name=mattermost-desktop",
|
"package:linux": "build --linux --x64 --ia32 --em.name=mattermost-desktop",
|
||||||
"manipulate-windows-zip": "node scripts/manipulate_windows_zip.js",
|
"manipulate-windows-zip": "node scripts/manipulate_windows_zip.js",
|
||||||
"prettify": "gulp prettify"
|
"lint:js": "eslint ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"7zip-bin": "^2.0.1",
|
"7zip-bin": "^2.0.1",
|
||||||
"babel-core": "^6.7.5",
|
"babel-core": "^6.7.5",
|
||||||
|
"babel-eslint": "^6.1.2",
|
||||||
"babel-loader": "^6.2.4",
|
"babel-loader": "^6.2.4",
|
||||||
"babel-preset-react": "^6.5.0",
|
"babel-preset-react": "^6.5.0",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
|
@ -45,8 +47,8 @@
|
||||||
"electron-builder": "^7.11.2",
|
"electron-builder": "^7.11.2",
|
||||||
"electron-connect": "~0.6.0",
|
"electron-connect": "~0.6.0",
|
||||||
"electron-prebuilt": "1.4.2",
|
"electron-prebuilt": "1.4.2",
|
||||||
"esformatter": "^0.9.6",
|
"eslint": "^3.4.0",
|
||||||
"esformatter-jsx": "^7.0.1",
|
"eslint-plugin-react": "^6.2.0",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-diff": "^1.0.0",
|
"gulp-diff": "^1.0.0",
|
||||||
"gulp-esformatter": "^6.0.0",
|
"gulp-esformatter": "^6.0.0",
|
||||||
|
@ -55,7 +57,6 @@
|
||||||
"mocha": "^3.0.2",
|
"mocha": "^3.0.2",
|
||||||
"mocha-circleci-reporter": "0.0.2",
|
"mocha-circleci-reporter": "0.0.2",
|
||||||
"spectron": "~3.4.0",
|
"spectron": "~3.4.0",
|
||||||
"through2": "^2.0.1",
|
|
||||||
"webpack": "^1.13.1",
|
"webpack": "^1.13.1",
|
||||||
"webpack-merge": "^0.14.1"
|
"webpack-merge": "^0.14.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,7 @@ const Col = ReactBootstrap.Col;
|
||||||
const Button = ReactBootstrap.Button;
|
const Button = ReactBootstrap.Button;
|
||||||
|
|
||||||
const LoginModal = React.createClass({
|
const LoginModal = React.createClass({
|
||||||
handleSubmit: function(event) {
|
handleSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const usernameNode = ReactDOM.findDOMNode(this.refs.username);
|
const usernameNode = ReactDOM.findDOMNode(this.refs.username);
|
||||||
const passwordNode = ReactDOM.findDOMNode(this.refs.password);
|
const passwordNode = ReactDOM.findDOMNode(this.refs.password);
|
||||||
|
@ -19,7 +19,7 @@ const LoginModal = React.createClass({
|
||||||
usernameNode.value = '';
|
usernameNode.value = '';
|
||||||
passwordNode.value = '';
|
passwordNode.value = '';
|
||||||
},
|
},
|
||||||
render: function() {
|
render() {
|
||||||
var theServer = '';
|
var theServer = '';
|
||||||
if (!this.props.show) {
|
if (!this.props.show) {
|
||||||
theServer = '';
|
theServer = '';
|
||||||
|
@ -30,34 +30,54 @@ const LoginModal = React.createClass({
|
||||||
}
|
}
|
||||||
const message = `${theServer} requires a username and password.`;
|
const message = `${theServer} requires a username and password.`;
|
||||||
return (
|
return (
|
||||||
<Modal show={ this.props.show }>
|
<Modal show={this.props.show}>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
<Modal.Title>Authentication Required</Modal.Title>
|
<Modal.Title>{'Authentication Required'}</Modal.Title>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<p>
|
<p>
|
||||||
{ message }
|
{ message }
|
||||||
</p>
|
</p>
|
||||||
<Form horizontal onSubmit={ this.handleSubmit }>
|
<Form
|
||||||
|
horizontal
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Col componentClass={ ControlLabel } sm={ 2 }>User Name</Col>
|
<Col
|
||||||
<Col sm={ 10 }>
|
componentClass={ControlLabel}
|
||||||
<FormControl type="text" placeholder="User Name" ref="username" />
|
sm={2}
|
||||||
|
>{'User Name'}</Col>
|
||||||
|
<Col sm={10}>
|
||||||
|
<FormControl
|
||||||
|
type='text'
|
||||||
|
placeholder='User Name'
|
||||||
|
ref='username'
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Col componentClass={ ControlLabel } sm={ 2 }>Password</Col>
|
<Col
|
||||||
<Col sm={ 10 }>
|
componentClass={ControlLabel}
|
||||||
<FormControl type="password" placeholder="Password" ref="password" />
|
sm={2}
|
||||||
|
>{'Password'}</Col>
|
||||||
|
<Col sm={10}>
|
||||||
|
<FormControl
|
||||||
|
type='password'
|
||||||
|
placeholder='Password'
|
||||||
|
ref='password'
|
||||||
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Col sm={ 12 }>
|
<Col sm={12}>
|
||||||
<div className="pull-right">
|
<div className='pull-right'>
|
||||||
<Button type="submit" bsStyle="primary">Login</Button>
|
<Button
|
||||||
{ ' ' }
|
type='submit'
|
||||||
<Button onClick={ this.props.onCancel }>Cancel</Button>
|
bsStyle='primary'
|
||||||
</div>
|
>{'Login'}</Button>
|
||||||
|
{ ' ' }
|
||||||
|
<Button onClick={this.props.onCancel}>{'Cancel'}</Button>
|
||||||
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
window.eval = global.eval = function() {
|
window.eval = global.eval = () => {
|
||||||
throw new Error("Sorry, Mattermost does not support window.eval() for security reasons.");
|
throw new Error('Sorry, Mattermost does not support window.eval() for security reasons.');
|
||||||
}
|
};
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
|
@ -13,25 +13,34 @@ const Row = ReactBootstrap.Row;
|
||||||
const Col = ReactBootstrap.Col;
|
const Col = ReactBootstrap.Col;
|
||||||
const Nav = ReactBootstrap.Nav;
|
const Nav = ReactBootstrap.Nav;
|
||||||
const NavItem = ReactBootstrap.NavItem;
|
const NavItem = ReactBootstrap.NavItem;
|
||||||
const Badge = ReactBootstrap.Badge;
|
|
||||||
const ListGroup = ReactBootstrap.ListGroup;
|
|
||||||
const ListGroupItem = ReactBootstrap.ListGroupItem;
|
|
||||||
|
|
||||||
const LoginModal = require('./components/loginModal.jsx');
|
const LoginModal = require('./components/loginModal.jsx');
|
||||||
|
|
||||||
const {remote, ipcRenderer, webFrame, shell} = require('electron');
|
const {remote, ipcRenderer, shell} = require('electron');
|
||||||
|
const electronContextMenu = require('electron-context-menu');
|
||||||
|
|
||||||
const osLocale = require('os-locale');
|
const osLocale = require('os-locale');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const settings = require('../common/settings');
|
const settings = require('../common/settings');
|
||||||
|
const badge = require('./js/badge');
|
||||||
|
|
||||||
remote.getCurrentWindow().removeAllListeners('focus');
|
remote.getCurrentWindow().removeAllListeners('focus');
|
||||||
|
|
||||||
|
var config;
|
||||||
|
try {
|
||||||
|
var configFile = remote.getGlobal('config-file');
|
||||||
|
config = settings.readFileSync(configFile);
|
||||||
|
} catch (e) {
|
||||||
|
window.location = 'settings.html';
|
||||||
|
}
|
||||||
|
if (config.teams.length === 0) {
|
||||||
|
window.location = 'settings.html';
|
||||||
|
}
|
||||||
|
|
||||||
var MainPage = React.createClass({
|
var MainPage = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
key: 0,
|
key: 0,
|
||||||
unreadCounts: new Array(this.props.teams.length),
|
unreadCounts: new Array(this.props.teams.length),
|
||||||
|
@ -41,84 +50,85 @@ var MainPage = React.createClass({
|
||||||
loginQueue: []
|
loginQueue: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
var thisObj = this;
|
var self = this;
|
||||||
ipcRenderer.on('login-request', function(event, request, authInfo) {
|
ipcRenderer.on('login-request', (event, request, authInfo) => {
|
||||||
thisObj.setState({
|
self.setState({
|
||||||
loginRequired: true
|
loginRequired: true
|
||||||
});
|
});
|
||||||
const loginQueue = thisObj.state.loginQueue;
|
const loginQueue = self.state.loginQueue;
|
||||||
loginQueue.push({
|
loginQueue.push({
|
||||||
request: request,
|
request,
|
||||||
authInfo: authInfo
|
authInfo
|
||||||
});
|
});
|
||||||
thisObj.setState({
|
self.setState({
|
||||||
loginQueue: loginQueue
|
loginQueue
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// can't switch tabs sequencially for some reason...
|
// can't switch tabs sequencially for some reason...
|
||||||
ipcRenderer.on('switch-tab', (event, key) => {
|
ipcRenderer.on('switch-tab', (event, key) => {
|
||||||
this.handleSelect(key);
|
this.handleSelect(key);
|
||||||
});
|
});
|
||||||
ipcRenderer.on('select-next-tab', (event) => {
|
ipcRenderer.on('select-next-tab', () => {
|
||||||
this.handleSelect(this.state.key + 1);
|
this.handleSelect(this.state.key + 1);
|
||||||
});
|
});
|
||||||
ipcRenderer.on('select-previous-tab', (event) => {
|
ipcRenderer.on('select-previous-tab', () => {
|
||||||
this.handleSelect(this.state.key - 1);
|
this.handleSelect(this.state.key - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
// reload the activated tab
|
// reload the activated tab
|
||||||
ipcRenderer.on('reload-tab', (event) => {
|
ipcRenderer.on('reload-tab', () => {
|
||||||
this.refs[`mattermostView${this.state.key}`].reload();
|
this.refs[`mattermostView${this.state.key}`].reload();
|
||||||
});
|
});
|
||||||
ipcRenderer.on('clear-cache-and-reload-tab', (event) => {
|
ipcRenderer.on('clear-cache-and-reload-tab', () => {
|
||||||
this.refs[`mattermostView${this.state.key}`].clearCacheAndReload();
|
this.refs[`mattermostView${this.state.key}`].clearCacheAndReload();
|
||||||
});
|
});
|
||||||
|
|
||||||
// activate search box in current tab
|
// activate search box in current tab
|
||||||
ipcRenderer.on('activate-search-box', (event) => {
|
ipcRenderer.on('activate-search-box', () => {
|
||||||
let webview = document.getElementById('mattermostView' + thisObj.state.key);
|
const webview = document.getElementById('mattermostView' + self.state.key);
|
||||||
webview.send('activate-search-box');
|
webview.send('activate-search-box');
|
||||||
});
|
});
|
||||||
|
|
||||||
// activate search box in current chunnel
|
// activate search box in current chunnel
|
||||||
ipcRenderer.on('activate-search-box-in-channel', (event) => {
|
ipcRenderer.on('activate-search-box-in-channel', () => {
|
||||||
let webview = document.getElementById('mattermostView' + thisObj.state.key);
|
const webview = document.getElementById('mattermostView' + self.state.key);
|
||||||
webview.send('activate-search-box-in-channel');
|
webview.send('activate-search-box-in-channel');
|
||||||
});
|
});
|
||||||
|
|
||||||
var focusListener = function() {
|
function focusListener() {
|
||||||
thisObj.handleOnTeamFocused(thisObj.state.key);
|
self.handleOnTeamFocused(self.state.key);
|
||||||
thisObj.refs[`mattermostView${thisObj.state.key}`].focusOnWebView();
|
self.refs[`mattermostView${self.state.key}`].focusOnWebView();
|
||||||
};
|
}
|
||||||
|
|
||||||
var currentWindow = remote.getCurrentWindow();
|
var currentWindow = remote.getCurrentWindow();
|
||||||
currentWindow.on('focus', focusListener);
|
currentWindow.on('focus', focusListener);
|
||||||
window.addEventListener('beforeunload', function() {
|
window.addEventListener('beforeunload', () => {
|
||||||
currentWindow.removeListener('focus', focusListener);
|
currentWindow.removeListener('focus', focusListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
//goBack and goForward
|
//goBack and goForward
|
||||||
ipcRenderer.on('go-back', () => {
|
ipcRenderer.on('go-back', () => {
|
||||||
const mattermost = thisObj.refs[`mattermostView${thisObj.state.key}`];
|
const mattermost = self.refs[`mattermostView${self.state.key}`];
|
||||||
if (mattermost.canGoBack()) {
|
if (mattermost.canGoBack()) {
|
||||||
mattermost.goBack();
|
mattermost.goBack();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('go-forward', () => {
|
ipcRenderer.on('go-forward', () => {
|
||||||
const mattermost = thisObj.refs[`mattermostView${thisObj.state.key}`];
|
const mattermost = self.refs[`mattermostView${self.state.key}`];
|
||||||
if (mattermost.canGoForward()) {
|
if (mattermost.canGoForward()) {
|
||||||
mattermost.goForward();
|
mattermost.goForward();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
|
if (prevState.key !== this.state.key) { // i.e. When tab has been changed
|
||||||
this.refs[`mattermostView${this.state.key}`].focusOnWebView();
|
this.refs[`mattermostView${this.state.key}`].focusOnWebView();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleSelect: function(key) {
|
handleSelect(key) {
|
||||||
const newKey = (this.props.teams.length + key) % this.props.teams.length;
|
const newKey = (this.props.teams.length + key) % this.props.teams.length;
|
||||||
this.setState({
|
this.setState({
|
||||||
key: newKey
|
key: newKey
|
||||||
|
@ -130,13 +140,14 @@ var MainPage = React.createClass({
|
||||||
title: webview.getTitle()
|
title: webview.getTitle()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleUnreadCountChange: function(index, unreadCount, mentionCount, isUnread, isMentioned) {
|
handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned) {
|
||||||
var unreadCounts = this.state.unreadCounts;
|
var unreadCounts = this.state.unreadCounts;
|
||||||
var mentionCounts = this.state.mentionCounts;
|
var mentionCounts = this.state.mentionCounts;
|
||||||
var unreadAtActive = this.state.unreadAtActive;
|
var unreadAtActive = this.state.unreadAtActive;
|
||||||
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
|
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
|
||||||
unreadCounts[index] = unreadCount;
|
unreadCounts[index] = unreadCount;
|
||||||
mentionCounts[index] = mentionCount;
|
mentionCounts[index] = mentionCount;
|
||||||
|
|
||||||
// Never turn on the unreadAtActive flag at current focused tab.
|
// Never turn on the unreadAtActive flag at current focused tab.
|
||||||
if (this.state.key !== index || !remote.getCurrentWindow().isFocused()) {
|
if (this.state.key !== index || !remote.getCurrentWindow().isFocused()) {
|
||||||
unreadAtActive[index] = unreadAtActive[index] || isUnread;
|
unreadAtActive[index] = unreadAtActive[index] || isUnread;
|
||||||
|
@ -145,49 +156,49 @@ var MainPage = React.createClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
unreadCounts: unreadCounts,
|
unreadCounts,
|
||||||
mentionCounts: mentionCounts,
|
mentionCounts,
|
||||||
unreadAtActive: unreadAtActive,
|
unreadAtActive,
|
||||||
mentionAtActiveCounts: mentionAtActiveCounts
|
mentionAtActiveCounts
|
||||||
});
|
});
|
||||||
this.handleUnreadCountTotalChange();
|
this.handleUnreadCountTotalChange();
|
||||||
},
|
},
|
||||||
markReadAtActive: function(index) {
|
markReadAtActive(index) {
|
||||||
var unreadAtActive = this.state.unreadAtActive;
|
var unreadAtActive = this.state.unreadAtActive;
|
||||||
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
|
var mentionAtActiveCounts = this.state.mentionAtActiveCounts;
|
||||||
unreadAtActive[index] = false;
|
unreadAtActive[index] = false;
|
||||||
mentionAtActiveCounts[index] = 0;
|
mentionAtActiveCounts[index] = 0;
|
||||||
this.setState({
|
this.setState({
|
||||||
unreadAtActive: unreadAtActive,
|
unreadAtActive,
|
||||||
mentionAtActiveCounts: mentionAtActiveCounts
|
mentionAtActiveCounts
|
||||||
});
|
});
|
||||||
this.handleUnreadCountTotalChange();
|
this.handleUnreadCountTotalChange();
|
||||||
},
|
},
|
||||||
handleUnreadCountTotalChange: function() {
|
handleUnreadCountTotalChange() {
|
||||||
if (this.props.onUnreadCountChange) {
|
if (this.props.onUnreadCountChange) {
|
||||||
var allUnreadCount = this.state.unreadCounts.reduce(function(prev, curr) {
|
var allUnreadCount = this.state.unreadCounts.reduce((prev, curr) => {
|
||||||
return prev + curr;
|
return prev + curr;
|
||||||
}, 0);
|
}, 0);
|
||||||
this.state.unreadAtActive.forEach(function(state) {
|
this.state.unreadAtActive.forEach((state) => {
|
||||||
if (state) {
|
if (state) {
|
||||||
allUnreadCount += 1;
|
allUnreadCount += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var allMentionCount = this.state.mentionCounts.reduce(function(prev, curr) {
|
var allMentionCount = this.state.mentionCounts.reduce((prev, curr) => {
|
||||||
return prev + curr;
|
return prev + curr;
|
||||||
}, 0);
|
}, 0);
|
||||||
this.state.mentionAtActiveCounts.forEach(function(count) {
|
this.state.mentionAtActiveCounts.forEach((count) => {
|
||||||
allMentionCount += count;
|
allMentionCount += count;
|
||||||
});
|
});
|
||||||
this.props.onUnreadCountChange(allUnreadCount, allMentionCount);
|
this.props.onUnreadCountChange(allUnreadCount, allMentionCount);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleOnTeamFocused: function(index) {
|
handleOnTeamFocused(index) {
|
||||||
// Turn off the flag to indicate whether unread message of active channel contains at current tab.
|
// Turn off the flag to indicate whether unread message of active channel contains at current tab.
|
||||||
this.markReadAtActive(index);
|
this.markReadAtActive(index);
|
||||||
},
|
},
|
||||||
|
|
||||||
visibleStyle: function(visible) {
|
visibleStyle(visible) {
|
||||||
var visibility = visible ? 'visible' : 'hidden';
|
var visibility = visible ? 'visible' : 'hidden';
|
||||||
return {
|
return {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -195,66 +206,91 @@ var MainPage = React.createClass({
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
visibility: visibility
|
visibility
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
handleLogin: function(request, username, password) {
|
handleLogin(request, username, password) {
|
||||||
ipcRenderer.send('login-credentials', request, username, password);
|
ipcRenderer.send('login-credentials', request, username, password);
|
||||||
const loginQueue = this.state.loginQueue;
|
const loginQueue = this.state.loginQueue;
|
||||||
loginQueue.shift();
|
loginQueue.shift();
|
||||||
this.setState(loginQueue);
|
this.setState(loginQueue);
|
||||||
},
|
},
|
||||||
handleLoginCancel: function() {
|
handleLoginCancel() {
|
||||||
const loginQueue = this.state.loginQueue;
|
const loginQueue = this.state.loginQueue;
|
||||||
loginQueue.shift();
|
loginQueue.shift();
|
||||||
this.setState(loginQueue);
|
this.setState(loginQueue);
|
||||||
},
|
},
|
||||||
render: function() {
|
render() {
|
||||||
var thisObj = this;
|
var self = this;
|
||||||
|
|
||||||
var tabs_row;
|
var tabsRow;
|
||||||
if (this.props.teams.length > 1) {
|
if (this.props.teams.length > 1) {
|
||||||
tabs_row = (
|
tabsRow = (
|
||||||
<Row>
|
<Row>
|
||||||
<TabBar id="tabBar" teams={ this.props.teams } unreadCounts={ this.state.unreadCounts } mentionCounts={ this.state.mentionCounts } unreadAtActive={ this.state.unreadAtActive } mentionAtActiveCounts={ this.state.mentionAtActiveCounts }
|
<TabBar
|
||||||
activeKey={ this.state.key } onSelect={ this.handleSelect }></TabBar>
|
id='tabBar'
|
||||||
|
teams={this.props.teams}
|
||||||
|
unreadCounts={this.state.unreadCounts}
|
||||||
|
mentionCounts={this.state.mentionCounts}
|
||||||
|
unreadAtActive={this.state.unreadAtActive}
|
||||||
|
mentionAtActiveCounts={this.state.mentionAtActiveCounts}
|
||||||
|
activeKey={this.state.key}
|
||||||
|
onSelect={this.handleSelect}
|
||||||
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var views = this.props.teams.map(function(team, index) {
|
var views = this.props.teams.map((team, index) => {
|
||||||
var handleUnreadCountChange = function(unreadCount, mentionCount, isUnread, isMentioned) {
|
function handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned) {
|
||||||
thisObj.handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned);
|
self.handleUnreadCountChange(index, unreadCount, mentionCount, isUnread, isMentioned);
|
||||||
};
|
}
|
||||||
var handleNotificationClick = function() {
|
function handleNotificationClick() {
|
||||||
thisObj.handleSelect(index);
|
self.handleSelect(index);
|
||||||
}
|
}
|
||||||
var id = 'mattermostView' + index;
|
var id = 'mattermostView' + index;
|
||||||
var is_active = thisObj.state.key === index;
|
var isActive = self.state.key === index;
|
||||||
return (<MattermostView key={ id } id={ id } style={ thisObj.visibleStyle(is_active) } src={ team.url } name={ team.name } onUnreadCountChange={ handleUnreadCountChange }
|
return (
|
||||||
onNotificationClick={ handleNotificationClick } ref={ id } active={ is_active } />)
|
<MattermostView
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
style={self.visibleStyle(isActive)}
|
||||||
|
src={team.url}
|
||||||
|
name={team.name}
|
||||||
|
onUnreadCountChange={handleUnreadCountChange}
|
||||||
|
onNotificationClick={handleNotificationClick}
|
||||||
|
ref={id}
|
||||||
|
active={isActive}
|
||||||
|
/>);
|
||||||
});
|
});
|
||||||
var views_row = (<Row>
|
var viewsRow = (
|
||||||
{ views }
|
<Row>
|
||||||
</Row>);
|
{views}
|
||||||
|
</Row>);
|
||||||
|
|
||||||
var request = null;
|
var request = null;
|
||||||
var authServerURL = null;
|
var authServerURL = null;
|
||||||
var authInfo = null;
|
var authInfo = null;
|
||||||
if (this.state.loginQueue.length !== 0) {
|
if (this.state.loginQueue.length !== 0) {
|
||||||
request = this.state.loginQueue[0].request;
|
request = this.state.loginQueue[0].request;
|
||||||
const tmp_url = url.parse(this.state.loginQueue[0].request.url);
|
const tmpURL = url.parse(this.state.loginQueue[0].request.url);
|
||||||
authServerURL = `${tmp_url.protocol}//${tmp_url.host}`;
|
authServerURL = `${tmpURL.protocol}//${tmpURL.host}`;
|
||||||
authInfo = this.state.loginQueue[0].authInfo;
|
authInfo = this.state.loginQueue[0].authInfo;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LoginModal show={ this.state.loginQueue.length !== 0 } request={ request } authInfo={ authInfo } authServerURL={ authServerURL } onLogin={ this.handleLogin }
|
<LoginModal
|
||||||
onCancel={ this.handleLoginCancel }></LoginModal>
|
show={this.state.loginQueue.length !== 0}
|
||||||
|
request={request}
|
||||||
|
authInfo={authInfo}
|
||||||
|
authServerURL={authServerURL}
|
||||||
|
onLogin={this.handleLogin}
|
||||||
|
onCancel={this.handleLoginCancel}
|
||||||
|
/>
|
||||||
<Grid fluid>
|
<Grid fluid>
|
||||||
{ tabs_row }
|
{ tabsRow }
|
||||||
{ views_row }
|
{ viewsRow }
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -262,9 +298,9 @@ var MainPage = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
var TabBar = React.createClass({
|
var TabBar = React.createClass({
|
||||||
render: function() {
|
render() {
|
||||||
var thisObj = this;
|
var self = this;
|
||||||
var tabs = this.props.teams.map(function(team, index) {
|
var tabs = this.props.teams.map((team, index) => {
|
||||||
var unreadCount = 0;
|
var unreadCount = 0;
|
||||||
var badgeStyle = {
|
var badgeStyle = {
|
||||||
background: '#FF1744',
|
background: '#FF1744',
|
||||||
|
@ -276,48 +312,64 @@ var TabBar = React.createClass({
|
||||||
lineHeight: '20px',
|
lineHeight: '20px',
|
||||||
height: '19px',
|
height: '19px',
|
||||||
marginLeft: '5px',
|
marginLeft: '5px',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (thisObj.props.unreadCounts[index] > 0) {
|
if (self.props.unreadCounts[index] > 0) {
|
||||||
unreadCount = thisObj.props.unreadCounts[index];
|
unreadCount = self.props.unreadCounts[index];
|
||||||
}
|
}
|
||||||
if (thisObj.props.unreadAtActive[index]) {
|
if (self.props.unreadAtActive[index]) {
|
||||||
unreadCount += 1;
|
unreadCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mentionCount = 0;
|
var mentionCount = 0;
|
||||||
if (thisObj.props.mentionCounts[index] > 0) {
|
if (self.props.mentionCounts[index] > 0) {
|
||||||
mentionCount = thisObj.props.mentionCounts[index];
|
mentionCount = self.props.mentionCounts[index];
|
||||||
}
|
}
|
||||||
if (thisObj.props.mentionAtActiveCounts[index] > 0) {
|
if (self.props.mentionAtActiveCounts[index] > 0) {
|
||||||
mentionCount += thisObj.props.mentionAtActiveCounts[index];
|
mentionCount += self.props.mentionAtActiveCounts[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
var badge;
|
var badgeDiv;
|
||||||
if (mentionCount != 0) {
|
if (mentionCount !== 0) {
|
||||||
badge = (<div style={ badgeStyle }>
|
badgeDiv = (
|
||||||
{ mentionCount }
|
<div style={badgeStyle}>
|
||||||
</div>);
|
{mentionCount}
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
if (unreadCount == 0) {
|
var id = 'teamTabItem' + index;
|
||||||
var id = 'teamTabItem' + index;
|
if (unreadCount === 0) {
|
||||||
return (<NavItem className="teamTabItem" key={ id } id={ id } eventKey={ index }>
|
return (
|
||||||
{ team.name }
|
<NavItem
|
||||||
{ ' ' }
|
className='teamTabItem'
|
||||||
{ badge }
|
key={id}
|
||||||
</NavItem>);
|
id={id}
|
||||||
} else {
|
eventKey={index}
|
||||||
var id = 'teamTabItem' + index;
|
>
|
||||||
return (<NavItem className="teamTabItem" key={ id } id={ id } eventKey={ index }>
|
{ team.name }
|
||||||
<b>{ team.name }</b>
|
{ ' ' }
|
||||||
{ ' ' }
|
{ badgeDiv }
|
||||||
{ badge }
|
</NavItem>);
|
||||||
</NavItem>);
|
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
<NavItem
|
||||||
|
className='teamTabItem'
|
||||||
|
key={id}
|
||||||
|
id={id}
|
||||||
|
eventKey={index}
|
||||||
|
>
|
||||||
|
<b>{ team.name }</b>
|
||||||
|
{ ' ' }
|
||||||
|
{ badgeDiv }
|
||||||
|
</NavItem>);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Nav id={ this.props.id } bsStyle="tabs" activeKey={ this.props.activeKey } onSelect={ this.props.onSelect }>
|
<Nav
|
||||||
|
id={this.props.id}
|
||||||
|
bsStyle='tabs'
|
||||||
|
activeKey={this.props.activeKey}
|
||||||
|
onSelect={this.props.onSelect}
|
||||||
|
>
|
||||||
{ tabs }
|
{ tabs }
|
||||||
</Nav>
|
</Nav>
|
||||||
);
|
);
|
||||||
|
@ -325,19 +377,19 @@ var TabBar = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
var MattermostView = React.createClass({
|
var MattermostView = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
errorInfo: null
|
errorInfo: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleUnreadCountChange: function(unreadCount, mentionCount, isUnread, isMentioned) {
|
handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned) {
|
||||||
if (this.props.onUnreadCountChange) {
|
if (this.props.onUnreadCountChange) {
|
||||||
this.props.onUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned);
|
this.props.onUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
var thisObj = this;
|
var self = this;
|
||||||
var webview = ReactDOM.findDOMNode(this.refs.webview);
|
var webview = ReactDOM.findDOMNode(this.refs.webview);
|
||||||
|
|
||||||
// This option disables the same-origin policy and allows js/css/plugins not only content like images.
|
// This option disables the same-origin policy and allows js/css/plugins not only content like images.
|
||||||
|
@ -346,19 +398,19 @@ var MattermostView = React.createClass({
|
||||||
webview.setAttribute('disablewebsecurity', true);
|
webview.setAttribute('disablewebsecurity', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
webview.addEventListener('did-fail-load', function(e) {
|
webview.addEventListener('did-fail-load', (e) => {
|
||||||
console.log(thisObj.props.name, 'webview did-fail-load', e);
|
console.log(self.props.name, 'webview did-fail-load', e);
|
||||||
if (e.errorCode === -3) { // An operation was aborted (due to user action).
|
if (e.errorCode === -3) { // An operation was aborted (due to user action).
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thisObj.setState({
|
self.setState({
|
||||||
errorInfo: e
|
errorInfo: e
|
||||||
});
|
});
|
||||||
const reload = () => {
|
function reload() {
|
||||||
window.removeEventListener('online', reload);
|
window.removeEventListener('online', reload);
|
||||||
thisObj.reload();
|
self.reload();
|
||||||
};
|
}
|
||||||
if (navigator.onLine) {
|
if (navigator.onLine) {
|
||||||
setTimeout(reload, 30000);
|
setTimeout(reload, 30000);
|
||||||
} else {
|
} else {
|
||||||
|
@ -367,7 +419,7 @@ var MattermostView = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Open link in browserWindow. for exmaple, attached files.
|
// Open link in browserWindow. for exmaple, attached files.
|
||||||
webview.addEventListener('new-window', function(e) {
|
webview.addEventListener('new-window', (e) => {
|
||||||
var currentURL = url.parse(webview.getURL());
|
var currentURL = url.parse(webview.getURL());
|
||||||
var destURL = url.parse(e.url);
|
var destURL = url.parse(e.url);
|
||||||
if (destURL.protocol !== 'http:' && destURL.protocol !== 'https:') {
|
if (destURL.protocol !== 'http:' && destURL.protocol !== 'https:') {
|
||||||
|
@ -383,22 +435,22 @@ var MattermostView = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
webview.addEventListener("dom-ready", function() {
|
webview.addEventListener('dom-ready', () => {
|
||||||
// webview.openDevTools();
|
// webview.openDevTools();
|
||||||
|
|
||||||
// Use 'Meiryo UI' and 'MS Gothic' to prevent CJK fonts on Windows(JP).
|
// Use 'Meiryo UI' and 'MS Gothic' to prevent CJK fonts on Windows(JP).
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
var applyCssFile = function(cssFile) {
|
function applyCssFile(cssFile) {
|
||||||
fs.readFile(cssFile, 'utf8', function(err, data) {
|
fs.readFile(cssFile, 'utf8', (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
webview.insertCSS(data);
|
webview.insertCSS(data);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
osLocale(function(err, locale) {
|
osLocale((err, locale) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return;
|
return;
|
||||||
|
@ -409,29 +461,30 @@ var MattermostView = React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
require('electron-context-menu')({
|
electronContextMenu({
|
||||||
window: webview
|
window: webview
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
webview.addEventListener('ipc-message', function(event) {
|
webview.addEventListener('ipc-message', (event) => {
|
||||||
switch (event.channel) {
|
switch (event.channel) {
|
||||||
case 'onUnreadCountChange':
|
case 'onUnreadCountChange':
|
||||||
var unreadCount = event.args[0];
|
var unreadCount = event.args[0];
|
||||||
var mentionCount = event.args[1];
|
var mentionCount = event.args[1];
|
||||||
// isUnread and isMentioned is pulse flag.
|
|
||||||
var isUnread = event.args[2];
|
// isUnread and isMentioned is pulse flag.
|
||||||
var isMentioned = event.args[3];
|
var isUnread = event.args[2];
|
||||||
thisObj.handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned);
|
var isMentioned = event.args[3];
|
||||||
break;
|
self.handleUnreadCountChange(unreadCount, mentionCount, isUnread, isMentioned);
|
||||||
case 'onNotificationClick':
|
break;
|
||||||
thisObj.props.onNotificationClick();
|
case 'onNotificationClick':
|
||||||
break;
|
self.props.onNotificationClick();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
webview.addEventListener('page-title-updated', function(event) {
|
webview.addEventListener('page-title-updated', (event) => {
|
||||||
if (thisObj.props.active) {
|
if (self.props.active) {
|
||||||
ipcRenderer.send('update-title', {
|
ipcRenderer.send('update-title', {
|
||||||
title: event.title
|
title: event.title
|
||||||
});
|
});
|
||||||
|
@ -441,22 +494,22 @@ var MattermostView = React.createClass({
|
||||||
webview.addEventListener('console-message', (e) => {
|
webview.addEventListener('console-message', (e) => {
|
||||||
const message = `[${this.props.name}] ${e.message}`;
|
const message = `[${this.props.name}] ${e.message}`;
|
||||||
switch (e.level) {
|
switch (e.level) {
|
||||||
case 0:
|
case 0:
|
||||||
console.log(message);
|
console.log(message);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
console.warn(message);
|
console.warn(message);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
console.error(message);
|
console.error(message);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(message);
|
console.log(message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
reload: function() {
|
reload() {
|
||||||
this.setState({
|
this.setState({
|
||||||
errorInfo: null
|
errorInfo: null
|
||||||
});
|
});
|
||||||
|
@ -473,7 +526,7 @@ var MattermostView = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
focusOnWebView: function() {
|
focusOnWebView() {
|
||||||
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
const webview = ReactDOM.findDOMNode(this.refs.webview);
|
||||||
if (!webview.getWebContents().isFocused()) {
|
if (!webview.getWebContents().isFocused()) {
|
||||||
webview.focus();
|
webview.focus();
|
||||||
|
@ -501,17 +554,33 @@ var MattermostView = React.createClass({
|
||||||
webview.getWebContents().goForward();
|
webview.getWebContents().goForward();
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const errorView = this.state.errorInfo ? (<ErrorView id={ this.props.id + '-fail' } style={ this.props.style } className="errorView" errorInfo={ this.state.errorInfo }></ErrorView>) : null;
|
const errorView = this.state.errorInfo ? (
|
||||||
|
<ErrorView
|
||||||
|
id={this.props.id + '-fail'}
|
||||||
|
style={this.props.style}
|
||||||
|
className='errorView'
|
||||||
|
errorInfo={this.state.errorInfo}
|
||||||
|
/>) : null;
|
||||||
|
|
||||||
// 'disablewebsecurity' is necessary to display external images.
|
// 'disablewebsecurity' is necessary to display external images.
|
||||||
// However, it allows also CSS/JavaScript.
|
// However, it allows also CSS/JavaScript.
|
||||||
// So webview should use 'allowDisplayingInsecureContent' as same as BrowserWindow.
|
// So webview should use 'allowDisplayingInsecureContent' as same as BrowserWindow.
|
||||||
|
|
||||||
// Need to keep webview mounted when failed to load.
|
// Need to keep webview mounted when failed to load.
|
||||||
return (<div>
|
return (
|
||||||
{ errorView }
|
<div>
|
||||||
<webview id={ this.props.id } className="mattermostView" style={ this.props.style } preload="webview/mattermost.js" src={ this.props.src } ref="webview" nodeintegration="false"></webview>
|
{ errorView }
|
||||||
</div>);
|
<webview
|
||||||
|
id={this.props.id}
|
||||||
|
className='mattermostView'
|
||||||
|
style={this.props.style}
|
||||||
|
preload='webview/mattermost.js'
|
||||||
|
src={this.props.src}
|
||||||
|
ref='webview'
|
||||||
|
nodeintegration='false'
|
||||||
|
/>
|
||||||
|
</div>);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -540,41 +609,57 @@ const errorPage = {
|
||||||
techInfo: {
|
techInfo: {
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: '#aaa'
|
color: '#aaa'
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var ErrorView = React.createClass({
|
var ErrorView = React.createClass({
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Grid id={ this.props.id } style={ this.props.style }>
|
<Grid
|
||||||
<div style={ errorPage.tableStyle }>
|
id={this.props.id}
|
||||||
<div style={ errorPage.cellStyle }>
|
style={this.props.style}
|
||||||
|
>
|
||||||
|
<div style={errorPage.tableStyle}>
|
||||||
|
<div style={errorPage.cellStyle}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={ 0 } sm={ 1 } md={ 1 } lg={ 2 } />
|
<Col
|
||||||
<Col xs={ 12 } sm={ 10 } md={ 10 } lg={ 8 }>
|
xs={0}
|
||||||
<h2>Cannot connect to Mattermost</h2>
|
sm={1}
|
||||||
<hr />
|
md={1}
|
||||||
<p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work please verify that:</p>
|
lg={2}
|
||||||
<br />
|
/>
|
||||||
<ul style={ errorPage.bullets }>
|
<Col
|
||||||
<li>Your computer is connected to the internet.</li>
|
xs={12}
|
||||||
<li>The Mattermost URL
|
sm={10}
|
||||||
{ ' ' }
|
md={10}
|
||||||
<a href={ this.props.errorInfo.validatedURL }>
|
lg={8}
|
||||||
{ this.props.errorInfo.validatedURL }
|
>
|
||||||
</a> is correct.</li>
|
<h2>{'Cannot connect to Mattermost'}</h2>
|
||||||
<li>You can reach
|
<hr/>
|
||||||
{ ' ' }
|
<p>{'We\'re having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work please verify that:'}</p>
|
||||||
<a href={ this.props.errorInfo.validatedURL }>
|
<br/>
|
||||||
{ this.props.errorInfo.validatedURL }
|
<ul style={errorPage.bullets}>
|
||||||
</a> from a browser window.</li>
|
<li>{'Your computer is connected to the internet.'}</li>
|
||||||
</ul>
|
<li>{'The Mattermost URL '}
|
||||||
<br />
|
<a href={this.props.errorInfo.validatedURL}>
|
||||||
<div style={ errorPage.techInfo }>
|
{this.props.errorInfo.validatedURL}
|
||||||
{ this.props.errorInfo.errorDescription } (
|
</a>{' is correct.'}</li>
|
||||||
{ this.props.errorInfo.errorCode })</div>
|
<li>{'You can reach '}
|
||||||
|
<a href={this.props.errorInfo.validatedURL}>
|
||||||
|
{this.props.errorInfo.validatedURL}
|
||||||
|
</a>{' from a browser window.'}</li>
|
||||||
|
</ul>
|
||||||
|
<br/>
|
||||||
|
<div style={errorPage.techInfo}>
|
||||||
|
{this.props.errorInfo.errorDescription}{' ('}
|
||||||
|
{this.props.errorInfo.errorCode }{')'}</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={ 0 } sm={ 1 } md={ 1 } lg={ 2 } />
|
<Col
|
||||||
|
xs={0}
|
||||||
|
sm={1}
|
||||||
|
md={1}
|
||||||
|
lg={2}
|
||||||
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -583,29 +668,17 @@ var ErrorView = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var config;
|
function showUnreadBadgeWindows(unreadCount, mentionCount) {
|
||||||
try {
|
function sendBadge(dataURL, description) {
|
||||||
var configFile = remote.getGlobal('config-file');
|
|
||||||
config = settings.readFileSync(configFile);
|
|
||||||
} catch (e) {
|
|
||||||
window.location = 'settings.html';
|
|
||||||
}
|
|
||||||
if (config.teams.length === 0) {
|
|
||||||
window.location = 'settings.html';
|
|
||||||
}
|
|
||||||
|
|
||||||
var showUnreadBadgeWindows = function(unreadCount, mentionCount) {
|
|
||||||
const badge = require('./js/badge');
|
|
||||||
const sendBadge = function(dataURL, description) {
|
|
||||||
// window.setOverlayIcon() does't work with NativeImage across remote boundaries.
|
// window.setOverlayIcon() does't work with NativeImage across remote boundaries.
|
||||||
// https://github.com/atom/electron/issues/4011
|
// https://github.com/atom/electron/issues/4011
|
||||||
ipcRenderer.send('update-unread', {
|
ipcRenderer.send('update-unread', {
|
||||||
overlayDataURL: dataURL,
|
overlayDataURL: dataURL,
|
||||||
description: description,
|
description,
|
||||||
unreadCount: unreadCount,
|
unreadCount,
|
||||||
mentionCount: mentionCount
|
mentionCount
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
if (mentionCount > 0) {
|
if (mentionCount > 0) {
|
||||||
const dataURL = badge.createDataURL(mentionCount.toString());
|
const dataURL = badge.createDataURL(mentionCount.toString());
|
||||||
|
@ -618,7 +691,7 @@ var showUnreadBadgeWindows = function(unreadCount, mentionCount) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var showUnreadBadgeOSX = function(unreadCount, mentionCount) {
|
function showUnreadBadgeOSX(unreadCount, mentionCount) {
|
||||||
if (mentionCount > 0) {
|
if (mentionCount > 0) {
|
||||||
remote.app.dock.setBadge(mentionCount.toString());
|
remote.app.dock.setBadge(mentionCount.toString());
|
||||||
} else if (unreadCount > 0 && config.showUnreadBadge) {
|
} else if (unreadCount > 0 && config.showUnreadBadge) {
|
||||||
|
@ -628,38 +701,41 @@ var showUnreadBadgeOSX = function(unreadCount, mentionCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcRenderer.send('update-unread', {
|
ipcRenderer.send('update-unread', {
|
||||||
unreadCount: unreadCount,
|
unreadCount,
|
||||||
mentionCount: mentionCount
|
mentionCount
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var showUnreadBadgeLinux = function(unreadCount, mentionCount) {
|
function showUnreadBadgeLinux(unreadCount, mentionCount) {
|
||||||
if (remote.app.isUnityRunning()) {
|
if (remote.app.isUnityRunning()) {
|
||||||
remote.app.setBadgeCount(mentionCount);
|
remote.app.setBadgeCount(mentionCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcRenderer.send('update-unread', {
|
ipcRenderer.send('update-unread', {
|
||||||
unreadCount: unreadCount,
|
unreadCount,
|
||||||
mentionCount: mentionCount
|
mentionCount
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var showUnreadBadge = function(unreadCount, mentionCount) {
|
function showUnreadBadge(unreadCount, mentionCount) {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
showUnreadBadgeWindows(unreadCount, mentionCount);
|
showUnreadBadgeWindows(unreadCount, mentionCount);
|
||||||
break;
|
break;
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
showUnreadBadgeOSX(unreadCount, mentionCount);
|
showUnreadBadgeOSX(unreadCount, mentionCount);
|
||||||
break;
|
break;
|
||||||
case 'linux':
|
case 'linux':
|
||||||
showUnreadBadgeLinux(unreadCount, mentionCount);
|
showUnreadBadgeLinux(unreadCount, mentionCount);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<MainPage teams={ config.teams } onUnreadCountChange={ showUnreadBadge } />,
|
<MainPage
|
||||||
|
teams={config.teams}
|
||||||
|
onUnreadCountChange={showUnreadBadge}
|
||||||
|
/>,
|
||||||
document.getElementById('content')
|
document.getElementById('content')
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var createDataURL = function(text) {
|
function createDataURL(text) {
|
||||||
const scale = 2; // should rely display dpi
|
const scale = 2; // should rely display dpi
|
||||||
const size = 16 * scale;
|
const size = 16 * scale;
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
|
@ -9,21 +9,21 @@ var createDataURL = function(text) {
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
// circle
|
// circle
|
||||||
ctx.fillStyle = "#FF1744"; // Material Red A400
|
ctx.fillStyle = '#FF1744'; // Material Red A400
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
|
ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
// text
|
// text
|
||||||
ctx.fillStyle = "#ffffff";
|
ctx.fillStyle = '#ffffff';
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
ctx.font = (11 * scale) + "px sans-serif";
|
ctx.font = (11 * scale) + 'px sans-serif';
|
||||||
ctx.fillText(text, size / 2, size / 2, size);
|
ctx.fillText(text, size / 2, size / 2, size);
|
||||||
|
|
||||||
return canvas.toDataURL();
|
return canvas.toDataURL();
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createDataURL: createDataURL
|
createDataURL
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const OriginalNotification = Notification;
|
const OriginalNotification = Notification;
|
||||||
|
const {remote} = require('electron');
|
||||||
|
|
||||||
const appIconURL = `file:///${require('electron').remote.app.getAppPath()}/resources/appicon.png`;
|
const appIconURL = `file:///${remote.app.getAppPath()}/resources/appicon.png`;
|
||||||
|
|
||||||
function override(eventHandlers) {
|
function override(eventHandlers) {
|
||||||
Notification = function(title, options) {
|
Notification = function constructor(title, options) {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
// Replace with application icon.
|
// Replace with application icon.
|
||||||
options.icon = appIconURL;
|
options.icon = appIconURL;
|
||||||
}
|
} else if (process.platform === 'darwin') {
|
||||||
else if (process.platform === 'darwin') {
|
|
||||||
// Notification Center shows app's icon, so there were two icons on the notification.
|
// Notification Center shows app's icon, so there were two icons on the notification.
|
||||||
delete options.icon;
|
Reflect.deleteProperty(options, 'icon');
|
||||||
}
|
}
|
||||||
this.notification = new OriginalNotification(title, options);
|
this.notification = new OriginalNotification(title, options);
|
||||||
if (eventHandlers.notification) {
|
if (eventHandlers.notification) {
|
||||||
|
@ -21,16 +21,16 @@ function override(eventHandlers) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// static properties
|
// static properties
|
||||||
Notification.__defineGetter__('permission', function() {
|
Notification.__defineGetter__('permission', () => {
|
||||||
return OriginalNotification.permission;
|
return OriginalNotification.permission;
|
||||||
});
|
});
|
||||||
|
|
||||||
// instance properties
|
// instance properties
|
||||||
var defineReadProperty = function(property) {
|
function defineReadProperty(property) {
|
||||||
Notification.prototype.__defineGetter__(property, function() {
|
Notification.prototype.__defineGetter__(property, function getter() {
|
||||||
return this.notification[property];
|
return this.notification[property];
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
defineReadProperty('title');
|
defineReadProperty('title');
|
||||||
defineReadProperty('dir');
|
defineReadProperty('dir');
|
||||||
defineReadProperty('lang');
|
defineReadProperty('lang');
|
||||||
|
@ -48,12 +48,12 @@ function override(eventHandlers) {
|
||||||
defineReadProperty('vibrate');
|
defineReadProperty('vibrate');
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
var defineEventHandler = function(event, callback) {
|
function defineEventHandler(event, callback) {
|
||||||
defineReadProperty(event);
|
defineReadProperty(event);
|
||||||
Notification.prototype.__defineSetter__(event, function(originalCallback) {
|
Notification.prototype.__defineSetter__(event, function setter(originalCallback) {
|
||||||
this.notification[event] = function() {
|
this.notification[event] = () => {
|
||||||
const callbackevent = {
|
const callbackevent = {
|
||||||
preventDefault: function() {
|
preventDefault() {
|
||||||
this.isPrevented = true;
|
this.isPrevented = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -62,13 +62,12 @@ function override(eventHandlers) {
|
||||||
if (!callbackevent.isPrevented) {
|
if (!callbackevent.isPrevented) {
|
||||||
originalCallback();
|
originalCallback();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
originalCallback();
|
originalCallback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
defineEventHandler('onclick', eventHandlers.onclick);
|
defineEventHandler('onclick', eventHandlers.onclick);
|
||||||
defineEventHandler('onerror', eventHandlers.onerror);
|
defineEventHandler('onerror', eventHandlers.onerror);
|
||||||
|
|
||||||
|
@ -77,16 +76,16 @@ function override(eventHandlers) {
|
||||||
defineEventHandler('onshow', eventHandlers.onshow);
|
defineEventHandler('onshow', eventHandlers.onshow);
|
||||||
|
|
||||||
// static methods
|
// static methods
|
||||||
Notification.requestPermission = function(callback) {
|
Notification.requestPermission = (callback) => {
|
||||||
OriginalNotification.requestPermission(callback);
|
OriginalNotification.requestPermission(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
// instance methods
|
// instance methods
|
||||||
Notification.prototype.close = function() {
|
Notification.prototype.close = function close() {
|
||||||
this.notification.close();
|
this.notification.close();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
override: override
|
override
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
window.eval = global.eval = function() {
|
window.eval = global.eval = () => {
|
||||||
throw new Error("Sorry, Mattermost does not support window.eval() for security reasons.");
|
throw new Error('Sorry, Mattermost does not support window.eval() for security reasons.');
|
||||||
}
|
};
|
||||||
|
|
||||||
const {remote, ipcRenderer} = require('electron');
|
const {remote, ipcRenderer} = require('electron');
|
||||||
const settings = require('../common/settings');
|
const settings = require('../common/settings');
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
const {Grid, Row, Col, Input, Button, ListGroup, ListGroupItem, Glyphicon, HelpBlock, Navbar, Nav} = require('react-bootstrap');
|
const {Grid, Row, Col, Input, Button, ListGroup, ListGroupItem, HelpBlock, Navbar} = require('react-bootstrap');
|
||||||
var AutoLaunch = require('auto-launch');
|
var AutoLaunch = require('auto-launch');
|
||||||
|
|
||||||
|
|
||||||
var appLauncher = new AutoLaunch({
|
var appLauncher = new AutoLaunch({
|
||||||
name: 'Mattermost',
|
name: 'Mattermost',
|
||||||
isHidden: true
|
isHidden: true
|
||||||
|
@ -23,7 +22,7 @@ function backToIndex() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var SettingsPage = React.createClass({
|
var SettingsPage = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
var initialState;
|
var initialState;
|
||||||
try {
|
try {
|
||||||
initialState = settings.readFileSync(this.props.configFile);
|
initialState = settings.readFileSync(this.props.configFile);
|
||||||
|
@ -36,23 +35,23 @@ var SettingsPage = React.createClass({
|
||||||
|
|
||||||
return initialState;
|
return initialState;
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
var self = this;
|
var self = this;
|
||||||
appLauncher.isEnabled().then(function(enabled) {
|
appLauncher.isEnabled().then((enabled) => {
|
||||||
self.setState({
|
self.setState({
|
||||||
autostart: enabled
|
autostart: enabled
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleTeamsChange: function(teams) {
|
handleTeamsChange(teams) {
|
||||||
this.setState({
|
this.setState({
|
||||||
showAddTeamForm: false,
|
showAddTeamForm: false,
|
||||||
teams: teams
|
teams
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSave: function() {
|
handleSave() {
|
||||||
var config = {
|
var config = {
|
||||||
teams: this.state.teams,
|
teams: this.state.teams,
|
||||||
hideMenuBar: this.state.hideMenuBar,
|
hideMenuBar: this.state.hideMenuBar,
|
||||||
|
@ -74,7 +73,7 @@ var SettingsPage = React.createClass({
|
||||||
currentWindow.setMenuBarVisibility(!config.hideMenuBar);
|
currentWindow.setMenuBarVisibility(!config.hideMenuBar);
|
||||||
|
|
||||||
var autostart = this.state.autostart;
|
var autostart = this.state.autostart;
|
||||||
appLauncher.isEnabled().then(function(enabled) {
|
appLauncher.isEnabled().then((enabled) => {
|
||||||
if (enabled && !autostart) {
|
if (enabled && !autostart) {
|
||||||
appLauncher.disable();
|
appLauncher.disable();
|
||||||
} else if (!enabled && autostart) {
|
} else if (!enabled && autostart) {
|
||||||
|
@ -88,20 +87,20 @@ var SettingsPage = React.createClass({
|
||||||
|
|
||||||
backToIndex();
|
backToIndex();
|
||||||
},
|
},
|
||||||
handleCancel: function() {
|
handleCancel() {
|
||||||
backToIndex();
|
backToIndex();
|
||||||
},
|
},
|
||||||
handleChangeDisableWebSecurity: function() {
|
handleChangeDisableWebSecurity() {
|
||||||
this.setState({
|
this.setState({
|
||||||
disablewebsecurity: this.refs.disablewebsecurity.getChecked()
|
disablewebsecurity: this.refs.disablewebsecurity.getChecked()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleChangeHideMenuBar: function() {
|
handleChangeHideMenuBar() {
|
||||||
this.setState({
|
this.setState({
|
||||||
hideMenuBar: this.refs.hideMenuBar.getChecked()
|
hideMenuBar: this.refs.hideMenuBar.getChecked()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleChangeShowTrayIcon: function() {
|
handleChangeShowTrayIcon() {
|
||||||
var shouldShowTrayIcon = this.refs.showTrayIcon.getChecked();
|
var shouldShowTrayIcon = this.refs.showTrayIcon.getChecked();
|
||||||
this.setState({
|
this.setState({
|
||||||
showTrayIcon: shouldShowTrayIcon
|
showTrayIcon: shouldShowTrayIcon
|
||||||
|
@ -113,96 +112,177 @@ var SettingsPage = React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleChangeTrayIconTheme: function() {
|
handleChangeTrayIconTheme() {
|
||||||
this.setState({
|
this.setState({
|
||||||
trayIconTheme: this.refs.trayIconTheme.getValue()
|
trayIconTheme: this.refs.trayIconTheme.getValue()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleChangeAutoStart: function() {
|
handleChangeAutoStart() {
|
||||||
this.setState({
|
this.setState({
|
||||||
autostart: this.refs.autostart.getChecked()
|
autostart: this.refs.autostart.getChecked()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleChangeMinimizeToTray: function() {
|
handleChangeMinimizeToTray() {
|
||||||
var shouldMinimizeToTray = (process.platform !== 'darwin' || this.refs.showTrayIcon.getChecked())
|
var shouldMinimizeToTray =
|
||||||
&& this.refs.minimizeToTray.getChecked();
|
(process.platform !== 'darwin' || this.refs.showTrayIcon.getChecked()) &&
|
||||||
|
this.refs.minimizeToTray.getChecked();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
minimizeToTray: shouldMinimizeToTray
|
minimizeToTray: shouldMinimizeToTray
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleChangeToggleWindowOnTrayIconClick: function() {
|
handleChangeToggleWindowOnTrayIconClick() {
|
||||||
this.setState({
|
this.setState({
|
||||||
toggleWindowOnTrayIconClick: this.refs.toggleWindowOnTrayIconClick.getChecked()
|
toggleWindowOnTrayIconClick: this.refs.toggleWindowOnTrayIconClick.getChecked()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
toggleShowTeamForm: function() {
|
toggleShowTeamForm() {
|
||||||
this.setState({
|
this.setState({
|
||||||
showAddTeamForm: !this.state.showAddTeamForm
|
showAddTeamForm: !this.state.showAddTeamForm
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleFlashWindow: function() {
|
handleFlashWindow() {
|
||||||
this.setState({
|
this.setState({
|
||||||
notifications: {
|
notifications: {
|
||||||
flashWindow: this.refs.flashWindow.getChecked() ? 2 : 0
|
flashWindow: this.refs.flashWindow.getChecked() ? 2 : 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleShowUnreadBadge: function() {
|
handleShowUnreadBadge() {
|
||||||
this.setState({
|
this.setState({
|
||||||
showUnreadBadge: this.refs.showUnreadBadge.getChecked()
|
showUnreadBadge: this.refs.showUnreadBadge.getChecked()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function() {
|
render() {
|
||||||
var teams_row = (
|
var teamsRow = (
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={ 12 }>
|
<Col md={12}>
|
||||||
<TeamList teams={ this.state.teams } showAddTeamForm={ this.state.showAddTeamForm } onTeamsChange={ this.handleTeamsChange } />
|
<TeamList
|
||||||
</Col>
|
teams={this.state.teams}
|
||||||
</Row>
|
showAddTeamForm={this.state.showAddTeamForm}
|
||||||
|
onTeamsChange={this.handleTeamsChange}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
);
|
);
|
||||||
|
|
||||||
var options = [];
|
var options = [];
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
options.push(<Input key="inputHideMenuBar" id="inputHideMenuBar" ref="hideMenuBar" type="checkbox" label="Hide menu bar (Press Alt to show menu bar)" checked={ this.state.hideMenuBar }
|
options.push(
|
||||||
onChange={ this.handleChangeHideMenuBar } />);
|
<Input
|
||||||
|
key='inputHideMenuBar'
|
||||||
|
id='inputHideMenuBar'
|
||||||
|
ref='hideMenuBar'
|
||||||
|
type='checkbox'
|
||||||
|
label='Hide menu bar (Press Alt to show menu bar)'
|
||||||
|
checked={this.state.hideMenuBar}
|
||||||
|
onChange={this.handleChangeHideMenuBar}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
if (process.platform === 'darwin' || process.platform === 'linux') {
|
if (process.platform === 'darwin' || process.platform === 'linux') {
|
||||||
options.push(<Input key="inputShowTrayIcon" id="inputShowTrayIcon" ref="showTrayIcon" type="checkbox" label={ process.platform === 'darwin' ? "Show icon on menu bar (need to restart the application)" : "Show icon in notification area (need to restart the application)" } checked={ this.state.showTrayIcon } onChange={ this.handleChangeShowTrayIcon }
|
options.push(
|
||||||
/>);
|
<Input
|
||||||
|
key='inputShowTrayIcon'
|
||||||
|
id='inputShowTrayIcon'
|
||||||
|
ref='showTrayIcon'
|
||||||
|
type='checkbox'
|
||||||
|
label={process.platform === 'darwin' ?
|
||||||
|
'Show icon on menu bar (need to restart the application)' :
|
||||||
|
'Show icon in notification area (need to restart the application)'}
|
||||||
|
checked={this.state.showTrayIcon}
|
||||||
|
onChange={this.handleChangeShowTrayIcon}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
options.push(<Input key="inputTrayIconTheme" ref="trayIconTheme" type="select" label="Icon theme (Need to restart the application)" value={ this.state.trayIconTheme } onChange={ this.handleChangeTrayIconTheme }>
|
options.push(
|
||||||
<option value="light">Light</option>
|
<Input
|
||||||
<option value="dark">Dark</option>
|
key='inputTrayIconTheme'
|
||||||
</Input>);
|
ref='trayIconTheme'
|
||||||
|
type='select'
|
||||||
|
label='Icon theme (Need to restart the application)'
|
||||||
|
value={this.state.trayIconTheme}
|
||||||
|
onChange={this.handleChangeTrayIconTheme}
|
||||||
|
>
|
||||||
|
<option value='light'>{'Light'}</option>
|
||||||
|
<option value='dark'>{'Dark'}</option>
|
||||||
|
</Input>);
|
||||||
}
|
}
|
||||||
options.push(<Input key="inputDisableWebSecurity" id="inputDisableWebSecurity" ref="disablewebsecurity" type="checkbox" label="Allow mixed content (Enabling allows both secure and insecure content, images and scripts to render and execute. Disabling allows only secure content.)"
|
options.push(
|
||||||
checked={ this.state.disablewebsecurity } onChange={ this.handleChangeDisableWebSecurity } />);
|
<Input
|
||||||
|
key='inputDisableWebSecurity'
|
||||||
|
id='inputDisableWebSecurity'
|
||||||
|
ref='disablewebsecurity'
|
||||||
|
type='checkbox'
|
||||||
|
label='Allow mixed content (Enabling allows both secure and insecure content, images and scripts to render and execute. Disabling allows only secure content.)'
|
||||||
|
checked={this.state.disablewebsecurity}
|
||||||
|
onChange={this.handleChangeDisableWebSecurity}
|
||||||
|
/>);
|
||||||
|
|
||||||
//OSX has an option in the Dock, to set the app to autostart, so we choose to not support this option for OSX
|
//OSX has an option in the Dock, to set the app to autostart, so we choose to not support this option for OSX
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
options.push(<Input key="inputAutoStart" id="inputAutoStart" ref="autostart" type="checkbox" label="Start app on login." checked={ this.state.autostart } onChange={ this.handleChangeAutoStart }
|
options.push(
|
||||||
/>);
|
<Input
|
||||||
|
key='inputAutoStart'
|
||||||
|
id='inputAutoStart'
|
||||||
|
ref='autostart'
|
||||||
|
type='checkbox'
|
||||||
|
label='Start app on login.'
|
||||||
|
checked={this.state.autostart}
|
||||||
|
onChange={this.handleChangeAutoStart}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin' || process.platform === 'linux') {
|
if (process.platform === 'darwin' || process.platform === 'linux') {
|
||||||
options.push(<Input key="inputMinimizeToTray" id="inputMinimizeToTray" ref="minimizeToTray" type="checkbox" label={ this.state.trayWasVisible || !this.state.showTrayIcon ? "Leave app running in notification area when the window is closed" : "Leave app running in notification area when the window is closed (available on next restart)" } disabled={ !this.state.showTrayIcon || !this.state.trayWasVisible } checked={ this.state.minimizeToTray }
|
options.push(
|
||||||
onChange={ this.handleChangeMinimizeToTray } />);
|
<Input
|
||||||
|
key='inputMinimizeToTray'
|
||||||
|
id='inputMinimizeToTray'
|
||||||
|
ref='minimizeToTray'
|
||||||
|
type='checkbox'
|
||||||
|
label={this.state.trayWasVisible || !this.state.showTrayIcon ? 'Leave app running in notification area when the window is closed' : 'Leave app running in notification area when the window is closed (available on next restart)'}
|
||||||
|
disabled={!this.state.showTrayIcon || !this.state.trayWasVisible}
|
||||||
|
checked={this.state.minimizeToTray}
|
||||||
|
onChange={this.handleChangeMinimizeToTray}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
options.push(<Input key="inputToggleWindowOnTrayIconClick" id="inputToggleWindowOnTrayIconClick" ref="toggleWindowOnTrayIconClick" type="checkbox" label="Toggle window visibility when clicking on the tray icon."
|
options.push(
|
||||||
checked={ this.state.toggleWindowOnTrayIconClick } onChange={ this.handleChangeToggleWindowOnTrayIconClick } />);
|
<Input
|
||||||
|
key='inputToggleWindowOnTrayIconClick'
|
||||||
|
id='inputToggleWindowOnTrayIconClick'
|
||||||
|
ref='toggleWindowOnTrayIconClick'
|
||||||
|
type='checkbox'
|
||||||
|
label='Toggle window visibility when clicking on the tray icon.'
|
||||||
|
checked={this.state.toggleWindowOnTrayIconClick}
|
||||||
|
onChange={this.handleChangeToggleWindowOnTrayIconClick}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||||
options.push(<Input key="inputShowUnreadBadge" id="inputShowUnreadBadge" ref="showUnreadBadge" type="checkbox" label="Show red badge on taskbar icon to indicate unread messages. Regardless of this setting, mentions are always indicated with a red badge and item count on the taskbar icon."
|
options.push(
|
||||||
checked={ this.state.showUnreadBadge } onChange={ this.handleShowUnreadBadge } />);
|
<Input
|
||||||
|
key='inputShowUnreadBadge'
|
||||||
|
id='inputShowUnreadBadge'
|
||||||
|
ref='showUnreadBadge'
|
||||||
|
type='checkbox'
|
||||||
|
label='Show red badge on taskbar icon to indicate unread messages. Regardless of this setting, mentions are always indicated with a red badge and item count on the taskbar icon.'
|
||||||
|
checked={this.state.showUnreadBadge}
|
||||||
|
onChange={this.handleShowUnreadBadge}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
options.push(<Input key="flashWindow" id="inputflashWindow" ref="flashWindow" type="checkbox" label="Flash the taskbar icon when a new message is received." checked={ this.state.notifications.flashWindow === 2 }
|
options.push(
|
||||||
onChange={ this.handleFlashWindow } />);
|
<Input
|
||||||
|
key='flashWindow'
|
||||||
|
id='inputflashWindow'
|
||||||
|
ref='flashWindow'
|
||||||
|
type='checkbox'
|
||||||
|
label='Flash the taskbar icon when a new message is received.'
|
||||||
|
checked={this.state.notifications.flashWindow === 2}
|
||||||
|
onChange={this.handleFlashWindow}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsPage = {
|
const settingsPage = {
|
||||||
|
@ -237,45 +317,79 @@ var SettingsPage = React.createClass({
|
||||||
footer: {
|
footer: {
|
||||||
padding: '0.4em 0'
|
padding: '0.4em 0'
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
var options_row = (options.length > 0) ? (
|
var optionsRow = (options.length > 0) ? (
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={ 12 }>
|
<Col md={12}>
|
||||||
<h2 style={ settingsPage.sectionHeading }>App options</h2>
|
<h2 style={settingsPage.sectionHeading}>{'App options'}</h2>
|
||||||
{ options }
|
{ options }
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Navbar className="navbar-fixed-top" style={ settingsPage.navbar }>
|
<Navbar
|
||||||
<div style={ { 'position': 'relative' } }>
|
className='navbar-fixed-top'
|
||||||
<h1 style={ settingsPage.heading }>Settings</h1>
|
style={settingsPage.navbar}
|
||||||
<div style={ settingsPage.close } onClick={ this.handleCancel }>
|
>
|
||||||
<span>×</span>
|
<div style={{position: 'relative'}}>
|
||||||
|
<h1 style={settingsPage.heading}>{'Settings'}</h1>
|
||||||
|
<div
|
||||||
|
style={settingsPage.close}
|
||||||
|
onClick={this.handleCancel}
|
||||||
|
>
|
||||||
|
<span>{'×'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<Grid className="settingsPage" style={ { 'padding': '100px 15px' } }>
|
<Grid
|
||||||
|
className='settingsPage'
|
||||||
|
style={{padding: '100px 15px'}}
|
||||||
|
>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={ 10 } xs={ 8 }>
|
<Col
|
||||||
<h2 style={ settingsPage.sectionHeading }>Team Management</h2>
|
md={10}
|
||||||
|
xs={8}
|
||||||
|
>
|
||||||
|
<h2 style={settingsPage.sectionHeading}>{'Team Management'}</h2>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={ 2 } xs={ 4 }>
|
<Col
|
||||||
<p className="text-right"><a style={ settingsPage.sectionHeadingLink } href="#" onClick={ this.toggleShowTeamForm }>⊞ Add new team</a></p>
|
md={2}
|
||||||
|
xs={4}
|
||||||
|
>
|
||||||
|
<p className='text-right'>
|
||||||
|
<a
|
||||||
|
style={settingsPage.sectionHeadingLink}
|
||||||
|
href='#'
|
||||||
|
onClick={this.toggleShowTeamForm}
|
||||||
|
>{'⊞ Add new team'}</a>
|
||||||
|
</p>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{ teams_row }
|
{ teamsRow }
|
||||||
<hr/>
|
<hr/>
|
||||||
{ options_row }
|
{ optionsRow }
|
||||||
</Grid>
|
</Grid>
|
||||||
<Navbar className="navbar-fixed-bottom">
|
<Navbar className='navbar-fixed-bottom'>
|
||||||
<div className='text-right' style={ settingsPage.footer }>
|
<div
|
||||||
<button id="btnCancel" className="btn btn-link" onClick={ this.handleCancel }>Cancel</button>
|
className='text-right'
|
||||||
|
style={settingsPage.footer}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
id='btnCancel'
|
||||||
|
className='btn btn-link'
|
||||||
|
onClick={this.handleCancel}
|
||||||
|
>{'Cancel'}</button>
|
||||||
{ ' ' }
|
{ ' ' }
|
||||||
<button id="btnSave" className="btn btn-primary navbar-btn" bsStyle="primary" onClick={ this.handleSave } disabled={ this.state.teams.length === 0 }>Save</button>
|
<button
|
||||||
|
id='btnSave'
|
||||||
|
className='btn btn-primary navbar-btn'
|
||||||
|
bsStyle='primary'
|
||||||
|
onClick={this.handleSave}
|
||||||
|
disabled={this.state.teams.length === 0}
|
||||||
|
>{'Save'}</button>
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
</div>
|
</div>
|
||||||
|
@ -284,7 +398,7 @@ var SettingsPage = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
var TeamList = React.createClass({
|
var TeamList = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
showTeamListItemNew: false,
|
showTeamListItemNew: false,
|
||||||
team: {
|
team: {
|
||||||
|
@ -294,17 +408,17 @@ var TeamList = React.createClass({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleTeamRemove: function(index) {
|
handleTeamRemove(index) {
|
||||||
console.log(index);
|
console.log(index);
|
||||||
var teams = this.props.teams;
|
var teams = this.props.teams;
|
||||||
teams.splice(index, 1);
|
teams.splice(index, 1);
|
||||||
this.props.onTeamsChange(teams);
|
this.props.onTeamsChange(teams);
|
||||||
},
|
},
|
||||||
handleTeamAdd: function(team) {
|
handleTeamAdd(team) {
|
||||||
var teams = this.props.teams;
|
var teams = this.props.teams;
|
||||||
|
|
||||||
// check if team already exists and then change existing team or add new one
|
// check if team already exists and then change existing team or add new one
|
||||||
if ((team.index !== undefined) && teams[team.index]) {
|
if ((typeof team.index !== 'undefined') && teams[team.index]) {
|
||||||
teams[team.index].name = team.name;
|
teams[team.index].name = team.name;
|
||||||
teams[team.index].url = team.url;
|
teams[team.index].url = team.url;
|
||||||
} else {
|
} else {
|
||||||
|
@ -322,7 +436,7 @@ var TeamList = React.createClass({
|
||||||
|
|
||||||
this.props.onTeamsChange(teams);
|
this.props.onTeamsChange(teams);
|
||||||
},
|
},
|
||||||
handleTeamEditing: function(teamName, teamUrl, teamIndex) {
|
handleTeamEditing(teamName, teamUrl, teamIndex) {
|
||||||
this.setState({
|
this.setState({
|
||||||
showTeamListItemNew: true,
|
showTeamListItemNew: true,
|
||||||
team: {
|
team: {
|
||||||
|
@ -330,35 +444,47 @@ var TeamList = React.createClass({
|
||||||
name: teamName,
|
name: teamName,
|
||||||
index: teamIndex
|
index: teamIndex
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
render: function() {
|
render() {
|
||||||
var thisObj = this;
|
var self = this;
|
||||||
var teamNodes = this.props.teams.map(function(team, i) {
|
var teamNodes = this.props.teams.map((team, i) => {
|
||||||
var handleTeamRemove = function() {
|
function handleTeamRemove() {
|
||||||
thisObj.handleTeamRemove(i);
|
self.handleTeamRemove(i);
|
||||||
};
|
}
|
||||||
|
|
||||||
var handleTeamEditing = function() {
|
function handleTeamEditing() {
|
||||||
thisObj.handleTeamEditing(team.name, team.url, i);
|
self.handleTeamEditing(team.name, team.url, i);
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TeamListItem index={ i } key={ "teamListItem" + i } name={ team.name } url={ team.url } onTeamRemove={ handleTeamRemove } onTeamEditing={ handleTeamEditing }
|
<TeamListItem
|
||||||
|
index={i}
|
||||||
|
key={'teamListItem' + i}
|
||||||
|
name={team.name}
|
||||||
|
url={team.url}
|
||||||
|
onTeamRemove={handleTeamRemove}
|
||||||
|
onTeamEditing={handleTeamEditing}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
var addTeamForm;
|
var addTeamForm;
|
||||||
if (this.props.showAddTeamForm || this.state.showTeamListItemNew) {
|
if (this.props.showAddTeamForm || this.state.showTeamListItemNew) {
|
||||||
addTeamForm = <TeamListItemNew key={ this.state.team.index } onTeamAdd={ this.handleTeamAdd } teamIndex={ this.state.team.index } teamName={ this.state.team.name } teamUrl={ this.state.team.url }
|
addTeamForm = (
|
||||||
/>;
|
<TeamListItemNew
|
||||||
|
key={this.state.team.index}
|
||||||
|
onTeamAdd={this.handleTeamAdd}
|
||||||
|
teamIndex={this.state.team.index}
|
||||||
|
teamName={this.state.team.name}
|
||||||
|
teamUrl={this.state.team.url}
|
||||||
|
/>);
|
||||||
} else {
|
} else {
|
||||||
addTeamForm = '';
|
addTeamForm = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListGroup class="teamList">
|
<ListGroup class='teamList'>
|
||||||
{ teamNodes }
|
{ teamNodes }
|
||||||
{ addTeamForm }
|
{ addTeamForm }
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
|
@ -367,30 +493,36 @@ var TeamList = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
var TeamListItem = React.createClass({
|
var TeamListItem = React.createClass({
|
||||||
handleTeamRemove: function() {
|
handleTeamRemove() {
|
||||||
this.props.onTeamRemove();
|
this.props.onTeamRemove();
|
||||||
},
|
},
|
||||||
handleTeamEditing: function() {
|
handleTeamEditing() {
|
||||||
this.props.onTeamEditing();
|
this.props.onTeamEditing();
|
||||||
},
|
},
|
||||||
render: function() {
|
render() {
|
||||||
var style = {
|
var style = {
|
||||||
left: {
|
left: {
|
||||||
"display": 'inline-block'
|
display: 'inline-block'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="teamListItem list-group-item">
|
<div className='teamListItem list-group-item'>
|
||||||
<div style={ style.left }>
|
<div style={style.left}>
|
||||||
<h4 className="list-group-item-heading">{ this.props.name }</h4>
|
<h4 className='list-group-item-heading'>{ this.props.name }</h4>
|
||||||
<p className="list-group-item-text">
|
<p className='list-group-item-text'>
|
||||||
{ this.props.url }
|
{ this.props.url }
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="pull-right">
|
<div className='pull-right'>
|
||||||
<a href="#" onClick={ this.handleTeamEditing }>Edit</a>
|
<a
|
||||||
{ ' - ' }
|
href='#'
|
||||||
<a href="#" onClick={ this.handleTeamRemove }>Remove</a>
|
onClick={this.handleTeamEditing}
|
||||||
|
>{'Edit'}</a>
|
||||||
|
{' - '}
|
||||||
|
<a
|
||||||
|
href='#'
|
||||||
|
onClick={this.handleTeamRemove}
|
||||||
|
>{'Remove'}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -398,7 +530,7 @@ var TeamListItem = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
var TeamListItemNew = React.createClass({
|
var TeamListItemNew = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
name: this.props.teamName,
|
name: this.props.teamName,
|
||||||
url: this.props.teamUrl,
|
url: this.props.teamUrl,
|
||||||
|
@ -406,7 +538,7 @@ var TeamListItemNew = React.createClass({
|
||||||
errorMessage: null
|
errorMessage: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
handleSubmit: function(e) {
|
handleSubmit(e) {
|
||||||
console.log('submit');
|
console.log('submit');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const errorMessage = this.getValidationErrorMessage();
|
const errorMessage = this.getValidationErrorMessage();
|
||||||
|
@ -420,7 +552,7 @@ var TeamListItemNew = React.createClass({
|
||||||
this.props.onTeamAdd({
|
this.props.onTeamAdd({
|
||||||
name: this.state.name.trim(),
|
name: this.state.name.trim(),
|
||||||
url: this.state.url.trim(),
|
url: this.state.url.trim(),
|
||||||
index: this.state.index,
|
index: this.state.index
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -430,20 +562,20 @@ var TeamListItemNew = React.createClass({
|
||||||
errorMessage: null
|
errorMessage: null
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleNameChange: function(e) {
|
handleNameChange(e) {
|
||||||
console.log('name');
|
console.log('name');
|
||||||
this.setState({
|
this.setState({
|
||||||
name: e.target.value
|
name: e.target.value
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleURLChange: function(e) {
|
handleURLChange(e) {
|
||||||
console.log('url');
|
console.log('url');
|
||||||
this.setState({
|
this.setState({
|
||||||
url: e.target.value
|
url: e.target.value
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getValidationErrorMessage: function() {
|
getValidationErrorMessage() {
|
||||||
if (this.state.name.trim() === '') {
|
if (this.state.name.trim() === '') {
|
||||||
return 'Name is required.';
|
return 'Name is required.';
|
||||||
} else if (this.state.url.trim() === '') {
|
} else if (this.state.url.trim() === '') {
|
||||||
|
@ -454,7 +586,7 @@ var TeamListItemNew = React.createClass({
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
const inputTeamName = ReactDOM.findDOMNode(this.refs.inputTeamName);
|
const inputTeamName = ReactDOM.findDOMNode(this.refs.inputTeamName);
|
||||||
const setErrorMessage = () => {
|
const setErrorMessage = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -466,8 +598,7 @@ var TeamListItemNew = React.createClass({
|
||||||
inputTeamURL.addEventListener('invalid', setErrorMessage);
|
inputTeamURL.addEventListener('invalid', setErrorMessage);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
|
|
||||||
var existingTeam = false;
|
var existingTeam = false;
|
||||||
if (this.state.name !== '' && this.state.url !== '') {
|
if (this.state.name !== '' && this.state.url !== '') {
|
||||||
existingTeam = true;
|
existingTeam = true;
|
||||||
|
@ -482,33 +613,53 @@ var TeamListItemNew = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListGroupItem>
|
<ListGroupItem>
|
||||||
<form className="form-inline" onSubmit={ this.handleSubmit }>
|
<form
|
||||||
<div className="form-group">
|
className='form-inline'
|
||||||
<label for="inputTeamName">Name</label>
|
onSubmit={this.handleSubmit}
|
||||||
|
>
|
||||||
|
<div className='form-group'>
|
||||||
|
<label htmlFor='inputTeamName'>{'Name'}</label>
|
||||||
{ ' ' }
|
{ ' ' }
|
||||||
<input type="text" required className="form-control" id="inputTeamName" ref="inputTeamName" placeholder="Example team" value={ this.state.name } onChange={ this.handleNameChange }
|
<input
|
||||||
|
type='text'
|
||||||
|
required
|
||||||
|
className='form-control'
|
||||||
|
id='inputTeamName'
|
||||||
|
ref='inputTeamName'
|
||||||
|
placeholder='Example team'
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={this.handleNameChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ ' ' }
|
{ ' ' }
|
||||||
<div className="form-group">
|
<div className='form-group'>
|
||||||
<label for="inputTeamURL">URL</label>
|
<label htmlFor='inputTeamURL'>{'URL'}</label>
|
||||||
{ ' ' }
|
{ ' ' }
|
||||||
<input type="url" required className="form-control" id="inputTeamURL" ref="inputTeamURL" placeholder="https://example.com/team" value={ this.state.url } onChange={ this.handleURLChange }
|
<input
|
||||||
|
type='url'
|
||||||
|
required
|
||||||
|
className='form-control'
|
||||||
|
id='inputTeamURL'
|
||||||
|
ref='inputTeamURL'
|
||||||
|
placeholder='https://example.com/team'
|
||||||
|
value={this.state.url}
|
||||||
|
onChange={this.handleURLChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ ' ' }
|
{ ' ' }
|
||||||
<Button type="submit">
|
<Button type='submit'>
|
||||||
{ btnAddText }
|
{ btnAddText }
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
{ (() => {
|
{ (() => {
|
||||||
if (this.state.errorMessage !== null) {
|
if (this.state.errorMessage !== null) {
|
||||||
return (<HelpBlock style={ { color: '#777777' } }>
|
return (
|
||||||
{ this.state.errorMessage }
|
<HelpBlock style={{color: '#777777'}}>
|
||||||
</HelpBlock>);
|
{ this.state.errorMessage }
|
||||||
}
|
</HelpBlock>);
|
||||||
return null;
|
}
|
||||||
})() }
|
return null;
|
||||||
|
})() }
|
||||||
</ListGroupItem>
|
</ListGroupItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -521,6 +672,6 @@ require('electron-context-menu')({
|
||||||
});
|
});
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<SettingsPage configFile={ configFile } />,
|
<SettingsPage configFile={configFile}/>,
|
||||||
document.getElementById('content')
|
document.getElementById('content')
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,19 +4,19 @@ const electron = require('electron');
|
||||||
const ipc = electron.ipcRenderer;
|
const ipc = electron.ipcRenderer;
|
||||||
const notification = require('../js/notification');
|
const notification = require('../js/notification');
|
||||||
|
|
||||||
window.eval = global.eval = function() {
|
window.eval = global.eval = () => {
|
||||||
throw new Error("Sorry, Mattermost does not support window.eval() for security reasons.");
|
throw new Error('Sorry, Mattermost does not support window.eval() for security reasons.');
|
||||||
};
|
};
|
||||||
|
|
||||||
var hasClass = function(element, className) {
|
function hasClass(element, className) {
|
||||||
var rclass = /[\t\r\n\f]/g;
|
var rclass = /[\t\r\n\f]/g;
|
||||||
if ((' ' + element.className + ' ').replace(rclass, ' ').indexOf(className) > -1) {
|
if ((' ' + element.className + ' ').replace(rclass, ' ').indexOf(className) > -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
setInterval(function() {
|
setInterval(function getUnreadCount() {
|
||||||
if (!this.unreadCount) {
|
if (!this.unreadCount) {
|
||||||
this.unreadCount = 0;
|
this.unreadCount = 0;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ setInterval(function() {
|
||||||
// unreadCount in sidebar
|
// unreadCount in sidebar
|
||||||
// Note: the active channel doesn't have '.unread-title'.
|
// Note: the active channel doesn't have '.unread-title'.
|
||||||
var unreadCount = document.getElementsByClassName('unread-title').length;
|
var unreadCount = document.getElementsByClassName('unread-title').length;
|
||||||
|
|
||||||
// mentionCount in sidebar
|
// mentionCount in sidebar
|
||||||
var elem = document.getElementsByClassName('badge');
|
var elem = document.getElementsByClassName('badge');
|
||||||
var mentionCount = 0;
|
var mentionCount = 0;
|
||||||
|
@ -58,18 +59,20 @@ setInterval(function() {
|
||||||
if (post === null) {
|
if (post === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find latest post and save.
|
// find latest post and save.
|
||||||
while (post = post.nextSibling) {
|
post = post.nextSibling;
|
||||||
|
while (post) {
|
||||||
if (post.nextSibling === null) {
|
if (post.nextSibling === null) {
|
||||||
if (post.getAttribute(postAttrName) !== null) {
|
if (post.getAttribute(postAttrName) !== null) {
|
||||||
this.lastCheckedPostId = post.getAttribute(postAttrName);
|
this.lastCheckedPostId = post.getAttribute(postAttrName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
post = post.nextSibling;
|
||||||
}
|
}
|
||||||
}
|
} else if (lastPostElem !== null) {
|
||||||
else if (lastPostElem !== null) {
|
var newPostElem = lastPostElem.nextSibling;
|
||||||
var newPostElem = lastPostElem;
|
while (newPostElem) {
|
||||||
while (newPostElem = newPostElem.nextSibling) {
|
|
||||||
this.lastCheckedPostId = newPostElem.getAttribute(postAttrName);
|
this.lastCheckedPostId = newPostElem.getAttribute(postAttrName);
|
||||||
isUnread = true;
|
isUnread = true;
|
||||||
var activeChannel = document.querySelector('.active .sidebar-channel');
|
var activeChannel = document.querySelector('.active .sidebar-channel');
|
||||||
|
@ -78,19 +81,19 @@ setInterval(function() {
|
||||||
// If active channel is DM, all posts is treated as menion.
|
// If active channel is DM, all posts is treated as menion.
|
||||||
isMentioned = true;
|
isMentioned = true;
|
||||||
break;
|
break;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// If active channel is public/private channel, only mentioned post is treated as mention.
|
// If active channel is public/private channel, only mentioned post is treated as mention.
|
||||||
var highlight = newPostElem.getElementsByClassName('mention-highlight');
|
var highlight = newPostElem.getElementsByClassName('mention-highlight');
|
||||||
if (highlight.length != 0 && isElementVisible(highlight[0])) {
|
if (highlight.length !== 0 && isElementVisible(highlight[0])) {
|
||||||
isMentioned = true;
|
isMentioned = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
newPostElem = newPostElem.nextSibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.unreadCount != unreadCount || this.mentionCount != mentionCount || isUnread || isMentioned) {
|
if (this.unreadCount !== unreadCount || this.mentionCount !== mentionCount || isUnread || isMentioned) {
|
||||||
ipc.sendToHost('onUnreadCountChange', unreadCount, mentionCount, isUnread, isMentioned);
|
ipc.sendToHost('onUnreadCountChange', unreadCount, mentionCount, isUnread, isMentioned);
|
||||||
}
|
}
|
||||||
this.unreadCount = unreadCount;
|
this.unreadCount = unreadCount;
|
||||||
|
@ -102,32 +105,30 @@ function isElementVisible(elem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.override({
|
notification.override({
|
||||||
|
|
||||||
// Send a notification event to the main process.
|
// Send a notification event to the main process.
|
||||||
notification: function(title, options) {
|
notification(title, options) {
|
||||||
ipc.send('notified', {
|
ipc.send('notified', {
|
||||||
title: title,
|
title,
|
||||||
options: options
|
options
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Show window even if it is hidden/minimized when notification is clicked.
|
// Show window even if it is hidden/minimized when notification is clicked.
|
||||||
onclick: function() {
|
onclick() {
|
||||||
const currentWindow = electron.remote.getCurrentWindow();
|
const currentWindow = electron.remote.getCurrentWindow();
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
// show() breaks Aero Snap state.
|
// show() breaks Aero Snap state.
|
||||||
if (currentWindow.isVisible()) {
|
if (currentWindow.isVisible()) {
|
||||||
currentWindow.focus();
|
currentWindow.focus();
|
||||||
}
|
} else if (currentWindow.isMinimized()) {
|
||||||
else if (currentWindow.isMinimized()) {
|
|
||||||
currentWindow.restore();
|
currentWindow.restore();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
currentWindow.show();
|
currentWindow.show();
|
||||||
}
|
}
|
||||||
}
|
} else if (currentWindow.isMinimized()) {
|
||||||
else if (currentWindow.isMinimized()) {
|
|
||||||
currentWindow.restore();
|
currentWindow.restore();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
currentWindow.show();
|
currentWindow.show();
|
||||||
}
|
}
|
||||||
ipc.sendToHost('onNotificationClick');
|
ipc.sendToHost('onNotificationClick');
|
||||||
|
@ -136,27 +137,27 @@ notification.override({
|
||||||
|
|
||||||
// get the last of href for the current channel in the sidebar.
|
// get the last of href for the current channel in the sidebar.
|
||||||
function getCurrentChannelString() {
|
function getCurrentChannelString() {
|
||||||
const active_channel_link = document.querySelector('.active a.sidebar-channel');
|
const activeChannelLink = document.querySelector('.active a.sidebar-channel');
|
||||||
const url_elements = active_channel_link.href.split('/');
|
const urlElements = activeChannelLink.href.split('/');
|
||||||
return url_elements[url_elements.length - 1];
|
return urlElements[urlElements.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc.on('activate-search-box', (event) => {
|
ipc.on('activate-search-box', () => {
|
||||||
const search_boxes = document.getElementsByClassName('search-bar'); // should use id
|
const searchBoxes = document.getElementsByClassName('search-bar'); // should use id
|
||||||
if (search_boxes.length === 0) {
|
if (searchBoxes.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const search_box = search_boxes[0];
|
const searchBox = searchBoxes[0];
|
||||||
search_box.focus();
|
searchBox.focus();
|
||||||
search_box.value = ``; //Clear the input box
|
searchBox.value = ''; //Clear the input box
|
||||||
});
|
});
|
||||||
|
|
||||||
ipc.on('activate-search-box-in-channel', (event) => {
|
ipc.on('activate-search-box-in-channel', () => {
|
||||||
const search_boxes = document.getElementsByClassName('search-bar'); // should use id
|
const searchBoxes = document.getElementsByClassName('search-bar'); // should use id
|
||||||
if (search_boxes.length === 0) {
|
if (searchBoxes.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const search_box = search_boxes[0];
|
const searchBox = searchBoxes[0];
|
||||||
search_box.focus();
|
searchBox.focus();
|
||||||
search_box.value = `in:${getCurrentChannelString()} `;
|
searchBox.value = `in:${getCurrentChannelString()} `;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
var release_split = os.release().split('.');
|
var releaseSplit = os.release().split('.');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
major: parseInt(release_split[0]),
|
major: parseInt(releaseSplit[0], 10),
|
||||||
minor: parseInt(release_split[1]),
|
minor: parseInt(releaseSplit[1], 10),
|
||||||
isLowerThanOrEqualWindows8_1: function() {
|
isLowerThanOrEqualWindows8_1() {
|
||||||
if (process.platform != 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// consider Windows 7 and later.
|
// consider Windows 7 and later.
|
||||||
return (this.major <= 6 && this.minor <= 3);
|
return (this.major <= 6 && this.minor <= 3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,81 +3,78 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const settingsVersion = 1;
|
const settingsVersion = 1;
|
||||||
|
|
||||||
var merge = function(base, target) {
|
function merge(base, target) {
|
||||||
var merged = base;
|
return Object.assign({}, base, target);
|
||||||
if (!target) {
|
}
|
||||||
target = {};
|
|
||||||
}
|
|
||||||
for (var prop in target) {
|
|
||||||
merged[prop] = target[prop];
|
|
||||||
}
|
|
||||||
return merged;
|
|
||||||
};
|
|
||||||
|
|
||||||
var loadDefault = function(version) {
|
function loadDefault(version) {
|
||||||
|
var ver = version;
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
version = settingsVersion;
|
ver = settingsVersion;
|
||||||
}
|
}
|
||||||
switch (version) {
|
switch (ver) {
|
||||||
case 1:
|
case 1:
|
||||||
return {
|
return {
|
||||||
teams: [],
|
teams: [],
|
||||||
hideMenuBar: false,
|
hideMenuBar: false,
|
||||||
showTrayIcon: false,
|
showTrayIcon: false,
|
||||||
trayIconTheme: '',
|
trayIconTheme: '',
|
||||||
disablewebsecurity: true,
|
disablewebsecurity: true,
|
||||||
toggleWindowOnTrayIconClick: false,
|
toggleWindowOnTrayIconClick: false,
|
||||||
version: 1,
|
version: 1,
|
||||||
notifications: {
|
notifications: {
|
||||||
flashWindow: 0 // 0 = flash never, 1 = only when idle (after 10 seconds), 2 = always
|
flashWindow: 0 // 0 = flash never, 1 = only when idle (after 10 seconds), 2 = always
|
||||||
},
|
},
|
||||||
showUnreadBadge: true
|
showUnreadBadge: true
|
||||||
};
|
};
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
var upgradeV0toV1 = function(config_v0) {
|
function upgradeV0toV1(configV0) {
|
||||||
var config = loadDefault(1);
|
var config = loadDefault(1);
|
||||||
config.teams.push({
|
config.teams.push({
|
||||||
name: 'Primary team',
|
name: 'Primary team',
|
||||||
url: config_v0.url
|
url: configV0.url
|
||||||
});
|
});
|
||||||
return config;
|
return config;
|
||||||
};
|
}
|
||||||
|
|
||||||
var upgrade = function(config, newAppVersion) {
|
function upgrade(config, newAppVersion) {
|
||||||
var config_version = config.version ? config.version : 0;
|
var configVersion = config.version ? config.version : 0;
|
||||||
if (newAppVersion)
|
if (newAppVersion) {
|
||||||
config.lastMattermostVersion = newAppVersion;
|
config.lastMattermostVersion = newAppVersion;
|
||||||
switch (config_version) {
|
|
||||||
case 0:
|
|
||||||
return upgrade(upgradeV0toV1(config));
|
|
||||||
default:
|
|
||||||
return config;
|
|
||||||
}
|
}
|
||||||
};
|
switch (configVersion) {
|
||||||
|
case 0:
|
||||||
|
return upgrade(upgradeV0toV1(config));
|
||||||
|
default:
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
version: settingsVersion,
|
version: settingsVersion,
|
||||||
|
|
||||||
upgrade: upgrade,
|
upgrade,
|
||||||
|
|
||||||
readFileSync: function(configFile) {
|
readFileSync(configFile) {
|
||||||
var config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
var config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
||||||
if (config.version === settingsVersion) {
|
if (config.version === settingsVersion) {
|
||||||
var default_config = this.loadDefault();
|
var defaultConfig = this.loadDefault();
|
||||||
config = merge(default_config, config);
|
config = merge(defaultConfig, config);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
|
|
||||||
writeFileSync: function(configFile, config) {
|
writeFileSync(configFile, config) {
|
||||||
if (config.version != settingsVersion) {
|
if (config.version !== settingsVersion) {
|
||||||
throw 'version ' + config.version + ' is not equal to ' + settingsVersion;
|
throw new Error('version ' + config.version + ' is not equal to ' + settingsVersion);
|
||||||
}
|
}
|
||||||
var data = JSON.stringify(config, null, ' ');
|
var data = JSON.stringify(config, null, ' ');
|
||||||
fs.writeFileSync(configFile, data, 'utf8');
|
fs.writeFileSync(configFile, data, 'utf8');
|
||||||
},
|
},
|
||||||
|
|
||||||
loadDefault: loadDefault
|
loadDefault
|
||||||
};
|
};
|
||||||
|
|
303
src/main.js
303
src/main.js
|
@ -12,32 +12,34 @@ const {
|
||||||
session
|
session
|
||||||
} = require('electron');
|
} = require('electron');
|
||||||
|
|
||||||
|
const AutoLaunch = require('auto-launch');
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
process.on('uncaughtException', (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
var cmd = process.argv[1];
|
var cmd = process.argv[1];
|
||||||
var AutoLaunch = require('auto-launch');
|
|
||||||
var appLauncher = new AutoLaunch({
|
var appLauncher = new AutoLaunch({
|
||||||
name: 'Mattermost',
|
name: 'Mattermost',
|
||||||
isHidden: true
|
isHidden: true
|
||||||
});
|
});
|
||||||
if (cmd === '--squirrel-uninstall') {
|
if (cmd === '--squirrel-uninstall') {
|
||||||
// If we're uninstalling, make sure we also delete our auto launch registry key
|
// If we're uninstalling, make sure we also delete our auto launch registry key
|
||||||
appLauncher.isEnabled().then(function(enabled) {
|
appLauncher.isEnabled().then((enabled) => {
|
||||||
if (enabled)
|
|
||||||
appLauncher.disable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
|
|
||||||
// If we're updating and already have an registry entry for auto launch, make sure to update the path
|
|
||||||
appLauncher.isEnabled().then(function(enabled) {
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
return appLauncher.disable().then(function() {
|
appLauncher.disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
|
||||||
|
// If we're updating and already have an registry entry for auto launch, make sure to update the path
|
||||||
|
appLauncher.isEnabled().then((enabled) => {
|
||||||
|
if (enabled) {
|
||||||
|
return appLauncher.disable().then(() => {
|
||||||
return appLauncher.enable();
|
return appLauncher.enable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,21 +48,27 @@ app.setAppUserModelId('com.squirrel.mattermost.Mattermost'); // Use explicit App
|
||||||
require('electron-squirrel-startup');
|
require('electron-squirrel-startup');
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
var settings = require('./common/settings');
|
var settings = require('./common/settings');
|
||||||
const osVersion = require('./common/osVersion');
|
const osVersion = require('./common/osVersion');
|
||||||
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
|
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
|
||||||
var appMenu = require('./main/menus/app');
|
const appMenu = require('./main/menus/app');
|
||||||
|
const trayMenu = require('./main/menus/tray');
|
||||||
const allowProtocolDialog = require('./main/allowProtocolDialog');
|
const allowProtocolDialog = require('./main/allowProtocolDialog');
|
||||||
|
|
||||||
var argv = require('yargs')
|
// Keep a global reference of the window object, if you don't, the window will
|
||||||
.parse(process.argv.slice(1));
|
// be closed automatically when the JavaScript object is garbage collected.
|
||||||
|
var mainWindow = null;
|
||||||
|
|
||||||
var client = null;
|
var argv = require('yargs').parse(process.argv.slice(1));
|
||||||
|
|
||||||
|
const electronConnect = argv.livereload ? require('electron-connect') : null;
|
||||||
|
var client;
|
||||||
if (argv.livereload) {
|
if (argv.livereload) {
|
||||||
client = require('electron-connect').client.create();
|
client = electronConnect.client.create();
|
||||||
client.on('reload', function() {
|
client.on('reload', () => {
|
||||||
mainWindow.reload();
|
mainWindow.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -73,56 +81,53 @@ if (argv.hidden) {
|
||||||
// TODO: We should document this if that hasn't been done already
|
// TODO: We should document this if that hasn't been done already
|
||||||
if (argv['config-file']) {
|
if (argv['config-file']) {
|
||||||
global['config-file'] = argv['config-file'];
|
global['config-file'] = argv['config-file'];
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
global['config-file'] = app.getPath('userData') + '/config.json';
|
global['config-file'] = app.getPath('userData') + '/config.json';
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = {};
|
var config = {};
|
||||||
try {
|
try {
|
||||||
var configFile = global['config-file'];
|
const configFile = global['config-file'];
|
||||||
config = settings.readFileSync(configFile);
|
config = settings.readFileSync(configFile);
|
||||||
if (config.version != settings.version || wasUpdated()) {
|
if (config.version !== settings.version || wasUpdated()) {
|
||||||
clearAppCache();
|
clearAppCache();
|
||||||
config = settings.upgrade(config, app.getVersion());
|
config = settings.upgrade(config, app.getVersion());
|
||||||
settings.writeFileSync(configFile, config);
|
settings.writeFileSync(configFile, config);
|
||||||
}
|
}
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
config = settings.loadDefault();
|
config = settings.loadDefault();
|
||||||
console.log('Failed to read or upgrade config.json', e);
|
console.log('Failed to read or upgrade config.json', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('update-config', () => {
|
ipcMain.on('update-config', () => {
|
||||||
|
const configFile = global['config-file'];
|
||||||
config = settings.readFileSync(configFile);
|
config = settings.readFileSync(configFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only for OS X
|
// Only for OS X
|
||||||
const switchMenuIconImages = function(icons, isDarkMode) {
|
function switchMenuIconImages(icons, isDarkMode) {
|
||||||
if (isDarkMode) {
|
if (isDarkMode) {
|
||||||
icons.normal = icons.clicked.normal;
|
icons.normal = icons.clicked.normal;
|
||||||
icons.unread = icons.clicked.unread;
|
icons.unread = icons.clicked.unread;
|
||||||
icons.mention = icons.clicked.mention;
|
icons.mention = icons.clicked.mention;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
icons.normal = icons.light.normal;
|
icons.normal = icons.light.normal;
|
||||||
icons.unread = icons.light.unread;
|
icons.unread = icons.light.unread;
|
||||||
icons.mention = icons.light.mention;
|
icons.mention = icons.light.mention;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Keep a global reference of the window object, if you don't, the window will
|
|
||||||
// be closed automatically when the JavaScript object is garbage collected.
|
|
||||||
var mainWindow = null;
|
|
||||||
var trayIcon = null;
|
var trayIcon = null;
|
||||||
const trayImages = function() {
|
const trayImages = (() => {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
return {
|
return {
|
||||||
normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray.ico')),
|
normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray.ico')),
|
||||||
unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_unread.ico')),
|
unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_unread.ico')),
|
||||||
mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_mention.ico'))
|
mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_mention.ico'))
|
||||||
};
|
};
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
|
{
|
||||||
const icons = {
|
const icons = {
|
||||||
light: {
|
light: {
|
||||||
normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIcon.png')),
|
normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIcon.png')),
|
||||||
|
@ -137,27 +142,31 @@ const trayImages = function() {
|
||||||
};
|
};
|
||||||
switchMenuIconImages(icons, systemPreferences.isDarkMode());
|
switchMenuIconImages(icons, systemPreferences.isDarkMode());
|
||||||
return icons;
|
return icons;
|
||||||
case 'linux':
|
}
|
||||||
var resourcesDir = 'resources/linux/' + (config.trayIconTheme || 'light') + '/';
|
case 'linux':
|
||||||
return {
|
var resourcesDir = 'resources/linux/' + (config.trayIconTheme || 'light') + '/';
|
||||||
normal: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconTemplate.png')),
|
return {
|
||||||
unread: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconUnreadTemplate.png')),
|
normal: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconTemplate.png')),
|
||||||
mention: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconMentionTemplate.png'))
|
unread: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconUnreadTemplate.png')),
|
||||||
};
|
mention: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconMentionTemplate.png'))
|
||||||
default:
|
};
|
||||||
return {};
|
default:
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
}();
|
})();
|
||||||
var willAppQuit = false;
|
var willAppQuit = false;
|
||||||
|
|
||||||
// If there is already an instance, activate the window in the existing instace and quit this one
|
// If there is already an instance, activate the window in the existing instace and quit this one
|
||||||
if (app.makeSingleInstance((commandLine, workingDirectory) => {
|
if (app.makeSingleInstance((/*commandLine, workingDirectory*/) => {
|
||||||
// Someone tried to run a second instance, we should focus our window.
|
// Someone tried to run a second instance, we should focus our window.
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
if (mainWindow.isMinimized()) {
|
||||||
else mainWindow.show();
|
mainWindow.restore();
|
||||||
|
} else {
|
||||||
|
mainWindow.show();
|
||||||
}
|
}
|
||||||
})) {
|
}
|
||||||
|
})) {
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,25 +181,24 @@ function shouldShowTrayIcon() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function wasUpdated() {
|
function wasUpdated() {
|
||||||
return config.lastMattermostVersion != app.getVersion();
|
return config.lastMattermostVersion !== app.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAppCache() {
|
function clearAppCache() {
|
||||||
//Wait for mainWindow
|
if (mainWindow) {
|
||||||
if (!mainWindow) {
|
|
||||||
setTimeout(clearAppCache, 100);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log('Clear cache after update');
|
console.log('Clear cache after update');
|
||||||
mainWindow.webContents.session.clearCache(function() {
|
mainWindow.webContents.session.clearCache(() => {
|
||||||
//Restart after cache clear
|
//Restart after cache clear
|
||||||
mainWindow.reload();
|
mainWindow.reload();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
//Wait for mainWindow
|
||||||
|
setTimeout(clearAppCache, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit when all windows are closed.
|
// Quit when all windows are closed.
|
||||||
app.on('window-all-closed', function() {
|
app.on('window-all-closed', () => {
|
||||||
// On OS X it is common for applications and their menu bar
|
// On OS X it is common for applications and their menu bar
|
||||||
// to stay active until the user quits explicitly with Cmd + Q
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
|
@ -199,7 +207,7 @@ app.on('window-all-closed', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// For win32, auto-hide menu bar.
|
// For win32, auto-hide menu bar.
|
||||||
app.on('browser-window-created', function(event, window) {
|
app.on('browser-window-created', (event, window) => {
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
if (config.hideMenuBar) {
|
if (config.hideMenuBar) {
|
||||||
window.setAutoHideMenuBar(true);
|
window.setAutoHideMenuBar(true);
|
||||||
|
@ -209,11 +217,11 @@ app.on('browser-window-created', function(event, window) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// For OSX, show hidden mainWindow when clicking dock icon.
|
// For OSX, show hidden mainWindow when clicking dock icon.
|
||||||
app.on('activate', function(event) {
|
app.on('activate', () => {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('before-quit', function() {
|
app.on('before-quit', () => {
|
||||||
// Make sure tray icon gets removed if the user exits via CTRL-Q
|
// Make sure tray icon gets removed if the user exits via CTRL-Q
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
trayIcon.destroy();
|
trayIcon.destroy();
|
||||||
|
@ -221,12 +229,11 @@ app.on('before-quit', function() {
|
||||||
willAppQuit = true;
|
willAppQuit = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('certificate-error', function(event, webContents, url, error, certificate, callback) {
|
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
||||||
if (certificateStore.isTrusted(url, certificate)) {
|
if (certificateStore.isTrusted(url, certificate)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
callback(true);
|
callback(true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var detail = `URL: ${url}\nError: ${error}`;
|
var detail = `URL: ${url}\nError: ${error}`;
|
||||||
if (certificateStore.isExisting(url)) {
|
if (certificateStore.isExisting(url)) {
|
||||||
detail = `Certificate is different from previous one.\n\n` + detail;
|
detail = `Certificate is different from previous one.\n\n` + detail;
|
||||||
|
@ -235,14 +242,14 @@ app.on('certificate-error', function(event, webContents, url, error, certificate
|
||||||
dialog.showMessageBox(mainWindow, {
|
dialog.showMessageBox(mainWindow, {
|
||||||
title: 'Certificate error',
|
title: 'Certificate error',
|
||||||
message: `Do you trust certificate from "${certificate.issuerName}"?`,
|
message: `Do you trust certificate from "${certificate.issuerName}"?`,
|
||||||
detail: detail,
|
detail,
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
buttons: [
|
buttons: [
|
||||||
'Yes',
|
'Yes',
|
||||||
'No'
|
'No'
|
||||||
],
|
],
|
||||||
cancelId: 1
|
cancelId: 1
|
||||||
}, function(response) {
|
}, (response) => {
|
||||||
if (response === 0) {
|
if (response === 0) {
|
||||||
certificateStore.add(url, certificate);
|
certificateStore.add(url, certificate);
|
||||||
certificateStore.save();
|
certificateStore.save();
|
||||||
|
@ -255,14 +262,14 @@ app.on('certificate-error', function(event, webContents, url, error, certificate
|
||||||
|
|
||||||
const loginCallbackMap = new Map();
|
const loginCallbackMap = new Map();
|
||||||
|
|
||||||
ipcMain.on('login-credentials', function(event, request, user, password) {
|
ipcMain.on('login-credentials', (event, request, user, password) => {
|
||||||
const callback = loginCallbackMap.get(JSON.stringify(request));
|
const callback = loginCallbackMap.get(JSON.stringify(request));
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
callback(user, password);
|
callback(user, password);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('login', function(event, webContents, request, authInfo, callback) {
|
app.on('login', (event, webContents, request, authInfo, callback) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
loginCallbackMap.set(JSON.stringify(request), callback);
|
loginCallbackMap.set(JSON.stringify(request), callback);
|
||||||
mainWindow.webContents.send('login-request', request, authInfo);
|
mainWindow.webContents.send('login-request', request, authInfo);
|
||||||
|
@ -272,8 +279,8 @@ allowProtocolDialog.init(mainWindow);
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
app.on('ready', function() {
|
app.on('ready', () => {
|
||||||
ipcMain.on('notified', function(event, arg) {
|
ipcMain.on('notified', () => {
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
if (config.notifications.flashWindow === 2) {
|
if (config.notifications.flashWindow === 2) {
|
||||||
mainWindow.flashFrame(true);
|
mainWindow.flashFrame(true);
|
||||||
|
@ -281,7 +288,7 @@ app.on('ready', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('update-title', function(event, arg) {
|
ipcMain.on('update-title', (event, arg) => {
|
||||||
mainWindow.setTitle(arg.title);
|
mainWindow.setTitle(arg.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -290,30 +297,27 @@ app.on('ready', function() {
|
||||||
trayIcon = new Tray(trayImages.normal);
|
trayIcon = new Tray(trayImages.normal);
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
trayIcon.setPressedImage(trayImages.clicked.normal);
|
trayIcon.setPressedImage(trayImages.clicked.normal);
|
||||||
systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', (event, userInfo) => {
|
systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => {
|
||||||
switchMenuIconImages(trayImages, systemPreferences.isDarkMode());
|
switchMenuIconImages(trayImages, systemPreferences.isDarkMode());
|
||||||
trayIcon.setImage(trayImages.normal);
|
trayIcon.setImage(trayImages.normal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
trayIcon.setToolTip(app.getName());
|
trayIcon.setToolTip(app.getName());
|
||||||
trayIcon.on('click', function() {
|
trayIcon.on('click', () => {
|
||||||
if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
|
if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
|
||||||
if (mainWindow.isMinimized()) {
|
if (mainWindow.isMinimized()) {
|
||||||
mainWindow.restore();
|
mainWindow.restore();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
}
|
}
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
app.dock.show();
|
app.dock.show();
|
||||||
}
|
}
|
||||||
}
|
} else if ((process.platform === 'win32') && config.toggleWindowOnTrayIconClick) {
|
||||||
else if ((process.platform === 'win32') && config.toggleWindowOnTrayIconClick) {
|
|
||||||
mainWindow.minimize();
|
mainWindow.minimize();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -321,10 +325,13 @@ app.on('ready', function() {
|
||||||
trayIcon.on('right-click', () => {
|
trayIcon.on('right-click', () => {
|
||||||
trayIcon.popUpContextMenu();
|
trayIcon.popUpContextMenu();
|
||||||
});
|
});
|
||||||
trayIcon.on('balloon-click', function() {
|
trayIcon.on('balloon-click', () => {
|
||||||
if (process.platform === 'win32' || process.platform === 'darwin') {
|
if (process.platform === 'win32' || process.platform === 'darwin') {
|
||||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
if (mainWindow.isMinimized()) {
|
||||||
else mainWindow.show();
|
mainWindow.restore();
|
||||||
|
} else {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
|
@ -333,7 +340,7 @@ app.on('ready', function() {
|
||||||
|
|
||||||
mainWindow.focus();
|
mainWindow.focus();
|
||||||
});
|
});
|
||||||
ipcMain.on('notified', function(event, arg) {
|
ipcMain.on('notified', (event, arg) => {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
// On Windows 8.1 and Windows 8, a shortcut with a Application User Model ID must be installed to the Start screen.
|
// On Windows 8.1 and Windows 8, a shortcut with a Application User Model ID must be installed to the Start screen.
|
||||||
// In current version, use tray balloon for notification
|
// In current version, use tray balloon for notification
|
||||||
|
@ -349,7 +356,7 @@ app.on('ready', function() {
|
||||||
|
|
||||||
// Set overlay icon from dataURL
|
// Set overlay icon from dataURL
|
||||||
// Set trayicon to show "dot"
|
// Set trayicon to show "dot"
|
||||||
ipcMain.on('update-unread', function(event, arg) {
|
ipcMain.on('update-unread', (event, arg) => {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
const overlay = arg.overlayDataURL ? nativeImage.createFromDataURL(arg.overlayDataURL) : null;
|
const overlay = arg.overlayDataURL ? nativeImage.createFromDataURL(arg.overlayDataURL) : null;
|
||||||
mainWindow.setOverlayIcon(overlay, arg.description);
|
mainWindow.setOverlayIcon(overlay, arg.description);
|
||||||
|
@ -361,15 +368,13 @@ app.on('ready', function() {
|
||||||
trayIcon.setPressedImage(trayImages.clicked.mention);
|
trayIcon.setPressedImage(trayImages.clicked.mention);
|
||||||
}
|
}
|
||||||
trayIcon.setToolTip(arg.mentionCount + ' unread mentions');
|
trayIcon.setToolTip(arg.mentionCount + ' unread mentions');
|
||||||
}
|
} else if (arg.unreadCount > 0) {
|
||||||
else if (arg.unreadCount > 0) {
|
|
||||||
trayIcon.setImage(trayImages.unread);
|
trayIcon.setImage(trayImages.unread);
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
trayIcon.setPressedImage(trayImages.clicked.unread);
|
trayIcon.setPressedImage(trayImages.clicked.unread);
|
||||||
}
|
}
|
||||||
trayIcon.setToolTip(arg.unreadCount + ' unread channels');
|
trayIcon.setToolTip(arg.unreadCount + ' unread channels');
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
trayIcon.setImage(trayImages.normal);
|
trayIcon.setImage(trayImages.normal);
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
trayIcon.setPressedImage(trayImages.clicked.normal);
|
trayIcon.setPressedImage(trayImages.clicked.normal);
|
||||||
|
@ -380,33 +385,31 @@ app.on('ready', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
var bounds_info_path = path.resolve(app.getPath("userData"), "bounds-info.json");
|
var boundsInfoPath = path.resolve(app.getPath('userData'), 'bounds-info.json');
|
||||||
var window_options;
|
var windowOptions;
|
||||||
try {
|
try {
|
||||||
window_options = JSON.parse(fs.readFileSync(bounds_info_path, 'utf-8'));
|
windowOptions = JSON.parse(fs.readFileSync(boundsInfoPath, 'utf-8'));
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
// follow Electron's defaults
|
// follow Electron's defaults
|
||||||
window_options = {};
|
windowOptions = {};
|
||||||
}
|
}
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
window_options.icon = path.resolve(__dirname, 'resources/appicon.png');
|
windowOptions.icon = path.resolve(__dirname, 'resources/appicon.png');
|
||||||
}
|
}
|
||||||
window_options.title = app.getName();
|
windowOptions.title = app.getName();
|
||||||
mainWindow = new BrowserWindow(window_options);
|
mainWindow = new BrowserWindow(windowOptions);
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
session.defaultSession.on('will-download', (event, item, webContents) => {
|
session.defaultSession.on('will-download', (event, item) => {
|
||||||
var filename = item.getFilename();
|
var filename = item.getFilename();
|
||||||
var savePath = dialog.showSaveDialog({
|
var savePath = dialog.showSaveDialog({
|
||||||
title: filename,
|
title: filename,
|
||||||
defaultPath: require('os').homedir() + '/Downloads/' + filename
|
defaultPath: os.homedir() + '/Downloads/' + filename
|
||||||
});
|
});
|
||||||
|
|
||||||
if (savePath) {
|
if (savePath) {
|
||||||
item.setSavePath(savePath);
|
item.setSavePath(savePath);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
item.cancel();
|
item.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -423,12 +426,11 @@ app.on('ready', function() {
|
||||||
mainWindow.setFullScreenable(true); // fullscreenable option has no effect.
|
mainWindow.setFullScreenable(true); // fullscreenable option has no effect.
|
||||||
if (hideOnStartup) {
|
if (hideOnStartup) {
|
||||||
mainWindow.minimize();
|
mainWindow.minimize();
|
||||||
}
|
} else {
|
||||||
else {
|
if (windowOptions.maximized) {
|
||||||
if (window_options.maximized) {
|
|
||||||
mainWindow.maximize();
|
mainWindow.maximize();
|
||||||
}
|
}
|
||||||
if (window_options.fullscreen) {
|
if (windowOptions.fullscreen) {
|
||||||
mainWindow.setFullScreen(true);
|
mainWindow.setFullScreen(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,20 +439,22 @@ app.on('ready', function() {
|
||||||
mainWindow.loadURL('file://' + __dirname + '/browser/index.html');
|
mainWindow.loadURL('file://' + __dirname + '/browser/index.html');
|
||||||
|
|
||||||
// Set application menu
|
// Set application menu
|
||||||
ipcMain.on('update-menu', (event, config) => {
|
ipcMain.on('update-menu', (event, configData) => {
|
||||||
var app_menu = appMenu.createMenu(mainWindow, config);
|
var aMenu = appMenu.createMenu(mainWindow, config);
|
||||||
Menu.setApplicationMenu(app_menu);
|
Menu.setApplicationMenu(aMenu);
|
||||||
|
|
||||||
// set up context menu for tray icon
|
// set up context menu for tray icon
|
||||||
if (shouldShowTrayIcon()) {
|
if (shouldShowTrayIcon()) {
|
||||||
const tray_menu = require('./main/menus/tray').createMenu(mainWindow, config);
|
const tMenu = trayMenu.createMenu(mainWindow, configData);
|
||||||
trayIcon.setContextMenu(tray_menu);
|
trayIcon.setContextMenu(tMenu);
|
||||||
if (process.platform === 'darwin' || process.platform === 'linux') {
|
if (process.platform === 'darwin' || process.platform === 'linux') {
|
||||||
// store the information, if the tray was initialized, for checking in the settings, if the application
|
// store the information, if the tray was initialized, for checking in the settings, if the application
|
||||||
// was restarted after setting "Show icon on menu bar"
|
// was restarted after setting "Show icon on menu bar"
|
||||||
if (trayIcon)
|
if (trayIcon) {
|
||||||
mainWindow.trayWasVisible = true;
|
mainWindow.trayWasVisible = true;
|
||||||
else
|
} else {
|
||||||
mainWindow.trayWasVisible = false;
|
mainWindow.trayWasVisible = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -459,47 +463,44 @@ app.on('ready', function() {
|
||||||
// Open the DevTools.
|
// Open the DevTools.
|
||||||
// mainWindow.openDevTools();
|
// mainWindow.openDevTools();
|
||||||
|
|
||||||
var saveWindowState = function(file, window) {
|
function saveWindowState(file, window) {
|
||||||
var window_state = window.getBounds();
|
var windowState = window.getBounds();
|
||||||
window_state.maximized = window.isMaximized();
|
windowState.maximized = window.isMaximized();
|
||||||
window_state.fullscreen = window.isFullScreen();
|
windowState.fullscreen = window.isFullScreen();
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(bounds_info_path, JSON.stringify(window_state));
|
fs.writeFileSync(boundsInfoPath, JSON.stringify(windowState));
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
// [Linux] error happens only when the window state is changed before the config dir is creatied.
|
// [Linux] error happens only when the window state is changed before the config dir is creatied.
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
mainWindow.on('close', function(event) {
|
mainWindow.on('close', (event) => {
|
||||||
if (willAppQuit) { // when [Ctrl|Cmd]+Q
|
if (willAppQuit) { // when [Ctrl|Cmd]+Q
|
||||||
saveWindowState(bounds_info_path, mainWindow);
|
saveWindowState(boundsInfoPath, mainWindow);
|
||||||
}
|
} else { // Minimize or hide the window for close button.
|
||||||
else { // Minimize or hide the window for close button.
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const hide_window = (window) => {
|
function hideWindow(window) {
|
||||||
window.hide();
|
window.hide();
|
||||||
window.blur(); // To move focus to the next top-level window in Windows
|
window.blur(); // To move focus to the next top-level window in Windows
|
||||||
};
|
}
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
hide_window(mainWindow);
|
hideWindow(mainWindow);
|
||||||
break;
|
break;
|
||||||
case 'linux':
|
case 'linux':
|
||||||
if (config.minimizeToTray) {
|
if (config.minimizeToTray) {
|
||||||
hide_window(mainWindow);
|
hideWindow(mainWindow);
|
||||||
}
|
} else {
|
||||||
else {
|
mainWindow.minimize();
|
||||||
mainWindow.minimize();
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case 'darwin':
|
||||||
case 'darwin':
|
hideWindow(mainWindow);
|
||||||
hide_window(mainWindow);
|
if (config.minimizeToTray) {
|
||||||
if (config.minimizeToTray) {
|
app.dock.hide();
|
||||||
app.dock.hide();
|
}
|
||||||
}
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -509,12 +510,12 @@ app.on('ready', function() {
|
||||||
// because main process is killed in such situations.
|
// because main process is killed in such situations.
|
||||||
// 'blur' event was effective in order to avoid this.
|
// 'blur' event was effective in order to avoid this.
|
||||||
// Ideally, app should detect that OS is shutting down.
|
// Ideally, app should detect that OS is shutting down.
|
||||||
mainWindow.on('blur', function() {
|
mainWindow.on('blur', () => {
|
||||||
saveWindowState(bounds_info_path, mainWindow);
|
saveWindowState(boundsInfoPath, mainWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emitted when the window is closed.
|
// Emitted when the window is closed.
|
||||||
mainWindow.on('closed', function() {
|
mainWindow.on('closed', () => {
|
||||||
// Dereference the window object, usually you would store windows
|
// Dereference the window object, usually you would store windows
|
||||||
// in an array if your app supports multi windows, this is the time
|
// in an array if your app supports multi windows, this is the time
|
||||||
// when you should delete the corresponding element.
|
// when you should delete the corresponding element.
|
||||||
|
@ -523,7 +524,7 @@ app.on('ready', function() {
|
||||||
|
|
||||||
// Deny drag&drop navigation in mainWindow.
|
// Deny drag&drop navigation in mainWindow.
|
||||||
// Drag&drop is allowed in webview of index.html.
|
// Drag&drop is allowed in webview of index.html.
|
||||||
mainWindow.webContents.on('will-navigate', function(event, url) {
|
mainWindow.webContents.on('will-navigate', (event, url) => {
|
||||||
var dirname = __dirname;
|
var dirname = __dirname;
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
dirname = '/' + dirname.replace(/\\/g, '/');
|
dirname = '/' + dirname.replace(/\\/g, '/');
|
||||||
|
|
|
@ -9,7 +9,7 @@ const {
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const allowedProtocolFile = path.resolve(app.getPath('userData'), 'allowedProtocols.json')
|
const allowedProtocolFile = path.resolve(app.getPath('userData'), 'allowedProtocols.json');
|
||||||
var allowedProtocols = [];
|
var allowedProtocols = [];
|
||||||
|
|
||||||
function init(mainWindow) {
|
function init(mainWindow) {
|
||||||
|
@ -41,22 +41,27 @@ function initDialogEvent(mainWindow) {
|
||||||
noLink: true
|
noLink: true
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case 1:
|
case 1: {
|
||||||
allowedProtocols.push(protocol);
|
allowedProtocols.push(protocol);
|
||||||
fs.writeFile(allowedProtocolFile, JSON.stringify(allowedProtocols), (err) => {
|
function handleError(err) {
|
||||||
if (err) console.error(err);
|
if (err) {
|
||||||
});
|
console.error(err);
|
||||||
// fallthrough
|
}
|
||||||
case 0:
|
}
|
||||||
shell.openExternal(URL);
|
fs.writeFile(allowedProtocolFile, JSON.stringify(allowedProtocols), handleError);
|
||||||
break;
|
shell.openExternal(URL);
|
||||||
default:
|
break;
|
||||||
break;
|
}
|
||||||
|
case 0:
|
||||||
|
shell.openExternal(URL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: init
|
init
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,37 +24,35 @@ function getHost(targetURL) {
|
||||||
return url.parse(targetURL).host;
|
return url.parse(targetURL).host;
|
||||||
}
|
}
|
||||||
|
|
||||||
var CertificateStore = function(storeFile) {
|
function CertificateStore(storeFile) {
|
||||||
this.storeFile = storeFile;
|
this.storeFile = storeFile;
|
||||||
let storeStr;
|
let storeStr;
|
||||||
try {
|
try {
|
||||||
storeStr = fs.readFileSync(storeFile, 'utf-8')
|
storeStr = fs.readFileSync(storeFile, 'utf-8');
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
storeStr = '{}';
|
storeStr = '{}';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.data = JSON.parse(storeStr);
|
this.data = JSON.parse(storeStr);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
console.log('Error when parsing', storeFile, ':', e);
|
console.log('Error when parsing', storeFile, ':', e);
|
||||||
this.data = {};
|
this.data = {};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
CertificateStore.prototype.save = function() {
|
CertificateStore.prototype.save = function save() {
|
||||||
fs.writeFileSync(this.storeFile, JSON.stringify(this.data, null, ' '));
|
fs.writeFileSync(this.storeFile, JSON.stringify(this.data, null, ' '));
|
||||||
};
|
};
|
||||||
|
|
||||||
CertificateStore.prototype.add = function(targetURL, certificate) {
|
CertificateStore.prototype.add = function add(targetURL, certificate) {
|
||||||
this.data[getHost(targetURL)] = comparableCertificate(certificate);
|
this.data[getHost(targetURL)] = comparableCertificate(certificate);
|
||||||
};
|
};
|
||||||
|
|
||||||
CertificateStore.prototype.isExisting = function(targetURL) {
|
CertificateStore.prototype.isExisting = function isExisting(targetURL) {
|
||||||
return this.data.hasOwnProperty(getHost(targetURL));
|
return this.data.hasOwnProperty(getHost(targetURL));
|
||||||
};
|
};
|
||||||
|
|
||||||
CertificateStore.prototype.isTrusted = function(targetURL, certificate) {
|
CertificateStore.prototype.isTrusted = function isTrusted(targetURL, certificate) {
|
||||||
var host = getHost(targetURL);
|
var host = getHost(targetURL);
|
||||||
if (!this.isExisting(targetURL)) {
|
if (!this.isExisting(targetURL)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -63,7 +61,7 @@ CertificateStore.prototype.isTrusted = function(targetURL, certificate) {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
load: function(storeFile) {
|
load(storeFile) {
|
||||||
return new CertificateStore(storeFile);
|
return new CertificateStore(storeFile);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,28 +3,28 @@
|
||||||
const electron = require('electron');
|
const electron = require('electron');
|
||||||
const Menu = electron.Menu;
|
const Menu = electron.Menu;
|
||||||
|
|
||||||
var createTemplate = function(mainWindow, config) {
|
function createTemplate(mainWindow, config) {
|
||||||
const separatorItem = {
|
const separatorItem = {
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
};
|
};
|
||||||
|
|
||||||
var app_name = electron.app.getName();
|
var appName = electron.app.getName();
|
||||||
var first_menu_name = (process.platform === 'darwin') ? app_name : 'File';
|
var firstMenuName = (process.platform === 'darwin') ? appName : 'File';
|
||||||
var template = [];
|
var template = [];
|
||||||
|
|
||||||
const platformAppMenu = process.platform === 'darwin' ? [{
|
const platformAppMenu = process.platform === 'darwin' ? [{
|
||||||
label: 'About ' + app_name,
|
label: 'About ' + appName,
|
||||||
role: 'about',
|
role: 'about',
|
||||||
click: function(item, focusedWindow) {
|
click() {
|
||||||
electron.dialog.showMessageBox(mainWindow, {
|
electron.dialog.showMessageBox(mainWindow, {
|
||||||
buttons: ["OK"],
|
buttons: ['OK'],
|
||||||
message: `${app_name} Desktop ${electron.app.getVersion()}`
|
message: `${appName} Desktop ${electron.app.getVersion()}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, separatorItem, {
|
}, separatorItem, {
|
||||||
label: 'Preferences...',
|
label: 'Preferences...',
|
||||||
accelerator: 'CmdOrCtrl+,',
|
accelerator: 'CmdOrCtrl+,',
|
||||||
click: function(item, focusedWindow) {
|
click() {
|
||||||
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
||||||
}
|
}
|
||||||
}, separatorItem, {
|
}, separatorItem, {
|
||||||
|
@ -38,19 +38,19 @@ var createTemplate = function(mainWindow, config) {
|
||||||
}] : [{
|
}] : [{
|
||||||
label: 'Settings...',
|
label: 'Settings...',
|
||||||
accelerator: 'CmdOrCtrl+,',
|
accelerator: 'CmdOrCtrl+,',
|
||||||
click: function(item, focusedWindow) {
|
click() {
|
||||||
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
||||||
}
|
}
|
||||||
}, separatorItem, {
|
}, separatorItem, {
|
||||||
role: 'quit',
|
role: 'quit',
|
||||||
accelerator: 'CmdOrCtrl+Q',
|
accelerator: 'CmdOrCtrl+Q',
|
||||||
click: function(item, focusedWindow) {
|
click() {
|
||||||
electron.app.quit();
|
electron.app.quit();
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
template.push({
|
template.push({
|
||||||
label: '&' + first_menu_name,
|
label: '&' + firstMenuName,
|
||||||
submenu: [
|
submenu: [
|
||||||
...platformAppMenu
|
...platformAppMenu
|
||||||
]
|
]
|
||||||
|
@ -84,12 +84,11 @@ var createTemplate = function(mainWindow, config) {
|
||||||
submenu: [{
|
submenu: [{
|
||||||
label: 'Reload',
|
label: 'Reload',
|
||||||
accelerator: 'CmdOrCtrl+R',
|
accelerator: 'CmdOrCtrl+R',
|
||||||
click: function(item, focusedWindow) {
|
click(item, focusedWindow) {
|
||||||
if (focusedWindow) {
|
if (focusedWindow) {
|
||||||
if (focusedWindow === mainWindow) {
|
if (focusedWindow === mainWindow) {
|
||||||
mainWindow.webContents.send('reload-tab');
|
mainWindow.webContents.send('reload-tab');
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
focusedWindow.reload();
|
focusedWindow.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,13 +96,12 @@ var createTemplate = function(mainWindow, config) {
|
||||||
}, {
|
}, {
|
||||||
label: 'Clear Cache and Reload',
|
label: 'Clear Cache and Reload',
|
||||||
accelerator: 'Shift+CmdOrCtrl+R',
|
accelerator: 'Shift+CmdOrCtrl+R',
|
||||||
click: function(item, focusedWindow) {
|
click(item, focusedWindow) {
|
||||||
if (focusedWindow) {
|
if (focusedWindow) {
|
||||||
if (focusedWindow === mainWindow) {
|
if (focusedWindow === mainWindow) {
|
||||||
mainWindow.webContents.send('clear-cache-and-reload-tab');
|
mainWindow.webContents.send('clear-cache-and-reload-tab');
|
||||||
}
|
} else {
|
||||||
else {
|
focusedWindow.webContents.session.clearCache(() => {
|
||||||
focusedWindow.webContents.session.clearCache(function() {
|
|
||||||
focusedWindow.reload();
|
focusedWindow.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -124,15 +122,13 @@ var createTemplate = function(mainWindow, config) {
|
||||||
role: 'zoomout'
|
role: 'zoomout'
|
||||||
}, separatorItem, {
|
}, separatorItem, {
|
||||||
label: 'Toggle Developer Tools',
|
label: 'Toggle Developer Tools',
|
||||||
accelerator: (function() {
|
accelerator: (() => {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
return 'Alt+Command+I';
|
return 'Alt+Command+I';
|
||||||
}
|
}
|
||||||
else {
|
return 'Ctrl+Shift+I';
|
||||||
return 'Ctrl+Shift+I';
|
|
||||||
}
|
|
||||||
})(),
|
})(),
|
||||||
click: function(item, focusedWindow) {
|
click(item, focusedWindow) {
|
||||||
if (focusedWindow) {
|
if (focusedWindow) {
|
||||||
focusedWindow.toggleDevTools();
|
focusedWindow.toggleDevTools();
|
||||||
}
|
}
|
||||||
|
@ -147,11 +143,8 @@ var createTemplate = function(mainWindow, config) {
|
||||||
click: (item, focusedWindow) => {
|
click: (item, focusedWindow) => {
|
||||||
if (focusedWindow === mainWindow) {
|
if (focusedWindow === mainWindow) {
|
||||||
mainWindow.webContents.send('go-back');
|
mainWindow.webContents.send('go-back');
|
||||||
}
|
} else if (focusedWindow.webContents.canGoBack()) {
|
||||||
else {
|
focusedWindow.goBack();
|
||||||
if (focusedWindow.webContents.canGoBack()) {
|
|
||||||
focusedWindow.goBack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@ -160,18 +153,14 @@ var createTemplate = function(mainWindow, config) {
|
||||||
click: (item, focusedWindow) => {
|
click: (item, focusedWindow) => {
|
||||||
if (focusedWindow === mainWindow) {
|
if (focusedWindow === mainWindow) {
|
||||||
mainWindow.webContents.send('go-forward');
|
mainWindow.webContents.send('go-forward');
|
||||||
}
|
} else if (focusedWindow.webContents.canGoForward()) {
|
||||||
else {
|
focusedWindow.goForward();
|
||||||
if (focusedWindow.webContents.canGoForward()) {
|
|
||||||
focusedWindow.goForward();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const windowMenu = {
|
||||||
const window_menu = {
|
|
||||||
label: '&Window',
|
label: '&Window',
|
||||||
submenu: [{
|
submenu: [{
|
||||||
role: 'minimize'
|
role: 'minimize'
|
||||||
|
@ -181,7 +170,7 @@ var createTemplate = function(mainWindow, config) {
|
||||||
return {
|
return {
|
||||||
label: team.name,
|
label: team.name,
|
||||||
accelerator: `CmdOrCtrl+${i + 1}`,
|
accelerator: `CmdOrCtrl+${i + 1}`,
|
||||||
click: (item, focusedWindow) => {
|
click() {
|
||||||
mainWindow.show(); // for OS X
|
mainWindow.show(); // for OS X
|
||||||
mainWindow.webContents.send('switch-tab', i);
|
mainWindow.webContents.send('switch-tab', i);
|
||||||
}
|
}
|
||||||
|
@ -189,26 +178,26 @@ var createTemplate = function(mainWindow, config) {
|
||||||
}), separatorItem, {
|
}), separatorItem, {
|
||||||
label: 'Select Next Team',
|
label: 'Select Next Team',
|
||||||
accelerator: (process.platform === 'darwin') ? 'Alt+Cmd+Right' : 'CmdOrCtrl+Tab',
|
accelerator: (process.platform === 'darwin') ? 'Alt+Cmd+Right' : 'CmdOrCtrl+Tab',
|
||||||
click: () => {
|
click() {
|
||||||
mainWindow.webContents.send('select-next-tab');
|
mainWindow.webContents.send('select-next-tab');
|
||||||
},
|
},
|
||||||
enabled: (config.teams.length > 1)
|
enabled: (config.teams.length > 1)
|
||||||
}, {
|
}, {
|
||||||
label: 'Select Previous Team',
|
label: 'Select Previous Team',
|
||||||
accelerator: (process.platform === 'darwin') ? 'Alt+Cmd+Left' : 'CmdOrCtrl+Shift+Tab',
|
accelerator: (process.platform === 'darwin') ? 'Alt+Cmd+Left' : 'CmdOrCtrl+Shift+Tab',
|
||||||
click: () => {
|
click() {
|
||||||
mainWindow.webContents.send('select-previous-tab');
|
mainWindow.webContents.send('select-previous-tab');
|
||||||
},
|
},
|
||||||
enabled: (config.teams.length > 1)
|
enabled: (config.teams.length > 1)
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
template.push(window_menu);
|
template.push(windowMenu);
|
||||||
|
|
||||||
template.push({
|
template.push({
|
||||||
label: '&Help',
|
label: '&Help',
|
||||||
submenu: [{
|
submenu: [{
|
||||||
label: `Learn More...`,
|
label: 'Learn More...',
|
||||||
click: function() {
|
click() {
|
||||||
electron.shell.openExternal('https://docs.mattermost.com/help/apps/desktop-guide.html');
|
electron.shell.openExternal('https://docs.mattermost.com/help/apps/desktop-guide.html');
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
@ -216,15 +205,15 @@ var createTemplate = function(mainWindow, config) {
|
||||||
}, {
|
}, {
|
||||||
label: `Version ${electron.app.getVersion()}`,
|
label: `Version ${electron.app.getVersion()}`,
|
||||||
enabled: false
|
enabled: false
|
||||||
}, ]
|
}]
|
||||||
});
|
});
|
||||||
return template;
|
return template;
|
||||||
};
|
}
|
||||||
|
|
||||||
var createMenu = function(mainWindow, config) {
|
function createMenu(mainWindow, config) {
|
||||||
return Menu.buildFromTemplate(createTemplate(mainWindow, config));
|
return Menu.buildFromTemplate(createTemplate(mainWindow, config));
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createMenu: createMenu
|
createMenu
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ function createTemplate(mainWindow, config) {
|
||||||
...config.teams.slice(0, 9).map((team, i) => {
|
...config.teams.slice(0, 9).map((team, i) => {
|
||||||
return {
|
return {
|
||||||
label: team.name,
|
label: team.name,
|
||||||
click: (item, focusedWindow) => {
|
click: () => {
|
||||||
showOrRestore(mainWindow);
|
showOrRestore(mainWindow);
|
||||||
mainWindow.webContents.send('switch-tab', i);
|
mainWindow.webContents.send('switch-tab', i);
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ function createTemplate(mainWindow, config) {
|
||||||
}), {
|
}), {
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
}, {
|
}, {
|
||||||
label: process.platform !== 'darwin' ? 'Settings' : 'Preferences...',
|
label: process.platform === 'darwin' ? 'Preferences...' : 'Settings',
|
||||||
click: () => {
|
click: () => {
|
||||||
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html');
|
||||||
showOrRestore(mainWindow);
|
showOrRestore(mainWindow);
|
||||||
|
@ -42,14 +42,18 @@ function createTemplate(mainWindow, config) {
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
var createMenu = function(mainWindow, config) {
|
function createMenu(mainWindow, config) {
|
||||||
return Menu.buildFromTemplate(createTemplate(mainWindow, config));
|
return Menu.buildFromTemplate(createTemplate(mainWindow, config));
|
||||||
};
|
}
|
||||||
|
|
||||||
function showOrRestore(window) {
|
function showOrRestore(window) {
|
||||||
window.isMinimized() ? window.restore() : window.show();
|
if (window.isMinimized()) {
|
||||||
|
window.restore();
|
||||||
|
} else {
|
||||||
|
window.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createMenu: createMenu
|
createMenu
|
||||||
};
|
};
|
||||||
|
|
14
test/.eslintrc.json
Normal file
14
test/.eslintrc.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"mocha": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"open_window": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"global-require": 0,
|
||||||
|
"max-nested-callbacks": 0,
|
||||||
|
"no-eval": 0,
|
||||||
|
"no-magic-numbers": 0
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,61 +6,58 @@ const chaiAsPromised = require('chai-as-promised');
|
||||||
chai.should();
|
chai.should();
|
||||||
chai.use(chaiAsPromised);
|
chai.use(chaiAsPromised);
|
||||||
|
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Application = require('spectron').Application;
|
const Application = require('spectron').Application;
|
||||||
|
|
||||||
const source_root_dir = path.join(__dirname, '../..');
|
const sourceRootDir = path.join(__dirname, '../..');
|
||||||
const electron_binary_path = (function() {
|
const electronBinaryPath = (() => {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
return path.join(source_root_dir, 'node_modules/electron-prebuilt/dist/Electron.app/Contents/MacOS/Electron');
|
return path.join(sourceRootDir, 'node_modules/electron-prebuilt/dist/Electron.app/Contents/MacOS/Electron');
|
||||||
}
|
|
||||||
else {
|
|
||||||
const exe_extension = (process.platform === 'win32') ? '.exe' : '';
|
|
||||||
return path.join(source_root_dir, 'node_modules/electron-prebuilt/dist/electron' + exe_extension);
|
|
||||||
}
|
}
|
||||||
|
const exeExtension = (process.platform === 'win32') ? '.exe' : '';
|
||||||
|
return path.join(sourceRootDir, 'node_modules/electron-prebuilt/dist/electron' + exeExtension);
|
||||||
})();
|
})();
|
||||||
const config_file_path = path.join(source_root_dir, 'test/test_config.json');
|
const configFilePath = path.join(sourceRootDir, 'test/test_config.json');
|
||||||
const mattermost_url = 'http://example.com/team';
|
const mattermostURL = 'http://example.com/team';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sourceRootDir: source_root_dir,
|
sourceRootDir,
|
||||||
configFilePath: config_file_path,
|
configFilePath,
|
||||||
mattermostURL: mattermost_url,
|
mattermostURL,
|
||||||
getSpectronApp: function() {
|
getSpectronApp() {
|
||||||
const app = new Application({
|
const app = new Application({
|
||||||
path: electron_binary_path,
|
path: electronBinaryPath,
|
||||||
args: [`${path.join(source_root_dir, 'dist')}`, '--config-file=' + config_file_path]
|
args: [`${path.join(sourceRootDir, 'dist')}`, '--config-file=' + configFilePath]
|
||||||
});
|
});
|
||||||
chaiAsPromised.transferPromiseness = app.transferPromiseness
|
chaiAsPromised.transferPromiseness = app.transferPromiseness;
|
||||||
return app;
|
return app;
|
||||||
},
|
},
|
||||||
|
|
||||||
addClientCommands: function(client) {
|
addClientCommands(client) {
|
||||||
client.addCommand('loadSettingsPage', function async() {
|
client.addCommand('loadSettingsPage', function async() {
|
||||||
return this
|
return this.url('file://' + path.join(sourceRootDir, 'dist/browser/settings.html')).waitUntilWindowLoaded();
|
||||||
.url('file://' + path.join(source_root_dir, 'dist/browser/settings.html'))
|
|
||||||
.waitUntilWindowLoaded();
|
|
||||||
});
|
});
|
||||||
client.addCommand('isNodeEnabled', function async() {
|
client.addCommand('isNodeEnabled', function async() {
|
||||||
return this.execute(function() {
|
return this.execute(() => {
|
||||||
try {
|
try {
|
||||||
return require('child_process') ? true : false;
|
if (require('child_process')) {
|
||||||
}
|
return true;
|
||||||
catch (e) {
|
}
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}).then((require_result) => {
|
}).then((requireResult) => {
|
||||||
return require_result.value;
|
return requireResult.value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// execute the test only when `condition` is true
|
// execute the test only when `condition` is true
|
||||||
shouldTest: function(it, condition) {
|
shouldTest(it, condition) {
|
||||||
return condition ? it : it.skip;
|
return condition ? it : it.skip;
|
||||||
},
|
},
|
||||||
isOneOf(platforms) {
|
isOneOf(platforms) {
|
||||||
return (platforms.indexOf(process.platform) !== -1);
|
return (platforms.indexOf(process.platform) !== -1);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,81 +1,86 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const env = require('../modules/environment');
|
const env = require('../modules/environment');
|
||||||
|
|
||||||
describe('application', function() {
|
describe('application', function desc() {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach((done) => {
|
||||||
this.app = env.getSpectronApp();
|
this.app = env.getSpectronApp();
|
||||||
fs.unlink(env.configFilePath, () => {
|
fs.unlink(env.configFilePath, () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
if (this.app && this.app.isRunning()) {
|
if (this.app && this.app.isRunning()) {
|
||||||
return this.app.stop()
|
return this.app.stop();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show a window', function() {
|
it('should show a window', () => {
|
||||||
return this.app.start().then(() => {
|
return this.app.start().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.
|
||||||
.getWindowCount().should.eventually.equal(1)
|
waitUntilWindowLoaded().
|
||||||
.browserWindow.isDevToolsOpened().should.eventually.be.false
|
getWindowCount().should.eventually.equal(1).
|
||||||
.browserWindow.isVisible().should.eventually.be.true
|
browserWindow.isDevToolsOpened().should.eventually.be.false.
|
||||||
|
browserWindow.isVisible().should.eventually.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show settings.html when there is no config file', function() {
|
it('should show settings.html when there is no config file', () => {
|
||||||
return this.app.start().then(() => {
|
return this.app.start().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.
|
||||||
.getUrl().should.eventually.match(/\/settings.html$/)
|
waitUntilWindowLoaded().
|
||||||
|
getUrl().should.eventually.match(/\/settings.html$/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show index.html when there is config file', function() {
|
it('should show index.html when there is config file', () => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
url: env.mattermostURL
|
url: env.mattermostURL
|
||||||
}));
|
}));
|
||||||
return this.app.start().then(() => {
|
return this.app.start().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.
|
||||||
.getUrl().should.eventually.match(/\/index.html$/)
|
waitUntilWindowLoaded().
|
||||||
|
getUrl().should.eventually.match(/\/index.html$/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should upgrade v0 config file', function() {
|
it('should upgrade v0 config file', () => {
|
||||||
const settings = require('../../src/common/settings');
|
const settings = require('../../src/common/settings');
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
url: env.mattermostURL
|
url: env.mattermostURL
|
||||||
}));
|
}));
|
||||||
return this.app.start().then(() => {
|
return this.app.start().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.
|
||||||
.getUrl().should.eventually.match(/\/index.html$/)
|
waitUntilWindowLoaded().
|
||||||
}).then(function() {
|
getUrl().should.eventually.match(/\/index.html$/);
|
||||||
|
}).then(() => {
|
||||||
var str = fs.readFileSync(env.configFilePath, 'utf8');
|
var str = fs.readFileSync(env.configFilePath, 'utf8');
|
||||||
var config = JSON.parse(str);
|
var config = JSON.parse(str);
|
||||||
config.version.should.equal(settings.version);
|
config.version.should.equal(settings.version);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should be stopped when the app instance already exists', function(done) {
|
it.skip('should be stopped when the app instance already exists', (done) => {
|
||||||
this.app.start().then(() => {
|
this.app.start().then(() => {
|
||||||
const secondApp = env.getSpectronApp();
|
const secondApp = env.getSpectronApp();
|
||||||
|
|
||||||
|
// In the correct case, 'start().then' is not called.
|
||||||
|
// So need to use setTimeout in order to finish this test.
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
done();
|
||||||
|
}, 3000);
|
||||||
secondApp.start().then(() => {
|
secondApp.start().then(() => {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
return secondApp.stop();
|
return secondApp.stop();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
done(new Error('Second app instance exists'));
|
done(new Error('Second app instance exists'));
|
||||||
});
|
});
|
||||||
// In the correct case, 'start().then' is not called.
|
|
||||||
// So need to use setTimeout in order to finish this test.
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 3000);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ const path = require('path');
|
||||||
|
|
||||||
const env = require('../../modules/environment');
|
const env = require('../../modules/environment');
|
||||||
|
|
||||||
describe('browser/index.html', function() {
|
describe('browser/index.html', function desc() {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
@ -22,66 +22,68 @@ describe('browser/index.html', function() {
|
||||||
|
|
||||||
const serverPort = 8181;
|
const serverPort = 8181;
|
||||||
|
|
||||||
before(function() {
|
before(() => {
|
||||||
this.server = http.createServer(function(req, res) {
|
function serverCallback(req, res) {
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'text/html'
|
'Content-Type': 'text/html'
|
||||||
});
|
});
|
||||||
res.end(fs.readFileSync(path.resolve(env.sourceRootDir, 'test/modules/test.html'), 'utf-8'));
|
res.end(fs.readFileSync(path.resolve(env.sourceRootDir, 'test/modules/test.html'), 'utf-8'));
|
||||||
}).listen(serverPort, '127.0.0.1');
|
}
|
||||||
|
this.server = http.createServer(serverCallback).listen(serverPort, '127.0.0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
|
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
|
||||||
this.app = env.getSpectronApp();
|
this.app = env.getSpectronApp();
|
||||||
return this.app.start();
|
return this.app.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
if (this.app && this.app.isRunning()) {
|
if (this.app && this.app.isRunning()) {
|
||||||
return this.app.stop()
|
return this.app.stop();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function(done) {
|
after((done) => {
|
||||||
this.server.close(done);
|
this.server.close(done);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should NOT show tabs when there is one team', function() {
|
it('should NOT show tabs when there is one team', () => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
url: env.mattermostURL
|
url: env.mattermostURL
|
||||||
}));
|
}));
|
||||||
return this.app.restart().then(() => {
|
return this.app.restart().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.waitUntilWindowLoaded().
|
||||||
.isExisting('#tabBar').should.eventually.be.false
|
isExisting('#tabBar').should.eventually.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set src of webview from config file', function() {
|
it('should set src of webview from config file', () => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.waitUntilWindowLoaded().
|
||||||
.getAttribute('#mattermostView0', 'src').should.eventually.equal(config.teams[0].url)
|
getAttribute('#mattermostView0', 'src').should.eventually.equal(config.teams[0].url).
|
||||||
.getAttribute('#mattermostView1', 'src').should.eventually.equal(config.teams[1].url)
|
getAttribute('#mattermostView1', 'src').should.eventually.equal(config.teams[1].url).
|
||||||
.isExisting('#mattermostView2').should.eventually.be.false
|
isExisting('#mattermostView2').should.eventually.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set name of tab from config file', function() {
|
it('should set name of tab from config file', () => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.waitUntilWindowLoaded().
|
||||||
.getText('#teamTabItem0').should.eventually.equal(config.teams[0].name)
|
getText('#teamTabItem0').should.eventually.equal(config.teams[0].name).
|
||||||
.getText('#teamTabItem1').should.eventually.equal(config.teams[1].name)
|
getText('#teamTabItem1').should.eventually.equal(config.teams[1].name);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show only the selected team', function() {
|
it('should show only the selected team', () => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.waitUntilWindowLoaded().
|
||||||
.isVisible('#mattermostView0').should.eventually.be.true
|
isVisible('#mattermostView0').should.eventually.be.true.
|
||||||
.isVisible('#mattermostView1').should.eventually.be.false
|
isVisible('#mattermostView1').should.eventually.be.false.
|
||||||
.click('#teamTabItem1')
|
click('#teamTabItem1').
|
||||||
.pause(1000)
|
pause(1000).
|
||||||
.isVisible('#mattermostView1').should.eventually.be.true
|
isVisible('#mattermostView1').should.eventually.be.true.
|
||||||
.isVisible('#mattermostView0').should.eventually.be.false
|
isVisible('#mattermostView0').should.eventually.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show error when using incorrect URL', function() {
|
it('should show error when using incorrect URL', () => {
|
||||||
this.timeout(30000)
|
this.timeout(30000);
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
teams: [{
|
teams: [{
|
||||||
|
@ -90,12 +92,12 @@ describe('browser/index.html', function() {
|
||||||
}]
|
}]
|
||||||
}));
|
}));
|
||||||
return this.app.restart().then(() => {
|
return this.app.restart().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded()
|
return this.app.client.waitUntilWindowLoaded().
|
||||||
.waitForVisible('#mattermostView0-fail', 20000)
|
waitForVisible('#mattermostView0-fail', 20000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set window title by using webview\'s one', function() {
|
it('should set window title by using webview\'s one', () => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
teams: [{
|
teams: [{
|
||||||
|
@ -104,14 +106,13 @@ describe('browser/index.html', function() {
|
||||||
}]
|
}]
|
||||||
}));
|
}));
|
||||||
return this.app.restart().then(() => {
|
return this.app.restart().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded().pause(1000);
|
return this.app.client.waitUntilWindowLoaded().pause(1000);
|
||||||
})
|
}).then(() => {
|
||||||
.then(() => {
|
return this.app.browserWindow.getTitle().should.eventually.equal('Mattermost Desktop testing html');
|
||||||
return this.app.browserWindow.getTitle().should.eventually.equal('Mattermost Desktop testing html');
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update window title when the activated tab\'s title is updated', function() {
|
it('should update window title when the activated tab\'s title is updated', () => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
teams: [{
|
teams: [{
|
||||||
|
@ -123,52 +124,51 @@ describe('browser/index.html', function() {
|
||||||
}]
|
}]
|
||||||
}));
|
}));
|
||||||
return this.app.restart().then(() => {
|
return this.app.restart().then(() => {
|
||||||
return this.app.client.waitUntilWindowLoaded().pause(500);
|
return this.app.client.waitUntilWindowLoaded().pause(500);
|
||||||
})
|
}).then(() => {
|
||||||
.then(() => {
|
return this.app.client.
|
||||||
return this.app.client
|
windowByIndex(1).
|
||||||
.windowByIndex(1)
|
execute(() => {
|
||||||
.execute(function() {
|
|
||||||
document.title = 'Title 0';
|
|
||||||
})
|
|
||||||
.windowByIndex(0)
|
|
||||||
.browserWindow.getTitle().should.eventually.equal('Title 0')
|
|
||||||
.windowByIndex(2)
|
|
||||||
.execute(function() {
|
|
||||||
document.title = 'Title 1';
|
|
||||||
})
|
|
||||||
.windowByIndex(0)
|
|
||||||
.browserWindow.getTitle().should.eventually.equal('Title 0')
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update window title when a tab is selected', function() {
|
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
|
||||||
version: 1,
|
|
||||||
teams: [{
|
|
||||||
name: 'title_test_0',
|
|
||||||
url: `http://localhost:${serverPort}`
|
|
||||||
}, {
|
|
||||||
name: 'title_test_1',
|
|
||||||
url: `http://localhost:${serverPort}`
|
|
||||||
}]
|
|
||||||
}));
|
|
||||||
return this.app.restart().then(() => {
|
|
||||||
return this.app.client
|
|
||||||
.waitUntilWindowLoaded()
|
|
||||||
.pause(500)
|
|
||||||
.windowByIndex(1)
|
|
||||||
.execute(function() {
|
|
||||||
document.title = 'Title 0';
|
document.title = 'Title 0';
|
||||||
})
|
}).
|
||||||
.windowByIndex(2)
|
windowByIndex(0).
|
||||||
.execute(function() {
|
browserWindow.getTitle().should.eventually.equal('Title 0').
|
||||||
|
windowByIndex(2).
|
||||||
|
execute(() => {
|
||||||
document.title = 'Title 1';
|
document.title = 'Title 1';
|
||||||
})
|
}).
|
||||||
.windowByIndex(0)
|
windowByIndex(0).
|
||||||
.browserWindow.getTitle().should.eventually.equal('Title 0')
|
browserWindow.getTitle().should.eventually.equal('Title 0');
|
||||||
.click('#teamTabItem1')
|
});
|
||||||
.browserWindow.getTitle().should.eventually.equal('Title 1');
|
});
|
||||||
|
|
||||||
|
it('should update window title when a tab is selected', () => {
|
||||||
|
fs.writeFileSync(env.configFilePath, JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
teams: [{
|
||||||
|
name: 'title_test_0',
|
||||||
|
url: `http://localhost:${serverPort}`
|
||||||
|
}, {
|
||||||
|
name: 'title_test_1',
|
||||||
|
url: `http://localhost:${serverPort}`
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
return this.app.restart().then(() => {
|
||||||
|
return this.app.client.
|
||||||
|
waitUntilWindowLoaded().
|
||||||
|
pause(500).
|
||||||
|
windowByIndex(1).
|
||||||
|
execute(() => {
|
||||||
|
document.title = 'Title 0';
|
||||||
|
}).
|
||||||
|
windowByIndex(2).
|
||||||
|
execute(() => {
|
||||||
|
document.title = 'Title 1';
|
||||||
|
}).
|
||||||
|
windowByIndex(0).
|
||||||
|
browserWindow.getTitle().should.eventually.equal('Title 0').
|
||||||
|
click('#teamTabItem1').
|
||||||
|
browserWindow.getTitle().should.eventually.equal('Title 1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const env = require('../../modules/environment');
|
const env = require('../../modules/environment');
|
||||||
|
|
||||||
describe('browser/settings.html', function() {
|
describe('browser/settings.html', function desc() {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
|
@ -19,177 +18,174 @@ describe('browser/settings.html', function() {
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
|
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
|
||||||
this.app = env.getSpectronApp();
|
this.app = env.getSpectronApp();
|
||||||
return this.app.start();
|
return this.app.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
if (this.app && this.app.isRunning()) {
|
if (this.app && this.app.isRunning()) {
|
||||||
return this.app.stop()
|
return this.app.stop();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show index.html when Cancel button is clicked', function() {
|
it('should show index.html when Cancel button is clicked', () => {
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.click('#btnCancel')
|
click('#btnCancel').
|
||||||
.pause(1000)
|
pause(1000).
|
||||||
.getUrl().should.eventually.match(/\/index.html$/)
|
getUrl().should.eventually.match(/\/index.html$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show index.html when Save button is clicked', function() {
|
it('should show index.html when Save button is clicked', () => {
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.click('#btnSave')
|
click('#btnSave').
|
||||||
.pause(1000)
|
pause(1000).
|
||||||
.getUrl().should.eventually.match(/\/index.html$/)
|
getUrl().should.eventually.match(/\/index.html$/);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Options', function() {
|
describe('Options', () => {
|
||||||
describe('Hide Menu Bar', function() {
|
describe('Hide Menu Bar', () => {
|
||||||
it('should appear on win32 or linux', function() {
|
it('should appear on win32 or linux', () => {
|
||||||
const expected = (process.platform === 'win32' || process.platform === 'linux');
|
const expected = (process.platform === 'win32' || process.platform === 'linux');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputHideMenuBar').should.eventually.equal(expected)
|
isExisting('#inputHideMenuBar').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
[true, false].forEach(function(v) {
|
[true, false].forEach((v) => {
|
||||||
env.shouldTest(it, env.isOneOf(['win32', 'linux']))
|
env.shouldTest(it, env.isOneOf(['win32', 'linux']))(`should be saved and loaded: ${v}`, () => {
|
||||||
(`should be saved and loaded: ${v}`, function() {
|
|
||||||
env.addClientCommands(this.app.client);
|
|
||||||
return this.app.client
|
|
||||||
.loadSettingsPage()
|
|
||||||
.scroll('#inputHideMenuBar')
|
|
||||||
.isSelected('#inputHideMenuBar input').then((isSelected) => {
|
|
||||||
if (isSelected !== v) {
|
|
||||||
return this.app.client.click('#inputHideMenuBar input')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.click('#btnSave')
|
|
||||||
.pause(1000).then(() => {
|
|
||||||
const saved_config = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
|
|
||||||
saved_config.hideMenuBar.should.equal(v);
|
|
||||||
})
|
|
||||||
// confirm actual behavior
|
|
||||||
.browserWindow.isMenuBarAutoHide().should.eventually.equal(v).then(() => {
|
|
||||||
return this.app.restart();
|
|
||||||
}).then(() => {
|
|
||||||
env.addClientCommands(this.app.client);
|
|
||||||
return this.app.client
|
|
||||||
// confirm actual behavior
|
|
||||||
.browserWindow.isMenuBarAutoHide().should.eventually.equal(v)
|
|
||||||
.loadSettingsPage()
|
|
||||||
.isSelected('#inputHideMenuBar input').should.eventually.equal(v);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Allow mixed content', function() {
|
|
||||||
[true, false].forEach(function(v) {
|
|
||||||
it(`should be saved and loaded: ${v}`, function() {
|
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.scroll('#inputDisableWebSecurity')
|
scroll('#inputHideMenuBar').
|
||||||
.isSelected('#inputDisableWebSecurity input').then((isSelected) => {
|
isSelected('#inputHideMenuBar input').then((isSelected) => {
|
||||||
if (isSelected !== v) {
|
if (isSelected !== v) {
|
||||||
return this.app.client.click('#inputDisableWebSecurity input')
|
return this.app.client.click('#inputHideMenuBar input');
|
||||||
}
|
}
|
||||||
})
|
return true;
|
||||||
.click('#btnSave')
|
}).
|
||||||
.pause(1000).then(() => {
|
click('#btnSave').
|
||||||
const saved_config = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
|
pause(1000).then(() => {
|
||||||
saved_config.disablewebsecurity.should.equal(v);
|
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
|
||||||
})
|
savedConfig.hideMenuBar.should.equal(v);
|
||||||
// confirm actual behavior
|
}).
|
||||||
.getAttribute('.mattermostView', 'disablewebsecurity').then((disablewebsecurity) => {
|
browserWindow.isMenuBarAutoHide().should.eventually.equal(v).then(() => { // confirm actual behavior
|
||||||
// disablewebsecurity is an array of String
|
|
||||||
disablewebsecurity.forEach((d) => {
|
|
||||||
v.toString().should.equal(d);
|
|
||||||
})
|
|
||||||
}).then(() => {
|
|
||||||
return this.app.restart();
|
return this.app.restart();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client. // confirm actual behavior
|
||||||
// confirm actual behavior
|
browserWindow.isMenuBarAutoHide().should.eventually.equal(v).
|
||||||
.getAttribute('.mattermostView', 'disablewebsecurity').then((disablewebsecurity) => {
|
loadSettingsPage().
|
||||||
// disablewebsecurity is an array of String
|
isSelected('#inputHideMenuBar input').should.eventually.equal(v);
|
||||||
disablewebsecurity.forEach((d) => {
|
|
||||||
v.toString().should.equal(d);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.loadSettingsPage()
|
|
||||||
.isSelected('#inputDisableWebSecurity input').should.eventually.equal(v);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Start app on login', function() {
|
describe('Allow mixed content', () => {
|
||||||
it('should appear on win32 or linux', function() {
|
[true, false].forEach((v) => {
|
||||||
|
it(`should be saved and loaded: ${v}`, () => {
|
||||||
|
env.addClientCommands(this.app.client);
|
||||||
|
return this.app.client.
|
||||||
|
loadSettingsPage().
|
||||||
|
scroll('#inputDisableWebSecurity').
|
||||||
|
isSelected('#inputDisableWebSecurity input').then((isSelected) => {
|
||||||
|
if (isSelected !== v) {
|
||||||
|
return this.app.client.click('#inputDisableWebSecurity input');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}).
|
||||||
|
click('#btnSave').
|
||||||
|
pause(1000).then(() => {
|
||||||
|
const savedConfig = JSON.parse(fs.readFileSync(env.configFilePath, 'utf8'));
|
||||||
|
savedConfig.disablewebsecurity.should.equal(v);
|
||||||
|
}).
|
||||||
|
getAttribute('.mattermostView', 'disablewebsecurity').then((disablewebsecurity) => { // confirm actual behavior
|
||||||
|
// disablewebsecurity is an array of String
|
||||||
|
disablewebsecurity.forEach((d) => {
|
||||||
|
v.toString().should.equal(d);
|
||||||
|
});
|
||||||
|
}).then(() => {
|
||||||
|
return this.app.restart();
|
||||||
|
}).then(() => {
|
||||||
|
env.addClientCommands(this.app.client);
|
||||||
|
return this.app.client. // confirm actual behavior
|
||||||
|
getAttribute('.mattermostView', 'disablewebsecurity').then((disablewebsecurity) => { // disablewebsecurity is an array of String
|
||||||
|
disablewebsecurity.forEach((d) => {
|
||||||
|
v.toString().should.equal(d);
|
||||||
|
});
|
||||||
|
}).
|
||||||
|
loadSettingsPage().
|
||||||
|
isSelected('#inputDisableWebSecurity input').should.eventually.equal(v);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Start app on login', () => {
|
||||||
|
it('should appear on win32 or linux', () => {
|
||||||
const expected = (process.platform === 'win32' || process.platform === 'linux');
|
const expected = (process.platform === 'win32' || process.platform === 'linux');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputAutoStart').should.eventually.equal(expected)
|
isExisting('#inputAutoStart').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Show tray icon', function() {
|
describe('Show tray icon', () => {
|
||||||
it('should appear on darwin or linux', function() {
|
it('should appear on darwin or linux', () => {
|
||||||
const expected = (process.platform === 'darwin' || process.platform === 'linux');
|
const expected = (process.platform === 'darwin' || process.platform === 'linux');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputShowTrayIcon').should.eventually.equal(expected)
|
isExisting('#inputShowTrayIcon').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Minimize to tray', function() {
|
describe('Minimize to tray', () => {
|
||||||
it('should appear on darwin or linux', function() {
|
it('should appear on darwin or linux', () => {
|
||||||
const expected = (process.platform === 'darwin' || process.platform === 'linux');
|
const expected = (process.platform === 'darwin' || process.platform === 'linux');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputMinimizeToTray').should.eventually.equal(expected)
|
isExisting('#inputMinimizeToTray').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Toggle window visibility when clicking on the tray icon', function() {
|
describe('Toggle window visibility when clicking on the tray icon', () => {
|
||||||
it('should appear on win32', function() {
|
it('should appear on win32', () => {
|
||||||
const expected = (process.platform === 'win32');
|
const expected = (process.platform === 'win32');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputToggleWindowOnTrayIconClick').should.eventually.equal(expected)
|
isExisting('#inputToggleWindowOnTrayIconClick').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Flash taskbar icon on new messages', function() {
|
describe('Flash taskbar icon on new messages', () => {
|
||||||
it('should appear on win32 and linux', function() {
|
it('should appear on win32 and linux', () => {
|
||||||
const expected = (process.platform === 'win32' || process.platform === 'linux');
|
const expected = (process.platform === 'win32' || process.platform === 'linux');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputflashWindow').should.eventually.equal(expected)
|
isExisting('#inputflashWindow').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Show red icon for unread', function() {
|
describe('Show red icon for unread', () => {
|
||||||
it('should appear on darwin or win32', function() {
|
it('should appear on darwin or win32', () => {
|
||||||
const expected = (process.platform === 'darwin' || process.platform === 'win32');
|
const expected = (process.platform === 'darwin' || process.platform === 'win32');
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.isExisting('#inputShowUnreadBadge').should.eventually.equal(expected)
|
isExisting('#inputShowUnreadBadge').should.eventually.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,11 +6,11 @@ const http = require('http');
|
||||||
|
|
||||||
const env = require('../modules/environment');
|
const env = require('../modules/environment');
|
||||||
|
|
||||||
describe('application', function() {
|
describe('application', function desc() {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
|
|
||||||
const serverPort = 8181;
|
const serverPort = 8181;
|
||||||
const testURL = `http://localhost:${serverPort}`
|
const testURL = `http://localhost:${serverPort}`;
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -23,8 +23,8 @@ describe('application', function() {
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
before(function() {
|
before(() => {
|
||||||
this.server = http.createServer(function(req, res) {
|
this.server = http.createServer((req, res) => {
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'text/html'
|
'Content-Type': 'text/html'
|
||||||
});
|
});
|
||||||
|
@ -32,66 +32,68 @@ describe('application', function() {
|
||||||
}).listen(serverPort, '127.0.0.1');
|
}).listen(serverPort, '127.0.0.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
|
fs.writeFileSync(env.configFilePath, JSON.stringify(config));
|
||||||
this.app = env.getSpectronApp();
|
this.app = env.getSpectronApp();
|
||||||
return this.app.start();
|
return this.app.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
if (this.app && this.app.isRunning()) {
|
if (this.app && this.app.isRunning()) {
|
||||||
return this.app.stop()
|
return this.app.stop();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function(done) {
|
after((done) => {
|
||||||
this.server.close(done);
|
this.server.close(done);
|
||||||
})
|
});
|
||||||
|
|
||||||
it('should NOT be able to call Node.js API in webview', function() {
|
it('should NOT be able to call Node.js API in webview', () => {
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.getAttribute('webview', 'nodeintegration').then((nodeintegration) => {
|
getAttribute('webview', 'nodeintegration').then((nodeintegration) => {
|
||||||
// nodeintegration is an array of string
|
// nodeintegration is an array of string
|
||||||
nodeintegration.forEach((n) => {
|
nodeintegration.forEach((n) => {
|
||||||
n.should.equal('false');
|
n.should.equal('false');
|
||||||
});
|
});
|
||||||
})
|
}).
|
||||||
|
|
||||||
// webview is handled as a window by chromedriver.
|
// webview is handled as a window by chromedriver.
|
||||||
.windowByIndex(1).isNodeEnabled().should.eventually.be.false
|
windowByIndex(1).isNodeEnabled().should.eventually.be.false.
|
||||||
.windowByIndex(2).isNodeEnabled().should.eventually.be.false;
|
windowByIndex(2).isNodeEnabled().should.eventually.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT be able to call Node.js API in a new window', function() {
|
it('should NOT be able to call Node.js API in a new window', () => {
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
const client = this.app.client;
|
const client = this.app.client;
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.windowByIndex(1) // in the first webview
|
windowByIndex(1). // in the first webview
|
||||||
.execute(function() {
|
execute(() => {
|
||||||
open_window();
|
open_window();
|
||||||
})
|
}).
|
||||||
.waitUntil(function async() {
|
waitUntil(() => {
|
||||||
return client.windowHandles().then((handles) => {
|
return client.windowHandles().then((handles) => {
|
||||||
return handles.value.length === 4;
|
return handles.value.length === 4;
|
||||||
});
|
});
|
||||||
}, 5000, 'expected a new window')
|
}, 5000, 'expected a new window').
|
||||||
.windowByIndex(3).isNodeEnabled().should.eventually.be.false;
|
windowByIndex(3).isNodeEnabled().should.eventually.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT be able to call eval() in any window', function() {
|
it('should NOT be able to call eval() in any window', () => {
|
||||||
env.addClientCommands(this.app.client);
|
env.addClientCommands(this.app.client);
|
||||||
const tryEval = (index) => {
|
const tryEval = (index) => {
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.windowByIndex(index)
|
windowByIndex(index).
|
||||||
.execute(function() {
|
execute(() => {
|
||||||
return eval('1 + 1');
|
return eval('1 + 1');
|
||||||
}).should.eventually.be.rejected;
|
}).should.eventually.be.rejected;
|
||||||
};
|
};
|
||||||
const tryEvalInSettingsPage = () => {
|
const tryEvalInSettingsPage = () => {
|
||||||
return this.app.client
|
return this.app.client.
|
||||||
.windowByIndex(0)
|
windowByIndex(0).
|
||||||
.loadSettingsPage()
|
loadSettingsPage().
|
||||||
.execute(function() {
|
execute(() => {
|
||||||
return eval('1 + 1');
|
return eval('1 + 1');
|
||||||
}).should.eventually.be.rejected;
|
}).should.eventually.be.rejected;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
const fs = require('fs');
|
|
||||||
const settings = require('../../src/common/settings');
|
const settings = require('../../src/common/settings');
|
||||||
|
|
||||||
const env = require('../modules/environment');
|
describe('common/settings.js', () => {
|
||||||
//const env.configFilePath = '../../test_config.json'
|
it('should upgrade v0 config file', () => {
|
||||||
|
const v0Config = {
|
||||||
describe('common/settings.js', function() {
|
|
||||||
|
|
||||||
it('should upgrade v0 config file', function() {
|
|
||||||
const v0_config = {
|
|
||||||
url: 'https://example.com/team'
|
url: 'https://example.com/team'
|
||||||
};
|
};
|
||||||
config = settings.upgrade(v0_config);
|
const config = settings.upgrade(v0Config);
|
||||||
config.teams.length.should.equal(1);
|
config.teams.length.should.equal(1);
|
||||||
config.teams[0].url.should.equal(v0_config.url);
|
config.teams[0].url.should.equal(v0Config.url);
|
||||||
config.version.should.equal(settings.version);
|
config.version.should.equal(settings.version);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue