Compare commits
153 Commits
v1.5.10
...
neilalexan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e051365b1 | ||
|
|
13251a4d6c | ||
|
|
b28708fbf3 | ||
|
|
46d659a936 | ||
|
|
9e19d910c1 | ||
|
|
92fe2b8935 | ||
|
|
7e15090364 | ||
|
|
d83c475bb7 | ||
|
|
b4c7d54fc0 | ||
|
|
e10d5eb0be | ||
|
|
ff6c4ac837 | ||
|
|
bcba891df1 | ||
|
|
708a7146d5 | ||
|
|
e4c67f39a6 | ||
|
|
a5ba31e127 | ||
|
|
f6aceba566 | ||
|
|
35bcc152ea | ||
|
|
ec45bb7976 | ||
|
|
b0fb043c2b | ||
|
|
4828869515 | ||
|
|
671315c36e | ||
|
|
9994c9ea65 | ||
|
|
9328519c29 | ||
|
|
330aa285e6 | ||
|
|
5c3f11a7c7 | ||
|
|
cdd32590fe | ||
|
|
52641c5674 | ||
|
|
6f43a14c43 | ||
|
|
c4f3d39d42 | ||
|
|
c84a01310f | ||
|
|
61bf0d5447 | ||
|
|
8002bc66d7 | ||
|
|
c3553a2649 | ||
|
|
d80285d971 | ||
|
|
3d74d336bf | ||
|
|
d4a578542f | ||
|
|
3dadc2d1d6 | ||
|
|
149098921f | ||
|
|
34817a0044 | ||
|
|
87e32baefa | ||
|
|
1f1f0f1264 | ||
|
|
789c52a9e1 | ||
|
|
32aa897ef8 | ||
|
|
bf13ec2285 | ||
|
|
4d1f969c4d | ||
|
|
b7384a9a56 | ||
|
|
6ec5dfc5f6 | ||
|
|
b0f71bc990 | ||
|
|
ecdfe24fa8 | ||
|
|
0688641554 | ||
|
|
38644d9d30 | ||
|
|
a751dd63fa | ||
|
|
7da915b1b0 | ||
|
|
c20028782c | ||
|
|
e2de059a78 | ||
|
|
a16faa2610 | ||
|
|
71f2f0e7c9 | ||
|
|
f5bd5a345e | ||
|
|
3ffce87d3a | ||
|
|
207677f1af | ||
|
|
d67d640ef9 | ||
|
|
60e5b5f94e | ||
|
|
959a6e91a0 | ||
|
|
872a7a74fb | ||
|
|
4548831ac5 | ||
|
|
d88f9ed2c0 | ||
|
|
6e68c106f7 | ||
|
|
2507073c20 | ||
|
|
096831c9df | ||
|
|
c26396d693 | ||
|
|
b52b0f525f | ||
|
|
0935ccd737 | ||
|
|
39ee5d77bd | ||
|
|
48c8f16a4c | ||
|
|
adc329c67f | ||
|
|
2a5438d636 | ||
|
|
706834939f | ||
|
|
4525ead1df | ||
|
|
1f42f33102 | ||
|
|
0e3e1f3a9d | ||
|
|
4399af4189 | ||
|
|
2514f11471 | ||
|
|
0546c74b3b | ||
|
|
340b6bf0d2 | ||
|
|
bb6afd7c30 | ||
|
|
09ed0e781a | ||
|
|
94ceb5a46b | ||
|
|
d077165858 | ||
|
|
43b70bf720 | ||
|
|
0c4048484e | ||
|
|
8710f7d196 | ||
|
|
620414187f | ||
|
|
e71dcacd2d | ||
|
|
178c61dbf7 | ||
|
|
c197661be0 | ||
|
|
fb656ce8bd | ||
|
|
749d7e8e4c | ||
|
|
e238fead7c | ||
|
|
0d9be8c255 | ||
|
|
0e37aa9f69 | ||
|
|
22bc98c8bc | ||
|
|
769c8b1f27 | ||
|
|
1043b0eb11 | ||
|
|
6bc79ad54a | ||
|
|
c70bfe1f91 | ||
|
|
a4512ffa6d | ||
|
|
d2635bd282 | ||
|
|
bb5aa94707 | ||
|
|
03f63397ff | ||
|
|
b29a3721d2 | ||
|
|
00244d5428 | ||
|
|
a4d01a5f04 | ||
|
|
4fced076b7 | ||
|
|
9b9d585af0 | ||
|
|
76430a9c77 | ||
|
|
dacbc4dc6d | ||
|
|
7c84e406ac | ||
|
|
096a828e44 | ||
|
|
4916ed0870 | ||
|
|
d7893e1d58 | ||
|
|
22dc0ba772 | ||
|
|
37e17133bc | ||
|
|
cf52ccad67 | ||
|
|
462fa91938 | ||
|
|
836236ea9b | ||
|
|
3fcc3c8bfd | ||
|
|
81578fc3cf | ||
|
|
bdb395aa41 | ||
|
|
420537a64d | ||
|
|
af0b57bcda | ||
|
|
9d1dbf4ce9 | ||
|
|
5117efaf98 | ||
|
|
b8766d5fb4 | ||
|
|
421cd41279 | ||
|
|
f98ca56b5e | ||
|
|
e3cbcc8fbd | ||
|
|
37c4da5614 | ||
|
|
af17ca11c7 | ||
|
|
b571ff766c | ||
|
|
9fb807772b | ||
|
|
aa56edddd8 | ||
|
|
65687dfd6f | ||
|
|
67eb261ddb | ||
|
|
5fcb67d646 | ||
|
|
82652de078 | ||
|
|
106ed43f36 | ||
|
|
de71e36843 | ||
|
|
120d36d70b | ||
|
|
d6587b9094 | ||
|
|
5cc0cef06c | ||
|
|
349d3e3d47 | ||
|
|
8a41f956f6 | ||
|
|
80411d38f7 |
@@ -1,82 +0,0 @@
|
||||
steps:
|
||||
- label: ":eslint: JS Lint"
|
||||
command:
|
||||
- "echo '--- Fetching Dependencies'"
|
||||
- "./scripts/fetch-develop.deps.sh --depth 1"
|
||||
- "yarn install"
|
||||
- "yarn lint:js"
|
||||
plugins:
|
||||
- docker#v3.0.1:
|
||||
image: "node:12"
|
||||
|
||||
# This layer doesn't have a TypeScript linter. This comment is to remind TravisR to fix that.
|
||||
# - label: ":eslint: TS Lint"
|
||||
# command:
|
||||
# - "echo '--- Install js-sdk'"
|
||||
# - "./scripts/ci/install-deps.sh"
|
||||
# - "yarn lint:ts"
|
||||
# plugins:
|
||||
# - docker#v3.0.1:
|
||||
# image: "node:12"
|
||||
|
||||
- label: ":eslint: Types Lint"
|
||||
command:
|
||||
- "yarn install"
|
||||
- "yarn lint:types"
|
||||
plugins:
|
||||
- docker#v3.0.1:
|
||||
image: "node:12"
|
||||
|
||||
- label: "🛠 Build"
|
||||
command:
|
||||
- "echo '--- Fetching Dependencies'"
|
||||
- "./scripts/fetch-develop.deps.sh --depth 1"
|
||||
- "yarn install"
|
||||
- "echo '+++ Building Project'"
|
||||
- "yarn build"
|
||||
plugins:
|
||||
- docker#v3.0.1:
|
||||
image: "node:12"
|
||||
|
||||
- label: ":jest: Tests"
|
||||
agents:
|
||||
# We use a medium sized instance instead of the normal small ones because
|
||||
# webpack loves to gorge itself on resources.
|
||||
queue: "medium"
|
||||
command:
|
||||
- "echo '--- Fetching Dependencies'"
|
||||
- "./scripts/fetch-develop.deps.sh --depth 1"
|
||||
- "yarn install"
|
||||
- "yarn build:genfiles" # We have to build the app to make sure the autogenned files are present
|
||||
- "echo '+++ Running Tests'"
|
||||
- "yarn test"
|
||||
plugins:
|
||||
- docker#v3.0.1:
|
||||
image: "node:10"
|
||||
|
||||
- label: "🌐 i18n"
|
||||
command:
|
||||
- "echo '--- Fetching Dependencies'"
|
||||
- "./scripts/fetch-develop.deps.sh --depth 1"
|
||||
- "yarn install"
|
||||
- "echo '+++ Testing i18n output'"
|
||||
- "yarn diff-i18n"
|
||||
plugins:
|
||||
- docker#v3.0.1:
|
||||
image: "node:10"
|
||||
|
||||
- wait: ~ # this wait is to perform deploy to /develop only if all other steps passed
|
||||
continue_on_failure: false
|
||||
|
||||
- label: ":hammer: Package"
|
||||
command:
|
||||
- "echo '--- Fetching Dependencies'"
|
||||
- "./scripts/fetch-develop.deps.sh --depth 1"
|
||||
- "yarn install"
|
||||
- "echo '+++ Packaging'"
|
||||
- "./scripts/ci_package.sh"
|
||||
branches: "develop"
|
||||
artifact_paths: "dist/riot-*.tar.gz"
|
||||
plugins:
|
||||
- docker#v3.0.1:
|
||||
image: "node:10"
|
||||
@@ -5,20 +5,30 @@
|
||||
"setClasses"
|
||||
],
|
||||
"feature-detects": [
|
||||
"test/css/animations",
|
||||
"test/css/displaytable",
|
||||
"test/css/filters",
|
||||
"test/css/flexbox",
|
||||
"test/es5/specification",
|
||||
"test/css/objectfit",
|
||||
"test/storage/localstorage",
|
||||
|
||||
"test/es5/date",
|
||||
"test/es5/function",
|
||||
"test/es5/object",
|
||||
"test/es5/undefined",
|
||||
|
||||
"test/es6/array",
|
||||
"test/es6/collections",
|
||||
"test/es6/promises",
|
||||
"test/es6/string",
|
||||
|
||||
"test/svg",
|
||||
"test/svg/asimg",
|
||||
"test/svg/filters",
|
||||
"test/css/animations",
|
||||
"test/css/filters",
|
||||
|
||||
"test/cors",
|
||||
"test/iframe/sandbox",
|
||||
"test/json",
|
||||
"test/network/fetch",
|
||||
"test/iframe/sandbox"
|
||||
"test/storage/localstorage"
|
||||
]
|
||||
}
|
||||
|
||||
77
CHANGELOG.md
77
CHANGELOG.md
@@ -1,3 +1,80 @@
|
||||
Changes in [1.5.13](https://github.com/vector-im/riot-web/releases/tag/v1.5.13) (2020-03-17)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v1.5.13-rc.1...v1.5.13)
|
||||
|
||||
* Upgrade to JS SDK 5.1.1 and React SDK 2.2.3
|
||||
|
||||
Changes in [1.5.13-rc.1](https://github.com/vector-im/riot-web/releases/tag/v1.5.13-rc.1) (2020-03-11)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v1.5.12...v1.5.13-rc.1)
|
||||
|
||||
* Update from Weblate
|
||||
[\#12688](https://github.com/vector-im/riot-web/pull/12688)
|
||||
* Fix Docker image version for develop builds
|
||||
[\#12670](https://github.com/vector-im/riot-web/pull/12670)
|
||||
* docker: optimize custom sdk builds
|
||||
[\#12612](https://github.com/vector-im/riot-web/pull/12612)
|
||||
* riot-desktop open SSO in browser so user doesn't have to auth twice
|
||||
[\#12590](https://github.com/vector-im/riot-web/pull/12590)
|
||||
* Fix SSO flows for electron 8.0.2 by re-breaking will-navigate
|
||||
[\#12585](https://github.com/vector-im/riot-web/pull/12585)
|
||||
* index.html: Place noscript on top of the page
|
||||
[\#12563](https://github.com/vector-im/riot-web/pull/12563)
|
||||
* Remove will-navigate comment after Electron fix
|
||||
[\#12561](https://github.com/vector-im/riot-web/pull/12561)
|
||||
* Update loading test for JS SDK IDB change
|
||||
[\#12552](https://github.com/vector-im/riot-web/pull/12552)
|
||||
* Upgrade deps
|
||||
[\#12528](https://github.com/vector-im/riot-web/pull/12528)
|
||||
|
||||
Changes in [1.5.12](https://github.com/vector-im/riot-web/releases/tag/v1.5.12) (2020-03-04)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v1.5.11...v1.5.12)
|
||||
|
||||
* Upgrade to React SDK 2.2.1
|
||||
* Revert to Electron 7.1.12 to fix Arch Linux tray icon
|
||||
* Fix image download links so they open in a new tab
|
||||
|
||||
Changes in [1.5.11](https://github.com/vector-im/riot-web/releases/tag/v1.5.11) (2020-03-02)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v1.5.11-rc.1...v1.5.11)
|
||||
|
||||
* Upgrade to JS SDK 5.1.0 and React SDK 2.2.0
|
||||
* Fix SSO flows for Electron 8.0.2 by disabling will-navigate
|
||||
[\#12585](https://github.com/vector-im/riot-web/pull/12585)
|
||||
|
||||
Changes in [1.5.11-rc.1](https://github.com/vector-im/riot-web/releases/tag/v1.5.11-rc.1) (2020-02-26)
|
||||
======================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v1.5.10...v1.5.11-rc.1)
|
||||
|
||||
* Upgrade to JS SDK 5.1.0-rc.1 and React SDK 2.2.0-rc.1
|
||||
* Change Windows signing to warning when missing token
|
||||
[\#12523](https://github.com/vector-im/riot-web/pull/12523)
|
||||
* Modernizr remove t3st/es6/contains
|
||||
[\#12524](https://github.com/vector-im/riot-web/pull/12524)
|
||||
* Switch out any eval-using Modernizr rules
|
||||
[\#12519](https://github.com/vector-im/riot-web/pull/12519)
|
||||
* Update from Weblate
|
||||
[\#12522](https://github.com/vector-im/riot-web/pull/12522)
|
||||
* Notify electron of language changes
|
||||
[\#12487](https://github.com/vector-im/riot-web/pull/12487)
|
||||
* Relax macOS notarisation check to print a warning
|
||||
[\#12503](https://github.com/vector-im/riot-web/pull/12503)
|
||||
* Clarify supported tier means desktop OSes
|
||||
[\#12486](https://github.com/vector-im/riot-web/pull/12486)
|
||||
* Use noreferrer in addition to noopener for edge case browsers
|
||||
[\#12477](https://github.com/vector-im/riot-web/pull/12477)
|
||||
* Document start / end composer shortcuts
|
||||
[\#12466](https://github.com/vector-im/riot-web/pull/12466)
|
||||
* Update from Weblate
|
||||
[\#12480](https://github.com/vector-im/riot-web/pull/12480)
|
||||
* Remove buildkite pipeline
|
||||
[\#12464](https://github.com/vector-im/riot-web/pull/12464)
|
||||
* Remove exec so release script continues
|
||||
[\#12435](https://github.com/vector-im/riot-web/pull/12435)
|
||||
* Use Persistent Storage where possible
|
||||
[\#12425](https://github.com/vector-im/riot-web/pull/12425)
|
||||
|
||||
Changes in [1.5.10](https://github.com/vector-im/riot-web/releases/tag/v1.5.10) (2020-02-19)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v1.5.9...v1.5.10)
|
||||
|
||||
@@ -11,8 +11,10 @@ Riot has several tiers of support for different environments:
|
||||
|
||||
* Supported
|
||||
* Definition: Issues **actively triaged**, regressions **block** the release
|
||||
* Last 2 major versions of Chrome, Firefox, and Safari
|
||||
* Latest release of official Riot Desktop app on macOS, Windows, and Linux versions that are actively supported by the OS vendor and receive security updates
|
||||
* Last 2 major versions of Chrome, Firefox, and Safari on desktop OSes
|
||||
* Latest release of official Riot Desktop app on desktop OSes
|
||||
* Desktop OSes means macOS, Windows, and Linux versions for desktop devices
|
||||
that are actively supported by the OS vendor and receive security updates
|
||||
* Experimental
|
||||
* Definition: Issues **accepted**, regressions **do not block** the release
|
||||
* Riot as an installed PWA via current stable version of Chrome, Firefox, and Safari
|
||||
|
||||
@@ -10,5 +10,6 @@ The modifier is <kbd>Ctrl</kbd> on Windows & Linux and <kbd>⌘</kbd> on Mac.
|
||||
- <kbd>↑</kbd>/<kbd>↓</kbd> - next/prev room when focus in room list
|
||||
- <kbd>Alt</kbd>+<kbd>↑</kbd>/<kbd>↓</kbd> - resend previous messages when the composer is in focus
|
||||
- <kbd>PageUp</kbd>/<kbd>PageDown</kbd> - scroll timeline up/down
|
||||
- <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>Home</kbd>/<kbd>End</kbd> - jump to timeline start/end
|
||||
- <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>Home</kbd>/<kbd>End</kbd> - jump to
|
||||
start/end of the composer when focused, otherwise jump to timeline start/end
|
||||
- <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>`</kbd> - toggle the top left menu
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "src/electron-main.js",
|
||||
"version": "1.5.10",
|
||||
"version": "1.5.13",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"dependencies": {
|
||||
|
||||
@@ -35,6 +35,7 @@ const tray = require('./tray');
|
||||
const vectorMenu = require('./vectormenu');
|
||||
const webContentsHandler = require('./webcontents-handler');
|
||||
const updater = require('./updater');
|
||||
const protocolInit = require('./protocol');
|
||||
|
||||
const windowStateKeeper = require('electron-window-state');
|
||||
const Store = require('electron-store');
|
||||
@@ -427,6 +428,9 @@ if (!gotLock) {
|
||||
app.exit();
|
||||
}
|
||||
|
||||
// do this after we know we are the primary instance of the app
|
||||
protocolInit();
|
||||
|
||||
const launcher = new AutoLaunch({
|
||||
name: vectorConfig.brand || 'Riot',
|
||||
isHidden: true,
|
||||
|
||||
53
electron_app/src/protocol.js
Normal file
53
electron_app/src/protocol.js
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 {app} = require('electron');
|
||||
|
||||
const processUrl = (url) => {
|
||||
if (!global.mainWindow) return;
|
||||
console.log("Handling link: ", url);
|
||||
global.mainWindow.loadURL(url.replace("riot://", "vector://"));
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
// get all args except `hidden` as it'd mean the app would not get focused
|
||||
// XXX: passing args to protocol handlers only works on Windows,
|
||||
// so unpackaged deep-linking and --profile passing won't work on Mac/Linux
|
||||
const args = process.argv.slice(1).filter(arg => arg !== "--hidden" && arg !== "-hidden");
|
||||
if (app.isPackaged) {
|
||||
app.setAsDefaultProtocolClient('riot', process.execPath, args);
|
||||
} else if (process.platform === 'win32') { // on Mac/Linux this would just cause the electron binary to open
|
||||
// special handler for running without being packaged, e.g `electron .` by passing our app path to electron
|
||||
app.setAsDefaultProtocolClient('riot', process.execPath, [app.getAppPath(), ...args]);
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
// Protocol handler for macos
|
||||
app.on('open-url', function(ev, url) {
|
||||
ev.preventDefault();
|
||||
processUrl(url);
|
||||
});
|
||||
} else {
|
||||
// Protocol handler for win32/Linux
|
||||
app.on('second-instance', (ev, commandLine) => {
|
||||
const url = commandLine[commandLine.length - 1];
|
||||
if (!url.startsWith("riot://")) return;
|
||||
processUrl(url);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -174,19 +174,10 @@ function onEditableContextMenu(ev, params) {
|
||||
|
||||
module.exports = (webContents) => {
|
||||
webContents.on('new-window', onWindowOrNavigate);
|
||||
// XXX: The below now does absolutely nothing because of
|
||||
// https://github.com/electron/electron/issues/8841
|
||||
// Whilst this isn't a security issue since without
|
||||
// node integration and with the sandbox, it should be
|
||||
// no worse than opening the site in Chrome, it obviously
|
||||
// means the user has to restart Riot to make it usable
|
||||
// again (often unintuitive because it minimises to the
|
||||
// system tray). We therefore need to be vigilant about
|
||||
// putting target="_blank" on links in Riot (although
|
||||
// we should generally be doing this anyway since links
|
||||
// navigating you away from Riot in the browser is
|
||||
// also annoying).
|
||||
webContents.on('will-navigate', onWindowOrNavigate);
|
||||
webContents.on('will-navigate', (ev, target) => {
|
||||
if (target.startsWith("vector://")) return;
|
||||
return onWindowOrNavigate(ev, target);
|
||||
});
|
||||
|
||||
webContents.on('context-menu', function(ev, params) {
|
||||
if (params.linkURL || params.srcURL) {
|
||||
|
||||
20
package.json
20
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "electron_app/src/electron-main.js",
|
||||
"version": "1.5.10",
|
||||
"version": "1.5.13",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -50,6 +50,8 @@
|
||||
"install:electron": "electron-builder install-app-deps",
|
||||
"dist": "scripts/package.sh",
|
||||
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n reskindex,reskindex-react,res,riot-js \"yarn reskindex:watch\" \"yarn reskindex:watch-react\" \"yarn start:res\" \"yarn start:js\"",
|
||||
"build:p2p": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n reskindex,reskindex-react,res,riot-js \"yarn reskindex\" \"node scripts/yarn-sub.js matrix-react-sdk reskindex\" \"node scripts/copy-res.js\" \"yarn build:p2p-bundle\"",
|
||||
"build:p2p-bundle": "cross-env webpack -p --progress --bail --mode development",
|
||||
"start:res": "node scripts/copy-res.js -w",
|
||||
"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",
|
||||
"electron": "yarn build && yarn install:electron && electron .",
|
||||
@@ -66,14 +68,16 @@
|
||||
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
||||
"gfm.css": "^1.1.2",
|
||||
"highlight.js": "^9.13.1",
|
||||
"matrix-js-sdk": "5.0.1",
|
||||
"matrix-react-sdk": "2.1.1",
|
||||
"localforage": "^1.7.3",
|
||||
"matrix-js-sdk": "5.1.1",
|
||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#p2p",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
|
||||
"postcss-easings": "^2.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"sanitize-html": "^1.19.1",
|
||||
"sql.js": "github:neilalexander/sql.js#252a72bf57b0538cbd49bbd6f70af71e516966ae",
|
||||
"ua-parser-js": "^0.7.19",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
@@ -188,7 +192,15 @@
|
||||
"output": "electron_app/dist",
|
||||
"app": "electron_app"
|
||||
},
|
||||
"afterSign": "scripts/electron_afterSign.js"
|
||||
"afterSign": "scripts/electron_afterSign.js",
|
||||
"protocols": [
|
||||
{
|
||||
"name": "riot",
|
||||
"schemes": [
|
||||
"riot"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"modulePathIgnorePatterns": [
|
||||
|
||||
@@ -20,6 +20,7 @@ cd `dirname $0`
|
||||
|
||||
for i in matrix-js-sdk matrix-react-sdk
|
||||
do
|
||||
echo "Checking version of $i..."
|
||||
depver=`cat package.json | jq -r .dependencies[\"$i\"]`
|
||||
latestver=`yarn info -s $i dist-tags.next`
|
||||
if [ "$depver" != "$latestver" ]
|
||||
@@ -61,7 +62,7 @@ git commit package.json -m "$tag"
|
||||
|
||||
cd ..
|
||||
|
||||
exec ./node_modules/matrix-js-sdk/release.sh -u vector-im -z "$orig_args"
|
||||
./node_modules/matrix-js-sdk/release.sh -u vector-im -z "$orig_args"
|
||||
|
||||
if [ $prerelease -eq 0 ]
|
||||
then
|
||||
|
||||
@@ -20,17 +20,15 @@ then
|
||||
fi
|
||||
|
||||
echo "Linking js-sdk"
|
||||
git clone $JS_SDK_REPO js-sdk
|
||||
git clone --depth 1 --branch $JS_SDK_BRANCH $JS_SDK_REPO js-sdk
|
||||
cd js-sdk
|
||||
git checkout $JS_SDK_BRANCH
|
||||
yarn link
|
||||
yarn --network-timeout=100000 install
|
||||
cd ../
|
||||
|
||||
echo "Linking react-sdk"
|
||||
git clone $REACT_SDK_REPO react-sdk
|
||||
git clone --depth 1 --branch $REACT_SDK_BRANCH $REACT_SDK_REPO react-sdk
|
||||
cd react-sdk
|
||||
git checkout $REACT_SDK_BRANCH
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
yarn --network-timeout=100000 install
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
@@ -11,7 +11,7 @@ DIST_VERSION=$TAG
|
||||
# a few SHAs rather than a version.
|
||||
# Docker Hub doesn't always check out the tag and sometimes checks out the branch, so we should look
|
||||
# for an appropriately tagged branch as well (heads/v1.2.3).
|
||||
if [ $BRANCH != 'HEAD' && $BRANCH != 'heads/v*' ]
|
||||
if [[ $BRANCH != 'HEAD' && $BRANCH != 'heads/v*' ]]
|
||||
then
|
||||
REACT_SHA=$(cd node_modules/matrix-react-sdk; git rev-parse --short=12 HEAD)
|
||||
JSSDK_SHA=$(cd node_modules/matrix-js-sdk; git rev-parse --short=12 HEAD)
|
||||
|
||||
@@ -10,7 +10,13 @@ exports.default = async function(context) {
|
||||
// from the keychain, so we need to get it from the environment.
|
||||
const userId = process.env.NOTARIZE_APPLE_ID;
|
||||
if (userId === undefined) {
|
||||
throw new Error("User ID not found. Set NOTARIZE_APPLE_ID.");
|
||||
console.warn(
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" +
|
||||
"! Skipping macOS notarisation. !\n" +
|
||||
"! User ID not found, set NOTARIZE_APPLE_ID. !\n" +
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Notarising macOS app. This may be some time.");
|
||||
|
||||
@@ -8,23 +8,31 @@ exports.default = async function(options) {
|
||||
const appOutDir = path.dirname(inPath);
|
||||
|
||||
// get the token passphrase from the keychain
|
||||
const tokenPassphrase = await new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
'security',
|
||||
['find-generic-password', '-s', 'riot_signing_token', '-w'],
|
||||
{},
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
console.error("Couldn't find signing token in keychain", err);
|
||||
// electron-builder seems to print '[object Object]' on the
|
||||
// console whether you reject with an Error or a string...
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(stdout.trim());
|
||||
}
|
||||
},
|
||||
let tokenPassphrase;
|
||||
try {
|
||||
tokenPassphrase = await new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
'security',
|
||||
['find-generic-password', '-s', 'riot_signing_token', '-w'],
|
||||
{},
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(stdout.trim());
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" +
|
||||
"! Skipping Windows signing. !\n" +
|
||||
"! Signing token not found in keychain. !\n" +
|
||||
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let cmdLine = 'osslsigncode sign ';
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class VectorEmbeddedPage extends EmbeddedPage {
|
||||
translate(s) {
|
||||
s = sanitizeHtml(_t(s));
|
||||
// ugly fix for https://github.com/vector-im/riot-web/issues/4243
|
||||
s = s.replace(/\[matrix\]/, '<a href="https://matrix.org" target="_blank" rel="noopener"><img width="79" height="34" alt="[matrix]" style="padding-left: 1px;vertical-align: middle" src="welcome/images/matrix.svg"/></a>');
|
||||
s = s.replace(/\[matrix\]/, '<a href="https://matrix.org" target="_blank" rel="noreferrer noopener"><img width="79" height="34" alt="[matrix]" style="padding-left: 1px;vertical-align: middle" src="welcome/images/matrix.svg"/></a>');
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ const VectorAuthFooter = () => {
|
||||
const authFooterLinks = [];
|
||||
for (const linkEntry of links) {
|
||||
authFooterLinks.push(
|
||||
<a href={linkEntry.url} key={linkEntry.text} target="_blank" rel="noopener">
|
||||
<a href={linkEntry.url} key={linkEntry.text} target="_blank" rel="noreferrer noopener">
|
||||
{linkEntry.text}
|
||||
</a>,
|
||||
);
|
||||
@@ -43,7 +43,7 @@ const VectorAuthFooter = () => {
|
||||
return (
|
||||
<div className="mx_AuthFooter">
|
||||
{authFooterLinks}
|
||||
<a href="https://matrix.org" target="_blank" rel="noopener">{ _t('powered by Matrix') }</a>
|
||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t('powered by Matrix') }</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,22 +4,22 @@
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Pro uskutečnění hovoru se sdílením obrazovky musíte používat HTTPS.",
|
||||
"Chat with Riot Bot": "Konverzovat s Riot Botem",
|
||||
"Dismiss": "Zahodit",
|
||||
"powered by Matrix": "poháněno Matrixem",
|
||||
"Riot Desktop on %(platformName)s": "Riot Desktop na %(platformName)s",
|
||||
"powered by Matrix": "používá protokol Matrix",
|
||||
"Riot Desktop on %(platformName)s": "Riot Desktop pro %(platformName)s",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s přes %(browserName)s na %(osName)s",
|
||||
"Custom Server Options": "Vlastní serverové volby",
|
||||
"Custom Server Options": "Vlastní nastavení serveru",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Decentralizovaný, šifrovaný chat a spolupráce na platformě [matrix]",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Ve vlastních serverových volbách si můžete nastavit použití vlastního domovského serveru. To Vám umožní používat Riot s existujícím Matrix účtem na jiném serveru.",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Ve vlastním nastavení serveru můžete nastavit použití vlastního domovského serveru. To vám umožní používat Riot s existujícím Matrix účtem na jiném serveru.",
|
||||
"Sign In": "Přihlásit se",
|
||||
"Create Account": "Vytvořit účet",
|
||||
"Need help?": "Chcete pomoct?",
|
||||
"Explore rooms": "Prohlížet místnosti",
|
||||
"Need help?": "Potřebujete pomoc?",
|
||||
"Explore rooms": "Procházet místnosti",
|
||||
"Room Directory": "Adresář místností",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Konfigurace Riotu obsahuje neplatný JSON. Opravte prosím tento problém a obnovte stránku.",
|
||||
"The message from the parser is: %(message)s": "Zpráva z parseru je: %(message)s",
|
||||
"Invalid JSON": "Neplatný JSON",
|
||||
"Your Riot is misconfigured": "Riot je špatně nakonfigurován",
|
||||
"Unexpected error preparing the app. See console for details.": "Neočekávaná chyba při přípravě aplikace. V konzoli je více informací.",
|
||||
"Unexpected error preparing the app. See console for details.": "Neočekávaná chyba při přípravě aplikace. Podrobnosti najdete v konzoli.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Neplatná konfigurace: je možné specifikovat pouze jednu volbu z default_server_config, default_server_name, nebo default_hs_url.",
|
||||
"Invalid configuration: no default server specified.": "Neplatná konfigurace: není zadán výchozí server."
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
"Welcome to Riot.im": "Willkommen bei Riot.im",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Dezentrale, verschlüsselte Chat- & Kollaborationslösung unterstützt von [matrix]",
|
||||
"Chat with Riot Bot": "Chatte mit dem Riot Bot",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Du kannst die erweiterte Serveroption nutzen um dich an einem anderen Matrixserver anzumelden, indem du eine andere Heimserver-URL angibst. Dies erlaubt dir, Riot mit einem existierenden Matrix-Konto auf einem anderen Heimserver zu nutzen.",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Du kannst benutzerdefinierte Server-Optionen nutzen, um dich an anderen Matrix Servern anzumelden, indem du eine andere Heimserver-URL angibst. Dies erlaubt dir, Riot mit einem existierenden Matrix-Konto auf einem anderen Heimserver zu nutzen.",
|
||||
"Sign In": "Anmelden",
|
||||
"Create Account": "Konto erstellen",
|
||||
"Create Account": "Account erstellen",
|
||||
"Need help?": "Brauchst du Hilfe?",
|
||||
"Explore rooms": "Räume erkunden",
|
||||
"Explore rooms": "Erkunde Räume",
|
||||
"Room Directory": "Raumverzeichnis",
|
||||
"Unexpected error preparing the app. See console for details.": "Unerwarteter Fehler bei der Vorbereitung der App. Siehe Konsole für Details.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Ungültige Konfiguration: Es kann nur eine der Optionen default_server_config, default_server_name oder default_hs_url angegeben werden.",
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Dein Riot ist falsch konfiguriert",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Deine Riot Konfiguration enthält ungültiges JSON. Bitte korrigiere das Problem und lade die Seite neu.",
|
||||
"The message from the parser is: %(message)s": "Die Nachricht des Parsers ist: %(message)s",
|
||||
"Invalid JSON": "Ungültiges JSON"
|
||||
"Invalid JSON": "Ungültiges JSON",
|
||||
"Go to your browser to complete Sign In": "Gehe zu deinem Browser, um die Anmeldung abzuschließen"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.",
|
||||
"Invalid configuration: no default server specified.": "Invalid configuration: no default server specified.",
|
||||
"Riot Desktop on %(platformName)s": "Riot Desktop on %(platformName)s",
|
||||
"Go to your browser to complete Sign In": "Go to your browser to complete Sign In",
|
||||
"Unknown device": "Unknown device",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s via %(browserName)s on %(osName)s",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "You need to be using HTTPS to place a screen-sharing call.",
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Via kliento Riot estas misagordita",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Viaj Riot-agordoj enhavas nevalidan JSON-on. Bonvolu korekti la problemon kaj reŝarĝi la paĝon.",
|
||||
"The message from the parser is: %(message)s": "La mesaĝo el la analizilo estas: %(message)s",
|
||||
"Invalid JSON": "Nevalida JSON"
|
||||
"Invalid JSON": "Nevalida JSON",
|
||||
"Go to your browser to complete Sign In": "Iru al via foliumilo por fini la saluton"
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Konfigurazio baliogabea: default_server_config, default_server_name, edo default_hs_url bat bakarra zehaztu daiteke.",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Zure Riot konfigurazioak baliogabeko JSON kodea du. Zuzendu arazoa eta kargatu orria berriro.",
|
||||
"The message from the parser is: %(message)s": "Prozesatzailearen mezua hau da: %(message)s",
|
||||
"Invalid JSON": "JSON baliogabea"
|
||||
"Invalid JSON": "JSON baliogabea",
|
||||
"Go to your browser to complete Sign In": "Joan zure nabigatzailera izena ematen bukatzeko"
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Votre Riot est mal configuré",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Votre configuration de Riot contient du JSON non valide. Corrigez ce problème et rechargez la page.",
|
||||
"The message from the parser is: %(message)s": "Le message de l’analyseur est : %(message)s",
|
||||
"Invalid JSON": "JSON non valide"
|
||||
"Invalid JSON": "JSON non valide",
|
||||
"Go to your browser to complete Sign In": "Utilisez votre navigateur pour terminer la connexion"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"Custom Server Options": "Egyedi szerverbeállítások",
|
||||
"Dismiss": "Eltüntet",
|
||||
"Dismiss": "Eltüntetés",
|
||||
"powered by Matrix": "Matrix hajtja",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s alkalmazás %(browserName)s böngészőn %(osName)s rendszeren",
|
||||
"Riot Desktop on %(platformName)s": "Riot Desktop itt: %(platformName)s",
|
||||
"Unknown device": "Ismeretlen készülék",
|
||||
"Unknown device": "Ismeretlen eszköz",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Képernyőmegosztás indításához HTTPS-t kell használnod.",
|
||||
"Welcome to Riot.im": "Üdvözöl a Riot.im",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Decentralizált, titkosított csevegés és kollaboráció [matrix] alapokon",
|
||||
"Chat with Riot Bot": "Csevegés a Riot Robottal",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Használhatod az egyedi szerver beállítást más Matrix szerverre való belépéshez, azzal, hogy megadod a Matrix szerver URL-jét. Ezzel a Riot-ot használhatod más Matrix szerveren lévő fiókkal.",
|
||||
"Sign In": "Bejelentkezés",
|
||||
"Create Account": "Fiók készítés",
|
||||
"Create Account": "Fiók létrehozása",
|
||||
"Need help?": "Segíthetünk?",
|
||||
"Explore rooms": "Szobák felderítése",
|
||||
"Room Directory": "Szobalista",
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "A Riotod hibásan van beállítva",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "A Riot beállításod érvénytelen JSON szöveget tartalmaz. Kérlek javítsd és töltsd újra az oldalt.",
|
||||
"The message from the parser is: %(message)s": "A feldolgozó algoritmus üzenete: %(message)s",
|
||||
"Invalid JSON": "Érvénytelen JSON"
|
||||
"Invalid JSON": "Érvénytelen JSON",
|
||||
"Go to your browser to complete Sign In": "A böngészőben fejezd be a bejelentkezést"
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Il tuo Riot è configurato male",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "La tua configurazione di Riot contiene un JSON non valido. Correggi il problema e ricarica la pagina.",
|
||||
"The message from the parser is: %(message)s": "Il messaggio dal parser è: %(message)s",
|
||||
"Invalid JSON": "JSON non valido"
|
||||
"Invalid JSON": "JSON non valido",
|
||||
"Go to your browser to complete Sign In": "Vai nel tuo browser per completare l'accesso"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"Unknown device": "Nežinomas įrenginys",
|
||||
"powered by Matrix": "veikia su Matrix",
|
||||
"Welcome to Riot.im": "Sveiki atvykę į Riot.im",
|
||||
"Chat with Riot Bot": "Kalbėtis su Riot botu",
|
||||
"Chat with Riot Bot": "Kalbėtis su Riot Botu",
|
||||
"Riot Desktop on %(platformName)s": "Riot Desktop, naudojant %(platformName)s",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s per %(browserName)s, naudojant %(osName)s",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Norint skambinti naudojant ekrano vaizdo dalijimosi funkciją, jūs turite naudoti HTTPS.",
|
||||
@@ -21,5 +21,6 @@
|
||||
"Unexpected error preparing the app. See console for details.": "Netikėta klaida ruošiant programą. Norėdami sužinoti daugiau detalių, žiūrėkite konsolę.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Klaidinga konfigūracija: galima nurodyti tik vieną iš default_server_config, default_server_name, arba default_hs_url.",
|
||||
"Invalid configuration: no default server specified.": "Klaidinga konfigūracija: nenurodytas numatytasis serveris.",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Jūs galite naudoti pasirinktinius serverio nustatymus, kad prisijungtumėte prie kitų Matrix serverių, nurodydami kito serverio URL. Tai leidžia jums naudotis Riot su esama Matrix paskyra kitame serveryje."
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Jūs galite naudoti pasirinktinius serverio nustatymus, kad prisijungtumėte prie kitų Matrix serverių, nurodydami kito serverio URL. Tai leidžia jums naudotis Riot su esama Matrix paskyra kitame serveryje.",
|
||||
"Go to your browser to complete Sign In": "Norėdami užbaigti prisijungimą, eikite į naršyklę"
|
||||
}
|
||||
|
||||
@@ -7,14 +7,19 @@
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Du er nødt til å bruke HTTPS for å ha en samtale med skjermdeling.",
|
||||
"Dismiss": "Avvis",
|
||||
"Welcome to Riot.im": "Velkommen til Riot.im",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Desentralisert, kryptert chat & sammabeid drevet av [matrix]",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Desentralisert, kryptert chat & samarbeid drevet av [matrix]",
|
||||
"Chat with Riot Bot": "Chat med Riot Bot",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Du kan bruke instillinger for «egendefinert tjener» til å logge inn på andre Matrix tjenere ved å spesifisere en annen URL. Dette lar deg bruke Riot med en eksisterende Matrix konto på en annen hjemmetjener.",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Du kan bruke instillinger for «egendefinert tjener» til å logge inn på andre Matrix-tjenere ved å spesifisere en annen URL. Dette lar deg bruke Riot med en eksisterende Matrix-konto på en annen hjemmetjener.",
|
||||
"Sign In": "Logg inn",
|
||||
"Create Account": "Lag konto",
|
||||
"Need help?": "Trenger du hjelp?",
|
||||
"Room Directory": "Alle rom",
|
||||
"Explore rooms": "Se alle rom",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Riot-konfigurasjonen din inneholder ugyldig JSON. Vennligst fiks problemet og oppdater siden.",
|
||||
"The message from the parser is: %(message)s": "Meldingen fra parseren er: %(message)s"
|
||||
"The message from the parser is: %(message)s": "Meldingen fra parseren er: %(message)s",
|
||||
"Invalid JSON": "Ugyldig JSON",
|
||||
"Your Riot is misconfigured": "Riot er feilkonfigurert",
|
||||
"Invalid configuration: no default server specified.": "Ugyldig konfigurasjon: ingen standardserver spesifisert.",
|
||||
"Unexpected error preparing the app. See console for details.": "Uventet feil oppsto mens appen ble gjort klar. Se konsollen for detaljer.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Ugyldig konfigurasjon: Spesifiser kun en av følgende: default_server_config, default_server_name eller default_hs_url."
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Uw Riot is onjuist geconfigureerd",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Uw Riot-configuratie bevat ongeldige JSON. Corrigeer het probleem en herlaad de pagina.",
|
||||
"The message from the parser is: %(message)s": "De ontleder meldt: %(message)s",
|
||||
"Invalid JSON": "Ongeldige JSON"
|
||||
"Invalid JSON": "Ongeldige JSON",
|
||||
"Go to your browser to complete Sign In": "Ga naar uw browser om de aanmelding te voltooien"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"Riot Desktop on %(platformName)s": "Riot på Skrivebord for %(platformName)s",
|
||||
"Unknown device": "Ukjend eining",
|
||||
"%(appName)s via %(browserName)s on %(osName)s": "%(appName)s gjennom %(browserName)s på %(osName)s",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Du må bruka HTTPS for å ha ei samtale med skjermdeling.",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Du må bruka HTTPS for å ha ein samtale med skjermdeling.",
|
||||
"Dismiss": "Avvis",
|
||||
"powered by Matrix": "Matrixdriven",
|
||||
"Welcome to Riot.im": "Velkomen til Riot.im",
|
||||
@@ -11,12 +11,12 @@
|
||||
"Custom Server Options": "Tilpassa tenar-innstillingar",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Du kan nytta dei eigendefinerte tenarinstillingane for å logga inn på andre Matrix-tenarar ved å uppgje ein annan heimtenar-URL. Dette lèt deg bruka Riot med ein Matrix-konto som allereie finst på ein annan heimtenar.",
|
||||
"Sign In": "Logg inn",
|
||||
"Create Account": "Lag brukar",
|
||||
"Create Account": "Opprett konto",
|
||||
"Need help?": "Treng du hjelp?",
|
||||
"Explore rooms": "Utforsk romma",
|
||||
"Room Directory": "Romkatalog",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Riot-konfigurasjonen din har ugyldig JSON-kode. Korriger dette og last inn sida på nytt.",
|
||||
"The message from the parser is: %(message)s": "Meldingen frå kodetolkaren er: %(message)s",
|
||||
"The message from the parser is: %(message)s": "Meldinga frå kodetolkaren er: %(message)s",
|
||||
"Invalid JSON": "Ugyldig JSON",
|
||||
"Your Riot is misconfigured": "Riot-klienten din er feilkonfiguert",
|
||||
"Unexpected error preparing the app. See console for details.": "Uventa feil under lasting av programmet. Sjå konsollen for detaljar.",
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Ваш Riot неправильно настроен",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Ваша конфигурация Riot содержит нерабочий JSON. Пожалуйста исправьте проблему и перезагрузите страницу.",
|
||||
"The message from the parser is: %(message)s": "Сообщение из парсера: %(message)s",
|
||||
"Invalid JSON": "Нерабочий JSON"
|
||||
"Invalid JSON": "Нерабочий JSON",
|
||||
"Go to your browser to complete Sign In": "Перейдите в браузер для завершения входа"
|
||||
}
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Riot-i juaj është i keqformësuar",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Formësimi juaj i Riot-it përmban JSON. Ju lutemi, ndreqeni problemin dhe ringarkoni faqen.",
|
||||
"The message from the parser is: %(message)s": "Mesazhi prej procesit është: %(message)s",
|
||||
"Invalid JSON": "JSON i pavlefshëm"
|
||||
"Invalid JSON": "JSON i pavlefshëm",
|
||||
"Go to your browser to complete Sign In": "Që të plotësoni Hyrjen, kaloni te shfletuesi juaj"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"Chat with Riot Bot": "Riot Bot ile Sohbet Et",
|
||||
"You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use Riot with an existing Matrix account on a different homeserver.": "Özel sunucu seçenekleri kullanıp farklı bir anamakine URL'si belirleyerek diğer Matrix sunucularına giriş yapabilirsin. Bu Riot'u varolan bir Matrix hesabı ile farklı anamakine de kullanmanı sağlar.",
|
||||
"Sign In": "Giriş Yap",
|
||||
"Create Account": "Kayıt Ol",
|
||||
"Create Account": "Hesap Oluştur",
|
||||
"Need help?": "Yardıma mı ihtiyacın var?",
|
||||
"Explore rooms": "Odaları keşfet",
|
||||
"Room Directory": "Oda Dizini",
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "Je Riot is verkeerd geconfigureerd gewist",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "Je Riot-configuroasje bevat oungeldigen JSON. Corrigeer ’t probleem en herload ’t blad.",
|
||||
"The message from the parser is: %(message)s": "’t Bericht van de verwerker is: %(message)s",
|
||||
"Invalid JSON": "Oungeldigen JSON"
|
||||
"Invalid JSON": "Oungeldigen JSON",
|
||||
"Go to your browser to complete Sign In": "Goa noa je browser voe d’anmeldienge te voltooin"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"Unknown device": "未知设备",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "您需要使用 HTTPS 以进行共享屏幕通话。",
|
||||
"Welcome to Riot.im": "欢迎来到 Riot.im",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "去中心化,加密聊天 & 由 [matrix] 驱动",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "去中心化、加密聊天与协作,由 [matrix] 驱动",
|
||||
"Chat with Riot Bot": "与 Riot 机器人聊天",
|
||||
"Sign In": "登入",
|
||||
"Create Account": "创建帐号",
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
"Your Riot is misconfigured": "您的 Riot 沒有設定好",
|
||||
"Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.": "您的 Riot 包含無效的 JSON。請修正問題並重新整理頁面。",
|
||||
"The message from the parser is: %(message)s": "從解析器而來的訊息為:%(message)s",
|
||||
"Invalid JSON": "無效的 JSON"
|
||||
"Invalid JSON": "無效的 JSON",
|
||||
"Go to your browser to complete Sign In": "到您的瀏覽器完成登入"
|
||||
}
|
||||
|
||||
@@ -21,19 +21,23 @@ limitations under the License.
|
||||
import olmWasmPath from 'olm/olm.wasm';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// add React and ReactPerf to the global namespace, to make them easier to
|
||||
// access via the console
|
||||
global.React = React;
|
||||
|
||||
import ReactDOM from 'react-dom';
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import * as sdk from 'matrix-react-sdk';
|
||||
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
|
||||
import Modal from 'matrix-react-sdk/src/Modal';
|
||||
import * as VectorConferenceHandler from 'matrix-react-sdk/src/VectorConferenceHandler';
|
||||
import * as languageHandler from 'matrix-react-sdk/src/languageHandler';
|
||||
import {_t, _td, newTranslatableError} from 'matrix-react-sdk/src/languageHandler';
|
||||
import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils';
|
||||
import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
|
||||
import * as Lifecycle from "matrix-react-sdk/src/Lifecycle";
|
||||
import dis from "matrix-react-sdk/src/dispatcher";
|
||||
|
||||
import url from 'url';
|
||||
|
||||
@@ -43,6 +47,7 @@ import ElectronPlatform from './platform/ElectronPlatform';
|
||||
import WebPlatform from './platform/WebPlatform';
|
||||
|
||||
import {MatrixClientPeg} from 'matrix-react-sdk/src/MatrixClientPeg';
|
||||
import {getCachedRoomIDForAlias} from 'matrix-react-sdk/src/RoomAliasCache';
|
||||
import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
import {setTheme} from "matrix-react-sdk/src/theme";
|
||||
@@ -123,6 +128,184 @@ function onNewScreen(screen) {
|
||||
const hash = '#/' + screen;
|
||||
lastLocationHashSet = hash;
|
||||
window.location.hash = hash;
|
||||
if (!window.matrixChat) {
|
||||
return;
|
||||
}
|
||||
let creds = null;
|
||||
if (screen === "register" || screen === "login" || screen === "welcome") {
|
||||
autoRegister().then((newCreds) => {
|
||||
creds = newCreds;
|
||||
return window.matrixChat.onUserCompletedLoginFlow(newCreds, "-");
|
||||
}, (err) => {
|
||||
console.error("Failed to auto-register:", err)
|
||||
}).then(() => {
|
||||
// first time user
|
||||
if (creds._registered) {
|
||||
p2pFirstTimeSetup();
|
||||
}
|
||||
})
|
||||
} else if (screen.startsWith("room/")) {
|
||||
// room/!foo:bar
|
||||
// room/#foo:bar
|
||||
// if this room is public then make sure it is published.
|
||||
p2pEnsurePublished(screen.split("/")[1])
|
||||
}
|
||||
}
|
||||
|
||||
class P2PDisplayNameDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
return <BaseDialog
|
||||
onFinished={this.props.onFinished}
|
||||
title={ _t('Set a display name:')}
|
||||
>
|
||||
<ChangeDisplayName onFinished={this.props.onFinished} />
|
||||
<DialogButtons
|
||||
primaryButton={_t('OK')}
|
||||
onPrimaryButtonClick={this.props.onFinished}
|
||||
hasCancel={false}
|
||||
/>
|
||||
</BaseDialog>;
|
||||
}
|
||||
}
|
||||
|
||||
function p2pFirstTimeSetup() {
|
||||
// Prompt them to set a display name
|
||||
Modal.createDialog(P2PDisplayNameDialog,
|
||||
{
|
||||
onFinished: () => {
|
||||
// View the room directory after display name has been sorted out
|
||||
dis.dispatch({
|
||||
action: 'view_room_directory',
|
||||
});
|
||||
},
|
||||
}, null, /* priority = */ false, /* static = */ true,
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchRoom(roomId) {
|
||||
const client = MatrixClientPeg.get();
|
||||
let room = client.getRoom(roomId);
|
||||
if (room) {
|
||||
return room;
|
||||
}
|
||||
console.log("p2pEnsurePublished fetchRoom waiting for room... ", roomId);
|
||||
room = await new Promise((resolve, reject) => {
|
||||
let fulfilled = false;
|
||||
let cb = function(room){
|
||||
if (fulfilled) {
|
||||
return;
|
||||
}
|
||||
let newRoomId = room.roomId;
|
||||
if (roomId === newRoomId) {
|
||||
fulfilled = true;
|
||||
console.log("p2pEnsurePublished fetchRoom found ", roomId);
|
||||
resolve(room);
|
||||
}
|
||||
}
|
||||
client.on("Room", cb);
|
||||
setTimeout(() => {
|
||||
if (fulfilled) {
|
||||
return;
|
||||
}
|
||||
console.log("p2pEnsurePublished fetchRoom timed out ", roomId);
|
||||
fulfilled = true;
|
||||
client.removeListener("Room", cb);
|
||||
reject(new Error("timed out waiting to see room " + roomId));
|
||||
}, 60 * 1000); // wait 60s
|
||||
});
|
||||
return room;
|
||||
}
|
||||
|
||||
async function p2pEnsurePublished(roomIdOrAlias) {
|
||||
// If the room has just been created, we need to wait for the join_rules to come down /sync
|
||||
// If the app has just been refreshed, we need to wait for the DB to be loaded.
|
||||
// Since we don't really care when this is done, just sleep a bit.
|
||||
await sleep(3000);
|
||||
console.log("p2pEnsurePublished ", roomIdOrAlias);
|
||||
try {
|
||||
const client = MatrixClientPeg.get();
|
||||
// convert alias to room ID
|
||||
let roomId;
|
||||
let aliasLocalpart;
|
||||
if (roomIdOrAlias.startsWith("!")) {
|
||||
roomId = roomIdOrAlias;
|
||||
} else {
|
||||
roomId = getCachedRoomIDForAlias(roomIdOrAlias);
|
||||
// extract the localpart so we can republish this alias on our server
|
||||
aliasLocalpart = roomIdOrAlias.split(":")[0].substring(1)
|
||||
}
|
||||
|
||||
// fetch the join_rules, check if public
|
||||
const room = await fetchRoom(roomId);
|
||||
if (!room) {
|
||||
throw new Error("No room for room ID: " + roomId);
|
||||
}
|
||||
if (!aliasLocalpart) {
|
||||
const roomName = room.currentState.getStateEvents("m.room.name", "");
|
||||
if (roomName) {
|
||||
aliasLocalpart = roomName.getContent().name;
|
||||
// room alias grammar is poorly defined. Synapse rejects whitespace, Riot barfs on slashes, it's a mess.
|
||||
// so for now, let's just do A-Za-z0-9_-
|
||||
aliasLocalpart = aliasLocalpart.replace(/[^A-Za-z0-9_-]/g, "")
|
||||
} else {
|
||||
// use the random part of the room ID as a fallback.
|
||||
aliasLocalpart = roomId.split(":")[0].substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
const joinRules = room.currentState.getStateEvents("m.room.join_rules", "");
|
||||
if (!joinRules) {
|
||||
throw new Error("No join_rules for room ID: " + roomId);
|
||||
}
|
||||
const isPublic = joinRules.getContent().join_rule === "public";
|
||||
|
||||
if (isPublic) {
|
||||
// make sure that there is an alias mapping
|
||||
try {
|
||||
for(let i = 0; i < 2; i++) {
|
||||
const newRoomAlias = `#${aliasLocalpart}:${client.getDomain()}`;
|
||||
let exists = false;
|
||||
let matches = false;
|
||||
try {
|
||||
const aliasResponse = await client.getRoomIdForAlias(newRoomAlias);
|
||||
matches = aliasResponse.room_id === roomId;
|
||||
exists = true;
|
||||
} catch (err) {}
|
||||
console.log("p2pEnsurePublished: room ID:", roomId, " want alias: ", newRoomAlias, " exists=", exists, " matches=", matches);
|
||||
if (!exists) {
|
||||
await client.createAlias(newRoomAlias, roomId);
|
||||
break;
|
||||
} else if (!matches) {
|
||||
// clashing room alias, use the room ID.
|
||||
aliasLocalpart = roomId.split(":")[0].substring(1);
|
||||
} else {
|
||||
// exists and matches, do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("p2pEnsurePublished: problem creating alias: ", err);
|
||||
}
|
||||
|
||||
// publish the room
|
||||
await client.setRoomDirectoryVisibility(roomId, "public");
|
||||
console.log("p2pEnsurePublished: Now published.");
|
||||
} else {
|
||||
// unpublish the room
|
||||
await client.setRoomDirectoryVisibility(roomId, "private");
|
||||
console.log("p2pEnsurePublished: Now hidden.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("p2pEnsurePublished encountered an error: ", err);
|
||||
}
|
||||
}
|
||||
|
||||
// We use this to work out what URL the SDK should
|
||||
@@ -172,6 +355,53 @@ function onTokenLoginCompleted() {
|
||||
window.location.href = formatted;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function autoRegister() {
|
||||
console.log("dendrite: Auto-registration in progress");
|
||||
const cli = Matrix.createClient({
|
||||
baseUrl: "https://p2p.riot.im",
|
||||
});
|
||||
const password = "this should be really really secure";
|
||||
|
||||
// make sure the server is up (active service worker)
|
||||
await navigator.serviceWorker.ready;
|
||||
// on Firefix, the ready promise resolves just prior to activation
|
||||
// on Chrome, the ready promise resolves just after activation.
|
||||
// We need to make requests AFTER we have been activated, else the /register request
|
||||
// will fail.
|
||||
await sleep(10);
|
||||
|
||||
let response = null;
|
||||
let didRegister = false;
|
||||
try {
|
||||
response = await cli.register("p2p", password, "", { type: "m.login.dummy" });
|
||||
console.log("dendrite: Auto-registration done ", response);
|
||||
didRegister = true;
|
||||
} catch (err) {
|
||||
console.error("dendrite: failed to register, trying to login:", err)
|
||||
response = await cli.login("m.login.password", {
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: "p2p",
|
||||
},
|
||||
password: password,
|
||||
initial_device_display_name: "p2p-dendrite",
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
userId: response.user_id,
|
||||
deviceId: response.device_id,
|
||||
homeserverUrl: cli.getHomeserverUrl(),
|
||||
accessToken: response.access_token,
|
||||
guest: cli.isGuest(),
|
||||
_registered: didRegister,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadApp() {
|
||||
// XXX: the way we pass the path to the worker script from webpack via html in body's dataset is a hack
|
||||
// but alternatives seem to require changing the interface to passing Workers to js-sdk
|
||||
@@ -183,7 +413,8 @@ export async function loadApp() {
|
||||
// make sure the indexeddb script is present, so fail hard.
|
||||
throw new Error("Missing indexeddb worker script!");
|
||||
}
|
||||
MatrixClientPeg.setIndexedDbWorkerScript(vectorIndexeddbWorkerScript);
|
||||
// MatrixClientPeg.setIndexedDbWorkerScript(vectorIndexeddbWorkerScript);
|
||||
|
||||
CallHandler.setConferenceHandler(VectorConferenceHandler);
|
||||
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
@@ -274,6 +505,45 @@ export async function loadApp() {
|
||||
return;
|
||||
}
|
||||
|
||||
// load dendrite, if available
|
||||
const vectorDendriteWorkerScript = document.body.dataset.vectorDendriteWorkerScript;
|
||||
if (vectorDendriteWorkerScript && 'serviceWorker' in navigator) {
|
||||
console.log("dendrite code exec... ", document.readyState);
|
||||
const loadDendriteSw = ()=>{
|
||||
console.log("Registering dendrite sw...", vectorDendriteWorkerScript);
|
||||
console.log("swjs: invoke navigator.serviceWorker.register")
|
||||
navigator.serviceWorker.register(vectorDendriteWorkerScript, { scope: "/" }).then(function(registration) {
|
||||
console.log("swjs: navigator.serviceWorker.register resolved", registration)
|
||||
// Registration was successful
|
||||
console.log('ServiceWorker sw.js registration successful with scope: ', registration.scope);
|
||||
// periodically check for updates
|
||||
setInterval(function() {
|
||||
console.log("swjs invoke registration.update")
|
||||
registration.update();
|
||||
}, 1000 * 60 * 30) // once every 30 minutes
|
||||
}, (err)=>{
|
||||
// registration failed :(
|
||||
console.log('dendrite: ServiceWorker registration failed: ', err);
|
||||
});
|
||||
// First, do a one-off check if there's currently a
|
||||
// service worker in control.
|
||||
if (navigator.serviceWorker.controller) {
|
||||
console.log('dendrite: This page is currently controlled by:', navigator.serviceWorker.controller);
|
||||
}
|
||||
|
||||
// Then, register a handler to detect when a new or
|
||||
// updated service worker takes control.
|
||||
navigator.serviceWorker.oncontrollerchange = function() {
|
||||
console.log('dendrite: This page is now controlled by:', navigator.serviceWorker.controller);
|
||||
};
|
||||
}
|
||||
if (document.readyState === "loading") {
|
||||
window.addEventListener('DOMContentLoaded', loadDendriteSw);
|
||||
} else {
|
||||
loadDendriteSw();
|
||||
}
|
||||
}
|
||||
|
||||
const validBrowser = checkBrowserFeatures();
|
||||
|
||||
const acceptInvalidBrowser = window.localStorage && window.localStorage.getItem('mx_accepts_unsupported_browser');
|
||||
|
||||
279
src/vector/dendrite-sw.js
Normal file
279
src/vector/dendrite-sw.js
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
// Copyright 2020 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.
|
||||
|
||||
// Dendrite Service Worker version
|
||||
// Bumping the patch version of this has no side-effects.
|
||||
// Bumping the minor version of this will delete databases.
|
||||
const version = "0.2.0";
|
||||
|
||||
const isVersionBump = function(oldVer, newVer) {
|
||||
oldVer = oldVer || "";
|
||||
const newSegments = newVer.split(".");
|
||||
const oldSegments = oldVer.split(".");
|
||||
if (oldSegments.length != 3) {
|
||||
return true; // brand new
|
||||
}
|
||||
return newSegments[1] > oldSegments[1];
|
||||
}
|
||||
|
||||
const bundle_path = self.location.href.replace("/dendrite_sw.js", "")
|
||||
const id = Math.random();
|
||||
console.log("swjs: ", id," dendrite-sw.js file running...")
|
||||
self.registration.addEventListener('updatefound', () => {
|
||||
console.log("swjs: ", id," updatefound registration event fired")
|
||||
const newWorker = self.registration.installing;
|
||||
if (!newWorker) {
|
||||
console.log("swjs: ", id," updatefound registration event fired, no installing worker")
|
||||
return;
|
||||
}
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
console.log("swjs: ", id," worker statechange: ", newWorker.state)
|
||||
});
|
||||
})
|
||||
|
||||
self.importScripts(`${bundle_path}/wasm_exec.js`,
|
||||
`${bundle_path}/sqlitejs.js`,
|
||||
`${bundle_path}/localforage.js`);
|
||||
|
||||
let isWriting = false;
|
||||
async function writeDatabasesToIndexedDB() {
|
||||
while (true) {
|
||||
await sleep(1000 * 30);
|
||||
try {
|
||||
await syncfs(false);
|
||||
}
|
||||
catch (err) {
|
||||
console.error("syncfs: failed to write to IDB:", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function syncfs(isStartup) {
|
||||
return new Promise((resolve, reject) => {
|
||||
global._go_sqlite.FS.syncfs(isStartup, function(err) {
|
||||
if (err) {
|
||||
console.error("syncfs failed:", err);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
console.log("syncfs OK");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDatabase(name) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const req = indexedDB.deleteDatabase(name);
|
||||
req.onsuccess = function () {
|
||||
console.log("Deleted database successfully: ", name);
|
||||
resolve();
|
||||
};
|
||||
req.onerror = function (event) {
|
||||
console.log("Couldn't delete database: ", name, event);
|
||||
reject(new Error("failed to delete database"));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function initDendrite() {
|
||||
console.log(`dendrite-sw.js: v${version} SW init`)
|
||||
// check if we need to purge databases (minor version bump)
|
||||
const prevVer = await global.localforage.getItem("dendrite_version")
|
||||
if (prevVer != version) {
|
||||
const nukeDatabase = isVersionBump(prevVer, version);
|
||||
console.log(`dendrite-sw.js: previous ver ${prevVer} current ${version} nuke databases: ${nukeDatabase}`);
|
||||
if (nukeDatabase) {
|
||||
await deleteDatabase("/idb");
|
||||
}
|
||||
}
|
||||
|
||||
global.process = {
|
||||
pid: 1,
|
||||
env: {
|
||||
DEBUG: "*",
|
||||
}
|
||||
};
|
||||
// we do this for prometheus so it doesn't panic on init()
|
||||
global.fs.stat = function(path, cb) {
|
||||
cb({
|
||||
code: "EINVAL",
|
||||
});
|
||||
}
|
||||
|
||||
const config = {
|
||||
locateFile: filename => `${bundle_path}/../../sql-wasm.wasm`
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
await sqlitejs.init(config);
|
||||
|
||||
// periodically write databases to disk
|
||||
if (!isWriting) {
|
||||
isWriting = true;
|
||||
// have to do this before we start dendrite for hopefully obvious reasons...
|
||||
console.log("syncfs", global._go_sqlite.IDBFS);
|
||||
global._go_sqlite.FS.mkdir("/idb");
|
||||
//global._go_sqlite.FS.unmount("/");
|
||||
console.log("syncfs at /idb mkdir ok");
|
||||
global._go_sqlite.FS.mount(global._go_sqlite.IDBFS, {}, "/idb");
|
||||
console.log("syncfs mounted");
|
||||
await syncfs(true); // load from IDB
|
||||
writeDatabasesToIndexedDB();
|
||||
}
|
||||
|
||||
console.log(`dendrite-sw.js: v${version} starting dendrite.wasm...`)
|
||||
const result = await WebAssembly.instantiateStreaming(fetch(`${bundle_path}/../../dendrite.wasm`), go.importObject)
|
||||
go.run(result.instance).then(() => {
|
||||
console.log(`dendrite-sw.js: v${version} dendrite.wasm terminated, restarting...`);
|
||||
// purge databases and p2p nodes.
|
||||
global._go_js_server = undefined;
|
||||
global._go_sqlite_dbs.clear();
|
||||
initDendritePromise = initDendrite();
|
||||
});
|
||||
// make fetch calls go through this sw - notably if a page registers a sw, it does NOT go through any sw by default
|
||||
// unless you refresh or call this function.
|
||||
console.log(`dendrite-sw.js: v${version} claiming open browser tabs`)
|
||||
console.log("swjs: ", id," invoke self.clients.claim()")
|
||||
self.clients.claim();
|
||||
|
||||
let serverIsUp = false;
|
||||
for (let i = 0; i < 30; i++) { // 3s
|
||||
if (global._go_js_server) {
|
||||
console.log("swjs: ", id," init dendrite promise resolving");
|
||||
serverIsUp = true;
|
||||
break;
|
||||
}
|
||||
await sleep(100);
|
||||
}
|
||||
if (!serverIsUp) {
|
||||
throw new Error("Timed out waiting for _go_js_server to be set.");
|
||||
}
|
||||
// persist the new version
|
||||
await global.localforage.setItem("dendrite_version", version);
|
||||
}
|
||||
|
||||
let initDendritePromise = initDendrite();
|
||||
|
||||
self.addEventListener('install', function(event) {
|
||||
console.log("swjs: ", id," install event fired:", event)
|
||||
console.log(`dendrite-sw.js: v${version} SW install`)
|
||||
// Tell the browser to kill old sw's running in other tabs and replace them with this one
|
||||
// This may cause spontaneous logouts.
|
||||
console.log("swjs: ", id," invoke self.skipWaiting")
|
||||
self.skipWaiting();
|
||||
})
|
||||
|
||||
self.addEventListener('activate', function(event) {
|
||||
console.log("swjs: ", id," activate event fired")
|
||||
console.log(`dendrite-sw.js: v${version} SW activate`)
|
||||
event.waitUntil(initDendritePromise)
|
||||
})
|
||||
|
||||
async function sendRequestToGo(event) {
|
||||
await initDendritePromise; // this sets the global fetch listener
|
||||
if (!global._go_js_server || !global._go_js_server.fetch) {
|
||||
console.log(`dendrite-sw.js: v${version} no fetch listener present for ${event.request.url}`);
|
||||
return
|
||||
}
|
||||
console.log(`dendrite-sw.js: v${version} forwarding ${event.request.url} to Go`);
|
||||
const req = event.request
|
||||
let reqHeaders = ''
|
||||
if (req.headers) {
|
||||
for (const header of req.headers) {
|
||||
// FIXME: is this a safe header encoding?
|
||||
reqHeaders += `${header[0]}: ${header[1]}\n`
|
||||
}
|
||||
}
|
||||
let jj = null;
|
||||
if (req.method === "POST" || req.method === "PUT") {
|
||||
jj = await req.json();
|
||||
jj = JSON.stringify(jj);
|
||||
reqHeaders += `Content-Length: ${new Blob([jj]).size}`; // include utf-8 chars properly
|
||||
}
|
||||
|
||||
if (reqHeaders.length > 0) {
|
||||
reqHeaders = `\r\n${reqHeaders}`
|
||||
}
|
||||
|
||||
// Replace the timeout value for /sync calls to be 20s not 30s because Firefox
|
||||
// will aggressively cull service workers after a 30s idle period. Chrome doesn't.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1378587
|
||||
const fullurl = req.url.replace("timeout=30000", "timeout=20000");
|
||||
|
||||
const reqString = `${req.method} ${fullurl} HTTP/1.0${reqHeaders}\r\n\r\n${jj ? jj : ''}`
|
||||
|
||||
const res = await global._go_js_server.fetch(reqString)
|
||||
if (res.error) {
|
||||
console.error(`dendrite-sw.js: v${version} Error for request: ${event.request.url} => ${res.error}`)
|
||||
return
|
||||
}
|
||||
const respString = res.result;
|
||||
|
||||
const m = respString.match(/^(HTTP\/1.[01]) ((.*?) (.*?))(\r\n([^]*?)?(\r\n\r\n([^]*?)))?$/)
|
||||
if (!m) {
|
||||
console.warn("couldn't parse resp", respString);
|
||||
return;
|
||||
}
|
||||
const response = {
|
||||
"proto": m[1],
|
||||
"status": m[2],
|
||||
"statusCode": parseInt(m[3]),
|
||||
"headers": m[6],
|
||||
"body": m[8],
|
||||
}
|
||||
|
||||
const respHeaders = new Headers()
|
||||
const headerLines = response.headers.split('\r\n')
|
||||
for (const headerLine of headerLines) {
|
||||
// FIXME: is this safe header parsing? Do we need to worry about line-wrapping?
|
||||
const match = headerLine.match(/^(.+?): *(.*?)$/)
|
||||
if (match) {
|
||||
respHeaders.append(match[1], match[2])
|
||||
}
|
||||
else {
|
||||
console.log("couldn't parse headerLine ", headerLine)
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.statusCode,
|
||||
headers: respHeaders,
|
||||
})
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', function(event) {
|
||||
event.respondWith((async () => {
|
||||
/*
|
||||
// If this is a page refresh for the current page, then shunt in the new sw
|
||||
// https://github.com/w3c/ServiceWorker/issues/1238
|
||||
if (event.request.mode === "navigate" && event.request.method === "GET" && registration.waiting && (await clients.matchAll()).length < 2) {
|
||||
console.log("Forcing new sw.js into page")
|
||||
registration.waiting.postMessage('skipWaiting');
|
||||
return new Response("", {headers: {"Refresh": "0"}});
|
||||
} */
|
||||
|
||||
if (event.request.url.match(/\/_matrix\/client/)) {
|
||||
return await sendRequestToGo(event);
|
||||
}
|
||||
else {
|
||||
return fetch(event.request);
|
||||
}
|
||||
})());
|
||||
})
|
||||
BIN
src/vector/dendrite.wasm
Executable file
BIN
src/vector/dendrite.wasm
Executable file
Binary file not shown.
@@ -49,9 +49,11 @@
|
||||
<% }
|
||||
} %>
|
||||
</head>
|
||||
<body style="height: 100%;" data-vector-indexeddb-worker-script="<%= htmlWebpackPlugin.files.chunks['indexeddb-worker'].entry %>">
|
||||
<section id="matrixchat" style="height: 100%; overflow: auto;"></section>
|
||||
<body style="height: 100%;"
|
||||
data-vector-indexeddb-worker-script="<%= htmlWebpackPlugin.files.chunks['indexeddb_worker'].entry %>"
|
||||
data-vector-dendrite-worker-script="<%= htmlWebpackPlugin.files.chunks['dendrite_sw'].entry %>">
|
||||
<noscript>Sorry, Riot requires JavaScript to be enabled.</noscript> <!-- TODO: Translate this? -->
|
||||
<section id="matrixchat" style="height: 100%; overflow: auto;"></section>
|
||||
<script src="<%= htmlWebpackPlugin.files.chunks['bundle'].entry %>"></script>
|
||||
<img src="<%= require('matrix-react-sdk/res/img/warning.svg') %>" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
|
||||
<img src="<%= require('matrix-react-sdk/res/img/e2e/warning.svg') %>" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
|
||||
|
||||
@@ -31,7 +31,7 @@ import './modernizr';
|
||||
|
||||
// load service worker if available on this platform
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('sw.js');
|
||||
// navigator.serviceWorker.register('sw.js');
|
||||
}
|
||||
|
||||
// Ensure the skin is the very first thing to load for the react-sdk. We don't even want to reference
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ Copyright 2016 Aviral Dasgupta
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +25,11 @@ import BaseEventIndexManager from 'matrix-react-sdk/src/indexing/BaseEventIndexM
|
||||
import dis from 'matrix-react-sdk/src/dispatcher';
|
||||
import { _t } from 'matrix-react-sdk/src/languageHandler';
|
||||
import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake';
|
||||
import {MatrixClient} from "matrix-js-sdk";
|
||||
import Modal from "matrix-react-sdk/src/Modal";
|
||||
import InfoDialog from "matrix-react-sdk/src/components/views/dialogs/InfoDialog";
|
||||
import Spinner from "matrix-react-sdk/src/components/views/elements/Spinner";
|
||||
import React from "react";
|
||||
|
||||
const ipcRenderer = window.ipcRenderer;
|
||||
|
||||
@@ -200,16 +206,6 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
|
||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||
|
||||
this._tryPersistStorage();
|
||||
}
|
||||
|
||||
async _tryPersistStorage() {
|
||||
if (navigator.storage && navigator.storage.persist) {
|
||||
const granted = await navigator.storage.persist();
|
||||
const persisted = await navigator.storage.persisted();
|
||||
console.log("Storage persist request granted: " + granted + " persisted: " + persisted);
|
||||
}
|
||||
}
|
||||
|
||||
async getConfig(): Promise<{}> {
|
||||
@@ -395,4 +391,25 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
getEventIndexingManager(): BaseEventIndexManager | null {
|
||||
return this.eventIndexManager;
|
||||
}
|
||||
|
||||
setLanguage(preferredLangs: string[]) {
|
||||
this._ipcCall('setLanguage', preferredLangs).catch(error => {
|
||||
console.log("Failed to send setLanguage IPC to Electron");
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
getSSOCallbackUrl(hsUrl: string, isUrl: string): URL {
|
||||
const url = super.getSSOCallbackUrl(hsUrl, isUrl);
|
||||
url.protocol = "riot";
|
||||
return url;
|
||||
}
|
||||
|
||||
startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas") {
|
||||
super.startSingleSignOn(mxClient, loginType); // this will get intercepted by electron-main will-navigate
|
||||
Modal.createTrackedDialog('Electron', 'SSO', InfoDialog, {
|
||||
title: _t("Go to your browser to complete Sign In"),
|
||||
description: <Spinner />,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
22
src/vector/sqlitejs.js
Normal file
22
src/vector/sqlitejs.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import initSqlJs from 'sql.js'
|
||||
|
||||
export function init(config) {
|
||||
return initSqlJs(config).then(SQL => {
|
||||
global._go_sqlite = SQL;
|
||||
console.log("Loaded sqlite")
|
||||
})
|
||||
}
|
||||
626
src/vector/wasm_exec.js
Normal file
626
src/vector/wasm_exec.js
Normal file
@@ -0,0 +1,626 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
(() => {
|
||||
// Map multiple JavaScript environments to a single common API,
|
||||
// preferring web standards over Node.js API.
|
||||
//
|
||||
// Environments considered:
|
||||
// - Browsers
|
||||
// - Node.js
|
||||
// - Electron
|
||||
// - Parcel
|
||||
// - Webpack
|
||||
|
||||
if (typeof global !== "undefined") {
|
||||
// global already exists
|
||||
} else if (typeof window !== "undefined") {
|
||||
window.global = window;
|
||||
} else if (typeof self !== "undefined") {
|
||||
self.global = self;
|
||||
} else {
|
||||
throw new Error("cannot export Go (neither global, window nor self is defined)");
|
||||
}
|
||||
|
||||
if (!global.require && typeof require !== "undefined") {
|
||||
global.require = require;
|
||||
}
|
||||
|
||||
if (!global.fs && global.require) {
|
||||
const fs = require("fs");
|
||||
if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
|
||||
global.fs = fs;
|
||||
}
|
||||
}
|
||||
|
||||
const enosys = () => {
|
||||
const err = new Error("not implemented");
|
||||
err.code = "ENOSYS";
|
||||
return err;
|
||||
};
|
||||
|
||||
if (!global.fs) {
|
||||
let outputBuf = "";
|
||||
global.fs = {
|
||||
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
|
||||
writeSync(fd, buf) {
|
||||
outputBuf += decoder.decode(buf);
|
||||
const nl = outputBuf.lastIndexOf("\n");
|
||||
if (nl != -1) {
|
||||
console.log(outputBuf.substr(0, nl));
|
||||
outputBuf = outputBuf.substr(nl + 1);
|
||||
}
|
||||
return buf.length;
|
||||
},
|
||||
write(fd, buf, offset, length, position, callback) {
|
||||
if (offset !== 0 || length !== buf.length || position !== null) {
|
||||
callback(enosys());
|
||||
return;
|
||||
}
|
||||
const n = this.writeSync(fd, buf);
|
||||
callback(null, n);
|
||||
},
|
||||
chmod(path, mode, callback) { callback(enosys()); },
|
||||
chown(path, uid, gid, callback) { callback(enosys()); },
|
||||
close(fd, callback) { callback(enosys()); },
|
||||
fchmod(fd, mode, callback) { callback(enosys()); },
|
||||
fchown(fd, uid, gid, callback) { callback(enosys()); },
|
||||
fstat(fd, callback) { callback(enosys()); },
|
||||
fsync(fd, callback) { callback(null); },
|
||||
ftruncate(fd, length, callback) { callback(enosys()); },
|
||||
lchown(path, uid, gid, callback) { callback(enosys()); },
|
||||
link(path, link, callback) { callback(enosys()); },
|
||||
lstat(path, callback) { callback(enosys()); },
|
||||
mkdir(path, perm, callback) { callback(enosys()); },
|
||||
open(path, flags, mode, callback) { callback(enosys()); },
|
||||
read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
|
||||
readdir(path, callback) { callback(enosys()); },
|
||||
readlink(path, callback) { callback(enosys()); },
|
||||
rename(from, to, callback) { callback(enosys()); },
|
||||
rmdir(path, callback) { callback(enosys()); },
|
||||
stat(path, callback) { callback(enosys()); },
|
||||
symlink(path, link, callback) { callback(enosys()); },
|
||||
truncate(path, length, callback) { callback(enosys()); },
|
||||
unlink(path, callback) { callback(enosys()); },
|
||||
utimes(path, atime, mtime, callback) { callback(enosys()); },
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.process) {
|
||||
global.process = {
|
||||
getuid() { return -1; },
|
||||
getgid() { return -1; },
|
||||
geteuid() { return -1; },
|
||||
getegid() { return -1; },
|
||||
getgroups() { throw enosys(); },
|
||||
pid: -1,
|
||||
ppid: -1,
|
||||
umask() { throw enosys(); },
|
||||
cwd() { throw enosys(); },
|
||||
chdir() { throw enosys(); },
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.crypto && global.require) {
|
||||
const nodeCrypto = require("crypto");
|
||||
global.crypto = {
|
||||
getRandomValues(b) {
|
||||
nodeCrypto.randomFillSync(b);
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!global.crypto) {
|
||||
throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
|
||||
}
|
||||
|
||||
if (!global.performance) {
|
||||
global.performance = {
|
||||
now() {
|
||||
const [sec, nsec] = process.hrtime();
|
||||
return sec * 1000 + nsec / 1000000;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.TextEncoder && global.require) {
|
||||
global.TextEncoder = require("util").TextEncoder;
|
||||
}
|
||||
if (!global.TextEncoder) {
|
||||
throw new Error("global.TextEncoder is not available, polyfill required");
|
||||
}
|
||||
|
||||
if (!global.TextDecoder && global.require) {
|
||||
global.TextDecoder = require("util").TextDecoder;
|
||||
}
|
||||
if (!global.TextDecoder) {
|
||||
throw new Error("global.TextDecoder is not available, polyfill required");
|
||||
}
|
||||
|
||||
// End of polyfills for common API.
|
||||
|
||||
const encoder = new TextEncoder("utf-8");
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
|
||||
global.Go = class {
|
||||
constructor() {
|
||||
this.argv = ["js"];
|
||||
this.env = {};
|
||||
this.exit = (code) => {
|
||||
if (code !== 0) {
|
||||
console.warn("exit code:", code);
|
||||
}
|
||||
};
|
||||
this._exitPromise = new Promise((resolve) => {
|
||||
this._resolveExitPromise = resolve;
|
||||
});
|
||||
this._pendingEvent = null;
|
||||
this._scheduledTimeouts = new Map();
|
||||
this._nextCallbackTimeoutID = 1;
|
||||
|
||||
const setInt64 = (addr, v) => {
|
||||
this.mem.setUint32(addr + 0, v, true);
|
||||
this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
|
||||
}
|
||||
|
||||
const getInt64 = (addr) => {
|
||||
const low = this.mem.getUint32(addr + 0, true);
|
||||
const high = this.mem.getInt32(addr + 4, true);
|
||||
return low + high * 4294967296;
|
||||
}
|
||||
|
||||
const loadValue = (addr) => {
|
||||
const f = this.mem.getFloat64(addr, true);
|
||||
if (f === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!isNaN(f)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
const id = this.mem.getUint32(addr, true);
|
||||
return this._values[id];
|
||||
}
|
||||
|
||||
const storeValue = (addr, v) => {
|
||||
const nanHead = 0x7FF80000;
|
||||
|
||||
if (typeof v === "number" && v !== 0) {
|
||||
if (isNaN(v)) {
|
||||
this.mem.setUint32(addr + 4, nanHead, true);
|
||||
this.mem.setUint32(addr, 0, true);
|
||||
return;
|
||||
}
|
||||
this.mem.setFloat64(addr, v, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (v === undefined) {
|
||||
this.mem.setFloat64(addr, 0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
let id = this._ids.get(v);
|
||||
if (id === undefined) {
|
||||
id = this._idPool.pop();
|
||||
if (id === undefined) {
|
||||
id = this._values.length;
|
||||
}
|
||||
this._values[id] = v;
|
||||
this._goRefCounts[id] = 0;
|
||||
this._ids.set(v, id);
|
||||
}
|
||||
this._goRefCounts[id]++;
|
||||
let typeFlag = 0;
|
||||
switch (typeof v) {
|
||||
case "object":
|
||||
if (v !== null) {
|
||||
typeFlag = 1;
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
typeFlag = 2;
|
||||
break;
|
||||
case "symbol":
|
||||
typeFlag = 3;
|
||||
break;
|
||||
case "function":
|
||||
typeFlag = 4;
|
||||
break;
|
||||
}
|
||||
this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
|
||||
this.mem.setUint32(addr, id, true);
|
||||
}
|
||||
|
||||
const loadSlice = (addr) => {
|
||||
const array = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
|
||||
}
|
||||
|
||||
const loadSliceOfValues = (addr) => {
|
||||
const array = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
const a = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
a[i] = loadValue(array + i * 8);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
const loadString = (addr) => {
|
||||
const saddr = getInt64(addr + 0);
|
||||
const len = getInt64(addr + 8);
|
||||
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
|
||||
}
|
||||
|
||||
const timeOrigin = Date.now() - performance.now();
|
||||
this.importObject = {
|
||||
go: {
|
||||
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
|
||||
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
|
||||
// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
|
||||
// This changes the SP, thus we have to update the SP used by the imported function.
|
||||
|
||||
// func wasmExit(code int32)
|
||||
"runtime.wasmExit": (sp) => {
|
||||
sp >>>= 0;
|
||||
const code = this.mem.getInt32(sp + 8, true);
|
||||
this.exited = true;
|
||||
delete this._inst;
|
||||
delete this._values;
|
||||
delete this._goRefCounts;
|
||||
delete this._ids;
|
||||
delete this._idPool;
|
||||
this.exit(code);
|
||||
},
|
||||
|
||||
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
|
||||
"runtime.wasmWrite": (sp) => {
|
||||
sp >>>= 0;
|
||||
const fd = getInt64(sp + 8);
|
||||
const p = getInt64(sp + 16);
|
||||
const n = this.mem.getInt32(sp + 24, true);
|
||||
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
|
||||
},
|
||||
|
||||
// func resetMemoryDataView()
|
||||
"runtime.resetMemoryDataView": (sp) => {
|
||||
sp >>>= 0;
|
||||
this.mem = new DataView(this._inst.exports.mem.buffer);
|
||||
},
|
||||
|
||||
// func nanotime1() int64
|
||||
"runtime.nanotime1": (sp) => {
|
||||
sp >>>= 0;
|
||||
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
|
||||
},
|
||||
|
||||
// func walltime1() (sec int64, nsec int32)
|
||||
"runtime.walltime1": (sp) => {
|
||||
sp >>>= 0;
|
||||
const msec = (new Date).getTime();
|
||||
setInt64(sp + 8, msec / 1000);
|
||||
this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
|
||||
},
|
||||
|
||||
// func scheduleTimeoutEvent(delay int64) int32
|
||||
"runtime.scheduleTimeoutEvent": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this._nextCallbackTimeoutID;
|
||||
this._nextCallbackTimeoutID++;
|
||||
this._scheduledTimeouts.set(id, setTimeout(
|
||||
() => {
|
||||
this._resume();
|
||||
while (this._scheduledTimeouts.has(id)) {
|
||||
// for some reason Go failed to register the timeout event, log and try again
|
||||
// (temporary workaround for https://github.com/golang/go/issues/28975)
|
||||
console.warn("scheduleTimeoutEvent: missed timeout event");
|
||||
this._resume();
|
||||
}
|
||||
},
|
||||
getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
|
||||
));
|
||||
this.mem.setInt32(sp + 16, id, true);
|
||||
},
|
||||
|
||||
// func clearTimeoutEvent(id int32)
|
||||
"runtime.clearTimeoutEvent": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this.mem.getInt32(sp + 8, true);
|
||||
clearTimeout(this._scheduledTimeouts.get(id));
|
||||
this._scheduledTimeouts.delete(id);
|
||||
},
|
||||
|
||||
// func getRandomData(r []byte)
|
||||
"runtime.getRandomData": (sp) => {
|
||||
sp >>>= 0;
|
||||
crypto.getRandomValues(loadSlice(sp + 8));
|
||||
},
|
||||
|
||||
// func finalizeRef(v ref)
|
||||
"syscall/js.finalizeRef": (sp) => {
|
||||
sp >>>= 0;
|
||||
const id = this.mem.getUint32(sp + 8, true);
|
||||
this._goRefCounts[id]--;
|
||||
if (this._goRefCounts[id] === 0) {
|
||||
const v = this._values[id];
|
||||
this._values[id] = null;
|
||||
this._ids.delete(v);
|
||||
this._idPool.push(id);
|
||||
}
|
||||
},
|
||||
|
||||
// func stringVal(value string) ref
|
||||
"syscall/js.stringVal": (sp) => {
|
||||
sp >>>= 0;
|
||||
storeValue(sp + 24, loadString(sp + 8));
|
||||
},
|
||||
|
||||
// func valueGet(v ref, p string) ref
|
||||
"syscall/js.valueGet": (sp) => {
|
||||
sp >>>= 0;
|
||||
const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 32, result);
|
||||
},
|
||||
|
||||
// func valueSet(v ref, p string, x ref)
|
||||
"syscall/js.valueSet": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
|
||||
},
|
||||
|
||||
// func valueDelete(v ref, p string)
|
||||
"syscall/js.valueDelete": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
|
||||
},
|
||||
|
||||
// func valueIndex(v ref, i int) ref
|
||||
"syscall/js.valueIndex": (sp) => {
|
||||
sp >>>= 0;
|
||||
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
||||
},
|
||||
|
||||
// valueSetIndex(v ref, i int, x ref)
|
||||
"syscall/js.valueSetIndex": (sp) => {
|
||||
sp >>>= 0;
|
||||
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
|
||||
},
|
||||
|
||||
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
||||
"syscall/js.valueCall": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const m = Reflect.get(v, loadString(sp + 16));
|
||||
const args = loadSliceOfValues(sp + 32);
|
||||
const result = Reflect.apply(m, v, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 56, result);
|
||||
this.mem.setUint8(sp + 64, 1);
|
||||
} catch (err) {
|
||||
storeValue(sp + 56, err);
|
||||
this.mem.setUint8(sp + 64, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueInvoke(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueInvoke": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const args = loadSliceOfValues(sp + 16);
|
||||
const result = Reflect.apply(v, undefined, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueNew(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueNew": (sp) => {
|
||||
sp >>>= 0;
|
||||
try {
|
||||
const v = loadValue(sp + 8);
|
||||
const args = loadSliceOfValues(sp + 16);
|
||||
const result = Reflect.construct(v, args);
|
||||
sp = this._inst.exports.getsp() >>> 0; // see comment above
|
||||
storeValue(sp + 40, result);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
} catch (err) {
|
||||
storeValue(sp + 40, err);
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueLength(v ref) int
|
||||
"syscall/js.valueLength": (sp) => {
|
||||
sp >>>= 0;
|
||||
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
||||
},
|
||||
|
||||
// valuePrepareString(v ref) (ref, int)
|
||||
"syscall/js.valuePrepareString": (sp) => {
|
||||
sp >>>= 0;
|
||||
const str = encoder.encode(String(loadValue(sp + 8)));
|
||||
storeValue(sp + 16, str);
|
||||
setInt64(sp + 24, str.length);
|
||||
},
|
||||
|
||||
// valueLoadString(v ref, b []byte)
|
||||
"syscall/js.valueLoadString": (sp) => {
|
||||
sp >>>= 0;
|
||||
const str = loadValue(sp + 8);
|
||||
loadSlice(sp + 16).set(str);
|
||||
},
|
||||
|
||||
// func valueInstanceOf(v ref, t ref) bool
|
||||
"syscall/js.valueInstanceOf": (sp) => {
|
||||
sp >>>= 0;
|
||||
this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
|
||||
},
|
||||
|
||||
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
||||
"syscall/js.copyBytesToGo": (sp) => {
|
||||
sp >>>= 0;
|
||||
const dst = loadSlice(sp + 8);
|
||||
const src = loadValue(sp + 32);
|
||||
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
setInt64(sp + 40, toCopy.length);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
},
|
||||
|
||||
// func copyBytesToJS(dst ref, src []byte) (int, bool)
|
||||
"syscall/js.copyBytesToJS": (sp) => {
|
||||
sp >>>= 0;
|
||||
const dst = loadValue(sp + 8);
|
||||
const src = loadSlice(sp + 16);
|
||||
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
||||
this.mem.setUint8(sp + 48, 0);
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
setInt64(sp + 40, toCopy.length);
|
||||
this.mem.setUint8(sp + 48, 1);
|
||||
},
|
||||
|
||||
"debug": (value) => {
|
||||
console.log(value);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async run(instance) {
|
||||
if (!(instance instanceof WebAssembly.Instance)) {
|
||||
throw new Error("Go.run: WebAssembly.Instance expected");
|
||||
}
|
||||
this._inst = instance;
|
||||
this.mem = new DataView(this._inst.exports.mem.buffer);
|
||||
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
||||
NaN,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
global,
|
||||
this,
|
||||
];
|
||||
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
|
||||
this._ids = new Map([ // mapping from JS values to reference ids
|
||||
[0, 1],
|
||||
[null, 2],
|
||||
[true, 3],
|
||||
[false, 4],
|
||||
[global, 5],
|
||||
[this, 6],
|
||||
]);
|
||||
this._idPool = []; // unused ids that have been garbage collected
|
||||
this.exited = false; // whether the Go program has exited
|
||||
|
||||
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
|
||||
let offset = 4096;
|
||||
|
||||
const strPtr = (str) => {
|
||||
const ptr = offset;
|
||||
const bytes = encoder.encode(str + "\0");
|
||||
new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
|
||||
offset += bytes.length;
|
||||
if (offset % 8 !== 0) {
|
||||
offset += 8 - (offset % 8);
|
||||
}
|
||||
return ptr;
|
||||
};
|
||||
|
||||
const argc = this.argv.length;
|
||||
|
||||
const argvPtrs = [];
|
||||
this.argv.forEach((arg) => {
|
||||
argvPtrs.push(strPtr(arg));
|
||||
});
|
||||
argvPtrs.push(0);
|
||||
|
||||
const keys = Object.keys(this.env).sort();
|
||||
keys.forEach((key) => {
|
||||
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
|
||||
});
|
||||
argvPtrs.push(0);
|
||||
|
||||
const argv = offset;
|
||||
argvPtrs.forEach((ptr) => {
|
||||
this.mem.setUint32(offset, ptr, true);
|
||||
this.mem.setUint32(offset + 4, 0, true);
|
||||
offset += 8;
|
||||
});
|
||||
|
||||
this._inst.exports.run(argc, argv);
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
await this._exitPromise;
|
||||
}
|
||||
|
||||
_resume() {
|
||||
if (this.exited) {
|
||||
throw new Error("Go program has already exited");
|
||||
}
|
||||
this._inst.exports.resume();
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
}
|
||||
|
||||
_makeFuncWrapper(id) {
|
||||
const go = this;
|
||||
return function () {
|
||||
const event = { id: id, this: this, args: arguments };
|
||||
go._pendingEvent = event;
|
||||
go._resume();
|
||||
return event.result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof module !== "undefined" &&
|
||||
global.require &&
|
||||
global.require.main === module &&
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
!global.process.versions.electron
|
||||
) {
|
||||
if (process.argv.length < 3) {
|
||||
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
go.argv = process.argv.slice(2);
|
||||
go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
|
||||
go.exit = process.exit;
|
||||
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
|
||||
process.on("exit", (code) => { // Node.js exits if no event handler is pending
|
||||
if (code === 0 && !go.exited) {
|
||||
// deadlock, make Go print error and stack traces
|
||||
go._pendingEvent = { id: 0 };
|
||||
go._resume();
|
||||
}
|
||||
});
|
||||
return go.run(result.instance);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -307,7 +307,7 @@ describe('loading:', function() {
|
||||
indexedDB,
|
||||
"matrix-js-sdk:crypto",
|
||||
);
|
||||
await cryptoStore._connect();
|
||||
await cryptoStore.startup();
|
||||
});
|
||||
|
||||
it('shows the last known room by default', function() {
|
||||
|
||||
@@ -32,8 +32,14 @@ module.exports = (env, argv) => {
|
||||
|
||||
entry: {
|
||||
"bundle": "./src/vector/index.js",
|
||||
"indexeddb-worker": "./src/vector/indexeddb-worker.js",
|
||||
"indexeddb_worker": "./src/vector/indexeddb-worker.js",
|
||||
"dendrite_sw": "./src/vector/dendrite-sw.js",
|
||||
"mobileguide": "./src/vector/mobile_guide/index.js",
|
||||
"sqlitejs": "./src/vector/sqlitejs.js",
|
||||
"localforage": "./node_modules/localforage/dist/localforage.min.js",
|
||||
"sql_wasm": "./node_modules/sql.js/dist/sql-wasm.wasm",
|
||||
"dendrite_wasm": "./src/vector/dendrite.wasm",
|
||||
"wasm_exec": "./src/vector/wasm_exec.js",
|
||||
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
|
||||
|
||||
// CSS themes
|
||||
@@ -228,7 +234,9 @@ module.exports = (env, argv) => {
|
||||
loader: "file-loader",
|
||||
type: "javascript/auto", // https://github.com/webpack/webpack/issues/6725
|
||||
options: {
|
||||
name: '[name].[hash:7].[ext]',
|
||||
// fixme: reintroduce [hash] once we can figure out how to pass it into the
|
||||
// dendrite service worker nicely
|
||||
name: '[name].[ext]',
|
||||
outputPath: '.',
|
||||
},
|
||||
},
|
||||
@@ -282,6 +290,10 @@ module.exports = (env, argv) => {
|
||||
]
|
||||
},
|
||||
|
||||
node: {
|
||||
fs: 'empty' // needed for sql.js
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
@@ -339,6 +351,8 @@ module.exports = (env, argv) => {
|
||||
// chunks even after the app is redeployed.
|
||||
filename: "bundles/[hash]/[name].js",
|
||||
chunkFilename: "bundles/[hash]/[name].js",
|
||||
libraryTarget: "var",
|
||||
library: "[name]",
|
||||
},
|
||||
|
||||
// configuration for the webpack-dev-server
|
||||
@@ -346,15 +360,20 @@ module.exports = (env, argv) => {
|
||||
// serve unwebpacked assets from webapp.
|
||||
contentBase: './webapp',
|
||||
|
||||
// Only output errors, warnings, or new compilations.
|
||||
// This hides the massive list of modules.
|
||||
stats: 'minimal',
|
||||
stats: {
|
||||
// don't fill the console up with a mahoosive list of modules
|
||||
chunks: false,
|
||||
},
|
||||
headers: {
|
||||
"Service-Worker-Allowed": "/"
|
||||
},
|
||||
|
||||
// hot module replacement doesn't work (I think we'd need react-hot-reload?)
|
||||
// so webpack-dev-server reloads the page on every update which is quite
|
||||
// tedious in Riot since that can take a while.
|
||||
hot: false,
|
||||
inline: false,
|
||||
writeToDisk: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user