Compare commits
156 Commits
jryans/p2p
...
include-po
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4bc759996 | ||
|
|
2504c062f1 | ||
|
|
9d356aa931 | ||
|
|
17f86b4c39 | ||
|
|
245d13875f | ||
|
|
e7cb419902 | ||
|
|
54b78a64b8 | ||
|
|
95a5a83868 | ||
|
|
ba5e690a38 | ||
|
|
37faa1caa9 | ||
|
|
49dfa4ee6c | ||
|
|
e6e701f5e9 | ||
|
|
c04daeaa20 | ||
|
|
1cffed2336 | ||
|
|
1d54312c9f | ||
|
|
fbb7d457e7 | ||
|
|
95d63d88ee | ||
|
|
af70b09fd7 | ||
|
|
9b5db05115 | ||
|
|
954ade8c23 | ||
|
|
5688079f72 | ||
|
|
c5280f0d83 | ||
|
|
153f6f1583 | ||
|
|
940f44c6d3 | ||
|
|
481f67514b | ||
|
|
90919c20c3 | ||
|
|
3219cc7605 | ||
|
|
89128f28fa | ||
|
|
e0c9a6cc8e | ||
|
|
5e6b791617 | ||
|
|
858d71218c | ||
|
|
2f0129425d | ||
|
|
5ec0ca9175 | ||
|
|
25b15d084d | ||
|
|
3840fc23bd | ||
|
|
9babd7bcd8 | ||
|
|
f64b1a9f0e | ||
|
|
81da85dac4 | ||
|
|
bf88906de8 | ||
|
|
fba49186ed | ||
|
|
4b5b0e9244 | ||
|
|
7cde32ed2e | ||
|
|
e136afe2eb | ||
|
|
cdb4c88b8c | ||
|
|
77edbc27f8 | ||
|
|
87fedc27b1 | ||
|
|
9d56b56116 | ||
|
|
bdb03c4490 | ||
|
|
b70eb4aba7 | ||
|
|
cfa47eec00 | ||
|
|
d75b0b4ee8 | ||
|
|
85e1225a2c | ||
|
|
bc07d91810 | ||
|
|
183cf150e8 | ||
|
|
13efb396a9 | ||
|
|
7619e35638 | ||
|
|
7f735a6c6e | ||
|
|
331678b913 | ||
|
|
b23aad28ca | ||
|
|
ebc1d01f30 | ||
|
|
ae2e3e8502 | ||
|
|
c7a935777d | ||
|
|
26fef6f294 | ||
|
|
827be982b7 | ||
|
|
c3382199bf | ||
|
|
75e4d16462 | ||
|
|
b03b4582c0 | ||
|
|
1aa04fa83c | ||
|
|
2635fc95f4 | ||
|
|
43695fbc58 | ||
|
|
8289d6937f | ||
|
|
2877ef8fcf | ||
|
|
f65231268d | ||
|
|
bd4c6b8c6c | ||
|
|
a0f55c9e8f | ||
|
|
fcf1f06b31 | ||
|
|
a79a05cd52 | ||
|
|
ca8cbacad7 | ||
|
|
2194e4cc10 | ||
|
|
43afa4c1e0 | ||
|
|
322c957f7f | ||
|
|
6c476bdc0d | ||
|
|
7aca548f65 | ||
|
|
80bc659b11 | ||
|
|
5646f3aff8 | ||
|
|
d58c4deebb | ||
|
|
280e6528f1 | ||
|
|
d4638f8825 | ||
|
|
22a25f0e95 | ||
|
|
b79868c57d | ||
|
|
55ca8d14bb | ||
|
|
c6a8675526 | ||
|
|
12b93c1696 | ||
|
|
503995270c | ||
|
|
6155c5bea0 | ||
|
|
6a89d15807 | ||
|
|
0b6789c290 | ||
|
|
202f3de54b | ||
|
|
20e5e86f1a | ||
|
|
3c10bc54ba | ||
|
|
c695ae05a8 | ||
|
|
609674cc17 | ||
|
|
54ebd0cca1 | ||
|
|
66ada58101 | ||
|
|
e3a301054e | ||
|
|
ad07728dfb | ||
|
|
2f59819817 | ||
|
|
2983215dee | ||
|
|
09d8ac1622 | ||
|
|
c7809ca751 | ||
|
|
b6937419fb | ||
|
|
111021987c | ||
|
|
ab1bea376e | ||
|
|
fac72921b6 | ||
|
|
1e83c35ece | ||
|
|
629630a086 | ||
|
|
8f92a28b12 | ||
|
|
f6f41dc1eb | ||
|
|
6e15d0f5e8 | ||
|
|
ede6a33f9d | ||
|
|
2760a36f47 | ||
|
|
0ed325c628 | ||
|
|
4d4e2c6230 | ||
|
|
e30e3722ae | ||
|
|
4c28a7f485 | ||
|
|
0b5e7da2d9 | ||
|
|
6d46ed8082 | ||
|
|
682247241f | ||
|
|
538837decf | ||
|
|
56fbfc1648 | ||
|
|
0b74d351c5 | ||
|
|
7a04b2f0c0 | ||
|
|
949cf98c77 | ||
|
|
9cb8e8957c | ||
|
|
2d4446fb65 | ||
|
|
cae8ffb5e4 | ||
|
|
f3a8930174 | ||
|
|
2dcc260afb | ||
|
|
35dd6d7521 | ||
|
|
796d00da3e | ||
|
|
5e415fc949 | ||
|
|
acee86f97c | ||
|
|
72d762ea0d | ||
|
|
8f8202494a | ||
|
|
27c7030034 | ||
|
|
3326242915 | ||
|
|
c4ba5ce02a | ||
|
|
f8da8254ce | ||
|
|
76d9f26a16 | ||
|
|
19a785ce18 | ||
|
|
1408bcd0e1 | ||
|
|
078d6a0d98 | ||
|
|
d914b13c1b | ||
|
|
6764c7e779 | ||
|
|
c044e1a00c | ||
|
|
8fdb41412f |
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -12,11 +12,11 @@ that aren't relevant to your particular case.
|
||||
Text between <!-- and --> marks will be invisible in the report.
|
||||
-->
|
||||
|
||||
### Description
|
||||
#### Description
|
||||
|
||||
Describe here the problem that you are experiencing, or the feature you are requesting.
|
||||
|
||||
### Steps to reproduce
|
||||
#### Steps to reproduce
|
||||
|
||||
- For bugs, list the steps
|
||||
- that reproduce the bug
|
||||
@@ -30,7 +30,7 @@ file a bug here too! -->
|
||||
|
||||
<!-- Include screenshots if possible: you can drag and drop images below. -->
|
||||
|
||||
### Version information
|
||||
#### Version information
|
||||
|
||||
<!-- IMPORTANT: please answer the following questions, to help us narrow down the problem -->
|
||||
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
6
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -21,11 +21,11 @@ that aren't relevant to your particular case.
|
||||
Text between <!-- and --> marks will be invisible in the report.
|
||||
-->
|
||||
|
||||
### Description
|
||||
#### Description
|
||||
|
||||
Describe here the problem that you are experiencing, or the feature you are requesting.
|
||||
|
||||
### Steps to reproduce
|
||||
#### Steps to reproduce
|
||||
|
||||
- For bugs, list the steps
|
||||
- that reproduce the bug
|
||||
@@ -40,7 +40,7 @@ Logs being sent: yes/no
|
||||
|
||||
<!-- Include screenshots if possible: you can drag and drop images below. -->
|
||||
|
||||
### Version information
|
||||
#### Version information
|
||||
|
||||
<!-- IMPORTANT: please answer the following questions, to help us narrow down the problem -->
|
||||
|
||||
|
||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,3 +1,11 @@
|
||||
<!-- Please read CONTRIBUTING.md before submitting your pull request -->
|
||||
<!-- Please read https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md before submitting your pull request -->
|
||||
|
||||
<!-- Include a Sign-Off at the end of your Pull Request (as described in CONTRIBUTING.md), or on every commit -->
|
||||
<!-- Include a Sign-Off as described in https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md#sign-off -->
|
||||
|
||||
<!-- To specify text for the changelog entry (otherwise the PR title will be used):
|
||||
Notes:
|
||||
|
||||
Changelog entries will also appear in element-desktop. For PRs that *only* affect the desktop version:
|
||||
Notes: none
|
||||
element-desktop notes: <notes>
|
||||
-->
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -19,3 +19,5 @@ electron/pub
|
||||
/src/component-index.js
|
||||
/.tmp
|
||||
/webpack-stats.json
|
||||
.vscode
|
||||
.vscode/
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"minify": true,
|
||||
"classPrefix": "modernizr_",
|
||||
"options": [
|
||||
"setClasses"
|
||||
],
|
||||
"enableClasses": false,
|
||||
"feature-detects": [
|
||||
"test/css/animations",
|
||||
"test/css/displaytable",
|
||||
|
||||
205
CHANGELOG.md
205
CHANGELOG.md
@@ -1,3 +1,208 @@
|
||||
Changes in [1.7.34](https://github.com/vector-im/element-desktop/releases/tag/v1.7.34) (2021-07-02)
|
||||
===================================================================================================
|
||||
|
||||
## 🔒 SECURITY FIXES
|
||||
* Sanitize untrusted variables from message previews before translation
|
||||
Fixes vector-im/element-web#18314
|
||||
|
||||
## ✨ Features
|
||||
* Fix editing of `<sub>` & `<sup`> & `<u>`
|
||||
[\#6469](https://github.com/matrix-org/matrix-react-sdk/pull/6469)
|
||||
Fixes #18211
|
||||
* Zoom images in lightbox to where the cursor points
|
||||
[\#6418](https://github.com/matrix-org/matrix-react-sdk/pull/6418)
|
||||
Fixes #17870
|
||||
* Avoid hitting the settings store from TextForEvent
|
||||
[\#6205](https://github.com/matrix-org/matrix-react-sdk/pull/6205)
|
||||
Fixes #17650
|
||||
* Initial MSC3083 + MSC3244 support
|
||||
[\#6212](https://github.com/matrix-org/matrix-react-sdk/pull/6212)
|
||||
Fixes #17686 and #17661
|
||||
* Navigate to the first room with notifications when clicked on space notification dot
|
||||
[\#5974](https://github.com/matrix-org/matrix-react-sdk/pull/5974)
|
||||
* Add matrix: to the list of permitted URL schemes
|
||||
[\#6388](https://github.com/matrix-org/matrix-react-sdk/pull/6388)
|
||||
* Add "Copy Link" to room context menu
|
||||
[\#6374](https://github.com/matrix-org/matrix-react-sdk/pull/6374)
|
||||
* 💭 Message bubble layout
|
||||
[\#6291](https://github.com/matrix-org/matrix-react-sdk/pull/6291)
|
||||
Fixes #4635, #17773 #16220 and #7687
|
||||
* Play only one audio file at a time
|
||||
[\#6417](https://github.com/matrix-org/matrix-react-sdk/pull/6417)
|
||||
Fixes #17439
|
||||
* Move download button for media to the action bar
|
||||
[\#6386](https://github.com/matrix-org/matrix-react-sdk/pull/6386)
|
||||
Fixes #17943
|
||||
* Improved display of one-to-one call history with summary boxes for each call
|
||||
[\#6121](https://github.com/matrix-org/matrix-react-sdk/pull/6121)
|
||||
Fixes #16409
|
||||
* Notification settings UI refresh
|
||||
[\#6352](https://github.com/matrix-org/matrix-react-sdk/pull/6352)
|
||||
Fixes #17782
|
||||
* Fix EventIndex double handling events and erroring
|
||||
[\#6385](https://github.com/matrix-org/matrix-react-sdk/pull/6385)
|
||||
Fixes #18008
|
||||
* Improve reply rendering
|
||||
[\#3553](https://github.com/matrix-org/matrix-react-sdk/pull/3553)
|
||||
Fixes vector-im/riot-web#9217, vector-im/riot-web#7633, vector-im/riot-web#7530, vector-im/riot-web#7169, vector-im/riot-web#7151, vector-im/riot-web#6692 vector-im/riot-web#6579 and #17440
|
||||
* Improve performance of room name calculation
|
||||
[\#1801](https://github.com/matrix-org/matrix-js-sdk/pull/1801)
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix browser history getting stuck looping back to the same room
|
||||
[\#18053](https://github.com/vector-im/element-web/pull/18053)
|
||||
* Fix space shortcuts on layouts with non-English keys in the places of numbers
|
||||
[\#17780](https://github.com/vector-im/element-web/pull/17780)
|
||||
Fixes #17776
|
||||
* Fix CreateRoomDialog exploding when making public room outside of a space
|
||||
[\#6493](https://github.com/matrix-org/matrix-react-sdk/pull/6493)
|
||||
* Fix regression where registration would soft-crash on captcha
|
||||
[\#6505](https://github.com/matrix-org/matrix-react-sdk/pull/6505)
|
||||
Fixes #18284
|
||||
* only send join rule event if we have a join rule to put in it
|
||||
[\#6517](https://github.com/matrix-org/matrix-react-sdk/pull/6517)
|
||||
* Improve the new download button's discoverability and interactions.
|
||||
[\#6510](https://github.com/matrix-org/matrix-react-sdk/pull/6510)
|
||||
* Fix voice recording UI looking broken while microphone permissions are being requested.
|
||||
[\#6479](https://github.com/matrix-org/matrix-react-sdk/pull/6479)
|
||||
Fixes #18223
|
||||
* Match colors of room and user avatars in DMs
|
||||
[\#6393](https://github.com/matrix-org/matrix-react-sdk/pull/6393)
|
||||
Fixes #2449
|
||||
* Fix onPaste handler to work with copying files from Finder
|
||||
[\#5389](https://github.com/matrix-org/matrix-react-sdk/pull/5389)
|
||||
Fixes #15536 and #16255
|
||||
* Fix infinite pagination loop when offline
|
||||
[\#6478](https://github.com/matrix-org/matrix-react-sdk/pull/6478)
|
||||
Fixes #18242
|
||||
* Fix blurhash rounded corners missing regression
|
||||
[\#6467](https://github.com/matrix-org/matrix-react-sdk/pull/6467)
|
||||
Fixes #18110
|
||||
* Fix position of the space hierarchy spinner
|
||||
[\#6462](https://github.com/matrix-org/matrix-react-sdk/pull/6462)
|
||||
Fixes #18182
|
||||
* Fix display of image messages that lack thumbnails
|
||||
[\#6456](https://github.com/matrix-org/matrix-react-sdk/pull/6456)
|
||||
Fixes #18175
|
||||
* Fix crash with large audio files.
|
||||
[\#6436](https://github.com/matrix-org/matrix-react-sdk/pull/6436)
|
||||
Fixes #18149
|
||||
* Make diff colors in codeblocks more pleasant
|
||||
[\#6355](https://github.com/matrix-org/matrix-react-sdk/pull/6355)
|
||||
Fixes #17939
|
||||
* Show the correct audio file duration while loading the file.
|
||||
[\#6435](https://github.com/matrix-org/matrix-react-sdk/pull/6435)
|
||||
Fixes #18160
|
||||
* Fix various timeline settings not applying immediately.
|
||||
[\#6261](https://github.com/matrix-org/matrix-react-sdk/pull/6261)
|
||||
Fixes #17748
|
||||
* Fix issues with room list duplication
|
||||
[\#6391](https://github.com/matrix-org/matrix-react-sdk/pull/6391)
|
||||
Fixes #14508
|
||||
* Fix grecaptcha throwing useless error sometimes
|
||||
[\#6401](https://github.com/matrix-org/matrix-react-sdk/pull/6401)
|
||||
Fixes #15142
|
||||
* Update Emojibase and Twemoji and switch to IamCal (Slack-style) shortcodes
|
||||
[\#6347](https://github.com/matrix-org/matrix-react-sdk/pull/6347)
|
||||
Fixes #13857 and #13334
|
||||
* Respect compound emojis in default avatar initial generation
|
||||
[\#6397](https://github.com/matrix-org/matrix-react-sdk/pull/6397)
|
||||
Fixes #18040
|
||||
* Fix bug where the 'other homeserver' field in the server selection dialog would become briefly focus and then unfocus when clicked.
|
||||
[\#6394](https://github.com/matrix-org/matrix-react-sdk/pull/6394)
|
||||
Fixes #18031
|
||||
* Standardise spelling and casing of homeserver, identity server, and integration manager
|
||||
[\#6365](https://github.com/matrix-org/matrix-react-sdk/pull/6365)
|
||||
* Fix widgets not receiving decrypted events when they have permission.
|
||||
[\#6371](https://github.com/matrix-org/matrix-react-sdk/pull/6371)
|
||||
Fixes #17615
|
||||
* Prevent client hangs when calculating blurhashes
|
||||
[\#6366](https://github.com/matrix-org/matrix-react-sdk/pull/6366)
|
||||
Fixes #17945
|
||||
* Exclude state events from widgets reading room events
|
||||
[\#6378](https://github.com/matrix-org/matrix-react-sdk/pull/6378)
|
||||
* Cache feature_spaces\* flags to improve performance
|
||||
[\#6381](https://github.com/matrix-org/matrix-react-sdk/pull/6381)
|
||||
|
||||
Changes in [1.7.33](https://github.com/vector-im/element-web/releases/tag/v1.7.33) (2021-07-19)
|
||||
===============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/element-web/compare/v1.7.33-rc.1...v1.7.33)
|
||||
|
||||
* No changes from rc.1
|
||||
|
||||
Changes in [1.7.33-rc.1](https://github.com/vector-im/element-web/releases/tag/v1.7.33-rc.1) (2021-07-14)
|
||||
=========================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/element-web/compare/v1.7.32...v1.7.33-rc.1)
|
||||
|
||||
* Translations update from Weblate
|
||||
[\#17991](https://github.com/vector-im/element-web/pull/17991)
|
||||
* Revert "Don't run nginx as root in docker"
|
||||
[\#17990](https://github.com/vector-im/element-web/pull/17990)
|
||||
* Don't run nginx as root in docker
|
||||
[\#17927](https://github.com/vector-im/element-web/pull/17927)
|
||||
* Add VS Code to gitignore
|
||||
[\#17982](https://github.com/vector-im/element-web/pull/17982)
|
||||
* Remove canvas native dependencies from Dockerfile
|
||||
[\#17973](https://github.com/vector-im/element-web/pull/17973)
|
||||
* Remove node-canvas devDependency
|
||||
[\#17967](https://github.com/vector-im/element-web/pull/17967)
|
||||
* Add `reskindex` to development steps
|
||||
[\#17926](https://github.com/vector-im/element-web/pull/17926)
|
||||
* Update Modernizr and stop it from polluting classes on the html tag
|
||||
[\#17921](https://github.com/vector-im/element-web/pull/17921)
|
||||
* Convert a few files to TS
|
||||
[\#17895](https://github.com/vector-im/element-web/pull/17895)
|
||||
* Do not generate a lockfile when running in CI
|
||||
[\#17902](https://github.com/vector-im/element-web/pull/17902)
|
||||
* Fix lockfile to match listed dependencies
|
||||
[\#17888](https://github.com/vector-im/element-web/pull/17888)
|
||||
* Remove PostCSS calc() processing
|
||||
[\#17856](https://github.com/vector-im/element-web/pull/17856)
|
||||
* Make issue template styling more consistent and improve PR template
|
||||
[\#17691](https://github.com/vector-im/element-web/pull/17691)
|
||||
* Update jsrsasign to ^10.2.0 (Includes fix for CVE-2021-30246)
|
||||
[\#17170](https://github.com/vector-im/element-web/pull/17170)
|
||||
* Migrate to `eslint-plugin-matrix-org`
|
||||
[\#17847](https://github.com/vector-im/element-web/pull/17847)
|
||||
* Remove spurious overflow: auto on #matrixchat element
|
||||
[\#17647](https://github.com/vector-im/element-web/pull/17647)
|
||||
* Enhance security by disallowing CSP object-src rule
|
||||
[\#17818](https://github.com/vector-im/element-web/pull/17818)
|
||||
|
||||
Changes in [1.7.32](https://github.com/vector-im/element-web/releases/tag/v1.7.32) (2021-07-05)
|
||||
===============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/element-web/compare/v1.7.32-rc.1...v1.7.32)
|
||||
|
||||
* No changes from rc.1
|
||||
|
||||
Changes in [1.7.32-rc.1](https://github.com/vector-im/element-web/releases/tag/v1.7.32-rc.1) (2021-06-29)
|
||||
=========================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/element-web/compare/v1.7.31...v1.7.32-rc.1)
|
||||
|
||||
* Update to react-sdk v3.25.0-rc.1 and js-sdk v12.0.1-rc.1
|
||||
* Translations update from Weblate
|
||||
[\#17832](https://github.com/vector-im/element-web/pull/17832)
|
||||
* Fix canvas-filter-polyfill mock path
|
||||
[\#17785](https://github.com/vector-im/element-web/pull/17785)
|
||||
* Mock context-filter-polyfill for app-tests
|
||||
[\#17774](https://github.com/vector-im/element-web/pull/17774)
|
||||
* Add libera.chat to default room directory
|
||||
[\#17772](https://github.com/vector-im/element-web/pull/17772)
|
||||
* Improve typing of Event Index Manager / Seshat
|
||||
[\#17704](https://github.com/vector-im/element-web/pull/17704)
|
||||
* Bump dns-packet from 1.3.1 to 1.3.4
|
||||
[\#17478](https://github.com/vector-im/element-web/pull/17478)
|
||||
* Update matrix-widget-api to fix build issues
|
||||
[\#17747](https://github.com/vector-im/element-web/pull/17747)
|
||||
* Fix whitespace in Dockerfile
|
||||
[\#17742](https://github.com/vector-im/element-web/pull/17742)
|
||||
* Upgrade @types/react and @types/react-dom
|
||||
[\#17723](https://github.com/vector-im/element-web/pull/17723)
|
||||
* Spaces keyboard shortcuts first cut
|
||||
[\#17457](https://github.com/vector-im/element-web/pull/17457)
|
||||
* Labs: feature_report_to_moderators
|
||||
[\#17694](https://github.com/vector-im/element-web/pull/17694)
|
||||
|
||||
Changes in [1.7.31](https://github.com/vector-im/element-web/releases/tag/v1.7.31) (2021-06-21)
|
||||
===============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/element-web/compare/v1.7.31-rc.1...v1.7.31)
|
||||
|
||||
@@ -9,10 +9,7 @@ ARG REACT_SDK_BRANCH="master"
|
||||
ARG JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git"
|
||||
ARG JS_SDK_BRANCH="master"
|
||||
|
||||
RUN apt-get update && apt-get install -y git dos2unix \
|
||||
# These packages are required for building Canvas on architectures like Arm
|
||||
# See https://www.npmjs.com/package/canvas#compiling
|
||||
build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
|
||||
RUN apt-get update && apt-get install -y git dos2unix
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
|
||||
@@ -298,6 +298,7 @@ cd element-web
|
||||
yarn link matrix-js-sdk
|
||||
yarn link matrix-react-sdk
|
||||
yarn install
|
||||
yarn reskindex
|
||||
yarn start
|
||||
```
|
||||
|
||||
|
||||
@@ -125,14 +125,6 @@ or feedback for this functionality at this time.
|
||||
Allows users to receive encrypted messages by creating a device that is stored
|
||||
encrypted on the server, as described in [MSC2697](https://github.com/matrix-org/matrix-doc/pull/2697).
|
||||
|
||||
## Voice messages (`feature_voice_messages`)
|
||||
|
||||
Offers a way to send more time-sensitive information through the power of voice. When enabled, use the microphone
|
||||
icon on the lower right to start recording your message. You will have a chance to review after you're done recording,
|
||||
and if it sounds fine then send it off for the rest of the room to hear.
|
||||
|
||||
Voice messages are automatically downloaded to ensure they are ready for playback as soon as possible.
|
||||
|
||||
## Do not disturb (`feature_dnd`)
|
||||
|
||||
Enables UI for turning on "do not disturb" mode for the current device. When DND mode is engaged, popups
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
## Step 0: Join #element-translations:matrix.org
|
||||
|
||||
1. Come and join https://matrix.to/#/#element-translations:matrix.org
|
||||
2. Read scrollback and/or ask if anyone else is working on your language, and co-ordinate if needed. In general little-or-no coordination is needed though :)
|
||||
1. Come and join https://matrix.to/#/#element-translations:matrix.org for general discussion
|
||||
2. Join https://matrix.to/#/#element-translators:matrix.org for language-specific rooms
|
||||
3. Read scrollback and/or ask if anyone else is working on your language, and co-ordinate if needed. In general little-or-no coordination is needed though :)
|
||||
|
||||
## Step 1: Preparing your Weblate Profile
|
||||
|
||||
|
||||
22
package.json
22
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.7.31",
|
||||
"version": "1.7.34",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -48,7 +48,8 @@
|
||||
"start:res": "yarn build:jitsi && 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 --disable-host-check",
|
||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
|
||||
"lint:js": "eslint src",
|
||||
"lint:js": "eslint --max-warnings 0 src",
|
||||
"lint:js-fix": "eslint --fix src",
|
||||
"lint:types": "tsc --noEmit --jsx react",
|
||||
"lint:style": "stylelint 'res/css/**/*.scss'",
|
||||
"test": "jest"
|
||||
@@ -60,7 +61,6 @@
|
||||
"highlight.js": "^10.5.0",
|
||||
"jsrsasign": "^10.2.0",
|
||||
"katex": "^0.12.0",
|
||||
"localforage": "^1.7.3",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
|
||||
"matrix-widget-api": "^0.1.0-beta.15",
|
||||
@@ -68,7 +68,6 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"sanitize-html": "^2.3.2",
|
||||
"sql.js": "github:neilalexander/sql.js#252a72bf57b0538cbd49bbd6f70af71e516966ae",
|
||||
"ua-parser-js": "^0.7.24"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -101,7 +100,6 @@
|
||||
"autoprefixer": "^9.8.6",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-loader": "^8.2.2",
|
||||
"canvas": "^2.6.1",
|
||||
"chokidar": "^3.5.1",
|
||||
"concurrently": "^5.3.0",
|
||||
"cpx": "^1.5.0",
|
||||
@@ -126,10 +124,9 @@
|
||||
"mini-css-extract-plugin": "^0.12.0",
|
||||
"minimist": "^1.2.5",
|
||||
"mkdirp": "^1.0.4",
|
||||
"modernizr": "^3.11.4",
|
||||
"modernizr": "^3.11.7",
|
||||
"node-fetch": "^2.6.1",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||
"postcss-calc": "^7.0.5",
|
||||
"postcss-easings": "^2.0.0",
|
||||
"postcss-extend": "^1.0.5",
|
||||
"postcss-hexrgba": "^2.0.1",
|
||||
@@ -145,16 +142,19 @@
|
||||
"shell-escape": "^0.2.0",
|
||||
"simple-proxy-agent": "^1.1.0",
|
||||
"stylelint": "^13.9.0",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"stylelint-scss": "^3.18.0",
|
||||
"terser-webpack-plugin": "^2.3.8",
|
||||
"typescript": "^4.1.3",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"worker-loader": "^2.0.0"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jest-environment-jsdom-sixteen",
|
||||
"testMatch": [
|
||||
"<rootDir>/test/**/*-test.js"
|
||||
"<rootDir>/test/**/*-test.[tj]s"
|
||||
],
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/node_modules/matrix-react-sdk/test/setupTests.js"
|
||||
@@ -171,7 +171,9 @@
|
||||
"decoderWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"decoderWorker\\.min\\.wasm": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"waveWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"context-filter-polyfill": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js"
|
||||
"context-filter-polyfill": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"FontManager.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/FontManager.js",
|
||||
"workers/(.+)\\.worker\\.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/workerMock.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!matrix-js-sdk).+$",
|
||||
|
||||
@@ -76,7 +76,7 @@ dodep matrix-org matrix-js-sdk
|
||||
|
||||
pushd matrix-js-sdk
|
||||
yarn link
|
||||
yarn install
|
||||
yarn install --pure-lockfile
|
||||
popd
|
||||
|
||||
yarn link matrix-js-sdk
|
||||
@@ -90,7 +90,7 @@ dodep matrix-org matrix-react-sdk
|
||||
pushd matrix-react-sdk
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
yarn install
|
||||
yarn install --pure-lockfile
|
||||
yarn reskindex
|
||||
popd
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
android = [];
|
||||
}
|
||||
|
||||
let mobileHeader = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
|
||||
let mobileHeader = <h2 id="step2_heading">{ _t("Use %(brand)s on mobile", { brand }) }</h2>;
|
||||
if (!android.length && !ios) {
|
||||
mobileHeader = null;
|
||||
}
|
||||
@@ -102,11 +102,11 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
'or <safariLink>Safari</safariLink> for the best experience.',
|
||||
{},
|
||||
{
|
||||
'chromeLink': (sub) => <a href="https://www.google.com/chrome">{sub}</a>,
|
||||
'firefoxLink': (sub) => <a href="https://firefox.com">{sub}</a>,
|
||||
'safariLink': (sub) => <a href="https://apple.com/safari">{sub}</a>,
|
||||
'chromeLink': (sub) => <a href="https://www.google.com/chrome">{ sub }</a>,
|
||||
'firefoxLink': (sub) => <a href="https://firefox.com">{ sub }</a>,
|
||||
'safariLink': (sub) => <a href="https://apple.com/safari">{ sub }</a>,
|
||||
},
|
||||
)}
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
@@ -124,9 +124,9 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
{mobileHeader}
|
||||
{ios}
|
||||
{android}
|
||||
{ mobileHeader }
|
||||
{ ios }
|
||||
{ android }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,9 +40,9 @@ const ErrorView: React.FC<IProps> = ({ title, messages }) => {
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">{ title }</h2>
|
||||
{messages && messages.map(msg => <p key={msg}>
|
||||
{ messages && messages.map(msg => <p key={msg}>
|
||||
{ msg }
|
||||
</p>)}
|
||||
</p>) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -35,14 +35,14 @@ const VectorAuthFooter = () => {
|
||||
for (const linkEntry of links) {
|
||||
authFooterLinks.push(
|
||||
<a href={linkEntry.url} key={linkEntry.text} target="_blank" rel="noreferrer noopener">
|
||||
{linkEntry.text}
|
||||
{ linkEntry.text }
|
||||
</a>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_AuthFooter">
|
||||
{authFooterLinks}
|
||||
{ authFooterLinks }
|
||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t('Powered by Matrix') }</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -32,5 +32,7 @@
|
||||
"I understand the risks and wish to continue": "Entenc els riscos i vull continuar",
|
||||
"Go to element.io": "Vés a element.io",
|
||||
"Failed to start": "Ha fallat l'inici",
|
||||
"Missing indexeddb worker script!": "Falta l'script del treballador indexeddb!"
|
||||
"Missing indexeddb worker script!": "Falta l'script del treballador indexeddb!",
|
||||
"Use %(brand)s on mobile": "Utilitza %(brand)s al mòbil",
|
||||
"Switch to space by number": "Canvia d'espai per número"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Powered by Matrix": "Běží na Matrixu",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s pro desktopový počítač (%(platformName)s)",
|
||||
"Missing indexeddb worker script!": "Nenačetl se skript spravující indexdb!",
|
||||
"Use %(brand)s on mobile": "Používání %(brand)s v mobilních zařízeních"
|
||||
"Use %(brand)s on mobile": "Používání %(brand)s v mobilních zařízeních",
|
||||
"Switch to space by number": "Přepnout na prostor podle čísla"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s verwendet erweiterte Browserfunktionen, die von deinem Browser nicht unterstützt werden.",
|
||||
"Your browser can't run %(brand)s": "Dein Browser kann %(brand)s nicht ausführen",
|
||||
"Powered by Matrix": "Betrieben mit Matrix",
|
||||
"Use %(brand)s on mobile": "Verwende %(brand)s am Handy"
|
||||
"Use %(brand)s on mobile": "Verwende %(brand)s am Handy",
|
||||
"Switch to space by number": "Zum n-ten Space wechseln"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"Missing indexeddb worker script!": "Missing indexeddb worker script!",
|
||||
"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.",
|
||||
"Your Element is misconfigured": "Your Element is misconfigured",
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "Tu navegador no es compatible con %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s usa funciones avanzadas que su navegador actual no soporta.",
|
||||
"Powered by Matrix": "Funciona con Matrix",
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s en modo móvil"
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s en modo móvil",
|
||||
"Switch to space by number": "Cambiar a espacio por número"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "%(brand)s ei toimi sinu brauseris",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s kasutab mitmeid uusi brauseri-põhiseid tehnoloogiaid, mis ei ole veel sinu veebibrauseris toetatud.",
|
||||
"Powered by Matrix": "Põhineb Matrix'il",
|
||||
"Use %(brand)s on mobile": "Kasuta %(brand)s rakendust nutiseadmes"
|
||||
"Use %(brand)s on mobile": "Kasuta %(brand)s rakendust nutiseadmes",
|
||||
"Switch to space by number": "Vaata kogukonnakeskust tema numbri alusel"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Go to element.io": "برو به element.io",
|
||||
"Failed to start": "مشکل در آغاز",
|
||||
"Powered by Matrix": "قدرتگرفته از ماتریکس",
|
||||
"Use %(brand)s on mobile": "استفاده از %(brand)s روی گوشی"
|
||||
"Use %(brand)s on mobile": "استفاده از %(brand)s روی گوشی",
|
||||
"Switch to space by number": "تغییر به فضا با شماره"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "O teu navegador non pode executar %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s utiliza características avanzadas do navegador que non están dispoñibles no teu navegador.",
|
||||
"Powered by Matrix": "Funciona grazas a Matrix",
|
||||
"Use %(brand)s on mobile": "Utiliza %(brand)s no móbil"
|
||||
"Use %(brand)s on mobile": "Utiliza %(brand)s no móbil",
|
||||
"Switch to space by number": "Cambiar a espazo polo número"
|
||||
}
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
"Unknown device": "מכשיר לא ידוע",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "עליך להשתמש ב HTTPS בכדי לבצע שיחת ווידאו משותפת.",
|
||||
"Welcome to Element": "ברוכים הבאים ל Element",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "צ'ט מוצפן & ושת\"פ נעשה ע\"י ה [matrix]",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "צא'ט וכלי שיתוף פעולה מבוזר ומוצפן & מופעל באמצעות [matrix]",
|
||||
"Invalid JSON": "JSON לא חוקי",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "תצורה שגויה: ניתן לציין רק אחד מהבאים, default_server_config, default_server_name, או default_hs_url.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "תצורה שגויה: ניתן לציין רק אחד מהערכים הבאים, default_server_config, default_server_name, או default_hs_url.",
|
||||
"Invalid configuration: no default server specified.": "תצורה שגויה: לא צוין שרת ברירת מחדל.",
|
||||
"Open user settings": "פתח הגדרות משתמש",
|
||||
"Go to your browser to complete Sign In": "עבור לדפדפן להמשך ההתחברות",
|
||||
"Explore rooms": "שיטוט בחדרים",
|
||||
"Explore rooms": "גלה חדרים",
|
||||
"Create Account": "יצירת חשבון",
|
||||
"Sign In": "כניסה",
|
||||
"Previous/next recently visited room or community": "הבא\\קודם חדרים וקהילות שביקרתם לאחרונה",
|
||||
"Open": "פתח",
|
||||
"Download Completed": "ההורדה הושלמה",
|
||||
"Unexpected error preparing the app. See console for details.": "שגיאה לא צפויה במהלך הכנת האפליקציה. ראו קונסול לפרטים נוספים.",
|
||||
"Unable to load config file: please refresh the page to try again.": "לא יכול לקרוא את קובץ ההגדרות: אנא אתחלו את הדף לנסות שנית.",
|
||||
"Unexpected error preparing the app. See console for details.": "שגיאה לא צפויה במהלך טעינת האפליקציה. ראו קונסול לפרטים נוספים.",
|
||||
"Unable to load config file: please refresh the page to try again.": "לא ניתן לטעון את קובץ ההגדרות: יש לרענן את הדף כדי לנסות שנית.",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "האלמנט מכיל הגדרת JSON שגויה, אנא תקנו את הבעיה ואתחלו את הדף.",
|
||||
"Your Element is misconfigured": "האלמנט מוגדר באופן שגוי",
|
||||
"Go to element.io": "חזור לאתר הראשי: element.io",
|
||||
@@ -31,6 +31,7 @@
|
||||
"Powered by Matrix": "מופעל על ידי מטריקס",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s שולחן עבודה %(platformName)s",
|
||||
"The message from the parser is: %(message)s": "ההודעה מהניתוח היא: %(message)s",
|
||||
"Missing indexeddb worker script!": "סקריפט indexeddb worker חסר!"
|
||||
"The message from the parser is: %(message)s": "ההודעה מהמנתח היא: %(message)s",
|
||||
"Missing indexeddb worker script!": "סקריפט indexeddb worker חסר!",
|
||||
"Switch to space by number": "עבור אל 'Space' על פי מספרו"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "A böngészője nem tudja futtatni ezt: %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s speciális böngészőfunkciókat használ, amelyeket a jelenlegi böngészője nem támogat.",
|
||||
"Powered by Matrix": "A gépházban: Matrix",
|
||||
"Use %(brand)s on mobile": "Mobilon használd ezt: %(brand)s"
|
||||
"Use %(brand)s on mobile": "Mobilon használd ezt: %(brand)s",
|
||||
"Switch to space by number": "Tér váltás számmal"
|
||||
}
|
||||
|
||||
@@ -7,5 +7,32 @@
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Obrolan terenkripsi, terdesentralisasi & kolaborasi didukung oleh [matrix]",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Konfigurasi Element Anda mengandung JSON yang tidak valid. Mohon perbaiki masalahnya dan muat ulang halaman nya.",
|
||||
"Invalid configuration: no default server specified.": "Konfigurasi tidak valid: server default belum ditentukan.",
|
||||
"Missing indexeddb worker script!": "Tidak ada script worker indexeddb!"
|
||||
"Missing indexeddb worker script!": "Tidak ada script worker indexeddb!",
|
||||
"Explore rooms": "Jelajahi ruang",
|
||||
"Create Account": "Buat Akun",
|
||||
"Switch to space by number": "Beralih ke ruang dengan nomor",
|
||||
"Go to your browser to complete Sign In": "Buka browser Anda untuk menyelesaikan Masuk",
|
||||
"Sign In": "Masuk",
|
||||
"Failed to start": "Gagal untuk memulai",
|
||||
"Go to element.io": "Buka element.io",
|
||||
"I understand the risks and wish to continue": "Saya memahami risikonya dan ingin melanjutkan",
|
||||
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Anda dapat lanjut menggunakan browser Anda saat ini, tetapi beberapa atau semua fitur mungkin tidak berfungsi dan tampilan serta nuansa aplikasi mungkin salah.",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Mohon instal <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, atau <safariLink>Safari</safariLink> untuk pengalaman terbaik.",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s menggunakan fitur browser lanjutan yang tidak didukung oleh browser Anda saat ini.",
|
||||
"Your browser can't run %(brand)s": "Browser Anda tidak bisa menjalankan %(brand)s",
|
||||
"Unsupported browser": "Browser tidak didukung",
|
||||
"Use %(brand)s on mobile": "Gunakan %(brand)s di ponsel",
|
||||
"Powered by Matrix": "Didukung oleh Matrix",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s Desktop (%(platformName)s)",
|
||||
"Previous/next recently visited room or community": "Ruangan atau komunitas yang baru saja dikunjungi sebelum/berikutnya",
|
||||
"Open user settings": "Buka pengaturan pengguna",
|
||||
"Open": "Buka",
|
||||
"Download Completed": "Unduh Selesai",
|
||||
"Unexpected error preparing the app. See console for details.": "Kesalahan tak terduga saat menyiapkan aplikasi. Lihat konsol untuk detail.",
|
||||
"Unable to load config file: please refresh the page to try again.": "Tidak bisa muat file konfigurasi: mohon segarkan halaman untuk mencoba lagi.",
|
||||
"Invalid JSON": "JSON tidak valid",
|
||||
"The message from the parser is: %(message)s": "Pesan dari pengurai adalah: %(message)s",
|
||||
"Your Element is misconfigured": "Element Anda salah dikonfigurasi",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Konfigurasi tidak valid: hanya bisa menentukan satu dari default_server_config, default_server_name, atau default_hs_url."
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "Il tuo browser non può eseguire %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s usa funzionalità avanzate del browser che non sono supportate dal tuo browser attuale.",
|
||||
"Powered by Matrix": "Offerto da Matrix",
|
||||
"Use %(brand)s on mobile": "Usa %(brand)s su mobile"
|
||||
"Use %(brand)s on mobile": "Usa %(brand)s su mobile",
|
||||
"Switch to space by number": "Passa allo spazio per numero"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"Welcome to Element": "Welkom bij Element",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Gedecentraliseerd en versleuteld chatten & samenwerken dankzij [matrix]",
|
||||
"Sign In": "Inloggen",
|
||||
"Create Account": "Registeren",
|
||||
"Create Account": "Registreren",
|
||||
"Explore rooms": "Gesprekken ontdekken",
|
||||
"Unexpected error preparing the app. See console for details.": "Er is een onverwachte fout opgetreden bij het voorbereiden van de app. Zie de console voor details.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Configuratiefout: kan slechts één van default_server_config, default_server_name, of default_hs_url opgeven.",
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "Uw browser kan %(brand)s niet starten",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s gebruikt geavanceerde functies die niet ondersteund worden in uw huidige browser.",
|
||||
"Powered by Matrix": "Mogelijk gemaakt door Matrix",
|
||||
"Use %(brand)s on mobile": "Gebruik %(brand)s op uw mobiel"
|
||||
"Use %(brand)s on mobile": "Gebruik %(brand)s op uw mobiel",
|
||||
"Switch to space by number": "Wissel naar space per nummer"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "Seu browser não consegue rodar %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s usa funcionalidade de browser avançada que não é suportada por seu browser atual.",
|
||||
"Powered by Matrix": "Powered by Matrix",
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s em celular"
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s em celular",
|
||||
"Switch to space by number": "Trocar para espaço por número"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Unable to load config file: please refresh the page to try again.": "Nu se poate încărca fișierul de configurație: vă rugăm sa reîncărcați pagina și să încercați din nou.",
|
||||
"The message from the parser is: %(message)s": "Mesajul de la parser este: %(message)s",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Configurația ta Element conține JSON invalid. Vă rugăm sa corectați problema și să reîncărcați pagina.",
|
||||
"Invalid configuration: no default server specified.": "Configurație invalidă: niciun server implicit specificat."
|
||||
"Invalid configuration: no default server specified.": "Configurație invalidă: niciun server implicit specificat.",
|
||||
"Switch to space by number": "Comută spațiul folosind un număr"
|
||||
}
|
||||
|
||||
@@ -4,5 +4,15 @@
|
||||
"Welcome to Element": "ඉලමන්ට් වෙත සාදරයෙන් පිළිගනිමු",
|
||||
"Open": "විවෘත කරන්න",
|
||||
"Powered by Matrix": "මැට්රික්ස් මඟින් බලගන්වා ඇත",
|
||||
"Sign In": "පිවිසෙන්න"
|
||||
"Sign In": "පිවිසෙන්න",
|
||||
"Dismiss": "ඉවතලන්න",
|
||||
"Explore rooms": "කාමර බලන්න",
|
||||
"Create Account": "ගිණුමක් සාදන්න",
|
||||
"Failed to start": "ඇරඹීමට අපොහොසත් විය",
|
||||
"Go to element.io": "element.io වෙත යන්න",
|
||||
"Your browser can't run %(brand)s": "ඔබගේ අතිරික්සුවට %(brand)s ධාවනය කළ නොහැකිය",
|
||||
"Unsupported browser": "සහය නොදක්වන අතිරික්සුව කි",
|
||||
"Go to your browser to complete Sign In": "පිවිසීම සම්පූර්ණ කිරීමට ඔබගේ අතිරික්සුව වෙත යන්න",
|
||||
"Download Completed": "බාගැනීම සම්පූර්ණයි",
|
||||
"Open user settings": "පරිශීලක සැකසුම් විවෘත කරන්න"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "Shfletuesi juaj s’mund të xhirojë %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s përdor veçori të thelluara të shfletuesit, të cilat shfletuesi juaj i tanishëm s’i mbulon.",
|
||||
"Powered by Matrix": "Bazuar në Matrix",
|
||||
"Use %(brand)s on mobile": "Përdor %(brand)s në celular"
|
||||
"Use %(brand)s on mobile": "Përdor %(brand)s në celular",
|
||||
"Switch to space by number": "Kalo te hapësira me numrin"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Powered by Matrix": "Drivs av Matrix",
|
||||
"Your browser can't run %(brand)s": "Din webbläsare kan inte köra %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s använder avancerade webbläsarfunktioner som inte stöds av din aktuella webbläsare.",
|
||||
"Use %(brand)s on mobile": "Använd %(brand)s på mobilen"
|
||||
"Use %(brand)s on mobile": "Använd %(brand)s på mobilen",
|
||||
"Switch to space by number": "Byt till utrymme med nummer"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s Masaüstü (%(platformName)s)",
|
||||
"Open": "Aç",
|
||||
"Missing indexeddb worker script!": "Indexeddb worker kodu eksik!",
|
||||
"Use %(brand)s on mobile": "Mobilde %(brand)s kullan"
|
||||
"Use %(brand)s on mobile": "Mobilde %(brand)s kullan",
|
||||
"Switch to space by number": "Sayı ile belirtilen alana geç"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Powered by Matrix": "Працює на Matrix",
|
||||
"Your browser can't run %(brand)s": "Ваш переглядач неспроможний запустити %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s використовує передові властивості, які ваш браузер не підтримує.",
|
||||
"Use %(brand)s on mobile": "Користуйтеся %(brand)s на мобільному"
|
||||
"Use %(brand)s on mobile": "Користуйтеся %(brand)s на мобільному",
|
||||
"Switch to space by number": "Перейдіть до простору за номером"
|
||||
}
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"Your browser can't run %(brand)s": "浏览器无法运行 %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "当前浏览器不支持 %(brand)s 所需的高级浏览器特性。",
|
||||
"Powered by Matrix": "由 Matrix 驱动",
|
||||
"Use %(brand)s on mobile": "在移动设备上使用 %(brand)s"
|
||||
"Use %(brand)s on mobile": "在移动设备上使用 %(brand)s",
|
||||
"Switch to space by number": "按数字切换空间"
|
||||
}
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
"Open": "開啟",
|
||||
"Your Element is misconfigured": "Element 配置錯誤",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Element 的配置中包含無效JSON,請更正錯誤並重新加載網頁。",
|
||||
"Your browser can't run %(brand)s": "當前瀏覽器無法運行%(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "當前瀏覽器不支持%(brand)s使用的高級特性。",
|
||||
"Your browser can't run %(brand)s": "您的瀏覽器無法執行 %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s 使用了您目前的瀏覽器不支援的進階瀏覽器功能。",
|
||||
"Powered by Matrix": "由 Matrix 提供",
|
||||
"Use %(brand)s on mobile": "在行動裝置上使用 %(brand)s"
|
||||
"Use %(brand)s on mobile": "在行動裝置上使用 %(brand)s",
|
||||
"Switch to space by number": "依數字切換至空間"
|
||||
}
|
||||
|
||||
@@ -25,21 +25,13 @@ window.React = React;
|
||||
|
||||
import * as sdk from 'matrix-react-sdk';
|
||||
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
|
||||
import { _t, _td, newTranslatableError } from 'matrix-react-sdk/src/languageHandler';
|
||||
import { _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 type MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixChat";
|
||||
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
// P2P only, probably should be relocated
|
||||
import Modal from 'matrix-react-sdk/src/Modal';
|
||||
import { IDialogProps } from 'matrix-react-sdk/src/components/views/dialogs/IDialogProps';
|
||||
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
|
||||
import { getCachedRoomIDForAlias } from 'matrix-react-sdk/src/RoomAliasCache';
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
|
||||
import { parseQs, parseQsFromFragment } from './url_utils';
|
||||
import VectorBasePlatform from "./platform/VectorBasePlatform";
|
||||
import { createClient } from "matrix-js-sdk/src/matrix";
|
||||
@@ -81,189 +73,19 @@ function onNewScreen(screen: string, replaceLast = false) {
|
||||
const hash = '#/' + screen;
|
||||
lastLocationHashSet = hash;
|
||||
|
||||
// if the new hash is a substring of the old one then we are stripping fields e.g `via` so replace history
|
||||
if (screen.startsWith("room/") &&
|
||||
window.location.hash.includes("/$") === hash.includes("/$") && // only if both did or didn't contain event link
|
||||
window.location.hash.startsWith(hash)
|
||||
) {
|
||||
replaceLast = true;
|
||||
}
|
||||
|
||||
if (replaceLast) {
|
||||
window.location.replace(hash);
|
||||
} else {
|
||||
window.location.assign(hash);
|
||||
}
|
||||
|
||||
if (!window.matrixChat) {
|
||||
return;
|
||||
}
|
||||
let creds = null;
|
||||
if (screen === "register" || screen === "login" || screen === "welcome") {
|
||||
autoRegister().then((newCreds) => {
|
||||
creds = newCreds;
|
||||
return (window.matrixChat as MatrixChatType).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]);
|
||||
}
|
||||
}
|
||||
|
||||
const P2PDisplayNameDialog: React.FC<IDialogProps> = ({ onFinished }) => {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
return <BaseDialog
|
||||
onFinished={onFinished}
|
||||
title={_t('Set a display name:')}
|
||||
>
|
||||
<ChangeDisplayName onFinished={onFinished} />
|
||||
<DialogButtons
|
||||
primaryButton={_t('OK')}
|
||||
onPrimaryButtonClick={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: string): Room {
|
||||
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;
|
||||
const cb = function(room) {
|
||||
if (fulfilled) {
|
||||
return;
|
||||
}
|
||||
const 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: string) {
|
||||
// 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
|
||||
@@ -313,112 +135,7 @@ function onTokenLoginCompleted() {
|
||||
window.history.replaceState(null, "", url.href);
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function autoRegister() {
|
||||
console.log("dendrite: Auto-registration in progress");
|
||||
const cli = createClient({
|
||||
baseUrl: window.location.origin,
|
||||
});
|
||||
const password = "this should be really really secure";
|
||||
|
||||
// Make sure the server is up (active service worker)
|
||||
await navigator.serviceWorker.ready;
|
||||
// On Firefox, 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.registerRequest({
|
||||
username: "p2p",
|
||||
password,
|
||||
auth: {
|
||||
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,
|
||||
initial_device_display_name: "p2p-dendrite",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
userId: response.user_id,
|
||||
deviceId: response.device_id,
|
||||
homeserverUrl: cli.getHomeserverUrl(),
|
||||
identityServerUrl: cli.getIdentityServerUrl(),
|
||||
accessToken: response.access_token,
|
||||
guest: cli.isGuest(),
|
||||
_registered: didRegister,
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadApp(fragParams: {}) {
|
||||
// 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
|
||||
const vectorIndexeddbWorkerScript = document.body.dataset.vectorIndexeddbWorkerScript;
|
||||
if (!vectorIndexeddbWorkerScript) {
|
||||
// If this is missing, something has probably gone wrong with
|
||||
// the bundling. The js-sdk will just fall back to accessing
|
||||
// indexeddb directly with no worker script, but we want to
|
||||
// make sure the indexeddb script is present, so fail hard.
|
||||
throw newTranslatableError(_td("Missing indexeddb worker script!"));
|
||||
}
|
||||
// MatrixClientPeg.setIndexedDbWorkerScript(vectorIndexeddbWorkerScript);
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
|
||||
const platform = PlatformPeg.get();
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
})());
|
||||
})
|
||||
Binary file not shown.
@@ -60,9 +60,7 @@
|
||||
</head>
|
||||
<body
|
||||
style="height: 100%; margin: 0;"
|
||||
data-vector-indexeddb-worker-script="<%= htmlWebpackPlugin.files.js.find(entry => entry.includes("indexeddb-worker.js")) %>"
|
||||
data-vector-recorder-worklet-script="<%= htmlWebpackPlugin.files.js.find(entry => entry.includes("recorder-worklet.js")) %>"
|
||||
data-vector-dendrite-worker-script="<%= htmlWebpackPlugin.files.js.find(entry => entry.includes("dendrite_sw.js")) %>"
|
||||
>
|
||||
<noscript>Sorry, Element requires JavaScript to be enabled.</noscript> <!-- TODO: Translate this? -->
|
||||
<section id="matrixchat" style="height: 100%;" class="notranslate"></section>
|
||||
|
||||
@@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IndexedDBStoreWorker } from 'matrix-js-sdk/src/indexeddb-worker.js';
|
||||
import { IndexedDBStoreWorker } from 'matrix-js-sdk/src/indexeddb-worker';
|
||||
|
||||
const remoteWorker = new IndexedDBStoreWorker(postMessage);
|
||||
const remoteWorker = new IndexedDBStoreWorker(postMessage as InstanceType<typeof Worker>["postMessage"]);
|
||||
|
||||
global.onmessage = remoteWorker.onMessage;
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
// We have to trick webpack into loading our CSS for us.
|
||||
require("./index.scss");
|
||||
|
||||
import * as qs from 'querystring';
|
||||
import { KJUR } from 'jsrsasign';
|
||||
import {
|
||||
IOpenIDCredentials,
|
||||
@@ -52,15 +51,16 @@ let meetApi: any; // JitsiMeetExternalAPI
|
||||
|
||||
(async function() {
|
||||
try {
|
||||
// The widget's options are encoded into the fragment to avoid leaking info to the server. The widget
|
||||
// spec on the other hand requires the widgetId and parentUrl to show up in the regular query string.
|
||||
const widgetQuery = qs.parse(window.location.hash.substring(1));
|
||||
const query = Object.assign({}, qs.parse(window.location.search.substring(1)), widgetQuery);
|
||||
// The widget's options are encoded into the fragment to avoid leaking info to the server.
|
||||
const widgetQuery = new URLSearchParams(window.location.hash.substring(1));
|
||||
// The widget spec on the other hand requires the widgetId and parentUrl to show up in the regular query string.
|
||||
const realQuery = new URLSearchParams(window.location.search.substring(1));
|
||||
const qsParam = (name: string, optional = false): string => {
|
||||
if (!optional && (!query[name] || typeof (query[name]) !== 'string')) {
|
||||
const vals = widgetQuery.has(name) ? widgetQuery.getAll(name) : realQuery.getAll(name);
|
||||
if (!optional && vals.length !== 1) {
|
||||
throw new Error(`Expected singular ${name} in query string`);
|
||||
}
|
||||
return <string>query[name];
|
||||
return <string>vals[0];
|
||||
};
|
||||
|
||||
// If we have these params, expect a widget API to be available (ie. to be in an iframe
|
||||
|
||||
@@ -326,7 +326,7 @@ body {
|
||||
<a class="mx_Button" id="configure_element_button" href="#">Configure</a>
|
||||
<p class="mx_Subtext mx_SubtextTop">Tap the button above, or manually enable <em>Use custom server</em> and enter:</p>
|
||||
<p class="mx_Subtext">Homeserver: <em id="hs_url"></em></p>
|
||||
<p class="mx_Subtext" id="custom_is">Identity Server: <em id="is_url"></em></p>
|
||||
<p class="mx_Subtext" id="custom_is">Identity server: <em id="is_url"></em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { getVectorConfig } from '../getconfig';
|
||||
|
||||
function onBackToElementClick() {
|
||||
function onBackToElementClick(): void {
|
||||
// Cookie should expire in 4 hours
|
||||
document.cookie = 'element_mobile_redirect_to_guide=false;path=/;max-age=14400';
|
||||
window.location.href = '../';
|
||||
}
|
||||
|
||||
// NEVER pass user-controlled content to this function! Hardcoded strings only please.
|
||||
function renderConfigError(message) {
|
||||
function renderConfigError(message: string): void {
|
||||
const contactMsg = "If this is unexpected, please contact your system administrator " +
|
||||
"or technical support representative.";
|
||||
message = `<h2>Error loading Element</h2><p>${message}</p><p>${contactMsg}</p>`;
|
||||
|
||||
const toHide = document.getElementsByClassName("mx_HomePage_container");
|
||||
const errorContainers = document.getElementsByClassName("mx_HomePage_errorContainer");
|
||||
const errorContainers = document.getElementsByClassName(
|
||||
"mx_HomePage_errorContainer",
|
||||
) as HTMLCollectionOf<HTMLDialogElement>;
|
||||
|
||||
for (const e of toHide) {
|
||||
// We have to clear the content because .style.display='none'; doesn't work
|
||||
@@ -26,7 +28,7 @@ function renderConfigError(message) {
|
||||
}
|
||||
}
|
||||
|
||||
async function initPage() {
|
||||
async function initPage(): Promise<void> {
|
||||
document.getElementById('back_to_element_button').onclick = onBackToElementClick;
|
||||
|
||||
const config = await getVectorConfig('..');
|
||||
@@ -92,7 +94,7 @@ async function initPage() {
|
||||
if (isUrl && !isUrl.endsWith('/')) isUrl += '/';
|
||||
|
||||
if (hsUrl !== 'https://matrix.org/') {
|
||||
document.getElementById('configure_element_button').href =
|
||||
(document.getElementById('configure_element_button') as HTMLAnchorElement).href =
|
||||
"https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) +
|
||||
"&is_url=" + encodeURIComponent(isUrl);
|
||||
document.getElementById('step1_heading').innerHTML= '1: Install the app';
|
||||
File diff suppressed because one or more lines are too long
@@ -22,10 +22,7 @@ import BaseEventIndexManager, {
|
||||
ICrawlerCheckpoint,
|
||||
IEventAndProfile,
|
||||
IIndexStats,
|
||||
IMatrixEvent,
|
||||
IMatrixProfile,
|
||||
ISearchArgs,
|
||||
ISearchResult,
|
||||
} from 'matrix-react-sdk/src/indexing/BaseEventIndexManager';
|
||||
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
|
||||
import { _t, _td } from 'matrix-react-sdk/src/languageHandler';
|
||||
@@ -54,6 +51,7 @@ import { CheckUpdatesPayload } from "matrix-react-sdk/src/dispatcher/payloads/Ch
|
||||
import ToastStore from "matrix-react-sdk/src/stores/ToastStore";
|
||||
import GenericExpiringToast from "matrix-react-sdk/src/components/views/toasts/GenericExpiringToast";
|
||||
import SettingsStore from 'matrix-react-sdk/src/settings/SettingsStore';
|
||||
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
|
||||
|
||||
import VectorBasePlatform from './VectorBasePlatform';
|
||||
|
||||
@@ -112,10 +110,10 @@ class SeshatIndexManager extends BaseEventIndexManager {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
electron.on('seshatReply', this._onIpcReply);
|
||||
electron.on('seshatReply', this.onIpcReply);
|
||||
}
|
||||
|
||||
async _ipcCall(name: string, ...args: any[]): Promise<any> {
|
||||
private async ipcCall(name: string, ...args: any[]): Promise<any> {
|
||||
// TODO this should be moved into the preload.js file.
|
||||
const ipcCallId = ++this.nextIpcCallId;
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -124,7 +122,7 @@ class SeshatIndexManager extends BaseEventIndexManager {
|
||||
});
|
||||
}
|
||||
|
||||
_onIpcReply = (ev: {}, payload: IPCPayload) => {
|
||||
private onIpcReply = (ev: {}, payload: IPCPayload) => {
|
||||
if (payload.id === undefined) {
|
||||
console.warn("Ignoring IPC reply with no ID");
|
||||
return;
|
||||
@@ -145,35 +143,35 @@ class SeshatIndexManager extends BaseEventIndexManager {
|
||||
};
|
||||
|
||||
async supportsEventIndexing(): Promise<boolean> {
|
||||
return this._ipcCall('supportsEventIndexing');
|
||||
return this.ipcCall('supportsEventIndexing');
|
||||
}
|
||||
|
||||
async initEventIndex(userId: string, deviceId: string): Promise<void> {
|
||||
return this._ipcCall('initEventIndex', userId, deviceId);
|
||||
return this.ipcCall('initEventIndex', userId, deviceId);
|
||||
}
|
||||
|
||||
async addEventToIndex(ev: IMatrixEvent, profile: IMatrixProfile): Promise<void> {
|
||||
return this._ipcCall('addEventToIndex', ev, profile);
|
||||
return this.ipcCall('addEventToIndex', ev, profile);
|
||||
}
|
||||
|
||||
async deleteEvent(eventId: string): Promise<boolean> {
|
||||
return this._ipcCall('deleteEvent', eventId);
|
||||
return this.ipcCall('deleteEvent', eventId);
|
||||
}
|
||||
|
||||
async isEventIndexEmpty(): Promise<boolean> {
|
||||
return this._ipcCall('isEventIndexEmpty');
|
||||
return this.ipcCall('isEventIndexEmpty');
|
||||
}
|
||||
|
||||
async isRoomIndexed(roomId: string): Promise<boolean> {
|
||||
return this._ipcCall('isRoomIndexed', roomId);
|
||||
return this.ipcCall('isRoomIndexed', roomId);
|
||||
}
|
||||
|
||||
async commitLiveEvents(): Promise<void> {
|
||||
return this._ipcCall('commitLiveEvents');
|
||||
return this.ipcCall('commitLiveEvents');
|
||||
}
|
||||
|
||||
async searchEventIndex(searchConfig: ISearchArgs): Promise<ISearchResult> {
|
||||
return this._ipcCall('searchEventIndex', searchConfig);
|
||||
async searchEventIndex(searchConfig: ISearchArgs): Promise<IResultRoomEvents> {
|
||||
return this.ipcCall('searchEventIndex', searchConfig);
|
||||
}
|
||||
|
||||
async addHistoricEvents(
|
||||
@@ -181,43 +179,43 @@ class SeshatIndexManager extends BaseEventIndexManager {
|
||||
checkpoint: ICrawlerCheckpoint | null,
|
||||
oldCheckpoint: ICrawlerCheckpoint | null,
|
||||
): Promise<boolean> {
|
||||
return this._ipcCall('addHistoricEvents', events, checkpoint, oldCheckpoint);
|
||||
return this.ipcCall('addHistoricEvents', events, checkpoint, oldCheckpoint);
|
||||
}
|
||||
|
||||
async addCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
|
||||
return this._ipcCall('addCrawlerCheckpoint', checkpoint);
|
||||
return this.ipcCall('addCrawlerCheckpoint', checkpoint);
|
||||
}
|
||||
|
||||
async removeCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
|
||||
return this._ipcCall('removeCrawlerCheckpoint', checkpoint);
|
||||
return this.ipcCall('removeCrawlerCheckpoint', checkpoint);
|
||||
}
|
||||
|
||||
async loadFileEvents(args): Promise<IEventAndProfile[]> {
|
||||
return this._ipcCall('loadFileEvents', args);
|
||||
return this.ipcCall('loadFileEvents', args);
|
||||
}
|
||||
|
||||
async loadCheckpoints(): Promise<ICrawlerCheckpoint[]> {
|
||||
return this._ipcCall('loadCheckpoints');
|
||||
return this.ipcCall('loadCheckpoints');
|
||||
}
|
||||
|
||||
async closeEventIndex(): Promise<void> {
|
||||
return this._ipcCall('closeEventIndex');
|
||||
return this.ipcCall('closeEventIndex');
|
||||
}
|
||||
|
||||
async getStats(): Promise<IIndexStats> {
|
||||
return this._ipcCall('getStats');
|
||||
return this.ipcCall('getStats');
|
||||
}
|
||||
|
||||
async getUserVersion(): Promise<number> {
|
||||
return this._ipcCall('getUserVersion');
|
||||
return this.ipcCall('getUserVersion');
|
||||
}
|
||||
|
||||
async setUserVersion(version: number): Promise<void> {
|
||||
return this._ipcCall('setUserVersion', version);
|
||||
return this.ipcCall('setUserVersion', version);
|
||||
}
|
||||
|
||||
async deleteEventIndex(): Promise<void> {
|
||||
return this._ipcCall('deleteEventIndex');
|
||||
return this.ipcCall('deleteEventIndex');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +249,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
electron.on('ipcReply', this._onIpcReply);
|
||||
electron.on('ipcReply', this.onIpcReply);
|
||||
electron.on('update-downloaded', this.onUpdateDownloaded);
|
||||
|
||||
electron.on('preferences', () => {
|
||||
@@ -259,12 +257,15 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
});
|
||||
|
||||
electron.on('userDownloadCompleted', (ev, { path, name }) => {
|
||||
const key = `DOWNLOAD_TOAST_${path}`;
|
||||
|
||||
const onAccept = () => {
|
||||
electron.send('userDownloadOpen', { path });
|
||||
ToastStore.sharedInstance().dismissToast(key);
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
key: `DOWNLOAD_TOAST_${path}`,
|
||||
key,
|
||||
title: _t("Download Completed"),
|
||||
props: {
|
||||
description: name,
|
||||
@@ -319,11 +320,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
});
|
||||
}
|
||||
|
||||
this._ipcCall("startSSOFlow", this.ssoID);
|
||||
this.ipcCall("startSSOFlow", this.ssoID);
|
||||
}
|
||||
|
||||
async getConfig(): Promise<{}> {
|
||||
return this._ipcCall('getConfig');
|
||||
return this.ipcCall('getConfig');
|
||||
}
|
||||
|
||||
onUpdateDownloaded = async (ev, { releaseNotes, releaseName }) => {
|
||||
@@ -390,7 +391,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
room_id: room.roomId,
|
||||
});
|
||||
window.focus();
|
||||
this._ipcCall('focusWindow');
|
||||
this.ipcCall('focusWindow');
|
||||
};
|
||||
|
||||
return notification;
|
||||
@@ -401,7 +402,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
async getAppVersion(): Promise<string> {
|
||||
return this._ipcCall('getAppVersion');
|
||||
return this.ipcCall('getAppVersion');
|
||||
}
|
||||
|
||||
supportsAutoLaunch(): boolean {
|
||||
@@ -409,11 +410,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
async getAutoLaunchEnabled(): Promise<boolean> {
|
||||
return this._ipcCall('getAutoLaunchEnabled');
|
||||
return this.ipcCall('getAutoLaunchEnabled');
|
||||
}
|
||||
|
||||
async setAutoLaunchEnabled(enabled: boolean): Promise<void> {
|
||||
return this._ipcCall('setAutoLaunchEnabled', enabled);
|
||||
return this.ipcCall('setAutoLaunchEnabled', enabled);
|
||||
}
|
||||
|
||||
supportsWarnBeforeExit(): boolean {
|
||||
@@ -421,11 +422,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
async shouldWarnBeforeExit(): Promise<boolean> {
|
||||
return this._ipcCall('shouldWarnBeforeExit');
|
||||
return this.ipcCall('shouldWarnBeforeExit');
|
||||
}
|
||||
|
||||
async setWarnBeforeExit(enabled: boolean): Promise<void> {
|
||||
return this._ipcCall('setWarnBeforeExit', enabled);
|
||||
return this.ipcCall('setWarnBeforeExit', enabled);
|
||||
}
|
||||
|
||||
supportsAutoHideMenuBar(): boolean {
|
||||
@@ -434,11 +435,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
async getAutoHideMenuBarEnabled(): Promise<boolean> {
|
||||
return this._ipcCall('getAutoHideMenuBarEnabled');
|
||||
return this.ipcCall('getAutoHideMenuBarEnabled');
|
||||
}
|
||||
|
||||
async setAutoHideMenuBarEnabled(enabled: boolean): Promise<void> {
|
||||
return this._ipcCall('setAutoHideMenuBarEnabled', enabled);
|
||||
return this.ipcCall('setAutoHideMenuBarEnabled', enabled);
|
||||
}
|
||||
|
||||
supportsMinimizeToTray(): boolean {
|
||||
@@ -447,15 +448,15 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
async getMinimizeToTrayEnabled(): Promise<boolean> {
|
||||
return this._ipcCall('getMinimizeToTrayEnabled');
|
||||
return this.ipcCall('getMinimizeToTrayEnabled');
|
||||
}
|
||||
|
||||
async setMinimizeToTrayEnabled(enabled: boolean): Promise<void> {
|
||||
return this._ipcCall('setMinimizeToTrayEnabled', enabled);
|
||||
return this.ipcCall('setMinimizeToTrayEnabled', enabled);
|
||||
}
|
||||
|
||||
async canSelfUpdate(): Promise<boolean> {
|
||||
const feedUrl = await this._ipcCall('getUpdateFeedUrl');
|
||||
const feedUrl = await this.ipcCall('getUpdateFeedUrl');
|
||||
return Boolean(feedUrl);
|
||||
}
|
||||
|
||||
@@ -494,7 +495,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
window.location.reload(false);
|
||||
}
|
||||
|
||||
async _ipcCall(name: string, ...args: any[]): Promise<any> {
|
||||
private async ipcCall(name: string, ...args: any[]): Promise<any> {
|
||||
const ipcCallId = ++this.nextIpcCallId;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.pendingIpcCalls[ipcCallId] = { resolve, reject };
|
||||
@@ -503,7 +504,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
});
|
||||
}
|
||||
|
||||
_onIpcReply = (ev, payload) => {
|
||||
private onIpcReply = (ev, payload) => {
|
||||
if (payload.id === undefined) {
|
||||
console.warn("Ignoring IPC reply with no ID");
|
||||
return;
|
||||
@@ -528,22 +529,22 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
async setLanguage(preferredLangs: string[]) {
|
||||
return this._ipcCall('setLanguage', preferredLangs);
|
||||
return this.ipcCall('setLanguage', preferredLangs);
|
||||
}
|
||||
|
||||
setSpellCheckLanguages(preferredLangs: string[]) {
|
||||
this._ipcCall('setSpellCheckLanguages', preferredLangs).catch(error => {
|
||||
this.ipcCall('setSpellCheckLanguages', preferredLangs).catch(error => {
|
||||
console.log("Failed to send setSpellCheckLanguages IPC to Electron");
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
async getSpellCheckLanguages(): Promise<string[]> {
|
||||
return this._ipcCall('getSpellCheckLanguages');
|
||||
return this.ipcCall('getSpellCheckLanguages');
|
||||
}
|
||||
|
||||
async getAvailableSpellCheckLanguages(): Promise<string[]> {
|
||||
return this._ipcCall('getAvailableSpellCheckLanguages');
|
||||
return this.ipcCall('getAvailableSpellCheckLanguages');
|
||||
}
|
||||
|
||||
getSSOCallbackUrl(fragmentAfterLogin: string): URL {
|
||||
@@ -563,7 +564,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
private navigateForwardBack(back: boolean) {
|
||||
this._ipcCall(back ? "navigateBack" : "navigateForward");
|
||||
this.ipcCall(back ? "navigateBack" : "navigateForward");
|
||||
}
|
||||
private navigateToSpace(num: number) {
|
||||
dis.dispatch<SwitchSpacePayload>({
|
||||
@@ -591,22 +592,18 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
case "8":
|
||||
case "9":
|
||||
case "0":
|
||||
if (SettingsStore.getValue("feature_spaces") && isOnlyCtrlOrCmdKeyEvent(ev)) {
|
||||
this.navigateToSpace(parseInt(ev.key, 10));
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
if (!handled &&
|
||||
// ideally we would use SpaceStore.spacesEnabled here but importing SpaceStore in this platform
|
||||
// breaks skinning as the platform is instantiated prior to the skin being loaded
|
||||
SettingsStore.getValue("feature_spaces") &&
|
||||
ev.code.startsWith("Digit") &&
|
||||
isOnlyCtrlOrCmdKeyEvent(ev)
|
||||
) {
|
||||
const spaceNumber = ev.code.slice(5); // Cut off the first 5 characters - "Digit"
|
||||
this.navigateToSpace(parseInt(spaceNumber, 10));
|
||||
handled = true;
|
||||
}
|
||||
|
||||
return handled;
|
||||
@@ -614,7 +611,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
|
||||
async getPickleKey(userId: string, deviceId: string): Promise<string | null> {
|
||||
try {
|
||||
return await this._ipcCall('getPickleKey', userId, deviceId);
|
||||
return await this.ipcCall('getPickleKey', userId, deviceId);
|
||||
} catch (e) {
|
||||
// if we can't connect to the password storage, assume there's no
|
||||
// pickle key
|
||||
@@ -624,7 +621,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
|
||||
async createPickleKey(userId: string, deviceId: string): Promise<string | null> {
|
||||
try {
|
||||
return await this._ipcCall('createPickleKey', userId, deviceId);
|
||||
return await this.ipcCall('createPickleKey', userId, deviceId);
|
||||
} catch (e) {
|
||||
// if we can't connect to the password storage, assume there's no
|
||||
// pickle key
|
||||
@@ -634,7 +631,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
|
||||
async destroyPickleKey(userId: string, deviceId: string): Promise<void> {
|
||||
try {
|
||||
await this._ipcCall('destroyPickleKey', userId, deviceId);
|
||||
await this.ipcCall('destroyPickleKey', userId, deviceId);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
|
||||
return this._favicon = new Favicon();
|
||||
}
|
||||
|
||||
_updateFavicon() {
|
||||
private updateFavicon() {
|
||||
let bgColor = "#d00";
|
||||
let notif: string | number = this.notificationCount;
|
||||
|
||||
@@ -64,13 +64,13 @@ export default abstract class VectorBasePlatform extends BasePlatform {
|
||||
setNotificationCount(count: number) {
|
||||
if (this.notificationCount === count) return;
|
||||
super.setNotificationCount(count);
|
||||
this._updateFavicon();
|
||||
this.updateFavicon();
|
||||
}
|
||||
|
||||
setErrorStatus(errorDidOccur: boolean) {
|
||||
if (this.errorDidOccur === errorDidOccur) return;
|
||||
super.setErrorStatus(errorDidOccur);
|
||||
this._updateFavicon();
|
||||
this.updateFavicon();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
super();
|
||||
// Register service worker if available on this platform
|
||||
if ('serviceWorker' in navigator) {
|
||||
// navigator.serviceWorker.register('sw.js');
|
||||
navigator.serviceWorker.register('sw.js');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
return notification;
|
||||
}
|
||||
|
||||
_getVersion(): Promise<string> {
|
||||
private getVersion(): Promise<string> {
|
||||
// We add a cachebuster to the request to make sure that we know about
|
||||
// the most recent version on the origin server. That might not
|
||||
// actually be the version we'd get on a reload (particularly in the
|
||||
@@ -132,7 +132,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
if (this.runningVersion !== null) {
|
||||
return Promise.resolve(this.runningVersion);
|
||||
}
|
||||
return this._getVersion();
|
||||
return this.getVersion();
|
||||
}
|
||||
|
||||
startUpdater() {
|
||||
@@ -145,7 +145,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
pollForUpdate = () => {
|
||||
return this._getVersion().then((ver) => {
|
||||
return this.getVersion().then((ver) => {
|
||||
if (this.runningVersion === null) {
|
||||
this.runningVersion = ver;
|
||||
} else if (this.runningVersion !== ver) {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// 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")
|
||||
})
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as qs from 'querystring';
|
||||
import { QueryDict, decodeParams } from "matrix-js-sdk/src/utils";
|
||||
|
||||
// We want to support some name / value pairs in the fragment
|
||||
// so we're re-using query string like format
|
||||
@@ -32,15 +32,15 @@ export function parseQsFromFragment(location: Location) {
|
||||
|
||||
const result = {
|
||||
location: decodeURIComponent(hashparts[0]),
|
||||
params: <qs.ParsedUrlQuery>{},
|
||||
params: <QueryDict>{},
|
||||
};
|
||||
|
||||
if (hashparts.length > 1) {
|
||||
result.params = qs.parse(hashparts[1]);
|
||||
result.params = decodeParams(hashparts[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function parseQs(location: Location) {
|
||||
return qs.parse(location.search.substring(1));
|
||||
export function parseQs(location: Location): QueryDict {
|
||||
return decodeParams(location.search.substring(1));
|
||||
}
|
||||
|
||||
@@ -1,626 +0,0 @@
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
})();
|
||||
50
test/unit-tests/url_utils-test.ts
Normal file
50
test/unit-tests/url_utils-test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
import { parseQsFromFragment, parseQs } from "../../src/vector/url_utils";
|
||||
|
||||
describe("url_utils.ts", function() {
|
||||
// @ts-ignore
|
||||
const location: Location = {
|
||||
hash: "",
|
||||
search: "",
|
||||
};
|
||||
|
||||
it("parseQsFromFragment", function() {
|
||||
location.hash = "/home?foo=bar";
|
||||
expect(parseQsFromFragment(location)).toEqual({
|
||||
location: "home",
|
||||
params: {
|
||||
"foo": "bar",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseQs", function() {
|
||||
location.search = "?foo=bar";
|
||||
expect(parseQs(location)).toEqual({
|
||||
"foo": "bar",
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseQs with arrays", function() {
|
||||
location.search = "?via=s1&via=s2&via=s2&foo=bar";
|
||||
expect(parseQs(location)).toEqual({
|
||||
"via": ["s1", "s2", "s2"],
|
||||
"foo": "bar",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -21,7 +21,12 @@
|
||||
"es2019",
|
||||
"dom",
|
||||
"dom.iterable"
|
||||
]
|
||||
],
|
||||
"paths": {
|
||||
"posthog-js": [
|
||||
"../matrix-react-sdk/src/@types/posthog.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
|
||||
@@ -50,25 +50,15 @@ module.exports = (env, argv) => {
|
||||
|
||||
node: {
|
||||
// Mock out the NodeFS module: The opus decoder imports this wrongly.
|
||||
// Also needed by sql.js in P2P mode.
|
||||
fs: 'empty',
|
||||
},
|
||||
|
||||
entry: {
|
||||
"bundle": "./src/vector/index.ts",
|
||||
"indexeddb-worker": "./src/vector/indexeddb-worker.js",
|
||||
"mobileguide": "./src/vector/mobile_guide/index.js",
|
||||
"mobileguide": "./src/vector/mobile_guide/index.ts",
|
||||
"jitsi": "./src/vector/jitsi/index.ts",
|
||||
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
|
||||
"recorder-worklet": "./node_modules/matrix-react-sdk/src/voice/RecorderWorklet.ts",
|
||||
|
||||
// P2P
|
||||
"dendrite_sw": "./src/vector/dendrite-sw.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",
|
||||
"recorder-worklet": "./node_modules/matrix-react-sdk/src/audio/RecorderWorklet.ts",
|
||||
|
||||
// CSS themes
|
||||
"theme-legacy": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
|
||||
@@ -160,6 +150,10 @@ module.exports = (env, argv) => {
|
||||
/olm[\\/](javascript[\\/])?olm\.js$/,
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
test: /\.worker\.ts$/,
|
||||
loader: "worker-loader",
|
||||
},
|
||||
{
|
||||
test: /\.(ts|js)x?$/,
|
||||
include: (f) => {
|
||||
@@ -260,7 +254,6 @@ module.exports = (env, argv) => {
|
||||
require("postcss-easings")(),
|
||||
require("postcss-strip-inline-comments")(),
|
||||
require("postcss-hexrgba")(),
|
||||
require("postcss-calc")(),
|
||||
|
||||
// It's important that this plugin is last otherwise we end
|
||||
// up with broken CSS.
|
||||
@@ -277,9 +270,7 @@ module.exports = (env, argv) => {
|
||||
loader: "file-loader",
|
||||
type: "javascript/auto", // https://github.com/webpack/webpack/issues/6725
|
||||
options: {
|
||||
// fixme: reintroduce [hash] once we can figure out how to pass it into the
|
||||
// dendrite service worker nicely
|
||||
name: '[name].[ext]',
|
||||
name: '[name].[hash:7].[ext]',
|
||||
outputPath: '.',
|
||||
},
|
||||
},
|
||||
@@ -461,8 +452,6 @@ 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
|
||||
@@ -474,16 +463,11 @@ module.exports = (env, argv) => {
|
||||
// This hides the massive list of modules.
|
||||
stats: 'minimal',
|
||||
|
||||
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