Compare commits
153 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a30b02be4 | ||
|
|
21eb8b9100 | ||
|
|
a0c205e4c9 | ||
|
|
40d7df793d | ||
|
|
e5c0af478d | ||
|
|
82cd88cf25 | ||
|
|
e94e712d49 | ||
|
|
021d6c5908 | ||
|
|
b09f0618a9 | ||
|
|
3236f72031 | ||
|
|
99b5695fec | ||
|
|
9e085511fd | ||
|
|
62a49af23f | ||
|
|
751a1dc543 | ||
|
|
baf3df5736 | ||
|
|
7527bdd2c7 | ||
|
|
6c6819f27c | ||
|
|
f7bdc9339c | ||
|
|
8cd37d28da | ||
|
|
b6d70f4434 | ||
|
|
fc4e1485ad | ||
|
|
b35903bb19 | ||
|
|
ff45dc6430 | ||
|
|
5a549519a6 | ||
|
|
0e580635eb | ||
|
|
60d3076038 | ||
|
|
0c428efea0 | ||
|
|
19f1489c92 | ||
|
|
17983c47d8 | ||
|
|
df155293b1 | ||
|
|
6ca0b3ad03 | ||
|
|
c2038a5ccc | ||
|
|
cb628d0799 | ||
|
|
7c4f67b1a1 | ||
|
|
a82f9d5266 | ||
|
|
b6493a2f82 | ||
|
|
74c81d6921 | ||
|
|
02eb8a284c | ||
|
|
bf2d792d94 | ||
|
|
10294ab240 | ||
|
|
88ff92a658 | ||
|
|
8d9987a6b5 | ||
|
|
6ba471421f | ||
|
|
5310b4c14b | ||
|
|
69567f9806 | ||
|
|
0007e669fb | ||
|
|
7247e97ff0 | ||
|
|
844dbcf740 | ||
|
|
10671aa06e | ||
|
|
373f850002 | ||
|
|
ac34ca11df | ||
|
|
710e295b44 | ||
|
|
c3b05cb34e | ||
|
|
b5c27ca074 | ||
|
|
704a599811 | ||
|
|
1dfaf70562 | ||
|
|
dc92b126c7 | ||
|
|
c6f70cb72c | ||
|
|
f701e2601a | ||
|
|
8cb8890ab3 | ||
|
|
a7a5679bfa | ||
|
|
03587abff1 | ||
|
|
496a5baffd | ||
|
|
8996b6b429 | ||
|
|
d483fb0605 | ||
|
|
c2ec5f39ab | ||
|
|
58f927538f | ||
|
|
d9434c7af5 | ||
|
|
6f54ff9e7f | ||
|
|
9eaba3d3f5 | ||
|
|
4805858ecb | ||
|
|
092ae7ef2f | ||
|
|
bbb128b672 | ||
|
|
15b8d410c0 | ||
|
|
8298e73205 | ||
|
|
819e6d9219 | ||
|
|
d2c87a0331 | ||
|
|
286c5e8895 | ||
|
|
7df77d7e5d | ||
|
|
6804a00af0 | ||
|
|
81c75eb14b | ||
|
|
e604dfe9e8 | ||
|
|
135a5d38ff | ||
|
|
07ed2e8d0a | ||
|
|
2a594414e7 | ||
|
|
d3496c0cb9 | ||
|
|
c15151a0af | ||
|
|
6d1640194e | ||
|
|
06740e5480 | ||
|
|
c338e491fe | ||
|
|
a570ee53a8 | ||
|
|
417a31f1fe | ||
|
|
a4a6f070e4 | ||
|
|
d287abcd68 | ||
|
|
79fd38a899 | ||
|
|
0aa0773ef7 | ||
|
|
154e8bdb50 | ||
|
|
8f37c22917 | ||
|
|
0aeef3187a | ||
|
|
8903eebe17 | ||
|
|
cc4d0af713 | ||
|
|
4a217d9e9f | ||
|
|
122caf7a95 | ||
|
|
a1e5c72eb9 | ||
|
|
7dd163ac85 | ||
|
|
01432d3848 | ||
|
|
d3cae13552 | ||
|
|
061838bdab | ||
|
|
fd3ed9e119 | ||
|
|
b53c86c5f4 | ||
|
|
cbe6d31ee3 | ||
|
|
5810221181 | ||
|
|
e9f4594eb9 | ||
|
|
2f991d190f | ||
|
|
ae90b9877a | ||
|
|
786c2a1fd7 | ||
|
|
eef4f7c4cf | ||
|
|
36cc5de0e7 | ||
|
|
854798f470 | ||
|
|
ca305399c3 | ||
|
|
00a8d44ce1 | ||
|
|
79c7f331c9 | ||
|
|
409c48efcb | ||
|
|
a6f8f707f3 | ||
|
|
e19dbe4f3b | ||
|
|
d393cd67f7 | ||
|
|
c80f5ddcd1 | ||
|
|
a02009d31f | ||
|
|
a4ed1afeb5 | ||
|
|
1fea7c022e | ||
|
|
e7f5aee9fb | ||
|
|
d3805f7545 | ||
|
|
70891f41a5 | ||
|
|
cebba5bfa3 | ||
|
|
0f5c62d3cc | ||
|
|
f9fa338960 | ||
|
|
ad7ac26ff3 | ||
|
|
10efaaa078 | ||
|
|
bb353b940e | ||
|
|
9732d10f24 | ||
|
|
9b80aa76ce | ||
|
|
bc935012da | ||
|
|
e4025f1aff | ||
|
|
b7622d8551 | ||
|
|
c470e2dcfb | ||
|
|
fe11ae73db | ||
|
|
70fe4f67ee | ||
|
|
6abd1de8b8 | ||
|
|
e277d02f83 | ||
|
|
c5ee92f4c3 | ||
|
|
30f0a7932b | ||
|
|
1f34d2d644 | ||
|
|
c511902356 |
25
.travis.yml
25
.travis.yml
@@ -11,25 +11,18 @@ sudo: required
|
||||
language: node_js
|
||||
node_js:
|
||||
# make sure we work with a range of node versions.
|
||||
# As of the time of writing:
|
||||
# - 4.x is still in LTS (until April 2018), but some of our deps (notably
|
||||
# extract-zip) don't work with it
|
||||
# - 5.x has been EOLed for nearly a year.
|
||||
# - 6.x is the active 'LTS' version
|
||||
# - 7.x is no longer supported
|
||||
# - 8.x is the current 'current' version (until October 2017)
|
||||
#
|
||||
# see: https://github.com/nodejs/LTS/
|
||||
#
|
||||
# anything before 6.3 ships with npm 3.9 or earlier, which had problems
|
||||
# with symlinks in node_modules (see
|
||||
# https://github.com/npm/npm/releases/tag/v3.10.0 'FIXES AND REFACTORING').
|
||||
- 6.3
|
||||
- 6
|
||||
- 7
|
||||
# Current status of node versions: https://github.com/nodejs/LTS/
|
||||
# We don't work with node 6 because it doesn't support package-lock
|
||||
# files which we need to avoid the broken version of base-x
|
||||
- 8
|
||||
- 10
|
||||
addons:
|
||||
chrome: stable
|
||||
install:
|
||||
# clone the deps with depth 1: we know we will only ever need that one
|
||||
# commit.
|
||||
- scripts/fetch-develop.deps.sh --depth 1 && npm install
|
||||
- npm install && scripts/fetch-develop.deps.sh --depth 1
|
||||
script:
|
||||
- npm run test
|
||||
- npm run lint
|
||||
|
||||
98
CHANGELOG.md
98
CHANGELOG.md
@@ -1,3 +1,101 @@
|
||||
Changes in [0.17.9](https://github.com/vector-im/riot-web/releases/tag/v0.17.9) (2019-01-22)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.9-rc.1...v0.17.9)
|
||||
|
||||
* Bugfix in react-sdk for setting DM rooms
|
||||
|
||||
Changes in [0.17.9-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.17.9-rc.1) (2019-01-17)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.8...v0.17.9-rc.1)
|
||||
|
||||
* Merge develop into experimental
|
||||
[\#8003](https://github.com/vector-im/riot-web/pull/8003)
|
||||
* Electron: Load app from custom protocol
|
||||
[\#7943](https://github.com/vector-im/riot-web/pull/7943)
|
||||
* Fix the IndexedDB worker
|
||||
[\#7920](https://github.com/vector-im/riot-web/pull/7920)
|
||||
* Make clear that the Debian package is for desktop
|
||||
[\#7919](https://github.com/vector-im/riot-web/pull/7919)
|
||||
* Run the Desktop app in a sandbox
|
||||
[\#7907](https://github.com/vector-im/riot-web/pull/7907)
|
||||
* Update to new electron single instance API
|
||||
[\#7908](https://github.com/vector-im/riot-web/pull/7908)
|
||||
* Update the tests to match https://github.com/matrix-org/matrix-react-
|
||||
sdk/pull/2340
|
||||
[\#7834](https://github.com/vector-im/riot-web/pull/7834)
|
||||
|
||||
Changes in [0.17.8](https://github.com/vector-im/riot-web/releases/tag/v0.17.8) (2018-12-10)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.8-rc.1...v0.17.8)
|
||||
|
||||
* No changes since rc.1
|
||||
|
||||
Changes in [0.17.8-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.17.8-rc.1) (2018-12-06)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.7...v0.17.8-rc.1)
|
||||
|
||||
* Update from Weblate.
|
||||
[\#7784](https://github.com/vector-im/riot-web/pull/7784)
|
||||
* Add a function to send a rageshake from the console
|
||||
[\#7755](https://github.com/vector-im/riot-web/pull/7755)
|
||||
* Re-apply "Run lint on travis builds and use modern node versions"
|
||||
[\#7738](https://github.com/vector-im/riot-web/pull/7738)
|
||||
* Revert "Run lint on travis builds and use modern node versions"
|
||||
[\#7737](https://github.com/vector-im/riot-web/pull/7737)
|
||||
* Run lint on travis builds and use modern node versions
|
||||
[\#7490](https://github.com/vector-im/riot-web/pull/7490)
|
||||
* Fix missing js-sdk logging
|
||||
[\#7736](https://github.com/vector-im/riot-web/pull/7736)
|
||||
* Add $accent-color-50pct as a CSS variable to the Status theme
|
||||
[\#7710](https://github.com/vector-im/riot-web/pull/7710)
|
||||
|
||||
Changes in [0.17.7](https://github.com/vector-im/riot-web/releases/tag/v0.17.7) (2018-11-22)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.6...v0.17.7)
|
||||
|
||||
* Warning when crypto DB is too new to use.
|
||||
* Fix missing entries from js-sdk in rageshake logs
|
||||
|
||||
Changes in [0.17.6](https://github.com/vector-im/riot-web/releases/tag/v0.17.6) (2018-11-19)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.6-rc.2...v0.17.6)
|
||||
|
||||
* No changes since rc.2
|
||||
|
||||
Changes in [0.17.6-rc.2](https://github.com/vector-im/riot-web/releases/tag/v0.17.6-rc.2) (2018-11-15)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.6-rc.1...v0.17.6-rc.2)
|
||||
|
||||
* Update to js-sdk 0.14 and react-sdk rc.2. rc.1 was broken as it was built against
|
||||
js-sdk 0.13 which does not use the new Olm 3.0 API.
|
||||
|
||||
Changes in [0.17.6-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.17.6-rc.1) (2018-11-15)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.5...v0.17.6-rc.1)
|
||||
|
||||
* Update from Weblate.
|
||||
[\#7708](https://github.com/vector-im/riot-web/pull/7708)
|
||||
* Add Japanese (#7599)
|
||||
[\#7673](https://github.com/vector-im/riot-web/pull/7673)
|
||||
* Allow Webpack dev server to listen to all interfaces
|
||||
[\#7674](https://github.com/vector-im/riot-web/pull/7674)
|
||||
* Remove the request-only stuff we don't need anymore
|
||||
[\#7637](https://github.com/vector-im/riot-web/pull/7637)
|
||||
* Correct the author of the electron app
|
||||
[\#7615](https://github.com/vector-im/riot-web/pull/7615)
|
||||
* Mock fs, tls, and net to support request in the browser
|
||||
[\#7552](https://github.com/vector-im/riot-web/pull/7552)
|
||||
* Update chokidar to transitively get newer fsevents
|
||||
[\#7598](https://github.com/vector-im/riot-web/pull/7598)
|
||||
* Support WebAssembly version of Olm
|
||||
[\#7385](https://github.com/vector-im/riot-web/pull/7385)
|
||||
|
||||
Changes in [0.17.5](https://github.com/vector-im/riot-web/releases/tag/v0.17.5) (2018-11-13)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.4...v0.17.5)
|
||||
|
||||
* Include change that was supposed to be included in orevious version
|
||||
|
||||
Changes in [0.17.4](https://github.com/vector-im/riot-web/releases/tag/v0.17.4) (2018-11-13)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.17.3...v0.17.4)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Contributing code to Riot
|
||||
=========================
|
||||
|
||||
Riot follows the same pattern as https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst.
|
||||
Riot follows the same pattern as https://github.com/matrix-org/matrix-js-sdk/blob/master/CONTRIBUTING.rst.
|
||||
|
||||
@@ -28,7 +28,8 @@ Note that Chrome does not allow microphone or webcam access for sites served
|
||||
over http (except localhost), so for working VoIP you will need to serve Riot
|
||||
over https.
|
||||
|
||||
### Installation Steps for Debian Stretch
|
||||
### Desktop Installation for Debian Stretch
|
||||
|
||||
1. Add the repository to your sources.list using either of the following two options:
|
||||
- Directly to sources.list: `echo "deb https://riot.im/packages/debian/ stretch main" | sudo tee -a /etc/apt/sources.list`
|
||||
- As a separate entry in sources.list.d: `echo "deb https://riot.im/packages/debian/ stretch main" | sudo tee /etc/apt/sources.list.d/riot.list`
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "src/electron-main.js",
|
||||
"version": "0.17.4",
|
||||
"version": "0.17.9",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"author": "New Vector Ltd.",
|
||||
"dependencies": {
|
||||
"auto-launch": "^5.0.1",
|
||||
"electron-window-state": "^4.1.0",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,16 +24,23 @@ const checkSquirrelHooks = require('./squirrelhooks');
|
||||
if (checkSquirrelHooks()) return;
|
||||
|
||||
const argv = require('minimist')(process.argv);
|
||||
const {app, ipcMain, powerSaveBlocker, BrowserWindow, Menu} = require('electron');
|
||||
const {app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol} = require('electron');
|
||||
const AutoLaunch = require('auto-launch');
|
||||
const path = require('path');
|
||||
|
||||
const tray = require('./tray');
|
||||
const vectorMenu = require('./vectormenu');
|
||||
const webContentsHandler = require('./webcontents-handler');
|
||||
const updater = require('./updater');
|
||||
const { migrateFromOldOrigin } = require('./originMigrator');
|
||||
|
||||
const windowStateKeeper = require('electron-window-state');
|
||||
|
||||
// boolean flag set whilst we are doing one-time origin migration
|
||||
// We only serve the origin migration script while we're actually
|
||||
// migrating to mitigate any risk of it being used maliciously.
|
||||
let migratingOrigin = false;
|
||||
|
||||
if (argv['profile']) {
|
||||
app.setPath('userData', `${app.getPath('userData')}-${argv['profile']}`);
|
||||
}
|
||||
@@ -97,27 +105,75 @@ ipcMain.on('app_onAction', function(ev, payload) {
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (ev, releaseNotes, releaseName, releaseDate, updateURL) => {
|
||||
if (!mainWindow) return;
|
||||
// forward to renderer
|
||||
mainWindow.webContents.send('update-downloaded', {
|
||||
releaseNotes,
|
||||
releaseName,
|
||||
releaseDate,
|
||||
updateURL,
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('ipcCall', async function(ev, payload) {
|
||||
if (!mainWindow) return;
|
||||
|
||||
const args = payload.args || [];
|
||||
let ret;
|
||||
|
||||
switch (payload.name) {
|
||||
case 'getUpdateFeedUrl':
|
||||
ret = autoUpdater.getFeedURL();
|
||||
break;
|
||||
case 'getAutoLaunchEnabled':
|
||||
ret = launcher.isEnabled;
|
||||
break;
|
||||
case 'setAutoLaunchEnabled':
|
||||
if (args[0]) {
|
||||
launcher.enable();
|
||||
} else {
|
||||
launcher.disable();
|
||||
}
|
||||
break;
|
||||
case 'getAppVersion':
|
||||
ret = app.getVersion();
|
||||
break;
|
||||
case 'focusWindow':
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
} else if (!mainWindow.isVisible()) {
|
||||
mainWindow.show();
|
||||
} else {
|
||||
mainWindow.focus();
|
||||
}
|
||||
case 'origin_migrate':
|
||||
migratingOrigin = true;
|
||||
await migrateFromOldOrigin();
|
||||
migratingOrigin = false;
|
||||
break;
|
||||
default:
|
||||
mainWindow.webContents.send('ipcReply', {
|
||||
id: payload.id,
|
||||
error: "Unknown IPC Call: " + payload.name,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
mainWindow.webContents.send('ipcReply', {
|
||||
id: payload.id,
|
||||
reply: ret,
|
||||
});
|
||||
});
|
||||
|
||||
app.commandLine.appendSwitch('--enable-usermedia-screen-capturing');
|
||||
|
||||
const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
|
||||
// If other instance launched with --hidden then skip showing window
|
||||
if (commandLine.includes('--hidden')) return;
|
||||
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (!mainWindow.isVisible()) mainWindow.show();
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldQuit) {
|
||||
const gotLock = app.requestSingleInstanceLock();
|
||||
if (!gotLock) {
|
||||
console.log('Other instance detected: exiting');
|
||||
app.exit();
|
||||
}
|
||||
|
||||
|
||||
const launcher = new AutoLaunch({
|
||||
name: vectorConfig.brand || 'Riot',
|
||||
isHidden: true,
|
||||
@@ -126,39 +182,12 @@ const launcher = new AutoLaunch({
|
||||
},
|
||||
});
|
||||
|
||||
const settings = {
|
||||
'auto-launch': {
|
||||
get: launcher.isEnabled,
|
||||
set: function(bool) {
|
||||
if (bool) {
|
||||
return launcher.enable();
|
||||
} else {
|
||||
return launcher.disable();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
ipcMain.on('settings_get', async function(ev) {
|
||||
const data = {};
|
||||
|
||||
try {
|
||||
await Promise.all(Object.keys(settings).map(async function (setting) {
|
||||
data[setting] = await settings[setting].get();
|
||||
}));
|
||||
|
||||
ev.sender.send('settings', data);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('settings_set', function(ev, key, value) {
|
||||
console.log(key, value);
|
||||
if (settings[key] && settings[key].set) {
|
||||
settings[key].set(value);
|
||||
}
|
||||
});
|
||||
// Register the scheme the app is served from as 'standard'
|
||||
// which allows things like relative URLs and IndexedDB to
|
||||
// work.
|
||||
// Also mark it as secure (ie. accessing resources from this
|
||||
// protocol and HTTPS won't trigger mixed content warnings).
|
||||
protocol.registerStandardSchemes(['vector'], {secure: true});
|
||||
|
||||
app.on('ready', () => {
|
||||
if (argv['devtools']) {
|
||||
@@ -175,6 +204,66 @@ app.on('ready', () => {
|
||||
}
|
||||
}
|
||||
|
||||
protocol.registerFileProtocol('vector', (request, callback) => {
|
||||
if (request.method !== 'GET') {
|
||||
callback({error: -322}); // METHOD_NOT_SUPPORTED from chromium/src/net/base/net_error_list.h
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsedUrl = new URL(request.url);
|
||||
if (parsedUrl.protocol !== 'vector:') {
|
||||
callback({error: -302}); // UNKNOWN_URL_SCHEME
|
||||
return;
|
||||
}
|
||||
if (parsedUrl.host !== 'vector') {
|
||||
callback({error: -105}); // NAME_NOT_RESOLVED
|
||||
return;
|
||||
}
|
||||
|
||||
const target = parsedUrl.pathname.split('/');
|
||||
|
||||
// path starts with a '/'
|
||||
if (target[0] !== '') {
|
||||
callback({error: -6}); // FILE_NOT_FOUND
|
||||
return;
|
||||
}
|
||||
|
||||
if (target[target.length - 1] == '') {
|
||||
target[target.length - 1] = 'index.html';
|
||||
}
|
||||
|
||||
let baseDir;
|
||||
// first part of the path determines where we serve from
|
||||
if (migratingOrigin && target[1] === 'origin_migrator_dest') {
|
||||
// the origin migrator destination page
|
||||
// (only the destination script needs to come from the
|
||||
// custom protocol: the source part is loaded from a
|
||||
// file:// as that's the origin we're migrating from).
|
||||
baseDir = __dirname + "/../../origin_migrator/dest";
|
||||
} else if (target[1] === 'webapp') {
|
||||
baseDir = __dirname + "/../../webapp";
|
||||
} else {
|
||||
callback({error: -6}); // FILE_NOT_FOUND
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalise the base dir and the target path separately, then make sure
|
||||
// the target path isn't trying to back out beyond its root
|
||||
baseDir = path.normalize(baseDir);
|
||||
|
||||
const relTarget = path.normalize(path.join(...target.slice(2)));
|
||||
if (relTarget.startsWith('..')) {
|
||||
callback({error: -6}); // FILE_NOT_FOUND
|
||||
return;
|
||||
}
|
||||
const absTarget = path.join(baseDir, relTarget);
|
||||
|
||||
callback({
|
||||
path: absTarget,
|
||||
});
|
||||
}, (error) => {
|
||||
if (error) console.error('Failed to register protocol')
|
||||
});
|
||||
|
||||
if (vectorConfig['update_base_url']) {
|
||||
console.log(`Starting auto update with base URL: ${vectorConfig['update_base_url']}`);
|
||||
@@ -191,6 +280,7 @@ app.on('ready', () => {
|
||||
defaultHeight: 768,
|
||||
});
|
||||
|
||||
const preloadScript = path.normalize(`${__dirname}/preload.js`);
|
||||
mainWindow = global.mainWindow = new BrowserWindow({
|
||||
icon: iconPath,
|
||||
show: false,
|
||||
@@ -200,8 +290,20 @@ app.on('ready', () => {
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
webPreferences: {
|
||||
preload: preloadScript,
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
enableRemoteModule: false,
|
||||
// We don't use this: it's useful for the preload script to
|
||||
// share a context with the main page so we can give select
|
||||
// objects to the main page. The sandbox option isolates the
|
||||
// main page from the background script.
|
||||
contextIsolation: false,
|
||||
webgl: false,
|
||||
},
|
||||
});
|
||||
mainWindow.loadURL(`file://${__dirname}/../../webapp/index.html`);
|
||||
mainWindow.loadURL('vector://vector/webapp/');
|
||||
Menu.setApplicationMenu(vectorMenu);
|
||||
|
||||
// explicitly hide because setApplicationMenu on Linux otherwise shows...
|
||||
@@ -268,6 +370,18 @@ app.on('before-quit', () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on('second-instance', (ev, commandLine, workingDirectory) => {
|
||||
// If other instance launched with --hidden then skip showing window
|
||||
if (commandLine.includes('--hidden')) return;
|
||||
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow) {
|
||||
if (!mainWindow.isVisible()) mainWindow.show();
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Set the App User Model ID to match what the squirrel
|
||||
// installer uses for the shortcut icon.
|
||||
// This makes notifications work on windows 8.1 (and is
|
||||
|
||||
62
electron_app/src/originMigrator.js
Normal file
62
electron_app/src/originMigrator.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2018 New Vector 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 { BrowserWindow, ipcMain } = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
async function migrateFromOldOrigin() {
|
||||
console.log("Attempting to migrate data between origins");
|
||||
|
||||
// We can use the same preload script: we just need ipcRenderer exposed
|
||||
const preloadScript = path.normalize(`${__dirname}/preload.js`);
|
||||
await new Promise(resolve => {
|
||||
const migrateWindow = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload: preloadScript,
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
enableRemoteModule: false,
|
||||
webgl: false,
|
||||
},
|
||||
});
|
||||
ipcMain.on('origin_migration_complete', (e, success, sentSummary, storedSummary) => {
|
||||
if (success) {
|
||||
console.log("Origin migration completed successfully!");
|
||||
} else {
|
||||
console.error("Origin migration failed!");
|
||||
}
|
||||
console.error("Data sent", sentSummary);
|
||||
console.error("Data stored", storedSummary);
|
||||
migrateWindow.close();
|
||||
resolve();
|
||||
});
|
||||
ipcMain.on('origin_migration_nodata', (e) => {
|
||||
console.log("No session to migrate from old origin");
|
||||
migrateWindow.close();
|
||||
resolve();
|
||||
});
|
||||
// Normalise the path because in the distribution, __dirname will be inside the
|
||||
// electron asar.
|
||||
const sourcePagePath = path.normalize(__dirname + '/../../origin_migrator/source.html');
|
||||
console.log("Loading path: " + sourcePagePath);
|
||||
migrateWindow.loadURL('file://' + sourcePagePath);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
migrateFromOldOrigin,
|
||||
};
|
||||
@@ -1,8 +1,5 @@
|
||||
// @flow
|
||||
|
||||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
let Platform = null;
|
||||
const { ipcRenderer, webFrame } = require('electron');
|
||||
|
||||
if (window && window.process && window.process && window.process.type === 'renderer') {
|
||||
// we're running inside electron
|
||||
Platform = require('./ElectronPlatform');
|
||||
} else {
|
||||
Platform = require('./WebPlatform');
|
||||
}
|
||||
// expose ipcRenderer to the renderer process
|
||||
window.ipcRenderer = ipcRenderer;
|
||||
|
||||
export default Platform;
|
||||
// Allow the fetch API to load resources from this
|
||||
// protocol: this is necessary to load olm.wasm.
|
||||
// (Also mark it a secure although we've already
|
||||
// done this in the main process).
|
||||
webFrame.registerURLSchemeAsPrivileged('vector', {
|
||||
secure: true,
|
||||
supportFetchAPI: true,
|
||||
});
|
||||
19
origin_migrator/dest/browser-matrix.min.js
vendored
Normal file
19
origin_migrator/dest/browser-matrix.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
origin_migrator/dest/dest.html
Normal file
6
origin_migrator/dest/dest.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="browser-matrix.min.js"></script>
|
||||
<script src="dest.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
125
origin_migrator/dest/dest.js
Normal file
125
origin_migrator/dest/dest.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2018 New Vector 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 SOURCE_ORIGIN = 'file://';
|
||||
|
||||
const IndexedDBCryptoStore = window.matrixcs.IndexedDBCryptoStore;
|
||||
const cryptoStore = new IndexedDBCryptoStore(window.indexedDB, 'matrix-js-sdk:crypto');
|
||||
|
||||
let accountStored = 0;
|
||||
let sessionsStored = 0;
|
||||
let inboundGroupSessionsStored = 0;
|
||||
let deviceDataStored = 0;
|
||||
let roomsStored = 0;
|
||||
let localStorageKeysStored = 0;
|
||||
|
||||
const promises = [];
|
||||
|
||||
async function onMessage(e) {
|
||||
if (e.origin !== SOURCE_ORIGIN) return;
|
||||
|
||||
const data = e.data.data; // bleh, naming clash
|
||||
switch (e.data.cmd) {
|
||||
case 'init':
|
||||
// start with clean stores before we migrate data in
|
||||
window.localStorage.clear();
|
||||
await cryptoStore.deleteAllData();
|
||||
|
||||
e.source.postMessage({
|
||||
cmd: 'initOK',
|
||||
}, SOURCE_ORIGIN);
|
||||
break;
|
||||
case 'storeAccount':
|
||||
promises.push(cryptoStore.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
||||
(txn) => {
|
||||
cryptoStore.storeAccount(txn, data);
|
||||
},
|
||||
).then(() => {
|
||||
++accountStored;
|
||||
}));
|
||||
break;
|
||||
case 'storeSessions':
|
||||
promises.push(cryptoStore.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_SESSIONS],
|
||||
(txn) => {
|
||||
for (const sess of data) {
|
||||
cryptoStore.storeEndToEndSession(sess.deviceKey, sess.sessionId, sess, txn);
|
||||
}
|
||||
},
|
||||
).then(() => {
|
||||
sessionsStored += data.length;
|
||||
}));
|
||||
break;
|
||||
case 'storeInboundGroupSessions':
|
||||
promises.push(cryptoStore.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS],
|
||||
(txn) => {
|
||||
for (const sess of data) {
|
||||
cryptoStore.addEndToEndInboundGroupSession(
|
||||
sess.senderKey, sess.sessionId, sess.sessionData, txn,
|
||||
);
|
||||
}
|
||||
},
|
||||
).then(() => {
|
||||
inboundGroupSessionsStored += data.length;
|
||||
}));
|
||||
break;
|
||||
case 'storeDeviceData':
|
||||
promises.push(cryptoStore.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_DEVICE_DATA],
|
||||
(txn) => {
|
||||
cryptoStore.storeEndToEndDeviceData(data, txn);
|
||||
},
|
||||
).then(() => {
|
||||
++deviceDataStored;
|
||||
}));
|
||||
break;
|
||||
case 'storeRooms':
|
||||
promises.push(cryptoStore.doTxn(
|
||||
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS],
|
||||
(txn) => {
|
||||
for (const [roomId, roomInfo] of Object.entries(data)) {
|
||||
cryptoStore.storeEndToEndRoom(roomId, roomInfo, txn);
|
||||
}
|
||||
},
|
||||
).then(() => {
|
||||
++roomsStored;
|
||||
}));
|
||||
break;
|
||||
case 'storeLocalStorage':
|
||||
window.localStorage.setItem(data.key, data.val);
|
||||
++localStorageKeysStored;
|
||||
break;
|
||||
case 'getSummary':
|
||||
await Promise.all(promises);
|
||||
e.source.postMessage({
|
||||
cmd: 'summary',
|
||||
data: {
|
||||
accountStored,
|
||||
sessionsStored,
|
||||
inboundGroupSessionsStored,
|
||||
deviceDataStored,
|
||||
roomsStored,
|
||||
localStorageKeysStored,
|
||||
},
|
||||
}, SOURCE_ORIGIN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', onMessage);
|
||||
|
||||
7
origin_migrator/source.html
Normal file
7
origin_migrator/source.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="dest/browser-matrix.min.js"></script>
|
||||
<script src="source.js"></script>
|
||||
<iframe name="dest" src="vector://vector/origin_migrator_dest/dest.html" onload="doMigrate()"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
210
origin_migrator/source.js
Normal file
210
origin_migrator/source.js
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Copyright 2018 New Vector 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 TARGET_ORIGIN = 'vector://vector';
|
||||
const BATCH_SIZE = 500;
|
||||
let destFrame;
|
||||
|
||||
let initResolver = null;
|
||||
let getSummaryResolver = null;
|
||||
|
||||
function onMessage(e) {
|
||||
if (e.origin !== TARGET_ORIGIN) return;
|
||||
|
||||
if (e.data.cmd === 'initOK' && initResolver) {
|
||||
initResolver();
|
||||
initResolver = null;
|
||||
} else if (e.data.cmd === 'summary' && getSummaryResolver) {
|
||||
getSummaryResolver(e.data.data);
|
||||
getSummaryResolver = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function initDestFrame() {
|
||||
return new Promise(resolve => {
|
||||
initResolver = resolve;
|
||||
destFrame.postMessage({
|
||||
cmd: 'init',
|
||||
}, TARGET_ORIGIN);
|
||||
});
|
||||
}
|
||||
|
||||
async function getSummary() {
|
||||
return new Promise(resolve => {
|
||||
getSummaryResolver = resolve;
|
||||
destFrame.postMessage({
|
||||
cmd: 'getSummary',
|
||||
}, TARGET_ORIGIN);
|
||||
});
|
||||
}
|
||||
|
||||
async function doMigrate() {
|
||||
let accountSent = 0;
|
||||
let sessionsSent = 0;
|
||||
let inboundGroupSessionsSent = 0;
|
||||
let deviceDataSent = 0;
|
||||
let roomsSent = 0;
|
||||
let localStorageKeysSent = 0;
|
||||
|
||||
if (!window.ipcRenderer) {
|
||||
console.error("ipcRenderer not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.localStorage.getItem('mx_user_id') === null) {
|
||||
window.ipcRenderer.send("origin_migration_nodata");
|
||||
return;
|
||||
}
|
||||
|
||||
destFrame = window.parent.frames.dest;
|
||||
|
||||
await initDestFrame();
|
||||
|
||||
const IndexedDBCryptoStore = window.matrixcs.IndexedDBCryptoStore;
|
||||
|
||||
const cryptoStore = new IndexedDBCryptoStore(window.indexedDB, 'matrix-js-sdk:crypto');
|
||||
|
||||
await cryptoStore.doTxn(
|
||||
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
|
||||
(txn) => {
|
||||
cryptoStore.getAccount(txn, (account) => {
|
||||
destFrame.postMessage({
|
||||
cmd: 'storeAccount',
|
||||
data: account,
|
||||
}, TARGET_ORIGIN);
|
||||
++accountSent;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await cryptoStore.doTxn(
|
||||
'readonly', [IndexedDBCryptoStore.STORE_SESSIONS],
|
||||
(txn) => {
|
||||
let sessBatch = [];
|
||||
cryptoStore.getAllEndToEndSessions(txn, (sessInfo) => {
|
||||
if (sessInfo) {
|
||||
++sessionsSent;
|
||||
sessBatch.push(sessInfo);
|
||||
}
|
||||
if (sessBatch.length >= BATCH_SIZE || sessInfo === null) {
|
||||
destFrame.postMessage({
|
||||
cmd: 'storeSessions',
|
||||
data: sessBatch,
|
||||
}, TARGET_ORIGIN);
|
||||
sessBatch = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await cryptoStore.doTxn(
|
||||
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS],
|
||||
(txn) => {
|
||||
let sessBatch = [];
|
||||
cryptoStore.getAllEndToEndInboundGroupSessions(txn, (sessInfo) => {
|
||||
if (sessInfo) {
|
||||
++inboundGroupSessionsSent;
|
||||
sessBatch.push(sessInfo);
|
||||
}
|
||||
if (sessBatch.length >= BATCH_SIZE || sessInfo === null) {
|
||||
destFrame.postMessage({
|
||||
cmd: 'storeInboundGroupSessions',
|
||||
data: sessBatch,
|
||||
}, TARGET_ORIGIN);
|
||||
sessBatch = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await cryptoStore.doTxn(
|
||||
'readonly', [IndexedDBCryptoStore.STORE_DEVICE_DATA],
|
||||
(txn) => {
|
||||
cryptoStore.getEndToEndDeviceData(txn, (deviceData) => {
|
||||
destFrame.postMessage({
|
||||
cmd: 'storeDeviceData',
|
||||
data: deviceData,
|
||||
}, TARGET_ORIGIN);
|
||||
++deviceDataSent;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await cryptoStore.doTxn(
|
||||
'readonly', [IndexedDBCryptoStore.STORE_ROOMS],
|
||||
(txn) => {
|
||||
cryptoStore.getEndToEndRooms(txn, (rooms) => {
|
||||
destFrame.postMessage({
|
||||
cmd: 'storeRooms',
|
||||
data: rooms,
|
||||
}, TARGET_ORIGIN);
|
||||
++roomsSent;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// we don't bother migrating;
|
||||
// * sync data (we can just initialsync again)
|
||||
// * logs
|
||||
// * key requests (worst case they'll just be re-sent)
|
||||
// * sessions needing backup (feature isn't available on Electron)
|
||||
|
||||
for (let i = 0; i < window.localStorage.length; ++i) {
|
||||
const key = window.localStorage.key(i);
|
||||
const val = window.localStorage.getItem(key);
|
||||
|
||||
destFrame.postMessage({
|
||||
cmd: 'storeLocalStorage',
|
||||
data: { key, val },
|
||||
}, TARGET_ORIGIN);
|
||||
++localStorageKeysSent;
|
||||
}
|
||||
|
||||
const summary = await getSummary();
|
||||
let success = false;
|
||||
if (
|
||||
summary.accountStored === accountSent &&
|
||||
summary.sessionsStored === sessionsSent &&
|
||||
summary.inboundGroupSessionsStored === inboundGroupSessionsSent &&
|
||||
summary.deviceDataStored === deviceDataSent &&
|
||||
summary.roomsStored === roomsSent &&
|
||||
summary.localStorageKeysStored === localStorageKeysSent
|
||||
) {
|
||||
success = true;
|
||||
window.localStorage.clear();
|
||||
await cryptoStore.deleteAllData();
|
||||
|
||||
// we don't bother migrating them, but also blow away the sync & logs db,
|
||||
// otherwise they'll just hang about taking up space
|
||||
await new Promise(resolve => {
|
||||
const req = window.indexedDB.deleteDatabase('matrix-js-sdk:riot-web-sync');
|
||||
req.onsuccess = resolve;
|
||||
req.onerror = resolve;
|
||||
});
|
||||
await new Promise(resolve => {
|
||||
const req = window.indexedDB.deleteDatabase('logs');
|
||||
req.onsuccess = resolve;
|
||||
req.onerror = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
window.ipcRenderer.send("origin_migration_complete", success, {
|
||||
accountSent, sessionsSent, inboundGroupSessionsSent,
|
||||
deviceDataSent, roomsSent, localStorageKeysSent,
|
||||
}, summary);
|
||||
}
|
||||
|
||||
window.addEventListener('message', onMessage);
|
||||
9448
package-lock.json
generated
9448
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "electron_app/src/electron-main.js",
|
||||
"version": "0.17.4",
|
||||
"version": "0.17.9",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -44,7 +44,7 @@
|
||||
"install:electron": "install-app-deps",
|
||||
"electron": "npm run install:electron && electron .",
|
||||
"start:res": "node scripts/copy-res.js -w",
|
||||
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --progress --mode development",
|
||||
"start:js": "webpack-dev-server --host=0.0.0.0 --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --progress --mode development",
|
||||
"start:js:prod": "cross-env NODE_ENV=production webpack-dev-server -w --progress",
|
||||
"start:js-sdk": "node scripts/npm-sub.js matrix-js-sdk run start:watch",
|
||||
"start:js-sdk:prod": "cross-env NODE_ENV=production node scripts/npm-sub.js matrix-js-sdk run start:watch",
|
||||
@@ -69,22 +69,22 @@
|
||||
"favico.js": "^0.3.10",
|
||||
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#b302279",
|
||||
"gfm.css": "^1.1.2",
|
||||
"highlight.js": "^9.13.0",
|
||||
"matrix-js-sdk": "0.12.1",
|
||||
"matrix-react-sdk": "0.14.3",
|
||||
"highlight.js": "^9.13.1",
|
||||
"matrix-js-sdk": "0.14.3",
|
||||
"matrix-react-sdk": "0.14.8",
|
||||
"modernizr": "^3.6.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.6.0",
|
||||
"react-dom": "^15.6.0",
|
||||
"sanitize-html": "^1.19.1",
|
||||
"ua-parser-js": "^0.7.18",
|
||||
"ua-parser-js": "^0.7.19",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.6.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^6.1.2",
|
||||
"babel-eslint": "^8.1.1",
|
||||
"babel-loader": "^7.1.5",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-transform-async-to-bluebird": "^1.1.1",
|
||||
@@ -96,15 +96,15 @@
|
||||
"babel-preset-es2017": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"chokidar": "^1.6.1",
|
||||
"chokidar": "^2.0.4",
|
||||
"concurrently": "^4.0.1",
|
||||
"cpx": "^1.3.2",
|
||||
"cross-env": "^4.0.0",
|
||||
"electron-builder": "^20.28.4",
|
||||
"electron-builder": "^20.29.0",
|
||||
"electron-builder-squirrel-windows": "^11.6.1",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"emojione": "^2.2.7",
|
||||
"eslint": "^5.7.0",
|
||||
"eslint": "^5.8.0",
|
||||
"eslint-config-google": "^0.7.1",
|
||||
"eslint-plugin-babel": "^4.1.2",
|
||||
"eslint-plugin-flowtype": "^2.50.3",
|
||||
@@ -113,7 +113,7 @@
|
||||
"fs-extra": "^0.30.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"json-loader": "^0.5.3",
|
||||
"karma": "^3.0.0",
|
||||
"karma": "^3.1.1",
|
||||
"karma-chrome-launcher": "^0.2.3",
|
||||
"karma-cli": "^1.0.1",
|
||||
"karma-junit-reporter": "^2.0.0",
|
||||
@@ -123,7 +123,7 @@
|
||||
"karma-spec-reporter": "0.0.31",
|
||||
"karma-summary-reporter": "^1.5.1",
|
||||
"karma-webpack": "4.0.0-beta.0",
|
||||
"matrix-mock-request": "^1.2.0",
|
||||
"matrix-mock-request": "^1.2.2",
|
||||
"matrix-react-test-utils": "^0.2.0",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
@@ -141,23 +141,24 @@
|
||||
"react-addons-test-utils": "^15.6.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack": "^4.23.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-dev-server": "^3.1.9"
|
||||
"webpack-dev-server": "^3.1.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"olm": "https://matrix.org/packages/npm/olm/olm-2.2.1.tgz"
|
||||
"olm": "https://matrix.org/packages/npm/olm/olm-3.0.0.tgz"
|
||||
},
|
||||
"build": {
|
||||
"appId": "im.riot.app",
|
||||
"electronVersion": "3.0.5",
|
||||
"electronVersion": "4.0.1",
|
||||
"files": [
|
||||
"node_modules/**",
|
||||
"src/**",
|
||||
"img/**"
|
||||
],
|
||||
"extraResources": [
|
||||
"webapp/**/*"
|
||||
"webapp/**/*",
|
||||
"origin_migrator/**/*"
|
||||
],
|
||||
"linux": {
|
||||
"target": "deb",
|
||||
|
||||
@@ -47,4 +47,4 @@ git commit package.json -m "$tag"
|
||||
|
||||
cd ..
|
||||
|
||||
exec ./node_modules/matrix-js-sdk/release.sh -z "$orig_args"
|
||||
exec ./node_modules/matrix-js-sdk/release.sh -u vector-im -z "$orig_args"
|
||||
|
||||
@@ -68,6 +68,7 @@ $focus-bg-color: #dddddd;
|
||||
// button UI (white-on-green in light skin)
|
||||
$accent-fg-color: #ffffff;
|
||||
$accent-color: #6CC1F6;
|
||||
$accent-color-50pct: #6CC1F67F;
|
||||
$accent-hover-color: #84cfff;
|
||||
|
||||
$selection-fg-color: $primary-bg-color;
|
||||
|
||||
@@ -25,6 +25,7 @@ const INCLUDE_LANGS = [
|
||||
{'value': 'gl', 'label': 'Galego'},
|
||||
{'value': 'hu', 'label': 'Magyar'},
|
||||
{'value': 'it', 'label': 'Italiano'},
|
||||
{'value': 'ja', 'label': '日本語'},
|
||||
{'value': 'ko', 'label': '한국어'},
|
||||
{'value': 'lv', 'label': 'Latviešu'},
|
||||
{'value': 'nb_NO', 'label': 'Norwegian Bokmål'},
|
||||
@@ -57,6 +58,11 @@ const COPY_LIST = [
|
||||
["res/themes/**", "webapp/themes"],
|
||||
["node_modules/emojione/assets/svg/*", "webapp/emojione/svg/"],
|
||||
["node_modules/emojione/assets/png/*", "webapp/emojione/png/"],
|
||||
// XXX: This is tied quite heavily to the matching olm.js so it really should be
|
||||
// in the bundle dir with the js to avoid caching issues giving us wasm that
|
||||
// doesn't match our js, but I cannot find any way to get webpack to do this.
|
||||
["node_modules/olm/olm.wasm", "webapp", { directwatch: 1 }],
|
||||
["node_modules/olm/olm_legacy.js", "webapp", { directwatch: 1 }],
|
||||
["./config.json", "webapp", { directwatch: 1 }],
|
||||
];
|
||||
|
||||
|
||||
@@ -4,21 +4,23 @@ set -e
|
||||
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
nvm use 6
|
||||
nvm use 10
|
||||
|
||||
set -x
|
||||
|
||||
# check out corresponding branches of dependencies.
|
||||
#
|
||||
# clone the deps with depth 1: we know we will only ever need that one
|
||||
# commit.
|
||||
`dirname $0`/fetch-develop.deps.sh --depth 1
|
||||
|
||||
npm install
|
||||
|
||||
# apparently npm 3.10.3 on node 6.4.0 doesn't upgrade #develop target with npm install unless explicitly asked.
|
||||
npm install olm
|
||||
|
||||
# check out corresponding branches of dependencies.
|
||||
#
|
||||
# clone the deps with depth 1: we know we will only ever need that one
|
||||
# commit.
|
||||
# We need to do this after npm install otherwise modern node versions
|
||||
# just reset it back.
|
||||
`dirname $0`/fetch-develop.deps.sh --depth 1
|
||||
|
||||
# install olm. A naive 'npm i ./olm/olm-*.tgz' fails because it uses the url
|
||||
# from our package.json (or even matrix-js-sdk's) in preference.
|
||||
#
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import HomePage from 'matrix-react-sdk/lib/components/structures/HomePage';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
|
||||
@@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require("react");
|
||||
var sanitizeHtml = require("sanitize-html");
|
||||
const React = require("react");
|
||||
const sanitizeHtml = require("sanitize-html");
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
@@ -47,5 +47,5 @@ module.exports = React.createClass({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
const React = require('react');
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import SettingsStore from 'matrix-react-sdk/lib/settings/SettingsStore';
|
||||
|
||||
@@ -29,8 +29,8 @@ module.exports = React.createClass({
|
||||
render: function() {
|
||||
// FIXME: replace this with a proper Status skin
|
||||
// ...except then we wouldn't be able to switch to the Status theme at runtime.
|
||||
if (SettingsStore.getValue("theme") === 'status') return <div/>;
|
||||
|
||||
if (SettingsStore.getValue("theme") === 'status') return <div />;
|
||||
|
||||
return (
|
||||
<div className="mx_Login_links">
|
||||
<a href="https://medium.com/@RiotChat">blog</a> ·
|
||||
@@ -39,5 +39,5 @@ module.exports = React.createClass({
|
||||
<a href="https://matrix.org">{ _t('powered by Matrix') }</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -35,9 +35,9 @@ module.exports = React.createClass({
|
||||
return (
|
||||
<div className="mx_Login_header">
|
||||
<div className="mx_Login_logo">
|
||||
<img src={this.props.icon || DEFAULT_LOGO_URI} alt="Riot"/>
|
||||
<img src={this.props.icon || DEFAULT_LOGO_URI} alt="Riot" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -34,5 +34,6 @@
|
||||
"Dev chat for the Dendrite dev team": "Babilado por la programista skipo de Dendrite",
|
||||
"Lots of rooms already exist in Matrix, linked to existing networks (Slack, IRC, Gitter etc) or independent. Check out the directory!": "Multaj ĉambroj jam ekzistas en Matrix; kaj sendependaj, kaj ligitaj kun jamaj retoj (Slock, IRC, Gitter, ktp.). Rigardu la ĉambrujon!",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s per %(browserName)s je %(osName)s",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.<br/>This allows you to use Riot with an existing Matrix account on a different home server.<br/><br/>You can also set a custom identity server but you won't be able to invite users by email address, or be invited by email address yourself.": "Vi povas uzi proprajn servilajn elektojn por saluti aliajn servilojn de Matrix, per specifo de alia hejmservila URL.<br/>Tio permesas al vi uzi klienton Riot kun jama konto de Matrix en alia hejmservilo.<br/><br/>Vi ankaŭ povas agordi propran identigan servilon, sed vi ne povos inviti uzantojn per retpoŝtadreso, aŭ esti invitata per retpoŝtadreso mem."
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.<br/>This allows you to use Riot with an existing Matrix account on a different home server.<br/><br/>You can also set a custom identity server but you won't be able to invite users by email address, or be invited by email address yourself.": "Vi povas uzi proprajn servilajn elektojn por saluti aliajn servilojn de Matrix, per specifo de alia hejmservila URL.<br/>Tio permesas al vi uzi klienton Riot kun jama konto de Matrix en alia hejmservilo.<br/><br/>Vi ankaŭ povas agordi propran identigan servilon, sed vi ne povos inviti uzantojn per retpoŝtadreso, aŭ esti invitata per retpoŝtadreso mem.",
|
||||
"Co-ordination for Riot translators": "Kunordigo por tradukantoj de Riot"
|
||||
}
|
||||
|
||||
@@ -1 +1,39 @@
|
||||
{}
|
||||
{
|
||||
"Riot is not supported on mobile web. Install the app?": "रायट फ़ोन पर समर्थन नहीं हैं। एप्लिकेशन इनस्टॉल करना चाहेंगे ?",
|
||||
"Riot Desktop on %(platformName)s": "%(platformName)s पर रायट डेस्कटॉप",
|
||||
"Unknown device": "अज्ञात यन्त्र",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(osName)s पर %(browserName)s के माध्यम से %(appName)s",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "स्क्रीन साझा की कॉल करने के लिए आपको HTTPS का उपयोग करने की आवश्यकता है।",
|
||||
"Custom Server Options": "कस्टम सर्वर विकल्प",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.<br/>This allows you to use Riot with an existing Matrix account on a different home server.<br/><br/>You can also set a custom identity server but you won't be able to invite users by email address, or be invited by email address yourself.": "आप एक अलग होम सर्वर यूआरएल निर्दिष्ट करके अन्य मैट्रिक्स सर्वरों में साइन इन करने के लिए कस्टम सर्वर विकल्प का उपयोग कर सकते हैं। <br/> यह आपको एक अलग होम सर्वर पर मौजूदा मैट्रिक्स खाते के साथ रायट का उपयोग करने की अनुमति देता है। <br/> <br/> आप अपना आइडेंटिटी सर्वर भी सेट कर सकते हैं लेकिन आप ईमेल पते से उपयोगकर्ताओं को आमंत्रित नहीं कर पाएंगे, या ईमेल पते से स्वयं आमंत्रित नहीं हों सकेंगे।",
|
||||
"Dismiss": "खारिज",
|
||||
"powered by Matrix": "मैट्रिक्स द्वारा संचालित",
|
||||
"Welcome to Riot.im": "Riot.im में आपका स्वागत है",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "[मैट्रिक्स] द्वारा संचालित विकेंद्रीकृत, एन्क्रिप्टेड चैट और सहयोगिता",
|
||||
"Search the room directory": "रूम डायरेक्टरी को खोजें",
|
||||
"Lots of rooms already exist in Matrix, linked to existing networks (Slack, IRC, Gitter etc) or independent. Check out the directory!": "मौजूदा नेटवर्क से जुड़े मैट्रिक्स में बहुत से कमरे पहले से मौजूद हैं (स्लैक, आईआरसी, गिटर इत्यादि) या स्वतंत्र। डायरेक्टरी देखें!",
|
||||
"Chat with Riot Bot": "रायट बॉट के साथ चैट करें",
|
||||
"Get started with some tips from Riot Bot!": "रायट बॉट से कुछ सुझावों के साथ शुरू करें!",
|
||||
"General discussion about Matrix and Riot": "मैट्रिक्स और रायट के बारे में सामान्य चर्चा",
|
||||
"Discussion of all things Matrix!": "मैट्रिक्स की सभी चीजों की चर्चा!",
|
||||
"Riot/Web & Desktop chat": "रायट/वेब और डेस्कटॉप चैट",
|
||||
"Riot/iOS & matrix-ios-sdk chat": "रायट / iOS और matrix-ios-sdk चैट",
|
||||
"Riot/Android & matrix-android-sdk chat": "रायट / एंड्रॉइड और matrix-android-sdk चैट",
|
||||
"Matrix technical discussions": "मैट्रिक्स तकनीकी चर्चाएं",
|
||||
"Running Matrix services": "मैट्रिक्स सेवाएं चलाना",
|
||||
"Community-run support for Synapse": "सामुदायिक चालित Synapse के लिए समर्थन",
|
||||
"Admin support for Dendrite": "डेंडर्राइट के लिए व्यवस्थापक समर्थन",
|
||||
"Announcements about Synapse releases": "Synapse रिलीज के बारे में घोषणाएं",
|
||||
"Support for those using and running matrix-appservice-irc": "Matrix-appservice-irc का उपयोग और चलाने वाले लोगों के लिए समर्थन",
|
||||
"Building services on Matrix": "मैट्रिक्स पर सेवाएं बनाना",
|
||||
"Support for those using the Matrix spec": "मैट्रिक्स spec का उपयोग करने वालों के लिए समर्थन",
|
||||
"Design and implementation of E2E in Matrix": "मैट्रिक्स में E2E के डिजाइन और कार्यान्वयन",
|
||||
"Implementing VR services with Matrix": "मैट्रिक्स के साथ VR सेवाओं को लागू करना",
|
||||
"Implementing VoIP services with Matrix": "मैट्रिक्स के साथ वीओआईपी सेवाओं को लागू करना",
|
||||
"Discussion of the Identity Service API": "आइडेंटिटी सर्विस API की चर्चा",
|
||||
"Support for those using, running and writing other bridges": "अन्य ब्रिज का उपयोग, चलाने और लिखने वालों के लिए समर्थन",
|
||||
"Contributing code to Matrix and Riot": "मैट्रिक्स और रायट में कोड योगदान करना",
|
||||
"Dev chat for the Riot/Web dev team": "रायट / वेब डेव टीम के लिए डेवलपर चैट",
|
||||
"Dev chat for the Dendrite dev team": "डेन्ड्राइट देव टीम के लिए डेवलपर चैट",
|
||||
"Co-ordination for Riot translators": "रायट अनुवादकों के लिए समन्वय"
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"Admin support for Dendrite": "Dendriteの管理者サポート",
|
||||
"Announcements about Synapse releases": "Synapseの公開についてのお知らせ",
|
||||
"Support for those using and running matrix-appservice-irc": "matrix-appservice-ircを利用並びに運営している方へのサポート",
|
||||
"Building services on Matrix": "Matrixでのサービスの開発",
|
||||
"Building services on Matrix": "Matrix上でのサービスの開発",
|
||||
"Support for those using the Matrix spec": "Matrixスペックを利用する方へのサポート",
|
||||
"Design and implementation of E2E in Matrix": "Matrixのデザインとエンドツーエンドの実装",
|
||||
"Implementing VR services with Matrix": "MatrixでのVRサービスの実装",
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s %(browserName)s ద్వర %(osName)s కి",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s %(browserName)s ద్వార %(osName)s కి",
|
||||
"Custom Server Options": "మలచిన సేవిక ఎంపికలు",
|
||||
"Dismiss": "రద్దుచేసే",
|
||||
"Riot Desktop on %(platformName)s": "రియట్ రంగస్థలం లో %(platformName)s",
|
||||
"Welcome to Riot.im": "రిమోట్.ఇం కి స్వగతం",
|
||||
"Search the room directory": "గది వివరాన్ని శోధించండి",
|
||||
"Chat with Riot Bot": "రియోట్ బొట్తో మాటామంతి చేయండి"
|
||||
"Chat with Riot Bot": "రియోట్ బొట్తో మాటామంతి చేయండి",
|
||||
"Unknown device": "తెలుయని పరికరం",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "తెర ని పంచే కాల్ కి HTTPS అవసరం.",
|
||||
"Riot is not supported on mobile web. Install the app?": "మొబైల్ బ్రౌజర్ లో రియట్ పనిచేయదు. ఆప్ ఇన్స్టాల్ చేయాలా?"
|
||||
}
|
||||
|
||||
@@ -36,28 +36,10 @@
|
||||
<body style="height: 100%;">
|
||||
<section id="matrixchat" style="height: 100%;"></section>
|
||||
<noscript>Sorry, Riot requires JavaScript to be enabled.</noscript> <!-- TODO: Translate this? -->
|
||||
<% for (var i=0; i < htmlWebpackPlugin.files.js.length; i++) {
|
||||
if (_.endsWith(htmlWebpackPlugin.files.js[i], 'olm.js')) {
|
||||
var array = htmlWebpackPlugin.files.js;
|
||||
htmlWebpackPlugin.files.js.unshift(htmlWebpackPlugin.files.js[i]);
|
||||
htmlWebpackPlugin.files.js.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i=0; i < htmlWebpackPlugin.files.js.length; i++) {
|
||||
// Not a particularly graceful way of not putting the indexeddb worker script
|
||||
// into the main page
|
||||
if (_.endsWith(htmlWebpackPlugin.files.js[i], 'indexeddb-worker.js')) {
|
||||
%>
|
||||
<script>
|
||||
window.vector_indexeddb_worker_script = '<%= htmlWebpackPlugin.files.js[i] %>';
|
||||
</script>
|
||||
<%
|
||||
continue;
|
||||
}
|
||||
%>
|
||||
<script src="<%= htmlWebpackPlugin.files.js[i] %>"></script>
|
||||
<% } %>
|
||||
<script>
|
||||
window.vector_indexeddb_worker_script = '<%= htmlWebpackPlugin.files.chunks['indexeddb-worker'].entry %>';
|
||||
</script>
|
||||
<script src="<%= htmlWebpackPlugin.files.chunks['bundle'].entry %>"></script>
|
||||
<img src="img/warning.svg" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
|
||||
<audio id="messageAudio">
|
||||
<source src="media/message.ogg" type="audio/ogg" />
|
||||
|
||||
@@ -24,6 +24,8 @@ require('gfm.css/gfm.css');
|
||||
require('highlight.js/styles/github.css');
|
||||
require('draft-js/dist/Draft.css');
|
||||
|
||||
import './rageshakesetup';
|
||||
|
||||
import React from 'react';
|
||||
// add React and ReactPerf to the global namespace, to make them easier to
|
||||
// access via the console
|
||||
@@ -32,7 +34,7 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
global.Perf = require('react-addons-perf');
|
||||
}
|
||||
|
||||
import RunModernizrTests from './modernizr'; // this side-effects a global
|
||||
import './modernizr';
|
||||
import ReactDOM from 'react-dom';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
@@ -41,20 +43,20 @@ import VectorConferenceHandler from 'matrix-react-sdk/lib/VectorConferenceHandle
|
||||
import Promise from 'bluebird';
|
||||
import request from 'browser-request';
|
||||
import * as languageHandler from 'matrix-react-sdk/lib/languageHandler';
|
||||
// Also import _t directly so we can call it just `_t` as this is what gen-i18n.js expects
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
|
||||
import url from 'url';
|
||||
|
||||
import {parseQs, parseQsFromFragment} from './url_utils';
|
||||
import Platform from './platform';
|
||||
|
||||
import ElectronPlatform from './platform/ElectronPlatform';
|
||||
import WebPlatform from './platform/WebPlatform';
|
||||
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
import SettingsStore, {SettingLevel} from "matrix-react-sdk/lib/settings/SettingsStore";
|
||||
import SettingsStore from "matrix-react-sdk/lib/settings/SettingsStore";
|
||||
import Tinter from 'matrix-react-sdk/lib/Tinter';
|
||||
import SdkConfig from "matrix-react-sdk/lib/SdkConfig";
|
||||
|
||||
import rageshake from "matrix-react-sdk/lib/rageshake/rageshake";
|
||||
import Olm from 'olm';
|
||||
|
||||
import CallHandler from 'matrix-react-sdk/lib/CallHandler';
|
||||
|
||||
@@ -66,33 +68,17 @@ let lastLocationHashSet = null;
|
||||
// and need to migrate, but they spam the console with warnings.
|
||||
Promise.config({warnings: false});
|
||||
|
||||
function initRageshake() {
|
||||
rageshake.init().then(() => {
|
||||
console.log("Initialised rageshake: See https://bugs.chromium.org/p/chromium/issues/detail?id=583193 to fix line numbers on Chrome.");
|
||||
|
||||
window.addEventListener('beforeunload', (e) => {
|
||||
console.log('riot-web closing');
|
||||
// try to flush the logs to indexeddb
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
rageshake.cleanup();
|
||||
}, (err) => {
|
||||
console.error("Failed to initialise rageshake: " + err);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBrowserFeatures(featureList) {
|
||||
if (!window.Modernizr) {
|
||||
console.error("Cannot check features - Modernizr global is missing.");
|
||||
return false;
|
||||
}
|
||||
var featureComplete = true;
|
||||
for (var i = 0; i < featureList.length; i++) {
|
||||
let featureComplete = true;
|
||||
for (let i = 0; i < featureList.length; i++) {
|
||||
if (window.Modernizr[featureList[i]] === undefined) {
|
||||
console.error(
|
||||
"Looked for feature '%s' but Modernizr has no results for this. " +
|
||||
"Has it been configured correctly?", featureList[i]
|
||||
"Has it been configured correctly?", featureList[i],
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -113,7 +99,7 @@ function getScreenFromLocation(location) {
|
||||
return {
|
||||
screen: fragparts.location.substring(1),
|
||||
params: fragparts.params,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Here, we do some crude URL analysis to allow
|
||||
@@ -138,10 +124,10 @@ function onHashChange(ev) {
|
||||
// so a web page can update the URL bar appropriately.
|
||||
function onNewScreen(screen) {
|
||||
console.log("newscreen "+screen);
|
||||
var hash = '#/' + screen;
|
||||
const hash = '#/' + screen;
|
||||
lastLocationHashSet = hash;
|
||||
window.location.hash = hash;
|
||||
};
|
||||
}
|
||||
|
||||
// We use this to work out what URL the SDK should
|
||||
// pass through when registering to allow the user to
|
||||
@@ -178,7 +164,7 @@ function makeRegistrationUrl(params) {
|
||||
return url;
|
||||
}
|
||||
|
||||
function getConfig(configJsonFilename) {
|
||||
export function getConfig(configJsonFilename) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
request(
|
||||
{ method: "GET", url: configJsonFilename },
|
||||
@@ -214,28 +200,51 @@ function onTokenLoginCompleted() {
|
||||
// if we did a token login, we're now left with the token, hs and is
|
||||
// url as query params in the url; a little nasty but let's redirect to
|
||||
// clear them.
|
||||
var parsedUrl = url.parse(window.location.href);
|
||||
const parsedUrl = url.parse(window.location.href);
|
||||
parsedUrl.search = "";
|
||||
var formatted = url.format(parsedUrl);
|
||||
const formatted = url.format(parsedUrl);
|
||||
console.log("Redirecting to " + formatted + " to drop loginToken " +
|
||||
"from queryparams");
|
||||
window.location.href = formatted;
|
||||
}
|
||||
|
||||
async function loadApp() {
|
||||
initRageshake();
|
||||
if (window.vector_indexeddb_worker_script === undefined) {
|
||||
// If this is missing, something has probably gone wrong with
|
||||
// the bundling. The js-sdk will just fall back to accessing
|
||||
// indexeddb directly with no worker script, but we want to
|
||||
// make sure the indexeddb script is present, so fail hard.
|
||||
throw new Error("Missing indexeddb worker script!");
|
||||
}
|
||||
MatrixClientPeg.setIndexedDbWorkerScript(window.vector_indexeddb_worker_script);
|
||||
CallHandler.setConferenceHandler(VectorConferenceHandler);
|
||||
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
|
||||
await loadOlm();
|
||||
|
||||
await loadLanguage();
|
||||
|
||||
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());
|
||||
// set the platform for react sdk
|
||||
if (window.ipcRenderer) {
|
||||
console.log("Using Electron platform");
|
||||
const plaf = new ElectronPlatform();
|
||||
PlatformPeg.set(plaf);
|
||||
|
||||
// Electron only: see if we need to do a one-time data
|
||||
// migration
|
||||
if (window.localStorage.getItem('mx_user_id') === null) {
|
||||
console.log("Migrating session from old origin...");
|
||||
await plaf.migrateFromOldOrigin();
|
||||
console.log("Origin migration complete");
|
||||
}
|
||||
} else {
|
||||
console.log("Using Web platform");
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
}
|
||||
|
||||
// Load the config file. First try to load up a domain-specific config of the
|
||||
// form "config.$domain.json" and if that fails, fall back to config.json.
|
||||
@@ -269,7 +278,6 @@ async function loadApp() {
|
||||
}
|
||||
|
||||
// as quickly as we possibly can, set a default theme...
|
||||
const styleElements = Object.create(null);
|
||||
let a;
|
||||
const theme = SettingsStore.getValue("theme");
|
||||
for (let i = 0; (a = document.getElementsByTagName("link")[i]); i++) {
|
||||
@@ -293,7 +301,7 @@ async function loadApp() {
|
||||
// in case it is the first time loading Riot.
|
||||
// `InstallTrigger` is a Object which only exists on Firefox
|
||||
// (it is used for their Plugins) and can be used as a
|
||||
// feature check.
|
||||
// feature check.
|
||||
// Firefox loads css always before js. This is why we dont use
|
||||
// onload or it's EventListener as thoose will never trigger.
|
||||
if (typeof InstallTrigger !== 'undefined') {
|
||||
@@ -345,23 +353,59 @@ async function loadApp() {
|
||||
initialScreenAfterLogin={getScreenFromLocation(window.location)}
|
||||
defaultDeviceDisplayName={platform.getDefaultDeviceDisplayName()}
|
||||
/>,
|
||||
document.getElementById('matrixchat')
|
||||
document.getElementById('matrixchat'),
|
||||
);
|
||||
} else {
|
||||
console.error("Browser is missing required features.");
|
||||
// take to a different landing page to AWOOOOOGA at the user
|
||||
var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
|
||||
const CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
|
||||
window.matrixChat = ReactDOM.render(
|
||||
<CompatibilityPage onAccept={function() {
|
||||
if (window.localStorage) window.localStorage.setItem('mx_accepts_unsupported_browser', true);
|
||||
console.log("User accepts the compatibility risks.");
|
||||
loadApp();
|
||||
}} />,
|
||||
document.getElementById('matrixchat')
|
||||
document.getElementById('matrixchat'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function loadOlm() {
|
||||
/* Load Olm. We try the WebAssembly version first, and then the legacy,
|
||||
* asm.js version if that fails. For this reason we need to wait for this
|
||||
* to finish before continuing to load the rest of the app. In future
|
||||
* we could somehow pass a promise down to react-sdk and have it wait on
|
||||
* that so olm can be loading in parallel with the rest of the app.
|
||||
*
|
||||
* We also need to tell the Olm js to look for its wasm file at the same
|
||||
* level as index.html. It really should be in the same place as the js,
|
||||
* ie. in the bundle directory, to avoid caching issues, but as far as I
|
||||
* can tell this is completely impossible with webpack.
|
||||
*/
|
||||
return Olm.init({
|
||||
locateFile: () => 'olm.wasm',
|
||||
}).then(() => {
|
||||
console.log("Using WebAssembly Olm");
|
||||
}).catch((e) => {
|
||||
console.log("Failed to load Olm: trying legacy version");
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = 'olm_legacy.js';
|
||||
s.onload = resolve;
|
||||
s.onerror = reject;
|
||||
document.body.appendChild(s);
|
||||
}).then(() => {
|
||||
// Init window.Olm, ie. the one just loaded by the script tag,
|
||||
// not 'Olm' which is still the failed wasm version.
|
||||
return window.Olm.init();
|
||||
}).then(() => {
|
||||
console.log("Using legacy Olm");
|
||||
}).catch((e) => {
|
||||
console.log("Both WebAssembly and asm.js Olm failed!", e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function loadLanguage() {
|
||||
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true);
|
||||
let langs = [];
|
||||
|
||||
@@ -18,4 +18,4 @@ import {IndexedDBStoreWorker} from 'matrix-js-sdk/lib/indexeddb-worker.js';
|
||||
|
||||
const remoteWorker = new IndexedDBStoreWorker(postMessage);
|
||||
|
||||
onmessage = remoteWorker.onMessage;
|
||||
global.onmessage = remoteWorker.onMessage;
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/* a very thin shim for loading olm.js: just sets the global OLM_OPTIONS and
|
||||
* requires the actual olm.js library.
|
||||
*
|
||||
* olm.js reads global.OLM_OPTIONS and defines global.Olm. The latter is fine for us,
|
||||
* but we need to prepare the former.
|
||||
*
|
||||
* We can't use webpack's definePlugin to do this, because we tell webpack not
|
||||
* to parse olm.js. We also can't put this code in index.js, because olm and
|
||||
* index.js are loaded in parallel, and we need to make sure OLM_OPTIONS is set
|
||||
* before olm.js is loaded.
|
||||
*/
|
||||
|
||||
/* total_memory must be a power of two, and at least twice the stack.
|
||||
*
|
||||
* We don't need a lot of stack, but we do need about 128K of heap to encrypt a
|
||||
* 64K event (enough to store the ciphertext and the plaintext, bearing in mind
|
||||
* that the plaintext can only be 48K because base64). We also have about 36K
|
||||
* of statics. So let's have 256K of memory.
|
||||
*/
|
||||
global.OLM_OPTIONS = {
|
||||
TOTAL_STACK: 64*1024,
|
||||
TOTAL_MEMORY: 256*1024,
|
||||
};
|
||||
|
||||
require('olm/olm.js');
|
||||
@@ -3,6 +3,7 @@
|
||||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,44 +22,26 @@ import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
import { _t } from 'matrix-react-sdk/lib/languageHandler';
|
||||
import Promise from 'bluebird';
|
||||
import {remote, ipcRenderer, desktopCapturer} from 'electron';
|
||||
import rageshake from 'matrix-react-sdk/lib/rageshake/rageshake';
|
||||
|
||||
remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
|
||||
|
||||
// try to flush the rageshake logs to indexeddb before quit.
|
||||
ipcRenderer.on('before-quit', function () {
|
||||
console.log('riot-desktop closing');
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
function onUpdateDownloaded(ev: Event, releaseNotes: string, ver: string, date: Date, updateURL: string) {
|
||||
dis.dispatch({
|
||||
action: 'new_version',
|
||||
currentVersion: remote.app.getVersion(),
|
||||
newVersion: ver,
|
||||
releaseNotes: releaseNotes,
|
||||
});
|
||||
}
|
||||
const ipcRenderer = window.ipcRenderer;
|
||||
|
||||
function platformFriendlyName(): string {
|
||||
console.log(window.process);
|
||||
switch (window.process.platform) {
|
||||
case 'darwin':
|
||||
return 'macOS';
|
||||
case 'freebsd':
|
||||
return 'FreeBSD';
|
||||
case 'openbsd':
|
||||
return 'OpenBSD';
|
||||
case 'sunos':
|
||||
return 'SunOS';
|
||||
case 'win32':
|
||||
return 'Windows';
|
||||
default:
|
||||
// Sorry, Linux users: you get lumped into here,
|
||||
// but only because Linux's capitalisation is
|
||||
// normal. We do care about you.
|
||||
return window.process.platform[0].toUpperCase() + window.process.platform.slice(1);
|
||||
// used to use window.process but the same info is available here
|
||||
if (navigator.userAgent.indexOf('Macintosh')) {
|
||||
return 'macOS';
|
||||
} else if (navigator.userAgent.indexOf('FreeBSD')) {
|
||||
return 'FreeBSD';
|
||||
} else if (navigator.userAgent.indexOf('OpenBSD')) {
|
||||
return 'OpenBSD';
|
||||
} else if (navigator.userAgent.indexOf('SunOS')) {
|
||||
return 'SunOS';
|
||||
} else if (navigator.userAgent.indexOf('Windows')) {
|
||||
return 'Windows';
|
||||
} else if (navigator.userAgent.indexOf('Linux')) {
|
||||
return 'Linux';
|
||||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +68,11 @@ function getUpdateCheckStatus(status) {
|
||||
export default class ElectronPlatform extends VectorBasePlatform {
|
||||
constructor() {
|
||||
super();
|
||||
dis.register(_onAction);
|
||||
this.updatable = Boolean(remote.autoUpdater.getFeedURL());
|
||||
|
||||
this._pendingIpcCalls = {};
|
||||
this._nextIpcCallId = 0;
|
||||
|
||||
dis.register(_onAction);
|
||||
/*
|
||||
IPC Call `check_updates` returns:
|
||||
true if there is an update available
|
||||
@@ -103,10 +88,28 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
this.showUpdateCheck = false;
|
||||
});
|
||||
|
||||
// try to flush the rageshake logs to indexeddb before quit.
|
||||
ipcRenderer.on('before-quit', function() {
|
||||
console.log('riot-desktop closing');
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
ipcRenderer.on('ipcReply', this._onIpcReply.bind(this));
|
||||
ipcRenderer.on('update-downloaded', this.onUpdateDownloaded.bind(this));
|
||||
|
||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||
}
|
||||
|
||||
async onUpdateDownloaded(ev, updateInfo) {
|
||||
dis.dispatch({
|
||||
action: 'new_version',
|
||||
currentVersion: await this.getAppVersion(),
|
||||
newVersion: updateInfo,
|
||||
releaseNotes: updateInfo.releaseNotes,
|
||||
});
|
||||
}
|
||||
|
||||
getHumanReadableName(): string {
|
||||
return 'Electron Platform'; // no translation required: only used for analytics
|
||||
}
|
||||
@@ -133,7 +136,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
// maybe we should pass basic styling (italics, bold, underline) through from MD
|
||||
// we only have to strip out < and > as the spec doesn't include anything about things like &
|
||||
// so we shouldn't assume that all implementations will treat those properly. Very basic tag parsing is done.
|
||||
if (window.process.platform === 'linux') {
|
||||
if (navigator.userAgent.indexOf('Linux')) {
|
||||
msg = msg.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
@@ -147,17 +150,13 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
},
|
||||
);
|
||||
|
||||
notification.onclick = function() {
|
||||
notification.onclick = () => {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.roomId,
|
||||
});
|
||||
global.focus();
|
||||
const win = remote.getCurrentWindow();
|
||||
|
||||
if (win.isMinimized()) win.restore();
|
||||
else if (!win.isVisible()) win.show();
|
||||
else win.focus();
|
||||
this._ipcCall('focusWindow');
|
||||
};
|
||||
|
||||
return notification;
|
||||
@@ -171,8 +170,25 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
notif.close();
|
||||
}
|
||||
|
||||
getAppVersion(): Promise<string> {
|
||||
return Promise.resolve(remote.app.getVersion());
|
||||
async getAppVersion(): Promise<string> {
|
||||
return await this._ipcCall('getAppVersion');
|
||||
}
|
||||
|
||||
supportsAutoLaunch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async getAutoLaunchEnabled() {
|
||||
return await this._ipcCall('getAutoLaunchEnabled');
|
||||
}
|
||||
|
||||
async setAutoLaunchEnabled(enabled) {
|
||||
return await this._ipcCall('setAutoLaunchEnabled', enabled);
|
||||
}
|
||||
|
||||
async canSelfUpdate(): boolean {
|
||||
const feedUrl = await this._ipcCall('getUpdateFeedUrl');
|
||||
return Boolean(feedUrl);
|
||||
}
|
||||
|
||||
startUpdateCheck() {
|
||||
@@ -197,52 +213,47 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
return null;
|
||||
}
|
||||
|
||||
isElectron(): boolean { return true; }
|
||||
|
||||
requestNotificationPermission(): Promise<string> {
|
||||
return Promise.resolve('granted');
|
||||
}
|
||||
|
||||
reload() {
|
||||
remote.getCurrentWebContents().reload();
|
||||
// we used to remote to the main process to get it to
|
||||
// reload the webcontents, but in practice this is unnecessary:
|
||||
// the normal way works fine.
|
||||
window.location.reload(false);
|
||||
}
|
||||
|
||||
/* BEGIN copied and slightly-modified code
|
||||
* setupScreenSharingForIframe function from:
|
||||
* https://github.com/jitsi/jitsi-meet-electron-utils
|
||||
* Copied directly here to avoid the need for a native electron module for
|
||||
* 'just a bit of JavaScript'
|
||||
* NOTE: Apache v2.0 licensed
|
||||
*/
|
||||
setupScreenSharingForIframe(iframe: Object) {
|
||||
iframe.contentWindow.JitsiMeetElectron = {
|
||||
/**
|
||||
* Get sources available for screensharing. The callback is invoked
|
||||
* with an array of DesktopCapturerSources.
|
||||
*
|
||||
* @param {Function} callback - The success callback.
|
||||
* @param {Function} errorCallback - The callback for errors.
|
||||
* @param {Object} options - Configuration for getting sources.
|
||||
* @param {Array} options.types - Specify the desktop source types
|
||||
* to get, with valid sources being "window" and "screen".
|
||||
* @param {Object} options.thumbnailSize - Specify how big the
|
||||
* preview images for the sources should be. The valid keys are
|
||||
* height and width, e.g. { height: number, width: number}. By
|
||||
* default electron will return images with height and width of
|
||||
* 150px.
|
||||
*/
|
||||
obtainDesktopStreams(callback, errorCallback, options = {}) {
|
||||
desktopCapturer.getSources(options,
|
||||
(error, sources) => {
|
||||
if (error) {
|
||||
errorCallback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(sources);
|
||||
});
|
||||
},
|
||||
};
|
||||
async migrateFromOldOrigin() {
|
||||
return this._ipcCall('origin_migrate');
|
||||
}
|
||||
|
||||
async _ipcCall(name, ...args) {
|
||||
const ipcCallId = ++this._nextIpcCallId;
|
||||
return new Promise((resolve, reject) => {
|
||||
this._pendingIpcCalls[ipcCallId] = {resolve, reject};
|
||||
window.ipcRenderer.send('ipcCall', {id: ipcCallId, name, args});
|
||||
// Maybe add a timeout to these? Probably not necessary.
|
||||
});
|
||||
}
|
||||
|
||||
_onIpcReply(ev, payload) {
|
||||
if (payload.id === undefined) {
|
||||
console.warn("Ignoring IPC reply with no ID");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._pendingIpcCalls[payload.id] === undefined) {
|
||||
console.warn("Unknown IPC payload ID: " + payload.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const callbacks = this._pendingIpcCalls[payload.id];
|
||||
delete this._pendingIpcCalls[payload.id];
|
||||
if (payload.error) {
|
||||
callbacks.reject(payload.error);
|
||||
} else {
|
||||
callbacks.resolve(payload.reply);
|
||||
}
|
||||
}
|
||||
/* END of copied and slightly-modified code */
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/*
|
||||
Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -45,7 +46,6 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
this.favicon = new Favico({animation: 'none'});
|
||||
this.showUpdateCheck = false;
|
||||
this._updateFavicon();
|
||||
this.updatable = true;
|
||||
|
||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||
@@ -60,8 +60,8 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
// 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;
|
||||
let bgColor = "#d00";
|
||||
let notif = this.notificationCount;
|
||||
|
||||
if (this.errorDidOccur) {
|
||||
notif = notif || "×";
|
||||
@@ -88,6 +88,19 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
this._updateFavicon();
|
||||
}
|
||||
|
||||
supportsAutoLaunch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX: Surely this should be a setting like any other?
|
||||
async getAutoLaunchEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async setAutoLaunchEnabled(enabled) {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin update polling, if applicable
|
||||
*/
|
||||
@@ -97,8 +110,8 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
/**
|
||||
* Whether we can call checkForUpdate on this platform build
|
||||
*/
|
||||
canSelfUpdate(): boolean {
|
||||
return this.updatable;
|
||||
async canSelfUpdate(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
startUpdateCheck() {
|
||||
@@ -114,7 +127,7 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
dis.dispatch({
|
||||
action: 'check_updates',
|
||||
value: false,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
getUpdateCheckStatusEnum() {
|
||||
@@ -136,4 +149,12 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
getDefaultDeviceDisplayName(): string {
|
||||
return _t("Unknown device");
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate account data from a previous origin
|
||||
* Used only for the electron app
|
||||
*/
|
||||
async migrateFromOldOrigin() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import Promise from 'bluebird';
|
||||
import url from 'url';
|
||||
import UAParser from 'ua-parser-js';
|
||||
|
||||
var POKE_RATE_MS = 10 * 60 * 1000; // 10 min
|
||||
const POKE_RATE_MS = 10 * 60 * 1000; // 10 min
|
||||
|
||||
export default class WebPlatform extends VectorBasePlatform {
|
||||
constructor() {
|
||||
@@ -142,6 +142,10 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
setInterval(this.pollForUpdate.bind(this), POKE_RATE_MS);
|
||||
}
|
||||
|
||||
async canSelfUpdate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
pollForUpdate() {
|
||||
return this._getVersion().then((ver) => {
|
||||
if (this.runningVersion === null) {
|
||||
|
||||
64
src/vector/rageshakesetup.js
Normal file
64
src/vector/rageshakesetup.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2018 New Vector 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Separate file that sets up rageshake logging when imported.
|
||||
* This is necessary so that rageshake logging is set up before
|
||||
* anything else. Webpack puts all import statements at the top
|
||||
* of the file before any code, so imports will always be
|
||||
* evaluated first. Other imports can cause other code to be
|
||||
* evaluated (eg. the loglevel library in js-sdk, which if set
|
||||
* up before rageshake causes some js-sdk logging to be missing
|
||||
* from the rageshake.)
|
||||
*/
|
||||
|
||||
import rageshake from "matrix-react-sdk/lib/rageshake/rageshake";
|
||||
import SdkConfig from "matrix-react-sdk/lib/SdkConfig";
|
||||
|
||||
function initRageshake() {
|
||||
rageshake.init().then(() => {
|
||||
console.log("Initialised rageshake.");
|
||||
console.log("To fix line numbers in Chrome: " +
|
||||
"Meatball menu → Settings → Blackboxing → Add /rageshake\\.js$");
|
||||
|
||||
window.addEventListener('beforeunload', (e) => {
|
||||
console.log('riot-web closing');
|
||||
// try to flush the logs to indexeddb
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
rageshake.cleanup();
|
||||
}, (err) => {
|
||||
console.error("Failed to initialise rageshake: " + err);
|
||||
});
|
||||
}
|
||||
|
||||
initRageshake();
|
||||
|
||||
global.mxSendRageshake = function(text, withLogs) {
|
||||
if (withLogs === undefined) withLogs = true;
|
||||
require(['matrix-react-sdk/lib/rageshake/submit-rageshake'], (s) => {
|
||||
s(SdkConfig.get().bug_report_endpoint_url, {
|
||||
userText: text,
|
||||
sendLogs: withLogs,
|
||||
progressCallback: console.log.bind(console),
|
||||
}).then(() => {
|
||||
console.log("Bug report sent!");
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -23,16 +23,16 @@ import qs from 'querystring';
|
||||
export function parseQsFromFragment(location) {
|
||||
// if we have a fragment, it will start with '#', which we need to drop.
|
||||
// (if we don't, this will return '').
|
||||
var fragment = location.hash.substring(1);
|
||||
const fragment = location.hash.substring(1);
|
||||
|
||||
// our fragment may contain a query-param-like section. we need to fish
|
||||
// this out *before* URI-decoding because the params may contain ? and &
|
||||
// characters which are only URI-encoded once.
|
||||
var hashparts = fragment.split('?');
|
||||
const hashparts = fragment.split('?');
|
||||
|
||||
var result = {
|
||||
const result = {
|
||||
location: decodeURIComponent(hashparts[0]),
|
||||
params: {}
|
||||
params: {},
|
||||
};
|
||||
|
||||
if (hashparts.length > 1) {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// ideally these unit tests could be run under nodejs rather than in a browser
|
||||
// via karma, but having two separate test frameworks in the same project
|
||||
// seems confusing
|
||||
var unit_tests = require.context('./unit-tests', true, /\.js$/);
|
||||
const unit_tests = require.context('./unit-tests', true, /\.js$/);
|
||||
unit_tests.keys().forEach(unit_tests);
|
||||
|
||||
var app_tests = require.context('./app-tests', true, /\.jsx?$/);
|
||||
const app_tests = require.context('./app-tests', true, /\.jsx?$/);
|
||||
app_tests.keys().forEach(app_tests);
|
||||
|
||||
@@ -17,40 +17,40 @@ limitations under the License.
|
||||
/* joining.js: tests for the various paths when joining a room */
|
||||
|
||||
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
import Platform from '../../src/vector/platform';
|
||||
import WebPlatform from '../../src/vector/platform/WebPlatform';
|
||||
|
||||
require('skin-sdk');
|
||||
|
||||
var jssdk = require('matrix-js-sdk');
|
||||
const jssdk = require('matrix-js-sdk');
|
||||
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var peg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var PageTypes = require('matrix-react-sdk/lib/PageTypes');
|
||||
var MatrixChat = sdk.getComponent('structures.MatrixChat');
|
||||
var RoomDirectory = sdk.getComponent('structures.RoomDirectory');
|
||||
var RoomPreviewBar = sdk.getComponent('rooms.RoomPreviewBar');
|
||||
var RoomView = sdk.getComponent('structures.RoomView');
|
||||
const sdk = require('matrix-react-sdk');
|
||||
const peg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
const dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
const PageTypes = require('matrix-react-sdk/lib/PageTypes');
|
||||
const MatrixChat = sdk.getComponent('structures.MatrixChat');
|
||||
const RoomDirectory = sdk.getComponent('structures.RoomDirectory');
|
||||
const RoomPreviewBar = sdk.getComponent('rooms.RoomPreviewBar');
|
||||
const RoomView = sdk.getComponent('structures.RoomView');
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var ReactTestUtils = require('react-addons-test-utils');
|
||||
var expect = require('expect');
|
||||
const React = require('react');
|
||||
const ReactDOM = require('react-dom');
|
||||
const ReactTestUtils = require('react-addons-test-utils');
|
||||
const expect = require('expect');
|
||||
import Promise from 'bluebird';
|
||||
|
||||
var test_utils = require('../test-utils');
|
||||
var MockHttpBackend = require('matrix-mock-request');
|
||||
const test_utils = require('../test-utils');
|
||||
const MockHttpBackend = require('matrix-mock-request');
|
||||
|
||||
var HS_URL='http://localhost';
|
||||
var IS_URL='http://localhost';
|
||||
var USER_ID='@me:localhost';
|
||||
var ACCESS_TOKEN='access_token';
|
||||
const HS_URL='http://localhost';
|
||||
const IS_URL='http://localhost';
|
||||
const USER_ID='@me:localhost';
|
||||
const ACCESS_TOKEN='access_token';
|
||||
|
||||
describe('joining a room', function () {
|
||||
describe('over federation', function () {
|
||||
var parentDiv;
|
||||
var httpBackend;
|
||||
var matrixChat;
|
||||
describe('joining a room', function() {
|
||||
describe('over federation', function() {
|
||||
let parentDiv;
|
||||
let httpBackend;
|
||||
let matrixChat;
|
||||
|
||||
beforeEach(function() {
|
||||
test_utils.beforeEach(this);
|
||||
@@ -72,8 +72,8 @@ describe('joining a room', function () {
|
||||
});
|
||||
|
||||
it('should not get stuck at a spinner', function() {
|
||||
var ROOM_ALIAS = '#alias:localhost';
|
||||
var ROOM_ID = '!id:localhost';
|
||||
const ROOM_ALIAS = '#alias:localhost';
|
||||
const ROOM_ID = '!id:localhost';
|
||||
|
||||
httpBackend.when('GET', '/pushrules').respond(200, {});
|
||||
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
||||
@@ -88,9 +88,9 @@ describe('joining a room', function () {
|
||||
localStorage.setItem("mx_access_token", ACCESS_TOKEN );
|
||||
localStorage.setItem("mx_user_id", USER_ID);
|
||||
|
||||
PlatformPeg.set(new Platform());
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
|
||||
var mc = (
|
||||
const mc = (
|
||||
<MatrixChat config={{}}
|
||||
makeRegistrationUrl={()=>{throw new Error("unimplemented");}}
|
||||
initialScreenAfterLogin={{
|
||||
@@ -100,7 +100,7 @@ describe('joining a room', function () {
|
||||
);
|
||||
matrixChat = ReactDOM.render(mc, parentDiv);
|
||||
|
||||
var roomView;
|
||||
let roomView;
|
||||
|
||||
// wait for /sync to happen. This may take some time, as the client
|
||||
// has to initialise indexeddb.
|
||||
@@ -118,11 +118,11 @@ describe('joining a room', function () {
|
||||
}).then(() => {
|
||||
console.log(`${Date.now()} App made requests for directory view; switching to a room.`);
|
||||
|
||||
var roomDir = ReactTestUtils.findRenderedComponentWithType(
|
||||
const roomDir = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, RoomDirectory);
|
||||
|
||||
// enter an alias in the input, and simulate enter
|
||||
var input = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
const input = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
roomDir, 'input');
|
||||
input.value = ROOM_ALIAS;
|
||||
ReactTestUtils.Simulate.change(input);
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
/* loading.js: test the myriad paths we have for loading the application */
|
||||
|
||||
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
import Platform from '../../src/vector/platform';
|
||||
import WebPlatform from '../../src/vector/platform/WebPlatform';
|
||||
|
||||
import 'skin-sdk';
|
||||
|
||||
@@ -40,10 +40,21 @@ import * as test_utils from '../test-utils';
|
||||
import MockHttpBackend from 'matrix-mock-request';
|
||||
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
|
||||
|
||||
var DEFAULT_HS_URL='http://my_server';
|
||||
var DEFAULT_IS_URL='http://my_is';
|
||||
const DEFAULT_HS_URL='http://my_server';
|
||||
const DEFAULT_IS_URL='http://my_is';
|
||||
|
||||
describe('loading:', function () {
|
||||
expect.extend({
|
||||
toStartWith(prefix) {
|
||||
expect.assert(
|
||||
this.actual.startsWith(prefix),
|
||||
'expected %s to start with %s',
|
||||
this.actual, prefix,
|
||||
);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
describe('loading:', function() {
|
||||
let parentDiv;
|
||||
let httpBackend;
|
||||
|
||||
@@ -74,7 +85,7 @@ describe('loading:', function () {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
afterEach(async function() {
|
||||
console.log(`${Date.now()}: loading: afterEach`);
|
||||
if (parentDiv) {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
@@ -112,12 +123,12 @@ describe('loading:', function () {
|
||||
toString: function() { return this.search + this.hash; },
|
||||
};
|
||||
|
||||
let tokenLoginCompleteDefer = Promise.defer();
|
||||
const tokenLoginCompleteDefer = Promise.defer();
|
||||
tokenLoginCompletePromise = tokenLoginCompleteDefer.promise;
|
||||
|
||||
function onNewScreen(screen) {
|
||||
console.log(Date.now() + " newscreen "+screen);
|
||||
var hash = '#/' + screen;
|
||||
const hash = '#/' + screen;
|
||||
windowLocation.hash = hash;
|
||||
console.log(Date.now() + " browser URI now "+ windowLocation);
|
||||
}
|
||||
@@ -129,7 +140,7 @@ describe('loading:', function () {
|
||||
return {
|
||||
screen: fragparts.location.substring(1),
|
||||
params: fragparts.params,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const MatrixChat = sdk.getComponent('structures.MatrixChat');
|
||||
@@ -140,9 +151,9 @@ describe('loading:', function () {
|
||||
default_is_url: DEFAULT_IS_URL,
|
||||
}, opts.config || {});
|
||||
|
||||
PlatformPeg.set(new Platform());
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
|
||||
var params = parseQs(windowLocation);
|
||||
const params = parseQs(windowLocation);
|
||||
matrixChat = ReactDOM.render(
|
||||
<MatrixChat
|
||||
onNewScreen={onNewScreen}
|
||||
@@ -153,7 +164,7 @@ describe('loading:', function () {
|
||||
onTokenLoginCompleted={() => tokenLoginCompleteDefer.resolve()}
|
||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
|
||||
makeRegistrationUrl={() => {throw new Error('Not implemented');}}
|
||||
/>, parentDiv
|
||||
/>, parentDiv,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -179,7 +190,7 @@ describe('loading:', function () {
|
||||
}
|
||||
|
||||
describe("Clean load with no stored credentials:", function() {
|
||||
it('gives a login panel by default', function (done) {
|
||||
it('gives a login panel by default', function(done) {
|
||||
loadApp();
|
||||
|
||||
Promise.delay(1).then(() => {
|
||||
@@ -257,7 +268,6 @@ describe('loading:', function () {
|
||||
}
|
||||
return completeLogin(matrixChat);
|
||||
}).then(() => {
|
||||
|
||||
// once the sync completes, we should have a room view
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.HomePage'));
|
||||
@@ -356,7 +366,7 @@ describe('loading:', function () {
|
||||
});
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
return awaitSyncingSpinner(matrixChat)
|
||||
return awaitSyncingSpinner(matrixChat);
|
||||
}).then(() => {
|
||||
// we got a sync spinner - let the sync complete
|
||||
return expectAndAwaitSync();
|
||||
@@ -380,7 +390,7 @@ describe('loading:', function () {
|
||||
it('shows a login view', function() {
|
||||
// we expect a single <Login> component
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.login.Login')
|
||||
matrixChat, sdk.getComponent('structures.login.Login'),
|
||||
);
|
||||
|
||||
// the only outstanding request should be a GET /login
|
||||
@@ -408,7 +418,7 @@ describe('loading:', function () {
|
||||
});
|
||||
|
||||
describe('Guest auto-registration:', function() {
|
||||
it('shows a home page by default', function (done) {
|
||||
it('shows a home page by default', function(done) {
|
||||
loadApp();
|
||||
|
||||
Promise.delay(1).then(() => {
|
||||
@@ -438,10 +448,7 @@ describe('loading:', function () {
|
||||
}).done(done, done);
|
||||
});
|
||||
|
||||
it('uses the last known homeserver to register with', function (done) {
|
||||
localStorage.setItem("mx_hs_url", "https://homeserver" );
|
||||
localStorage.setItem("mx_is_url", "https://idserver" );
|
||||
|
||||
it('uses the default homeserver to register with', function(done) {
|
||||
loadApp();
|
||||
|
||||
Promise.delay(1).then(() => {
|
||||
@@ -450,7 +457,7 @@ describe('loading:', function () {
|
||||
assertAtLoadingSpinner(matrixChat);
|
||||
|
||||
httpBackend.when('POST', '/register').check(function(req) {
|
||||
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
|
||||
expect(req.path).toStartWith(DEFAULT_HS_URL);
|
||||
expect(req.queryParams.kind).toEqual('guest');
|
||||
}).respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
@@ -463,21 +470,21 @@ describe('loading:', function () {
|
||||
}).then(() => {
|
||||
return expectAndAwaitSync();
|
||||
}).then((req) => {
|
||||
expect(req.path).toMatch(new RegExp("^https://homeserver/"));
|
||||
expect(req.path).toStartWith(DEFAULT_HS_URL);
|
||||
|
||||
// once the sync completes, we should have a home page
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.HomePage'));
|
||||
expect(windowLocation.hash).toEqual("#/home");
|
||||
expect(MatrixClientPeg.get().baseUrl).toEqual("https://homeserver");
|
||||
expect(MatrixClientPeg.get().idBaseUrl).toEqual("https://idserver");
|
||||
expect(MatrixClientPeg.get().baseUrl).toEqual(DEFAULT_HS_URL);
|
||||
expect(MatrixClientPeg.get().idBaseUrl).toEqual(DEFAULT_IS_URL);
|
||||
}).done(done, done);
|
||||
});
|
||||
|
||||
it('shows a room view if we followed a room link', function(done) {
|
||||
loadApp({
|
||||
uriFragment: "#/room/!room:id"
|
||||
uriFragment: "#/room/!room:id",
|
||||
});
|
||||
Promise.delay(1).then(() => {
|
||||
// at this point, we're trying to do a guest registration;
|
||||
@@ -547,7 +554,7 @@ describe('loading:', function () {
|
||||
|
||||
// we expect a single <Login> component
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('structures.login.Login')
|
||||
matrixChat, sdk.getComponent('structures.login.Login'),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -580,7 +587,7 @@ describe('loading:', function () {
|
||||
});
|
||||
|
||||
describe('Token login:', function() {
|
||||
it('logs in successfully', function (done) {
|
||||
it('logs in successfully', function(done) {
|
||||
loadApp({
|
||||
queryString: "?loginToken=secretToken&homeserver=https%3A%2F%2Fhomeserver&identityServer=https%3A%2F%2Fidserver",
|
||||
});
|
||||
@@ -658,7 +665,7 @@ describe('loading:', function () {
|
||||
|
||||
// assert that we are on the loading page
|
||||
function assertAtLoadingSpinner(matrixChat) {
|
||||
var domComponent = ReactDOM.findDOMNode(matrixChat);
|
||||
const domComponent = ReactDOM.findDOMNode(matrixChat);
|
||||
expect(domComponent.className).toEqual("mx_MatrixChat_splash");
|
||||
|
||||
// just the spinner
|
||||
@@ -697,12 +704,12 @@ function awaitSyncingSpinner(matrixChat, retryLimit, retryCount) {
|
||||
}
|
||||
|
||||
function assertAtSyncingSpinner(matrixChat) {
|
||||
var domComponent = ReactDOM.findDOMNode(matrixChat);
|
||||
const domComponent = ReactDOM.findDOMNode(matrixChat);
|
||||
expect(domComponent.className).toEqual("mx_MatrixChat_splash");
|
||||
|
||||
ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, sdk.getComponent('elements.Spinner'));
|
||||
var logoutLink = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
const logoutLink = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
matrixChat, 'a');
|
||||
expect(logoutLink.text).toEqual("Logout");
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
* Skins the react-sdk with the vector components
|
||||
*/
|
||||
|
||||
var sdk = require('matrix-react-sdk');
|
||||
const sdk = require('matrix-react-sdk');
|
||||
sdk.loadSkin(require('../src/component-index'));
|
||||
|
||||
@@ -8,7 +8,7 @@ import Promise from 'bluebird';
|
||||
* @param {Mocha.Context} context The test context
|
||||
*/
|
||||
export function beforeEach(context) {
|
||||
var desc = context.currentTest.fullTitle();
|
||||
const desc = context.currentTest.fullTitle();
|
||||
console.log();
|
||||
console.log(desc);
|
||||
console.log(new Array(1 + desc.length).join("="));
|
||||
@@ -22,7 +22,7 @@ export function beforeEach(context) {
|
||||
* returns true if the current environment supports webrtc
|
||||
*/
|
||||
export function browserSupportsWebRTC() {
|
||||
var n = global.window.navigator;
|
||||
const n = global.window.navigator;
|
||||
return n.getUserMedia || n.webkitGetUserMedia ||
|
||||
n.mozGetUserMedia;
|
||||
}
|
||||
|
||||
@@ -15,15 +15,6 @@ module.exports = {
|
||||
|
||||
"mobileguide": "./src/vector/mobile_guide/index.js",
|
||||
|
||||
// We ship olm.js as a separate lump of javascript. This makes it get
|
||||
// loaded via a separate <script/> tag in index.html (which loads it
|
||||
// into the browser global `Olm`, where js-sdk expects to find it).
|
||||
//
|
||||
// (we should probably make js-sdk load it asynchronously at some
|
||||
// point, so that it doesn't block the pageload, but that is a separate
|
||||
// problem)
|
||||
"olm": "./src/vector/olm-loader.js",
|
||||
|
||||
// CSS themes
|
||||
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.scss",
|
||||
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.scss",
|
||||
@@ -117,11 +108,6 @@ module.exports = {
|
||||
"matrix-js-sdk": path.resolve('./node_modules/matrix-js-sdk'),
|
||||
},
|
||||
},
|
||||
externals: {
|
||||
// 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({
|
||||
'process.env': {
|
||||
@@ -171,12 +157,3 @@ module.exports = {
|
||||
inline: false,
|
||||
},
|
||||
};
|
||||
|
||||
// olm is an optional dependency. Ignore it if it's not installed, to avoid a
|
||||
// scary-looking error.
|
||||
try {
|
||||
require('olm');
|
||||
} catch (e) {
|
||||
console.log("Olm is not installed; not shipping it");
|
||||
delete(module.exports.entry["olm"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user