Merge pull request #92 from mattermost/release/v1.1.0

Release/v1.1.0
This commit is contained in:
Yuya Ochiai 2016-03-30 21:07:56 +09:00
commit 108cf91bf6
18 changed files with 446 additions and 113 deletions

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
root = true
[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true

View file

@ -1,17 +1,54 @@
# Mattermost Desktop Application Changelog # Mattermost Desktop Application Changelog
## IN PROGRESS: Release v1.0.8 (Beta) ## Release v1.1.0 (Beta)
The `electron-mattermost` project is now the official desktop application for the Mattermost open source project. The `electron-mattermost` project is now the official desktop application for the Mattermost open source project.
### Changes ### Changes
- Renaming project from `electron-mattermost` to `desktop`
#### All platforms
- Rename project from `electron-mattermost` to `desktop`
- Rename the executable file from `electron-mattermost` to `Mattermost`
- The configuration directory is also different from previous versions.
- Should execute following command to take over `config.json`.
- Windows: `copy %APPDATA%\electron-mattermost\config.json %APPDATA%\Mattermost\config.json`
- OS X: `cp ~/Library/Application\ Support/electron-mattermost/config.json ~/Library/Application\ Support/Mattermost/config.json`
- Linux: `cp ~/.config/electron-mattermost/config.json ~/.config/Mattermost/config.json`
### Improvements
#### All platforms
- Refine application icon.
- Show error messages when the application failed in loading Mattermost server.
- Show confirmation dialog to continue connection when there is certificate error.
- Add validation to check whether both of **Name** and **URL** fields are not blank.
- Add simple basic HTTP authentication (requires a command line).
#### Windows
- Show a small circle on the tray icon when there are new messages.
### Fixes ### Fixes
- On **Settings Page** added validation so that **Name** field value is required before team site can be added.
#### Windows
- **File** > **About** does not bring up version number dialog.
#### Linux
- **File** > **About** does not bring up version number dialog.
- Ubuntu: Notification is not showing up.
- The view crashes when freetype 2.6.3 is used in system.
### Known issues ### Known issues
- Windows and Linux: **File** > **About** does not bring up version number dialog #### All platforms
- Windows: Application does not appear in Windows volume mixer - Images with `http://` do not render.
- All platforms: Embedded markdown images with `http://` do not render - Basic Authentication is not working.
- Some keyboard shortcuts are missing. (e.g. <kbd>Ctrl+W</kbd>, <kbd>Command+,</kbd>)
- Basic authentication requires a command line.
#### Windows
- Application does not appear properly in Windows volume mixer.

View file

@ -5,8 +5,8 @@ Thank you for your contributing! My requests are few things. Please read below.
Thank you for feedback. When you report a problem, please pay attention to following points. Thank you for feedback. When you report a problem, please pay attention to following points.
### Does it happen on web browsers? (especially Chrome) ### Does it happen on web browsers? (especially Chrome)
electron-mattermost is based on Electron. It integrates Chrome as a browser window. Mattermost Desktop is based on Electron, which integrates the Chrome engine within a standalone application.
If the problem appears on web browsers, it may be the issue for Mattermost (or Chrome). If the problem you encounter can be reproduced on web browsers, it may be an issue with Mattermost server (or Chrome).
### Try "Clear Cache and Reload" ### Try "Clear Cache and Reload"
It's available as `Ctrl(Command) + Shift + R`. It's available as `Ctrl(Command) + Shift + R`.
@ -14,14 +14,15 @@ Some layout problems are caused by browser cache.
Especially, this kind of issue might happen when you have updated Mattermost server. Especially, this kind of issue might happen when you have updated Mattermost server.
### Write detailed information ### Write detailed information
Following points are very helpful to understand the problem. Detailed information is very helpful to understand the problem.
For example:
* How to reproduce, step-by-step * How to reproduce, step-by-step
* Expected behavior (or what is wrong) * Expected behavior (or what is wrong)
* Screenshots (for GUI issues) * Screenshots (for GUI issues)
* Application version * Application version
* Operating system * Operating system
* Mattermost version * Mattermost server version
## Feature idea ## Feature idea
Please see http://www.mattermost.org/feature-requests/ . Please see http://www.mattermost.org/feature-requests/ .
@ -35,4 +36,3 @@ Pull requests are welcome. Thank you for your great work!
* Mattermost server version on which you tested * Mattermost server version on which you tested
* New or updated unit tests for your changes * New or updated unit tests for your changes
3. Please complete the [Mattermost CLA](http://www.mattermost.org/mattermost-contributor-agreement/) prior to submitting a PR. 3. Please complete the [Mattermost CLA](http://www.mattermost.org/mattermost-contributor-agreement/) prior to submitting a PR.

View file

@ -50,6 +50,16 @@ Configuration will be saved into Electron's userData directory:
*When you upgrade from electron-mattermost, please copy `config.json` from `electron-mattermost`. *When you upgrade from electron-mattermost, please copy `config.json` from `electron-mattermost`.
Otherwise, you have to configure again.* Otherwise, you have to configure again.*
### Proxy
Normally, the application will follow your system settings to use proxy.
Or you can set proxy by following command line options.
* `--proxy-server=<SERVER>:<PORT>`
* `--proxy-pac-url=<URL>`
*Note: Authorization is not supported yet.*
## Testing and Development ## Testing and Development
Node.js is required to test this app. Node.js is required to test this app.

View file

@ -13,6 +13,8 @@ dependencies:
cache_directories: cache_directories:
- "~/.electron" - "~/.electron"
- "src/node_modules" - "src/node_modules"
pre:
- npm install -g npm@3.3.12
post: post:
- mkdir -p ~/.electron - mkdir -p ~/.electron
- docker run --rm -it -v `pwd`:/home/xclient/electron-mattermost -v ~/.electron:/home/xclient/.electron yuyaoc/em-builder:dev ./electron-mattermost/docker/package_in_docker.sh - docker run --rm -it -v `pwd`:/home/xclient/electron-mattermost -v ~/.electron:/home/xclient/.electron yuyaoc/em-builder:dev ./electron-mattermost/docker/package_in_docker.sh
@ -30,6 +32,7 @@ dependencies:
test: test:
override: override:
- node_modules/.bin/mocha --reporter mocha-circleci-reporter - node_modules/.bin/mocha --reporter mocha-circleci-reporter
- node_modules/.bin/gulp prettify:verify
post: post:
- mv test-results.xml $CIRCLE_TEST_REPORTS/ - mv test-results.xml $CIRCLE_TEST_REPORTS/

View file

@ -7,6 +7,8 @@ var webpack = require('webpack-stream');
var named = require('vinyl-named'); var named = require('vinyl-named');
var changed = require('gulp-changed'); var changed = require('gulp-changed');
var esformatter = require('gulp-esformatter'); var esformatter = require('gulp-esformatter');
var esformatter_origin = require('esformatter');
var through = require('through2');
var del = require('del'); var del = require('del');
var electron = require('electron-connect').server.create({ var electron = require('electron-connect').server.create({
path: './dist' path: './dist'
@ -16,35 +18,65 @@ var packager = require('electron-packager');
var sources = ['**/*.js', '**/*.css', '**/*.html', '!**/node_modules/**', '!dist/**', '!release/**']; var sources = ['**/*.js', '**/*.css', '**/*.html', '!**/node_modules/**', '!dist/**', '!release/**'];
gulp.task('prettify', ['prettify:sources', 'prettify:jsx']); gulp.task('prettify', ['prettify:sources', 'prettify:jsx']);
gulp.task('prettify:verify', ['prettify:sources:verify', 'prettify:jsx:verify'])
gulp.task('prettify:sources', ['sync-meta'], function() { var prettify_options = {
return gulp.src(sources)
.pipe(prettify({
html: { html: {
eol: '\n',
indentSize: 2 indentSize: 2
}, },
css: { css: {
eol: '\n',
indentSize: 2 indentSize: 2
}, },
js: { js: {
eol: '\n',
indentSize: 2, indentSize: 2,
braceStyle: "end-expand" braceStyle: "end-expand"
} }
})) };
gulp.task('prettify:sources', ['sync-meta'], function() {
prettify_options.mode = "VERIFY_AND_WRITE";
return gulp.src(sources)
.pipe(prettify(prettify_options))
.pipe(gulp.dest('.')); .pipe(gulp.dest('.'));
}); });
gulp.task('prettify:jsx', function() { gulp.task('prettify:sources:verify', function() {
return gulp.src('src/browser/**/*.jsx') prettify_options.mode = "VERIFY_ONLY";
.pipe(esformatter({ prettify_options.showDiff = false;
return gulp.src(sources)
.pipe(prettify(prettify_options));
});
var esformatter_jsx_options = {
indent: { indent: {
value: ' ' value: ' '
}, },
plugins: ['esformatter-jsx'] plugins: ['esformatter-jsx']
})) };
gulp.task('prettify:jsx', function() {
return gulp.src('src/browser/**/*.jsx')
.pipe(esformatter(esformatter_jsx_options))
.pipe(gulp.dest('src/browser')); .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', 'webpack', 'copy'], function() { gulp.task('build', ['sync-meta', 'webpack', 'copy'], function() {
return gulp.src('src/package.json') return gulp.src('src/package.json')
.pipe(gulp.dest('dist')); .pipe(gulp.dest('dist'));

View file

@ -1,11 +1,14 @@
{ {
"name": "mattermost-desktop", "name": "mattermost-desktop",
"productName": "Mattermost", "productName": "Mattermost",
"version": "1.0.7", "version": "1.1.0",
"description": "Mattermost Desktop application for Windows, Mac and Linux", "description": "Mattermost Desktop application for Windows, Mac and Linux",
"main": "main.js", "main": "main.js",
"author": "Yuya Ochiai", "author": "Yuya Ochiai",
"license": "MIT", "license": "MIT",
"engines": {
"node": ">=4.2.0"
},
"scripts": { "scripts": {
"install": "cd src && npm install", "install": "cd src && npm install",
"postinstall": "npm run build", "postinstall": "npm run build",
@ -13,7 +16,7 @@
"start": "electron dist", "start": "electron dist",
"watch": "gulp watch", "watch": "gulp watch",
"serve": "gulp watch", "serve": "gulp watch",
"test": "gulp build && mocha", "test": "gulp build && mocha && gulp prettify:verify",
"package": "gulp package", "package": "gulp package",
"package:windows": "gulp package:windows", "package:windows": "gulp package:windows",
"package:osx": "gulp package:osx", "package:osx": "gulp package:osx",
@ -29,7 +32,7 @@
"del": "^2.2.0", "del": "^2.2.0",
"electron-connect": "^0.3.3", "electron-connect": "^0.3.3",
"electron-packager": "^5.1.0", "electron-packager": "^5.1.0",
"electron-prebuilt": "0.36.7", "electron-prebuilt": "0.36.11",
"esformatter": "^0.8.1", "esformatter": "^0.8.1",
"esformatter-jsx": "^4.0.6", "esformatter-jsx": "^4.0.6",
"gulp": "^3.9.0", "gulp": "^3.9.0",
@ -42,6 +45,7 @@
"mocha-circleci-reporter": "0.0.1", "mocha-circleci-reporter": "0.0.1",
"should": "^8.0.1", "should": "^8.0.1",
"style-loader": "^0.13.0", "style-loader": "^0.13.0",
"through2": "^2.0.1",
"vinyl-named": "^1.1.0", "vinyl-named": "^1.1.0",
"webdriverio": "^3.3.0", "webdriverio": "^3.3.0",
"webpack-stream": "^3.1.0" "webpack-stream": "^3.1.0"

View file

@ -10,6 +10,8 @@ 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 Badge = ReactBootstrap.Badge;
const ListGroup = ReactBootstrap.ListGroup;
const ListGroupItem = ReactBootstrap.ListGroupItem;
const electron = require('electron'); const electron = require('electron');
const remote = electron.remote; const remote = electron.remote;
@ -204,6 +206,7 @@ var TabBar = React.createClass({
var MattermostView = React.createClass({ var MattermostView = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
did_fail_load: null
}; };
}, },
handleUnreadCountChange: function(unreadCount, mentionCount, isUnread, isMentioned) { handleUnreadCountChange: function(unreadCount, mentionCount, isUnread, isMentioned) {
@ -216,6 +219,22 @@ var MattermostView = React.createClass({
var thisObj = this; var thisObj = this;
var webview = ReactDOM.findDOMNode(this.refs.webview); var webview = ReactDOM.findDOMNode(this.refs.webview);
webview.addEventListener('did-fail-load', function(e) {
console.log(thisObj.props.name, 'webview did-fail-load', e);
if (e.errorCode === -3) { // An operation was aborted (due to user action).
return;
}
// should use permanent way to indicate
var did_fail_load_notification = new Notification(`Failed to load "${thisObj.props.name}"`, {
body: `ErrorCode: ${e.errorCode}`,
icon: '../resources/appicon.png'
});
thisObj.setState({
did_fail_load: e
});
});
// 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', function(e) {
var currentURL = url.parse(webview.getURL()); var currentURL = url.parse(webview.getURL());
@ -293,7 +312,40 @@ var MattermostView = React.createClass({
// '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.
if (this.state.did_fail_load === null) {
return (<webview id={ this.props.id } className="mattermostView" style={ this.props.style } preload="webview/mattermost.js" src={ this.props.src } ref="webview"></webview>); return (<webview id={ this.props.id } className="mattermostView" style={ this.props.style } preload="webview/mattermost.js" src={ this.props.src } ref="webview"></webview>);
} else {
return (<ErrorView id={ this.props.id + '-fail' } className="errorView" errorInfo={ this.state.did_fail_load } style={ this.props.style }></ErrorView>)
}
}
});
// ErrorCode: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h
// FIXME: need better wording in English
var ErrorView = React.createClass({
render: function() {
return (
<Grid id={ this.props.id } style={ this.props.style }>
<h1>Failed to load the URL</h1>
<p>
{ 'URL: ' }
{ this.props.errorInfo.validatedURL }
</p>
<p>
{ 'Error code: ' }
{ this.props.errorInfo.errorCode }
</p>
<p>
{ this.props.errorInfo.errorDescription }
</p>
<p>Please check below. Then, reload this window. (Ctrl+R or Command+R)</p>
<ListGroup>
<ListGroupItem>Is your computer online?</ListGroupItem>
<ListGroupItem>Is the server alive?</ListGroupItem>
<ListGroupItem>Is the URL correct?</ListGroupItem>
</ListGroup>
</Grid>
);
} }
}); });
@ -321,7 +373,9 @@ var showUnreadBadgeWindows = function(unreadCount, mentionCount) {
// https://github.com/atom/electron/issues/4011 // https://github.com/atom/electron/issues/4011
electron.ipcRenderer.send('win32-overlay', { electron.ipcRenderer.send('win32-overlay', {
overlayDataURL: dataURL, overlayDataURL: dataURL,
description: description description: description,
unreadCount: unreadCount,
mentionCount: mentionCount
}); });
}; };
@ -332,7 +386,7 @@ var showUnreadBadgeWindows = function(unreadCount, mentionCount) {
const dataURL = badge.createDataURL('•'); const dataURL = badge.createDataURL('•');
sendBadge(dataURL, 'You have unread channels'); sendBadge(dataURL, 'You have unread channels');
} else { } else {
remote.getCurrentWindow().setOverlayIcon(null, ''); sendBadge(null, 'You have no unread messages');
} }
} }

View file

@ -0,0 +1,80 @@
const OriginalNotification = Notification;
function override(eventHandlers) {
Notification = function(title, options) {
this.notification = new OriginalNotification(title, options);
if (eventHandlers.notification) {
eventHandlers.notification(title, options);
}
};
// static properties
Notification.__defineGetter__('permission', function() {
return OriginalNotification.permission;
});
// instance properties
var defineReadProperty = function(property) {
Notification.prototype.__defineGetter__(property, function() {
return this.notification[property];
});
};
defineReadProperty('title');
defineReadProperty('dir');
defineReadProperty('lang');
defineReadProperty('body');
defineReadProperty('tag');
defineReadProperty('icon');
defineReadProperty('data');
defineReadProperty('silent');
// unsupported properties
defineReadProperty('noscreen');
defineReadProperty('renotify');
defineReadProperty('sound');
defineReadProperty('sticky');
defineReadProperty('vibrate');
// event handlers
var defineEventHandler = function(event, callback) {
defineReadProperty(event);
Notification.prototype.__defineSetter__(event, function(originalCallback) {
this.notification[event] = function() {
callbackevent = {
preventDefault: function() {
this.isPrevented = true;
}
};
if (callback) {
callback(callbackevent);
if (!callbackevent.isPrevented) {
originalCallback();
}
}
else {
originalCallback();
}
}
});
}
defineEventHandler('onclick', eventHandlers.onclick);
defineEventHandler('onerror', eventHandlers.onerror);
// obsolete handlers
defineEventHandler('onclose', eventHandlers.onclose);
defineEventHandler('onshow', eventHandlers.onshow);
// static methods
Notification.requestPermission = function(callback) {
OriginalNotification.requestPermission(callback);
};
// instance methods
Notification.prototype.close = function() {
this.notification.close();
};
}
module.exports = {
override: override
};

View file

@ -169,8 +169,8 @@ var TeamListItemNew = React.createClass({
console.log('submit'); console.log('submit');
e.preventDefault(); e.preventDefault();
this.props.onTeamAdd({ this.props.onTeamAdd({
name: this.state.name, name: this.state.name.trim(),
url: this.state.url url: this.state.url.trim()
}); });
this.setState(this.getInitialState()); this.setState(this.getInitialState());
}, },
@ -186,6 +186,9 @@ var TeamListItemNew = React.createClass({
url: e.target.value url: e.target.value
}); });
}, },
shouldEnableAddButton: function() {
return (this.state.name.trim() !== '') && (this.state.url.trim() !== '');
},
render: function() { render: function() {
return ( return (
<ListGroupItem> <ListGroupItem>
@ -202,7 +205,7 @@ var TeamListItemNew = React.createClass({
<input type="url" className="form-control" id="inputTeamURL" placeholder="https://example.com/team" value={ this.state.url } onChange={ this.handleURLChange } /> <input type="url" className="form-control" id="inputTeamURL" placeholder="https://example.com/team" value={ this.state.url } onChange={ this.handleURLChange } />
</div> </div>
{ ' ' } { ' ' }
<Button type="submit">Add</Button> <Button type="submit" disabled={ !this.shouldEnableAddButton() }>Add</Button>
</form> </form>
</ListGroupItem> </ListGroupItem>
); );

View file

@ -2,7 +2,7 @@
const electron = require('electron'); const electron = require('electron');
const ipc = electron.ipcRenderer; const ipc = electron.ipcRenderer;
const NativeNotification = Notification; const notification = require('../js/notification');
var hasClass = function(element, className) { var hasClass = function(element, className) {
var rclass = /[\t\r\n\f]/g; var rclass = /[\t\r\n\f]/g;
@ -107,35 +107,21 @@ function isLowerThanOrEqualWindows8_1() {
return (osVersion.major <= 6 && osVersion.minor <= 3); return (osVersion.major <= 6 && osVersion.minor <= 3);
}; };
if (process.platform === 'win32' && isLowerThanOrEqualWindows8_1()) {
// Show balloon when notified. // Show balloon when notified.
function overrideNotificationWithBalloon() { notification.override({
Notification = function(title, options) { notification: function(title, options) {
ipc.send('notified', { ipc.send('notified', {
title: title, title: title,
options: options options: options
}); });
}; }
Notification.permission = NativeNotification.permission; });
Notification.requestPermission = function(callback) { }
callback('granted'); else {
};
Notification.prototype.close = function() {};
};
// Show window even if it is hidden/minimized when notification is clicked. // Show window even if it is hidden/minimized when notification is clicked.
function overrideNotification() { notification.override({
Notification = function(title, options) { onclick: function() {
this.notification = new NativeNotification(title, options);
};
Notification.permission = NativeNotification.permission;
Notification.requestPermission = function(callback) {
callback('granted');
};
Notification.prototype.close = function() {
this.notification.close();
};
Notification.prototype.__defineSetter__('onclick', function(callback) {
this.notification.onclick = function() {
if (process.platform === 'win32') { if (process.platform === 'win32') {
// show() breaks Aero Snap state. // show() breaks Aero Snap state.
electron.remote.getCurrentWindow().focus(); electron.remote.getCurrentWindow().focus();
@ -144,14 +130,6 @@ function overrideNotification() {
electron.remote.getCurrentWindow().show(); electron.remote.getCurrentWindow().show();
} }
ipc.sendToHost('onNotificationClick'); ipc.sendToHost('onNotificationClick');
callback(); }
};
}); });
} }
if (process.platform === 'win32' && isLowerThanOrEqualWindows8_1()) {
overrideNotificationWithBalloon();
}
else {
overrideNotification();
}

View file

@ -10,6 +10,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
var settings = require('./common/settings'); var settings = require('./common/settings');
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
var appMenu = require('./main/menus/app'); var appMenu = require('./main/menus/app');
var argv = require('yargs').argv; var argv = require('yargs').argv;
@ -89,6 +90,38 @@ app.on('before-quit', function() {
willAppQuit = true; willAppQuit = true;
}); });
app.on('certificate-error', function(event, webContents, url, error, certificate, callback) {
if (certificateStore.isTrusted(url, certificate)) {
event.preventDefault();
callback(true);
}
else {
var detail = `URL: ${url}\nError: ${error}`;
if (certificateStore.isExisting(url)) {
detail = `Certificate is different from previous one.\n\n` + detail;
}
electron.dialog.showMessageBox(mainWindow, {
title: 'Certificate error',
message: `Do you trust certificate from "${certificate.issuerName}"?`,
detail: detail,
type: 'warning',
buttons: [
'Yes',
'No'
],
cancelId: 1
}, function(response) {
if (response === 0) {
certificateStore.add(url, certificate);
certificateStore.save();
webContents.loadURL(url);
}
});
callback(false);
}
});
// 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', function() {
@ -113,9 +146,22 @@ app.on('ready', function() {
}); });
// Set overlay icon from dataURL // Set overlay icon from dataURL
// Set trayicon to show "dot"
ipc.on('win32-overlay', function(event, arg) { ipc.on('win32-overlay', function(event, arg) {
var overlay = electron.nativeImage.createFromDataURL(arg.overlayDataURL); const overlay = arg.overlayDataURL ? electron.nativeImage.createFromDataURL(arg.overlayDataURL) : null;
mainWindow.setOverlayIcon(overlay, arg.description); mainWindow.setOverlayIcon(overlay, arg.description);
var tray_image = null;
if (arg.mentionCount > 0) {
tray_image = 'tray_mention.png';
}
else if (arg.unreadCount > 0) {
tray_image = 'tray_unread.png';
}
else {
tray_image = 'tray.png';
}
trayIcon.setImage(path.resolve(__dirname, 'resources', tray_image));
}); });
} }
@ -129,7 +175,8 @@ app.on('ready', function() {
// follow Electron's defaults // follow Electron's defaults
window_options = {}; window_options = {};
} }
if (process.platform === 'linux') { if (process.platform === 'win32' || process.platform === 'linux') {
// On HiDPI Windows environment, the taskbar icon is pixelated. So this line is necessary.
window_options.icon = path.resolve(__dirname, 'resources/appicon.png'); window_options.icon = path.resolve(__dirname, 'resources/appicon.png');
} }
window_options.fullScreenable = true; window_options.fullScreenable = true;

View file

@ -0,0 +1,62 @@
'use strict';
const fs = require('fs');
const url = require('url');
function comparableCertificate(certificate) {
return {
data: certificate.data.toString(),
issuerName: certificate.issuerName
};
}
function areEqual(certificate0, certificate1) {
if (certificate0.data !== certificate1.data) {
return false;
}
if (certificate0.issuerName !== certificate1.issuerName) {
return false;
}
return true;
}
function getHost(targetURL) {
return url.parse(targetURL).host;
}
var CertificateStore = function(storeFile) {
this.storeFile = storeFile
try {
this.data = JSON.parse(fs.readFileSync(storeFile, 'utf-8'));
}
catch (e) {
console.log(e);
this.data = {};
}
};
CertificateStore.prototype.save = function() {
fs.writeFileSync(this.storeFile, JSON.stringify(this.data, null, ' '));
};
CertificateStore.prototype.add = function(targetURL, certificate) {
this.data[getHost(targetURL)] = comparableCertificate(certificate);
};
CertificateStore.prototype.isExisting = function(targetURL) {
return this.data.hasOwnProperty(getHost(targetURL));
};
CertificateStore.prototype.isTrusted = function(targetURL, certificate) {
var host = getHost(targetURL);
if (!this.isExisting(targetURL)) {
return false;
}
return areEqual(this.data[host], comparableCertificate(certificate));
};
module.exports = {
load: function(storeFile) {
return new CertificateStore(storeFile);
}
};

View file

@ -1,7 +1,7 @@
{ {
"name": "mattermost-desktop", "name": "mattermost-desktop",
"productName": "Mattermost", "productName": "Mattermost",
"version": "1.0.7", "version": "1.1.0",
"description": "Mattermost Desktop application for Windows, Mac and Linux", "description": "Mattermost Desktop application for Windows, Mac and Linux",
"main": "main.js", "main": "main.js",
"author": "Yuya Ochiai", "author": "Yuya Ochiai",

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

View file

@ -171,6 +171,21 @@ describe('mattermost-desktop', function() {
}) })
.end(); .end();
}); });
it('should show error when using incorrect URL', function() {
this.timeout(30000)
fs.writeFileSync(config_file_path, JSON.stringify({
version: 1,
teams: [{
name: 'error_1',
url: 'http://false'
}]
}));
return client
.init()
.waitForVisible('#mattermostView0-fail', 20000)
.end();
});
}); });
describe('settings.html', function() { describe('settings.html', function() {