mirror of
https://github.com/element-hq/element-web
synced 2024-11-26 03:05:51 +03:00
Merge pull request #8003 from vector-im/develop
Merge develop into experimental
This commit is contained in:
commit
373c587ef8
22 changed files with 861 additions and 179 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,3 +1,28 @@
|
|||
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)
|
||||
|
|
|
@ -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,7 +2,7 @@
|
|||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "src/electron-main.js",
|
||||
"version": "0.17.7",
|
||||
"version": "0.17.8",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"dependencies": {
|
||||
|
|
|
@ -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);
|
11
package.json
11
package.json
|
@ -2,7 +2,7 @@
|
|||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "electron_app/src/electron-main.js",
|
||||
"version": "0.17.7",
|
||||
"version": "0.17.8",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
|
@ -70,8 +70,8 @@
|
|||
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#b302279",
|
||||
"gfm.css": "^1.1.2",
|
||||
"highlight.js": "^9.13.1",
|
||||
"matrix-js-sdk": "0.14.1",
|
||||
"matrix-react-sdk": "0.14.6",
|
||||
"matrix-js-sdk": "0.14.2",
|
||||
"matrix-react-sdk": "0.14.7",
|
||||
"modernizr": "^3.6.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.6.0",
|
||||
|
@ -150,14 +150,15 @@
|
|||
},
|
||||
"build": {
|
||||
"appId": "im.riot.app",
|
||||
"electronVersion": "3.0.5",
|
||||
"electronVersion": "3.0.10",
|
||||
"files": [
|
||||
"node_modules/**",
|
||||
"src/**",
|
||||
"img/**"
|
||||
],
|
||||
"extraResources": [
|
||||
"webapp/**/*"
|
||||
"webapp/**/*",
|
||||
"origin_migrator/**/*"
|
||||
],
|
||||
"linux": {
|
||||
"target": "deb",
|
||||
|
|
|
@ -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": "रायट अनुवादकों के लिए समन्वय"
|
||||
}
|
||||
|
|
|
@ -47,7 +47,9 @@ import * as languageHandler 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 from "matrix-react-sdk/lib/settings/SettingsStore";
|
||||
|
@ -226,8 +228,23 @@ async function loadApp() {
|
|||
const fragparts = parseQsFromFragment(window.location);
|
||||
const params = parseQs(window.location);
|
||||
|
||||
// set the platform for react sdk (our Platform object automatically picks the right one)
|
||||
PlatformPeg.set(new Platform());
|
||||
// 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.
|
||||
|
|
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -26,10 +26,13 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import rageshake from "matrix-react-sdk/lib/rageshake/rageshake";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
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.");
|
||||
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');
|
||||
|
@ -44,3 +47,18 @@ function initRageshake() {
|
|||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ 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');
|
||||
|
||||
|
@ -88,7 +88,7 @@ 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());
|
||||
|
||||
const mc = (
|
||||
<MatrixChat config={{}}
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -43,6 +43,17 @@ import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
|
|||
const DEFAULT_HS_URL='http://my_server';
|
||||
const DEFAULT_IS_URL='http://my_is';
|
||||
|
||||
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;
|
||||
|
@ -140,7 +151,7 @@ describe('loading:', function() {
|
|||
default_is_url: DEFAULT_IS_URL,
|
||||
}, opts.config || {});
|
||||
|
||||
PlatformPeg.set(new Platform());
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
|
||||
const params = parseQs(windowLocation);
|
||||
matrixChat = ReactDOM.render(
|
||||
|
@ -437,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(() => {
|
||||
|
@ -449,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",
|
||||
|
@ -462,15 +470,15 @@ 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);
|
||||
});
|
||||
|
||||
|
|
|
@ -109,11 +109,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': {
|
||||
|
|
Loading…
Reference in a new issue