Compare commits

...
Sign in to create a new pull request.

40 commits

Author SHA1 Message Date
David Baker
7e66fef17b Merge remote-tracking branch 'origin/develop' into aviraldg-electron 2016-11-02 13:57:38 +00:00
David Baker
56f080619e Just include dev tools in the menu: it's handy 2016-10-28 12:06:55 +01:00
David Baker
c8b39a1755 Use the same icon as the mobile apps 2016-10-28 10:14:17 +01:00
David Baker
cbbbedc0cc Put try block in the right place 2016-10-27 12:29:10 +01:00
David Baker
d068499184 Fix other merge conflict 2016-10-27 10:52:17 +01:00
David Baker
62b1282060 Fix merge fail 2016-10-27 10:48:37 +01:00
David Baker
24610f4d18 Merge remote-tracking branch 'origin/develop' into aviraldg-electron 2016-10-27 10:44:08 +01:00
David Baker
02e85fcffb Catch electron exception 2016-10-27 10:16:05 +01:00
David Baker
fac539c102 Add the 'silent' flag to notifications
Because we play our own sounds
2016-10-26 15:10:53 +01:00
David Baker
c2b487cc79 Support no config file when loading from files
file: URIs don't produce 404s.
2016-10-26 14:05:00 +01:00
David Baker
8edefe9e22 Oops, electron.app not app 2016-10-25 17:32:49 +01:00
David Baker
058d7c0ac7 Also put the poll rate back to something sensible 2016-10-25 16:43:42 +01:00
David Baker
752682220f Not the testing code, though 2016-10-25 16:43:09 +01:00
David Baker
c02f3a2230 Make autoupdate work on electron.
Changes the update process on web a bit to pull more out to the
platform classes.
2016-10-25 16:41:20 +01:00
David Baker
3c44f8a2cb Higher res icon for windows 2016-10-24 17:25:14 +01:00
David Baker
b6939b8138 Clear notifications on electron 2016-10-24 16:56:00 +01:00
David Baker
7b4694c637 Move platform setting to its own thing 2016-10-24 15:12:16 +01:00
David Baker
f62f2e8f25 More helpful description 2016-10-21 16:00:21 +01:00
David Baker
e2b123b56a Commented auto-update line 2016-10-21 15:16:55 +01:00
David Baker
fd2a2f25ad OK, we;ll use the standard icon paths
but with a different 'build' directory
2016-10-21 14:54:14 +01:00
David Baker
1c7d859b2b Add menu & update author
(since it's in the about screen)
2016-10-21 10:03:19 +01:00
David Baker
dc1952a993 Copyright headers 2016-10-21 09:41:05 +01:00
David Baker
11aed8cf07 Icon 2016-10-20 19:52:59 +01:00
David Baker
4e01bd7675 Tiny context menu on links 2016-10-20 18:33:07 +01:00
David Baker
680f599df5 Oh wait, don't need node-open
electron has it built in
2016-10-20 16:50:19 +01:00
David Baker
eef846e490 oops 2016-10-20 16:43:40 +01:00
David Baker
6c16ccce68 Open URLs in a browser 2016-10-20 16:41:33 +01:00
David Baker
27604d2798 Don't turn the favicon animations on 2016-10-20 15:01:07 +01:00
David Baker
5e4f572a46 More s/IntegrationManager/Platform/ 2016-10-20 14:57:21 +01:00
David Baker
1b0c561ac1 s/IntegrationManager/Platform/
Because we call bots integrations and I'll get confused
2016-10-20 14:50:20 +01:00
David Baker
ddb2ff5994 Don't try to bundle electron
Turns out this part of the webpack config was necessary, and I've
now figure out what it does, which the comment now hopefully
explains.
2016-10-20 14:44:18 +01:00
David Baker
3c55629208 Behave like a single-window Mac app on Mac 2016-10-19 18:05:33 +01:00
David Baker
31607f0776 We use semicolons 2016-10-19 17:53:10 +01:00
David Baker
f20786226a Don't call the variable 'window' 2016-10-19 17:51:43 +01:00
David Baker
da30338aa7 BIGGER!
At the default window size, Riot starts with the left bar folded
down which is not ideal as a default.
2016-10-19 17:48:41 +01:00
David Baker
eef629137e Fix electron build paths 2016-10-19 17:39:12 +01:00
David Baker
2026142595 Don't webpack electron main, separate electron
* Don't webpack electron-main.js: no need to webpack in electron
 * Keep the electron file separate from the webapp, in their own
   'electron' directory
2016-10-19 17:18:35 +01:00
David Baker
976c20a2f7 Merge branch 'electron' of https://github.com/aviraldg/vector-web into aviraldg-electron 2016-10-19 14:14:35 +01:00
Aviral Dasgupta
919cc8cffc Don't open devtools automatically 2016-10-04 16:23:30 +05:30
Aviral Dasgupta
4f185211ce Set up Electron integration and builds 2016-10-04 16:04:31 +05:30
15 changed files with 729 additions and 71 deletions

1
.gitignore vendored
View file

@ -13,3 +13,4 @@
/vector/olm.*
.DS_Store
npm-debug.log
electron/dist

BIN
electron/build/icon.icns Normal file

Binary file not shown.

BIN
electron/build/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,157 @@
// @flow
/*
Copyright 2016 Aviral Dasgupta and OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const electron = require('electron');
const url = require('url');
const VectorMenu = require('./vectormenu');
const PERMITTED_URL_SCHEMES = [
'http:',
'https:',
'mailto:',
];
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
let mainWindow = null;
let appQuitting = false;
function safeOpenURL(target) {
// openExternal passes the target to open/start/xdg-open,
// so put fairly stringent limits on what can be opened
// (for instance, open /bin/sh does indeed open a terminal
// with a shell, albeit with no arguments)
const parsed_url = url.parse(target);
if (PERMITTED_URL_SCHEMES.indexOf(parsed_url.protocol) > -1) {
// explicitly use the URL re-assembled by the url library,
// so we know the url parser has understood all the parts
// of the input string
const new_target = url.format(parsed_url);
electron.shell.openExternal(new_target);
}
}
function onWindowOrNavigate(ev, target) {
// always prevent the default: if something goes wrong,
// we don't want to end up opening it in the electron
// app, as we could end up opening any sort of random
// url in a window that has node scripting access.
ev.preventDefault();
safeOpenURL(target);
}
function onLinkContextMenu(ev, params) {
const popup_menu = new electron.Menu();
popup_menu.append(new electron.MenuItem({
label: params.linkURL,
click() {
safeOpenURL(params.linkURL);
},
}));
popup_menu.popup();
ev.preventDefault();
}
function installUpdate() {
// for some reason, quitAndInstall does not fire the
// before-quit event, so we need to set the flag here.
appQuitting = true;
electron.autoUpdater.quitAndInstall();
}
function pollForUpdates() {
try {
electron.autoUpdater.checkForUpdates();
} catch (e) {
console.log("Couldn't check for update", e);
}
}
electron.ipcMain.on('install_update', installUpdate);
electron.app.on('ready', () => {
try {
// For reasons best known to Squirrel, the way it checks for updates
// is completely different between macOS and windows. On macOS, it
// hits a URL that either gives it a 200 with some json or
// 204 No Content. On windows it takes a base path and looks for
// files under that path.
if (process.platform == 'darwin') {
electron.autoUpdater.setFeedURL("https://riot.im/autoupdate/desktop/");
} else if (process.platform == 'win32') {
electron.autoUpdater.setFeedURL("https://riot.im/download/desktop/win32/");
} else {
// Squirrel / electron only supports auto-update on these two platforms.
// I'm not even going to try to guess which feed style they'd use if they
// implemented it on Linux, or if it would be different again.
console.log("Auto update not supported on this platform");
}
// We check for updates ourselves rather than using 'updater' because we need to
// do it in the main process (and we don't really need to check every 10 minutes:
// every hour should be just fine for a desktop app)
// However, we still let the main window listen for the update events.
pollForUpdates();
setInterval(pollForUpdates, UPDATE_POLL_INTERVAL_MS);
} catch (err) {
// will fail if running in debug mode
console.log("Couldn't enable update checking", err);
}
mainWindow = new electron.BrowserWindow({
icon: `${__dirname}/../../vector/img/logo.png`,
width: 1024, height: 768,
});
mainWindow.loadURL(`file://${__dirname}/../../vector/index.html`);
electron.Menu.setApplicationMenu(VectorMenu);
mainWindow.on('closed', () => {
mainWindow = null;
});
mainWindow.on('close', (e) => {
if (process.platform == 'darwin' && !appQuitting) {
// On Mac, closing the window just hides it
// (this is generally how single-window Mac apps
// behave, eg. Mail.app)
e.preventDefault();
mainWindow.hide();
return false;
}
});
mainWindow.webContents.on('new-window', onWindowOrNavigate);
mainWindow.webContents.on('will-navigate', onWindowOrNavigate);
mainWindow.webContents.on('context-menu', function(ev, params) {
if (params.linkURL) {
onLinkContextMenu(ev, params);
}
});
});
electron.app.on('window-all-closed', () => {
electron.app.quit();
});
electron.app.on('activate', () => {
mainWindow.show();
});
electron.app.on('before-quit', () => {
appQuitting = true;
});

184
electron/src/vectormenu.js Normal file
View file

@ -0,0 +1,184 @@
/*
Copyright 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const electron = require('electron');
// Menu template from http://electron.atom.io/docs/api/menu/, edited
const template = [
{
label: 'Edit',
submenu: [
{
role: 'undo'
},
{
role: 'redo'
},
{
type: 'separator'
},
{
role: 'cut'
},
{
role: 'copy'
},
{
role: 'paste'
},
{
role: 'pasteandmatchstyle'
},
{
role: 'delete'
},
{
role: 'selectall'
}
]
},
{
label: 'View',
submenu: [
{
type: 'separator'
},
{
role: 'resetzoom'
},
{
role: 'zoomin'
},
{
role: 'zoomout'
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
},
{
label: 'Toggle Developer Tools',
accelerator: process.platform == 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
click: function(item, focusedWindow) {
if (focusedWindow) focusedWindow.toggleDevTools();
}
}
]
},
{
role: 'window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
}
]
},
{
role: 'help',
submenu: [
{
label: 'riot.im',
click () { electron.shell.openExternal('https://riot.im/') }
}
]
}
];
if (process.platform === 'darwin') {
const name = electron.app.getName()
template.unshift({
label: name,
submenu: [
{
role: 'about'
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},
{
role: 'hideothers'
},
{
role: 'unhide'
},
{
type: 'separator'
},
{
role: 'quit'
}
]
})
// Edit menu.
template[1].submenu.push(
{
type: 'separator'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
}
)
// Window menu.
template[3].submenu = [
{
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
},
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
},
{
label: 'Zoom',
role: 'zoom'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
role: 'front'
}
]
};
module.exports = electron.Menu.buildFromTemplate(template)

View file

@ -1,8 +1,10 @@
{
"name": "vector-web",
"productName": "Riot",
"main": "electron/src/electron-main.js",
"version": "0.8.4-rc.2",
"description": "Vector webapp",
"author": "matrix.org",
"description": "A feature-rich client for Matrix.org",
"author": "Vector Creations Ltd.",
"repository": {
"type": "git",
"url": "https://github.com/vector-im/vector-web"
@ -31,6 +33,7 @@
"build:compile": "babel --source-maps -d lib src",
"build:bundle": "NODE_ENV=production webpack -p --progress",
"build:bundle:dev": "webpack --optimize-occurence-order --progress",
"build:electron": "build -lwm",
"build": "node scripts/babelcheck.js && npm run build:emojione && npm run build:css && npm run build:bundle",
"build:dev": "npm run build:emojione && npm run build:css && npm run build:bundle:dev",
"dist": "scripts/package.sh",
@ -53,6 +56,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",
@ -90,6 +94,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",
@ -116,5 +121,24 @@
},
"optionalDependencies": {
"olm": "https://matrix.org/packages/npm/olm/olm-2.0.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": [
"!**/*",
"electron/src/**",
"vector/**",
"package.json"
]
},
"directories": {
"buildResources": "electron/build",
"output": "electron/dist"
}
}

View file

@ -19,6 +19,7 @@ limitations under the License.
var React = require('react');
var sdk = require('matrix-react-sdk');
import Modal from 'matrix-react-sdk/lib/Modal';
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
/**
* Check a version string is compatible with the Changelog
@ -31,30 +32,50 @@ function checkVersion(ver) {
export default function NewVersionBar(props) {
const onChangelogClicked = () => {
const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog');
Modal.createDialog(ChangelogDialog, {
version: props.version,
newVersion: props.newVersion,
onFinished: (update) => {
if(update) {
window.location.reload();
if (props.releaseNotes) {
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createDialog(QuestionDialog, {
title: "What's New",
description: <pre className="changelog_text">{props.releaseNotes}</pre>,
button: "Update",
onFinished: (update) => {
if(update && PlatformPeg.get()) {
PlatformPeg.get().installUpdate();
}
}
}
});
});
} else {
const ChangelogDialog = sdk.getComponent('dialogs.ChangelogDialog');
Modal.createDialog(ChangelogDialog, {
version: props.version,
newVersion: props.newVersion,
releaseNotes: releaseNotes,
onFinished: (update) => {
if(update && PlatformPeg.get()) {
PlatformPeg.get().installUpdate();
}
}
});
}
};
let changelog_button;
if (checkVersion(props.version) && checkVersion(props.newVersion)) {
changelog_button = <button className="mx_MatrixToolbar_action" onClick={onChangelogClicked}>Changelog</button>;
const onUpdateClicked = () => {
PlatformPeg.get().installUpdate();
};
let action_button;
if (props.releaseNotes || (checkVersion(props.version) && checkVersion(props.newVersion))) {
action_button = <button className="mx_MatrixToolbar_action" onClick={onChangelogClicked}>What's new?</button>;
} else if (PlatformPeg.get()) {
action_button = <button className="mx_MatrixToolbar_action" onClick={onUpdateClicked}>Update</button>;
}
return (
<div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
<div className="mx_MatrixToolbar_content">
A new version of Riot is available. Refresh your browser.
A new version of Riot is available.
</div>
{changelog_button}
{action_button}
</div>
);
}
@ -62,4 +83,5 @@ export default function NewVersionBar(props) {
NewVersionBar.propTypes = {
version: React.PropTypes.string.isRequired,
newVersion: React.PropTypes.string.isRequired,
releaseNotes: React.PropTypes.string,
};

View file

@ -288,3 +288,7 @@ textarea {
cursor: pointer;
display: inline;
}
.changelog_text {
font-family: 'Open Sans', Arial, Helvetica, Sans-Serif;
}

View file

@ -36,7 +36,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");
@ -47,6 +46,7 @@ if (process.env.NODE_ENV !== 'production') {
var RunModernizrTests = require("./modernizr"); // this side-effects a global
var ReactDOM = require("react-dom");
var sdk = require("matrix-react-sdk");
var PlatformPeg = require("matrix-react-sdk/lib/PlatformPeg");
sdk.loadSkin(require('../component-index'));
var VectorConferenceHandler = require('../VectorConferenceHandler');
var UpdateChecker = require("./updater");
@ -57,6 +57,7 @@ import UAParser from 'ua-parser-js';
import url from 'url';
import {parseQs, parseQsFromFragment} from './url_utils';
import Platform from './platform';
var lastLocationHashSet = null;
@ -111,10 +112,6 @@ function onHashChange(ev) {
routeUrl(window.location);
}
function onVersion(current, latest) {
window.matrixChat.onVersion(current, latest);
}
var loaded = false;
var lastLoadedScreen = null;
@ -163,8 +160,7 @@ window.onload = function() {
if (!validBrowser) {
return;
}
UpdateChecker.setVersionListener(onVersion);
UpdateChecker.run();
UpdateChecker.start();
routeUrl(window.location);
loaded = true;
if (lastLoadedScreen) {
@ -177,14 +173,27 @@ function getConfig() {
let deferred = q.defer();
request(
{ method: "GET", url: "config.json", json: true },
{ method: "GET", url: "config.json" },
(err, response, body) => {
if (err || response.status < 200 || response.status >= 300) {
// Lack of a config isn't an error, we should
// just use the defaults.
// Also treat a blank config as no config because
// we don't get 404s from file: URIs so this is the
// only way we can not fail if the file doesn't exist
// when loading from a file:// URI.
if (( err && err.response.status == 404) || body == '') {
deferred.resolve({});
}
deferred.reject({err: err, response: response});
return;
}
deferred.resolve(body);
// We parse the JSON ourselves rather than use the JSON
// parameter, since this throws a parse error on empty
// which breaks if there's no config.json and we're
// loading from the filesystem (see above).
deferred.resolve(iJSON.parse(body));
}
);
@ -210,6 +219,9 @@ async function loadApp() {
const fragparts = parseQsFromFragment(window.location);
const params = parseQs(window.location);
// set the platform for react sdk (our Platform object automatically picks the right one)
PlatformPeg.set(new Platform());
// don't try to redirect to the native apps if we're
// verifying a 3pid
const preventRedirect = Boolean(fragparts.params.client_secret);
@ -234,13 +246,7 @@ async function loadApp() {
try {
configJson = await getConfig();
} catch (e) {
// On 404 errors, carry on without a config,
// but on other errors, fail, otherwise it will
// lead to subtle errors where the app runs with
// the default config if it fails to fetch config.json.
if (e.response.status != 404) {
configError = e;
}
configError = e;
}
console.log("Vector starting at "+window.location);

View file

@ -0,0 +1,32 @@
// @flow
/*
Copyright 2016 Aviral Dasgupta and OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export default class BasePlatform {
constructor() {
this.notificationCount = 0;
this.errorDidOccur = false;
}
setNotificationCount(count: number) {
this.notificationCount = count;
}
setErrorStatus(errorDidOccur: boolean) {
this.errorDidOccur = errorDidOccur;
}
}

View file

@ -0,0 +1,93 @@
// @flow
/*
Copyright 2016 Aviral Dasgupta and OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import BasePlatform from './BasePlatform';
import dis from 'matrix-react-sdk/lib/dispatcher';
function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) {
dis.dispatch({
action: 'new_version',
currentVersion: electron.remote.app.getVersion(),
newVersion: ver,
releaseNotes: releaseNotes,
});
}
// 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');
electron.remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
remote = electron.remote;
}
export default class ElectronPlatform extends BasePlatform {
setNotificationCount(count: number) {
super.setNotificationCount(count);
// this sometimes throws because electron is made of fail:
// https://github.com/electron/electron/issues/7351
// For now, let's catch the error, but I suspect it may
// continue to fail and we might just have to accept that
// electron's remote RPC is a non-starter for now and use IPC
try {
remote.app.setBadgeCount(count);
} catch (e) {
console.error("Failed to set notification count", e);
}
}
displayNotification(title: string, msg: string, avatarUrl: string): Notification {
// Notifications in Electron use the HTML5 notification API
const notification = new global.Notification(
title,
{
body: msg,
icon: avatarUrl,
tag: "vector",
silent: true, // we play our own sounds
}
);
notification.onclick = function() {
dis.dispatch({
action: 'view_room',
room_id: room.roomId
});
global.focus();
};
return notification;
}
clearNotification(notif: Notification) {
notif.close();
}
pollForUpdate() {
// In electron we control the update process ourselves, since
// it needs to run in the main process, so we just run the timer
// loop in the main electron process instead.
}
installUpdate() {
// IPC to the main process to install the update, since quitAndInstall
// doesn't fire the before-quit event so the main process needs to know
// it should exit.
electron.ipcRenderer.send('install_update');
}
}

View file

@ -0,0 +1,131 @@
// @flow
/*
Copyright 2016 Aviral Dasgupta and OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import BasePlatform from './BasePlatform';
import Favico from 'favico.js';
import request from 'browser-request';
import dis from 'matrix-react-sdk/lib/dispatcher.js';
import q from 'q';
export default class WebPlatform extends BasePlatform {
constructor() {
super();
this.runningVersion = null;
// The 'animations' are really low framerate and look terrible.
// Also it re-starts the animationb every time you set the badge,
// and we set the state each time, even if the value hasn't changed,
// so we'd need to fix that if enabling the animation.
this.favicon = new Favico({animation: 'none'});
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();
}
displayNotification(title: string, msg: string, avatarUrl: string) {
const notification = new global.Notification(
title,
{
body: msg,
icon: avatarUrl,
tag: "vector",
silent: true, // we play our own sounds
}
);
notification.onclick = function() {
dis.dispatch({
action: 'view_room',
room_id: room.roomId
});
global.focus();
};
// Chrome only dismisses notifications after 20s, which
// is waaaaay too long
global.setTimeout(function() {
notification.close();
}, 5 * 1000);
}
_getVersion() {
const deferred = q.defer();
request(
{ method: "GET", url: "version" },
(err, response, body) => {
if (err || response.status < 200 || response.status >= 300) {
if (err == null) err = { status: response.status };
deferred.reject(err);
return;
}
const ver = body.trim();
deferred.resolve(ver);
}
);
return deferred.promise;
}
pollForUpdate() {
this._getVersion().done((ver) => {
if (this.runningVersion == null) {
this.runningVersion = ver;
} else if (this.runningVersion != ver) {
dis.dispatch({
action: 'new_version',
currentVersion: this.runningVersion,
newVersion: ver,
});
}
}, (err) => {
console.error("Failed to poll for update", err);
});
}
installUpdate() {
window.location.reload();
}
}

View file

@ -0,0 +1,31 @@
// @flow
/*
Copyright 2016 Aviral Dasgupta and OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import ElectronPlatform from './ElectronPlatform';
import WebPlatform from './WebPlatform';
let Platform = null;
if (window && window.process && window.process && window.process.type === 'renderer') {
// we're running inside electron
Platform = ElectronPlatform;
} else {
Platform = WebPlatform;
}
export default Platform;

View file

@ -13,48 +13,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
var POKE_RATE_MS = 10 * 60 * 1000; // 10 min
var currentVersion = null;
var latestVersion = null;
var listener = function(){}; // NOP
module.exports = {
setVersionListener: function(fn) { // invoked with fn(currentVer, newVer)
listener = fn;
start: function() {
module.exports.poll();
setInterval(module.exports.poll, POKE_RATE_MS);
},
run: function() {
var req = new XMLHttpRequest();
req.addEventListener("load", function() {
if (!req.responseText) {
return;
}
var ver = req.responseText.trim();
if (!currentVersion) {
currentVersion = ver;
listener(currentVersion, currentVersion);
}
if (ver !== latestVersion) {
latestVersion = ver;
if (module.exports.hasNewVersion()) {
console.log("Current=%s Latest=%s", currentVersion, latestVersion);
listener(currentVersion, latestVersion);
}
}
});
var cacheBuster = "?ts=" + new Date().getTime();
req.open("GET", "version" + cacheBuster);
req.send(); // can't suppress 404s from being logged.
setTimeout(module.exports.run, POKE_RATE_MS);
},
getCurrentVersion: function() {
return currentVersion;
},
hasNewVersion: function() {
return currentVersion && latestVersion && (currentVersion !== latestVersion);
poll: function() {
PlatformPeg.get().pollForUpdate();
}
};

View file

@ -75,6 +75,9 @@ module.exports = {
},
externals: {
"olm": "Olm",
// Don't try to bundle electron: leave it as a commonjs dependency
// (the 'commonjs' here means it will output a 'require')
"electron": "commonjs electron",
},
plugins: [
new webpack.DefinePlugin({