From 4f185211cef701d40bc2ce68337fec4587a4691b Mon Sep 17 00:00:00 2001 From: Aviral Dasgupta Date: Tue, 4 Oct 2016 16:04:03 +0530 Subject: [PATCH] Set up Electron integration and builds --- .gitignore | 2 + package.json | 28 +++++++++++-- src/electron-main.js | 19 +++++++++ src/vector/index.js | 4 +- .../integration/BaseIntegrationManager.js | 16 +++++++ .../integration/ElectronIntegrationManager.js | 16 +++++++ .../integration/WebIntegrationManager.js | 42 +++++++++++++++++++ src/vector/integration/index.js | 15 +++++++ webpack.config.js | 39 +++++++++++++++-- 9 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 src/electron-main.js create mode 100644 src/vector/integration/BaseIntegrationManager.js create mode 100644 src/vector/integration/ElectronIntegrationManager.js create mode 100644 src/vector/integration/WebIntegrationManager.js create mode 100644 src/vector/integration/index.js diff --git a/.gitignore b/.gitignore index ac9c2c300d..7670c2f250 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,10 @@ /node_modules /packages/ /vector/bundle.* +/vector/electron-main.* /vector/components.css /vector/emojione/ +/vector/electron/ /vector/config.json /vector/olm.js .DS_Store diff --git a/package.json b/package.json index da15356bbb..ba85b4adc4 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,7 @@ { "name": "vector-web", + "productName": "Riot", + "main": "vector/electron-main.js", "version": "0.8.1", "description": "Vector webapp", "author": "matrix.org", @@ -16,15 +18,16 @@ "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", "build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch", "build:compile": "babel --source-maps -d lib src", - "build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js", + "build:bundle": "NODE_ENV=production webpack", "build:bundle:dev": "webpack --optimize-occurence-order lib/vector/index.js vector/bundle.js", "build:staticfiles": "cpx -v node_modules/olm/olm.js vector/", + "build:electron": "build -lwm", "build": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle", "build:dev": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle:dev", "package": "scripts/package.sh", "start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" vector/emojione/svg/ -w", - "start:js": "webpack -w src/vector/index.js vector/bundle.js", - "start:js:prod": "NODE_ENV=production webpack -w src/vector/index.js vector/bundle.js", + "start:js": "webpack -w", + "start:js:prod": "NODE_ENV=production webpack -w", "start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css", "start:staticfiles": "cpx -Lwv node_modules/olm/olm.js vector/", "//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270", @@ -41,6 +44,7 @@ "classnames": "^2.1.2", "draft-js": "^0.8.1", "extract-text-webpack-plugin": "^0.9.1", + "favico.js": "^0.3.10", "filesize": "^3.1.2", "flux": "~2.0.3", "gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279", @@ -67,6 +71,7 @@ "catw": "^1.0.1", "cpx": "^1.3.2", "css-raw-loader": "^0.1.1", + "electron-builder": "^7.10.2", "emojione": "^2.2.3", "expect": "^1.16.0", "fs-extra": "^0.30.0", @@ -91,5 +96,22 @@ }, "optionalDependencies": { "olm": "https://matrix.org/packages/npm/olm/olm-1.3.0.tgz" + }, + "build": { + "appId": "im.riot.app", + "category": "Network", + "electronVersion": "1.4.2", + "//asar=false": "https://github.com/electron-userland/electron-builder/issues/675", + "asar": false, + "dereference": true, + "//files": "We bundle everything, so we only need to include vector/", + "files": [ + "!**/*", + "vector/**", + "package.json" + ] + }, + "directories": { + "output": "vector/electron" } } diff --git a/src/electron-main.js b/src/electron-main.js new file mode 100644 index 0000000000..83e392880e --- /dev/null +++ b/src/electron-main.js @@ -0,0 +1,19 @@ +// @flow +const {app, BrowserWindow} = require('electron'); + +let window = null; + +app.on('ready', () => { + window = new BrowserWindow({ + icon: `${__dirname}/img/logo.png`, + }); + window.loadURL(`file://${__dirname}/index.html`); + window.webContents.openDevTools(); + window.on('closed', () => { + window = null; + }) +}); + +app.on('window-all-closed', () => { + app.quit(); +}); diff --git a/src/vector/index.js b/src/vector/index.js index acad100ba0..e1eb6e50e1 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -28,7 +28,6 @@ require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); require('draft-js/dist/Draft.css'); - // add React and ReactPerf to the global namespace, to make them easier to // access via the console global.React = require("react"); @@ -49,6 +48,7 @@ import UAParser from 'ua-parser-js'; import url from 'url'; import {parseQs, parseQsFromFragment} from './url_utils'; +import IntegrationManager from './integration'; var lastLocationHashSet = null; @@ -201,6 +201,7 @@ function onLoadCompleted() { async function loadApp() { const fragparts = parseQsFromFragment(window.location); const params = parseQs(window.location); + const integrationManager = new IntegrationManager(); // don't try to redirect to the native apps if we're // verifying a 3pid @@ -254,6 +255,7 @@ async function loadApp() { enableGuest={true} onLoadCompleted={onLoadCompleted} defaultDeviceDisplayName={getDefaultDeviceDisplayName()} + integrationManager={integrationManager} />, document.getElementById('matrixchat') ); diff --git a/src/vector/integration/BaseIntegrationManager.js b/src/vector/integration/BaseIntegrationManager.js new file mode 100644 index 0000000000..c83e2b2351 --- /dev/null +++ b/src/vector/integration/BaseIntegrationManager.js @@ -0,0 +1,16 @@ +// @flow + +export default class BaseIntegrationManager { + constructor() { + this.notificationCount = 0; + this.errorDidOccur = false; + } + + setNotificationCount(count: number) { + this.notificationCount = count; + } + + setErrorStatus(errorDidOccur: boolean) { + this.errorDidOccur = errorDidOccur; + } +} diff --git a/src/vector/integration/ElectronIntegrationManager.js b/src/vector/integration/ElectronIntegrationManager.js new file mode 100644 index 0000000000..c0c72f1ee9 --- /dev/null +++ b/src/vector/integration/ElectronIntegrationManager.js @@ -0,0 +1,16 @@ +// @flow +import BaseIntegrationManager from './BaseIntegrationManager'; + +// index.js imports us unconditionally, so we need this check here as well +let electron = null, remote = null; +if (window && window.process && window.process && window.process.type === 'renderer') { + electron = require('electron'); + remote = electron.remote; +} + +export default class ElectronIntegrationManager extends BaseIntegrationManager { + setNotificationCount(count: number) { + super.setNotificationCount(count); + remote.app.setBadgeCount(count); + } +} diff --git a/src/vector/integration/WebIntegrationManager.js b/src/vector/integration/WebIntegrationManager.js new file mode 100644 index 0000000000..21b5be64d8 --- /dev/null +++ b/src/vector/integration/WebIntegrationManager.js @@ -0,0 +1,42 @@ +// @flow +import BaseIntegrationManager from './BaseIntegrationManager'; +import Favico from 'favico.js'; + +export default class WebIntegrationManager extends BaseIntegrationManager { + constructor() { + super(); + this.favicon = new Favico({animation: 'popFade'}); + this.updateFavicon(); + } + + updateFavicon() { + try { + // This needs to be in in a try block as it will throw + // if there are more than 100 badge count changes in + // its internal queue + let bgColor = "#d00", + notif = this.notificationCount; + + if (this.errorDidOccur) { + notif = notif || "×"; + bgColor = "#f00"; + } + + this.favicon.badge(notif, { + bgColor: bgColor + }); + } catch (e) { + console.warn(`Failed to set badge count: ${e.message}`); + } + } + + setNotificationCount(count: number) { + super.setNotificationCount(count); + this.updateFavicon(); + } + + setErrorStatus(errorDidOccur: boolean) { + super.setErrorStatus(errorDidOccur); + this.updateFavicon(); + } +} diff --git a/src/vector/integration/index.js b/src/vector/integration/index.js new file mode 100644 index 0000000000..129c10b670 --- /dev/null +++ b/src/vector/integration/index.js @@ -0,0 +1,15 @@ +// @flow + +import ElectronIntegrationManager from './ElectronIntegrationManager'; +import WebIntegrationManager from './WebIntegrationManager'; + +let IntegrationManager = null; + +if (window && window.process && window.process && window.process.type === 'renderer') { + // we're running inside electron + IntegrationManager = ElectronIntegrationManager; +} else { + IntegrationManager = WebIntegrationManager; +} + +export default IntegrationManager; diff --git a/webpack.config.js b/webpack.config.js index 95afcfba98..e46d266385 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -2,7 +2,8 @@ var path = require('path'); var webpack = require('webpack'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); -module.exports = { +module.exports = [{ + entry: './src/vector/index.js', module: { preLoaders: [ { test: /\.js$/, loader: "source-map-loader" } @@ -32,7 +33,9 @@ module.exports = { // various levels of '.' and '..' // Also, sometimes the resource path is absolute. return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, ''); - } + }, + path: './vector/', + filename: 'bundle.js' }, resolve: { alias: { @@ -52,6 +55,7 @@ module.exports = { // loads it into the browser global `Olm`), and reference it as an // external here. "olm": "Olm", + "electron": 'commonjs electron' }, plugins: [ new webpack.DefinePlugin({ @@ -65,4 +69,33 @@ module.exports = { }), ], devtool: 'source-map' -}; +}, { + entry: './src/electron-main.js', + target: "electron", + output: { + devtoolModuleFilenameTemplate: function(info) { + // Reading input source maps gives only relative paths here for + // everything. Until I figure out how to fix this, this is a + // workaround. + // We use the relative resource path with any '../'s on the front + // removed which gives a tree with matrix-react-sdk and vector + // trees smashed together, but this fixes everything being under + // various levels of '.' and '..' + // Also, sometimes the resource path is absolute. + return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, ''); + }, + filename: 'vector/electron-main.js' + }, + node: { + __dirname: false, + __filename: false + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV) + } + }), + ], + devtool: 'source-map', +}];