Merge branch 'better-dev-mode'

This commit is contained in:
Yuya Ochiai
2017-03-08 20:17:51 +09:00
20 changed files with 117 additions and 201 deletions

View File

@@ -1,4 +1,4 @@
dist/ **/*_bundle.js
node_modules/ node_modules/
release/ release/
src/node_modules/ src/node_modules/

2
.gitignore vendored
View File

@@ -3,7 +3,7 @@ Thumbs.db
*.log *.log
node_modules/ node_modules/
dist/ *_bundle.js
release/ release/
npm-debug.log* npm-debug.log*

View File

@@ -19,6 +19,7 @@ dependencies:
- sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib - sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib
- unset DISPLAY && wineboot --init - unset DISPLAY && wineboot --init
- npm run build
- npm run package:all - npm run package:all
- sh -x ./scripts/cp_artifacts.sh release $CIRCLE_ARTIFACTS - sh -x ./scripts/cp_artifacts.sh release $CIRCLE_ARTIFACTS

View File

@@ -2,9 +2,15 @@
"appId": "com.mattermost.desktop", "appId": "com.mattermost.desktop",
"directories": { "directories": {
"buildResources": "resources", "buildResources": "resources",
"app": "dist", "app": "src",
"output": "release" "output": "release"
}, },
"files": [
"main_bundle.js",
"browser/**/*.{html,css,_bundle.js}",
"assets/**/*",
"node_modules/bootstrap/dist/**"
],
"deb": { "deb": {
"synopsis": "Mattermost" "synopsis": "Mattermost"
}, },

View File

@@ -1,129 +0,0 @@
'use strict';
var gulp = require('gulp');
var prettify = require('gulp-jsbeautifier');
var diff = require('gulp-diff');
var electron = require('electron-connect').server.create({
path: './dist'
});
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
const distPackageAuthor = 'Mattermost, Inc.';
var sources = ['**/*.js', '**/*.json', '**/*.css', '**/*.html', '!**/node_modules/**', '!dist/**', '!release/**', '!**/test_config.json'];
gulp.task('prettify', ['prettify:sources']);
gulp.task('prettify:verify', ['prettify:sources:verify']);
var prettifyOptions = {
html: {
indent_size: 2,
end_with_newline: true
},
css: {
indent_size: 2,
end_with_newline: true
},
js: {
indent_size: 2,
brace_style: 'end-expand',
end_with_newline: true
}
};
gulp.task('prettify:sources', ['sync-meta'], () => {
return gulp.src(sources).
pipe(prettify(prettifyOptions)).
pipe(prettify.reporter()).
pipe(diff()).
pipe(diff.reporter({
quiet: true,
fail: false
})).
pipe(gulp.dest('.'));
});
gulp.task('prettify:sources:verify', () => {
return gulp.src(sources).
pipe(prettify(prettifyOptions)).
pipe(prettify.reporter()).
pipe(diff()).
pipe(diff.reporter({
quiet: true,
fail: true
}));
});
gulp.task('build', ['sync-meta', 'copy'], (cb) => {
const appPackageJson = require('./src/package.json');
const distPackageJson = Object.assign({}, appPackageJson, {
author: {
name: distPackageAuthor,
email: 'noreply'
}
});
fs.writeFile('./dist/package.json', JSON.stringify(distPackageJson, null, ' '), cb);
});
gulp.task('copy', ['copy:assets', 'copy:html/css', 'copy:modules']);
gulp.task('copy:assets', () => {
return gulp.src('src/assets/**').
pipe(gulp.dest('dist/assets'));
});
gulp.task('copy:html/css', () => {
return gulp.src(['src/browser/**/*.html', 'src/browser/**/*.css']).
pipe(gulp.dest('dist/browser'));
});
gulp.task('copy:modules', () => {
return gulp.src(['src/node_modules/bootstrap/dist/**']).
pipe(gulp.dest('dist/browser/modules/bootstrap'));
});
function spawnWebpack(config, cb) {
const ext = process.platform === 'win32' ? '.cmd' : '';
spawn(path.resolve(`./node_modules/.bin/webpack${ext}`), ['--config', config], {
stdio: 'inherit'
}).on('exit', (code) => {
cb(code);
});
}
gulp.task('webpack:main', (cb) => {
spawnWebpack('webpack.config.main.js', cb);
});
gulp.task('webpack:renderer', (cb) => {
spawnWebpack('webpack.config.renderer.js', cb);
});
gulp.task('watch', ['build', 'webpack:main', 'webpack:renderer'], () => {
var options = ['--livereload'];
electron.start(options);
gulp.watch(['src/main.js', 'src/main/**/*.js', 'src/common/**/*.js'], ['webpack:main']);
gulp.watch(['src/browser/**/*.js', 'src/browser/**/*.jsx'], ['webpack:renderer']);
gulp.watch(['src/browser/**/*.css', 'src/browser/**/*.html', 'src/assets/**/*.png'], ['copy']);
gulp.watch(['dist/main.js', 'dist/assets/**'], () => {
electron.restart(options);
});
gulp.watch(['dist/browser/*.js'], electron.reload);
});
gulp.task('sync-meta', () => {
var appPackageJson = require('./src/package.json');
var packageJson = require('./package.json');
appPackageJson.name = packageJson.name;
appPackageJson.productName = packageJson.productName;
appPackageJson.version = packageJson.version;
appPackageJson.description = packageJson.description;
appPackageJson.author = packageJson.author;
appPackageJson.license = packageJson.license;
fs.writeFileSync('./src/package.json', JSON.stringify(appPackageJson, null, ' ') + '\n');
});

View File

@@ -17,14 +17,14 @@
"url": "git://github.com/mattermost/desktop.git" "url": "git://github.com/mattermost/desktop.git"
}, },
"scripts": { "scripts": {
"install": "cd src && npm install", "postinstall": "install-app-deps",
"postinstall": "npm run build",
"build": "npm-run-all build:*", "build": "npm-run-all build:*",
"build:main": "cross-env NODE_ENV=production webpack --bail --config webpack.config.main.js", "build:main": "cross-env NODE_ENV=production webpack --bail --config webpack.config.main.js",
"build:renderer": "cross-env NODE_ENV=production webpack --bail --config webpack.config.renderer.js", "build:renderer": "cross-env NODE_ENV=production webpack --bail --config webpack.config.renderer.js",
"build:others": "gulp build", "start": "electron src --disable-dev-mode",
"start": "electron dist", "watch": "run-p watch:*",
"watch": "gulp watch", "watch:main": "node scripts/watch_main_and_preload.js",
"watch:renderer": "webpack-dev-server --config webpack.config.renderer.js",
"serve": "gulp watch", "serve": "gulp watch",
"test": "npm-run-all build test:* lint:*", "test": "npm-run-all build test:* lint:*",
"test:app": "mocha --reporter mocha-circleci-reporter --recursive test/specs", "test:app": "mocha --reporter mocha-circleci-reporter --recursive test/specs",
@@ -52,14 +52,12 @@
"electron-connect": "^0.6.1", "electron-connect": "^0.6.1",
"eslint": "^3.16.1", "eslint": "^3.16.1",
"eslint-plugin-react": "^6.10.0", "eslint-plugin-react": "^6.10.0",
"gulp": "^3.9.1",
"gulp-diff": "^1.0.0",
"gulp-jsbeautifier": "^2.0.4",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"mocha-circleci-reporter": "0.0.2", "mocha-circleci-reporter": "0.0.2",
"npm-run-all": "^4.0.2", "npm-run-all": "^4.0.2",
"spectron": "~3.6.0", "spectron": "~3.6.0",
"webpack": "^2.2.1", "webpack": "^2.2.1",
"webpack-dev-server": "^2.4.1",
"webpack-merge": "^3.0.0" "webpack-merge": "^3.0.0"
} }
} }

View File

@@ -0,0 +1,34 @@
const webpack = require('webpack');
const mainConfig = require('../webpack.config.main.js');
const rendererConfig = require('../webpack.config.renderer.js');
const electron = require('electron-connect').server.create({path: 'src'});
let started = false;
const mainCompiler = webpack(mainConfig);
mainCompiler.watch({}, (err, stats) => {
process.stdout.write(stats.toString({colors: true}));
process.stdout.write('\n');
if (!stats.hasErrors()) {
if (started) {
electron.restart();
} else {
electron.start();
started = true;
}
}
});
for (const key in rendererConfig.entry) {
if (!key.startsWith('webview/')) {
if ({}.hasOwnProperty.call(rendererConfig.entry, key)) {
delete rendererConfig.entry[key];
}
}
}
const preloadCompiler = webpack(rendererConfig);
preloadCompiler.watch({}, (err) => {
if (err) {
console.log(err);
}
});

View File

@@ -1,6 +1,6 @@
const React = require('react'); const React = require('react');
const {findDOMNode} = require('react-dom'); const {findDOMNode} = require('react-dom');
const {ipcRenderer, shell} = require('electron'); const {ipcRenderer, remote, shell} = require('electron');
const fs = require('fs'); const fs = require('fs');
const url = require('url'); const url = require('url');
const osLocale = require('os-locale'); const osLocale = require('os-locale');
@@ -8,6 +8,8 @@ const electronContextMenu = require('electron-context-menu');
const ErrorView = require('./ErrorView.jsx'); const ErrorView = require('./ErrorView.jsx');
const preloadJS = `file://${remote.app.getAppPath()}/browser/webview/mattermost_bundle.js`;
const MattermostView = React.createClass({ const MattermostView = React.createClass({
propTypes: { propTypes: {
disablewebsecurity: React.PropTypes.bool, disablewebsecurity: React.PropTypes.bool,
@@ -98,7 +100,7 @@ const MattermostView = React.createClass({
osLocale().then((locale) => { osLocale().then((locale) => {
if (locale === 'ja_JP') { if (locale === 'ja_JP') {
applyCssFile(__dirname + '/css/jp_fonts.css'); applyCssFile(remote.app.getAppPath() + '/css/jp_fonts.css');
} }
}); });
} }
@@ -228,7 +230,7 @@ const MattermostView = React.createClass({
id={this.props.id} id={this.props.id}
className='mattermostView' className='mattermostView'
style={this.props.style} style={this.props.style}
preload='webview/mattermost.js' preload={preloadJS}
src={this.props.src} src={this.props.src}
ref='webview' ref='webview'
nodeintegration='false' nodeintegration='false'

View File

@@ -18,7 +18,8 @@ const appLauncher = new AutoLaunch({
function backToIndex(index) { function backToIndex(index) {
const target = typeof index === 'undefined' ? 0 : index; const target = typeof index === 'undefined' ? 0 : index;
remote.getCurrentWindow().loadURL(`file://${__dirname}/index.html?index=${target}`); const indexURL = remote.getGlobal('isDev') ? 'http://localhost:8080/browser/index.html' : `file://${remote.app.getAppPath()}/browser/index.html`;
remote.getCurrentWindow().loadURL(`${indexURL}?index=${target}`);
} }
const SettingsPage = React.createClass({ const SettingsPage = React.createClass({

View File

@@ -3,13 +3,13 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="modules/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/index.css"> <link rel="stylesheet" href="css/index.css">
</head> </head>
<body> <body>
<div id="content"></div> <div id="content"></div>
<script src="index.js"></script> <script src="index_bundle.js"></script>
</body> </body>
</html> </html>

View File

@@ -103,3 +103,8 @@ ReactDOM.render(
/>, />,
document.getElementById('content') document.getElementById('content')
); );
// Deny drag&drop navigation in mainWindow.
// Drag&drop is allowed in webview of index.html.
document.addEventListener('dragover', (event) => event.preventDefault());
document.addEventListener('drop', (event) => event.preventDefault());

View File

@@ -4,14 +4,14 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Settings</title> <title>Settings</title>
<link rel="stylesheet" href="modules/bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/index.css"> <link rel="stylesheet" href="css/index.css">
<link rel="stylesheet" href="css/settings.css"> <link rel="stylesheet" href="css/settings.css">
</head> </head>
<body> <body>
<div id="content"></div> <div id="content"></div>
<script src="settings.js"></script> <script src="settings_bundle.js"></script>
</body> </body>
</html> </html>

View File

@@ -20,3 +20,7 @@ ReactDOM.render(
<SettingsPage configFile={configFile}/>, <SettingsPage configFile={configFile}/>,
document.getElementById('content') document.getElementById('content')
); );
// Deny drag&drop navigation in mainWindow.
document.addEventListener('dragover', (event) => event.preventDefault());
document.addEventListener('drop', (event) => event.preventDefault());

View File

@@ -11,6 +11,8 @@ const {
systemPreferences, systemPreferences,
session session
} = require('electron'); } = require('electron');
const isDev = require('electron-is-dev');
const installExtension = require('electron-devtools-installer');
const AutoLaunch = require('auto-launch'); const AutoLaunch = require('auto-launch');
@@ -70,15 +72,6 @@ var mainWindow = null;
var argv = require('yargs').parse(process.argv.slice(1)); var argv = require('yargs').parse(process.argv.slice(1));
const electronConnect = argv.livereload ? require('electron-connect') : null;
var client;
if (argv.livereload) {
client = electronConnect.client.create();
client.on('reload', () => {
mainWindow.reload();
});
}
var hideOnStartup; var hideOnStartup;
if (argv.hidden) { if (argv.hidden) {
hideOnStartup = true; hideOnStartup = true;
@@ -88,6 +81,8 @@ if (argv['data-dir']) {
app.setPath('userData', path.resolve(argv['data-dir'])); app.setPath('userData', path.resolve(argv['data-dir']));
} }
global.isDev = isDev && !argv.disableDevMode;
var config = {}; var config = {};
try { try {
const configFile = app.getPath('userData') + '/config.json'; const configFile = app.getPath('userData') + '/config.json';
@@ -315,6 +310,12 @@ app.on('ready', () => {
if (willAppQuit) { if (willAppQuit) {
return; return;
} }
if (global.isDev) {
installExtension.default(installExtension.REACT_DEVELOPER_TOOLS).
then((name) => console.log(`Added Extension: ${name}`)).
catch((err) => console.log('An error occurred: ', err));
}
ipcMain.on('notified', () => { 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) {
@@ -483,16 +484,17 @@ app.on('ready', () => {
} }
// and load the index.html of the app. // and load the index.html of the app.
mainWindow.loadURL('file://' + __dirname + '/browser/index.html'); const indexURL = global.isDev ? 'http://localhost:8080/browser/index.html' : `file://${app.getAppPath()}/browser/index.html`;
mainWindow.loadURL(indexURL);
// Set application menu // Set application menu
ipcMain.on('update-menu', (event, configData) => { ipcMain.on('update-menu', (event, configData) => {
var aMenu = appMenu.createMenu(mainWindow, configData); var aMenu = appMenu.createMenu(mainWindow, configData, global.isDev);
Menu.setApplicationMenu(aMenu); Menu.setApplicationMenu(aMenu);
// set up context menu for tray icon // set up context menu for tray icon
if (shouldShowTrayIcon()) { if (shouldShowTrayIcon()) {
const tMenu = trayMenu.createMenu(mainWindow, configData); const tMenu = trayMenu.createMenu(mainWindow, configData, global.isDev);
trayIcon.setContextMenu(tMenu); 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
@@ -565,18 +567,4 @@ app.on('ready', () => {
// when you should delete the corresponding element. // when you should delete the corresponding element.
mainWindow = null; mainWindow = null;
}); });
// Deny drag&drop navigation in mainWindow.
// Drag&drop is allowed in webview of index.html.
mainWindow.webContents.on('will-navigate', (event, url) => {
var dirname = __dirname;
if (process.platform === 'win32') {
dirname = '/' + dirname.replace(/\\/g, '/');
}
var index = url.indexOf('file://' + dirname);
if (index !== 0) {
event.preventDefault();
}
});
}); });

View File

@@ -3,7 +3,9 @@
const electron = require('electron'); const electron = require('electron');
const Menu = electron.Menu; const Menu = electron.Menu;
function createTemplate(mainWindow, config) { function createTemplate(mainWindow, config, isDev) {
const settingsURL = isDev ? 'http://localhost:8080/browser/settings.html' : `file://${electron.app.getAppPath()}/browser/settings.html`;
const separatorItem = { const separatorItem = {
type: 'separator' type: 'separator'
}; };
@@ -25,7 +27,7 @@ function createTemplate(mainWindow, config) {
label: 'Preferences...', label: 'Preferences...',
accelerator: 'CmdOrCtrl+,', accelerator: 'CmdOrCtrl+,',
click() { click() {
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html'); mainWindow.loadURL(settingsURL);
} }
}, { }, {
label: 'Sign in to Another Server', label: 'Sign in to Another Server',
@@ -44,7 +46,7 @@ function createTemplate(mainWindow, config) {
label: 'Settings...', label: 'Settings...',
accelerator: 'CmdOrCtrl+,', accelerator: 'CmdOrCtrl+,',
click() { click() {
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html'); mainWindow.loadURL(settingsURL);
} }
}, { }, {
label: 'Sign in to Another Server', label: 'Sign in to Another Server',
@@ -217,8 +219,8 @@ function createTemplate(mainWindow, config) {
return template; return template;
} }
function createMenu(mainWindow, config) { function createMenu(mainWindow, config, isDev) {
return Menu.buildFromTemplate(createTemplate(mainWindow, config)); return Menu.buildFromTemplate(createTemplate(mainWindow, config, isDev));
} }
module.exports = { module.exports = {

View File

@@ -5,7 +5,8 @@ const {
Menu Menu
} = require('electron'); } = require('electron');
function createTemplate(mainWindow, config) { function createTemplate(mainWindow, config, isDev) {
const settingsURL = isDev ? 'http://localhost:8080/browser/settings.html' : `file://${app.getAppPath()}/browser/settings.html`;
var template = [ var template = [
...config.teams.slice(0, 9).map((team, i) => { ...config.teams.slice(0, 9).map((team, i) => {
return { return {
@@ -25,7 +26,7 @@ function createTemplate(mainWindow, config) {
}, { }, {
label: process.platform === 'darwin' ? 'Preferences...' : 'Settings', label: process.platform === 'darwin' ? 'Preferences...' : 'Settings',
click: () => { click: () => {
mainWindow.loadURL('file://' + __dirname + '/browser/settings.html'); mainWindow.loadURL(settingsURL);
showOrRestore(mainWindow); showOrRestore(mainWindow);
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@@ -42,8 +43,8 @@ function createTemplate(mainWindow, config) {
return template; return template;
} }
function createMenu(mainWindow, config) { function createMenu(mainWindow, config, isDev) {
return Menu.buildFromTemplate(createTemplate(mainWindow, config)); return Menu.buildFromTemplate(createTemplate(mainWindow, config, isDev));
} }
function showOrRestore(window) { function showOrRestore(window) {

View File

@@ -4,20 +4,19 @@
"desktopName": "Mattermost.desktop", "desktopName": "Mattermost.desktop",
"version": "3.6.0", "version": "3.6.0",
"description": "Mattermost", "description": "Mattermost",
"main": "main.js", "main": "main_bundle.js",
"author": { "author": {
"name": "Yuya Ochiai", "name": "Yuya Ochiai",
"email": "yuya0321@gmail.com" "email": "yuya0321@gmail.com"
}, },
"homepage": "https://about.mattermost.com", "homepage": "https://about.mattermost.com",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": {
"electron-connect": "^0.6.1"
},
"dependencies": { "dependencies": {
"auto-launch": "^5.0.1", "auto-launch": "^5.0.1",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"electron-context-menu": "^0.8.0", "electron-context-menu": "^0.8.0",
"electron-devtools-installer": "^2.1.0",
"electron-is-dev": "^0.1.2",
"electron-squirrel-startup": "^1.0.0", "electron-squirrel-startup": "^1.0.0",
"os-locale": "^2.0.0", "os-locale": "^2.0.0",
"react": "^15.4.2", "react": "^15.4.2",

View File

@@ -44,7 +44,7 @@ module.exports = {
getSpectronApp() { getSpectronApp() {
const app = new Application({ const app = new Application({
path: electronBinaryPath, path: electronBinaryPath,
args: [`${path.join(sourceRootDir, 'dist')}`, `--data-dir=${userDataDir}`] args: [`${path.join(sourceRootDir, 'src')}`, `--data-dir=${userDataDir}`, '--disable-dev-mode']
}); });
chaiAsPromised.transferPromiseness = app.transferPromiseness; chaiAsPromised.transferPromiseness = app.transferPromiseness;
return app; return app;
@@ -52,7 +52,7 @@ module.exports = {
addClientCommands(client) { addClientCommands(client) {
client.addCommand('loadSettingsPage', function async() { client.addCommand('loadSettingsPage', function async() {
return this.url('file://' + path.join(sourceRootDir, 'dist/browser/settings.html')).waitUntilWindowLoaded(); return this.url('file://' + path.join(sourceRootDir, 'src/browser/settings.html')).waitUntilWindowLoaded();
}); });
client.addCommand('isNodeEnabled', function async() { client.addCommand('isNodeEnabled', function async() {
return this.execute(() => { return this.execute(() => {

View File

@@ -6,14 +6,11 @@ const base = require('./webpack.config.base');
module.exports = merge(base, { module.exports = merge(base, {
entry: './src/main.js', entry: './src/main.js',
output: { output: {
filename: './dist/main.js' filename: './src/[name]_bundle.js'
}, },
node: { node: {
__filename: false, __filename: true,
__dirname: false __dirname: true
}, },
target: 'electron-main', target: 'electron-main'
externals: {
remote: true // for electron-connect
}
}); });

View File

@@ -1,5 +1,6 @@
'use strict'; 'use strict';
const path = require('path');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const base = require('./webpack.config.base'); const base = require('./webpack.config.base');
@@ -10,8 +11,9 @@ module.exports = merge(base, {
'webview/mattermost': './src/browser/webview/mattermost.js' 'webview/mattermost': './src/browser/webview/mattermost.js'
}, },
output: { output: {
path: './dist/browser', path: path.join(__dirname, 'src/browser'),
filename: '[name].js' publicPath: 'browser',
filename: '[name]_bundle.js'
}, },
module: { module: {
rules: [{ rules: [{
@@ -26,8 +28,13 @@ module.exports = merge(base, {
}] }]
}, },
node: { node: {
__filename: false, __filename: true,
__dirname: false __dirname: true
}, },
target: 'electron-renderer' target: 'electron-renderer',
devServer: {
contentBase: path.join(__dirname, 'src'),
inline: true,
publicPath: '/browser/'
}
}); });