Compare commits

..

59 Commits

Author SHA1 Message Date
Michael Telatynski
a52d4aed9f Specify node-version for setup-node action 2022-10-13 08:52:52 +01:00
Michael Telatynski
66798c75b5 Trim /version file response (#23473) 2022-10-13 05:52:38 +01:00
Michael Telatynski
2ef6abbfb8 Move from browser-request to fetch (#23427) 2022-10-12 18:59:10 +01:00
Michael Telatynski
326a1a9056 Make ErrorView & CompatibilityView scrollable (#23468) 2022-10-12 18:14:27 +01:00
Michael Telatynski
341b0b469e Make bash scripts exit on error rather than continue (#23467) 2022-10-12 16:15:51 +01:00
Kerry
0e8e472138 Device manager - tweak string formatting of default device name (#23457)
* tweak string formatting of default device name

* cheaters path to beating quality gate

* more electronplatform test coverage

* remove test that throw errors

* cover some more window.electrons

* more coverage

* empty line
2022-10-12 15:35:52 +02:00
RiotRobot
44eeb6fddc Reset matrix-react-sdk back to develop branch 2022-10-11 18:04:37 +01:00
RiotRobot
8383c021f1 Reset matrix-js-sdk back to develop branch 2022-10-11 18:04:19 +01:00
RiotRobot
7afa416ed5 Merge branch 'master' into develop
# Conflicts:
#	package.json
#	yarn.lock
2022-10-11 18:03:36 +01:00
RiotRobot
bd5d22d0e9 v1.11.10 2022-10-11 17:57:53 +01:00
RiotRobot
93b57b2baa Prepare changelog for v1.11.10 2022-10-11 17:57:52 +01:00
RiotRobot
e356c38948 Upgrade matrix-react-sdk to 3.58.1 2022-10-11 17:55:10 +01:00
RiotRobot
d1f7454dc0 Reset matrix-react-sdk back to develop branch 2022-10-11 14:10:12 +01:00
RiotRobot
3bf3237774 Reset matrix-js-sdk back to develop branch 2022-10-11 14:09:53 +01:00
RiotRobot
f0e6673fec Merge branch 'master' into develop 2022-10-11 14:09:34 +01:00
RiotRobot
8ac2e0100e v1.11.9 2022-10-11 14:05:22 +01:00
RiotRobot
f42c865878 Prepare changelog for v1.11.9 2022-10-11 14:05:22 +01:00
RiotRobot
2aae3de956 Upgrade matrix-react-sdk to 3.58.0 2022-10-11 14:02:50 +01:00
RiotRobot
c61556e885 Upgrade matrix-js-sdk to 20.1.0 2022-10-11 14:01:39 +01:00
Šimon Brandner
8891698745 Add Element Call participant limit (#23431) 2022-10-07 22:00:38 +02:00
Šimon Brandner
ec4cc52b7e Add Element Call brand (#23443) 2022-10-07 19:32:12 +02:00
RiotRobot
d2c2a51ce6 v1.11.9-rc.2 2022-10-05 14:18:07 +01:00
RiotRobot
3e3d7566a6 Prepare changelog for v1.11.9-rc.2 2022-10-05 14:18:06 +01:00
RiotRobot
5f355bf25e Upgrade matrix-react-sdk to 3.58.0-rc.2 2022-10-05 14:16:02 +01:00
RiotRobot
1708f7f1b8 Upgrade matrix-js-sdk to 20.1.0-rc.2 2022-10-05 13:48:58 +01:00
renovate[bot]
fb686ec5b6 Update dependency jest-mock to v29 (#23421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-04 22:18:56 +01:00
renovate[bot]
1d86a75a29 Update typescript-eslint monorepo to v5.39.0 (#23420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-04 22:18:33 +01:00
renovate[bot]
ee5b20e12e Update jest monorepo to v29.1.1 (#23418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-04 17:57:36 +01:00
renovate[bot]
c8184e2497 Update babel monorepo to v7.19.3 (#23417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-04 16:34:12 +00:00
RiotRobot
4d00249d97 v1.11.9-rc.1 2022-10-04 14:46:47 +01:00
RiotRobot
4c43daf336 Prepare changelog for v1.11.9-rc.1 2022-10-04 14:46:46 +01:00
RiotRobot
e5dcb6e8c2 Upgrade matrix-react-sdk to 3.58.0-rc.1 2022-10-04 14:44:19 +01:00
RiotRobot
db89364395 Upgrade matrix-js-sdk to 20.1.0-rc.1 2022-10-04 14:26:21 +01:00
Element Translate Bot
6bd52ff6f5 Translations update from Weblate (#23413)
* Translated using Weblate (Galician)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/gl/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/uk/

* Translated using Weblate (Estonian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/et/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/zh_Hant/

* Translated using Weblate (Russian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ru/

* Translated using Weblate (Russian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ru/

* Translated using Weblate (Italian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/it/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/sv/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/sk/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/es/

* Translated using Weblate (Albanian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/sq/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/nl/

* Added translation using Weblate (Armenian)

* Translated using Weblate (Armenian)

Currently translated at 41.9% (13 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/hy/

* Translated using Weblate (French)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/fr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/de/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/tr/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ne/

* Translated using Weblate (Nepali)

Currently translated at 3.2% (1 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ne/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/pt_BR/

* Translated using Weblate (Icelandic)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/is/

* Translated using Weblate (Esperanto)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/eo/

* Translated using Weblate (Lao)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/lo/

* Translated using Weblate (Uzbek)

Currently translated at 12.9% (4 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/uz/

* Added translation using Weblate (Bengali)

* Translated using Weblate (Bengali (Bangladesh))

Currently translated at 3.2% (1 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/bn_BD/

* Translated using Weblate (Lao)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/lo/

* Translated using Weblate (Bengali)

Currently translated at 6.4% (2 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/bn/

* Translated using Weblate (Vietnamese)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/vi/

* Translated using Weblate (Tamil)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ta/

* Translated using Weblate (Polish)

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/pl/

* Translated using Weblate (Danish)

Currently translated at 96.7% (30 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/da/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (31 of 31 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/zh_Hans/

* Translated using Weblate (Greek)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/el/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ar/

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/he/

* Translated using Weblate (Azerbaijani)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/az/

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/bg/

* Translated using Weblate (Sinhala)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/si/

* Translated using Weblate (Korean)

Currently translated at 96.6% (29 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ko/

* Translated using Weblate (Korean)

Currently translated at 96.6% (29 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ko/

* Translated using Weblate (Persian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/fa/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/zh_Hans/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/zh_Hans/

* Translated using Weblate (Russian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ru/

* Translated using Weblate (Telugu)

Currently translated at 33.3% (10 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/te/

* Translated using Weblate (Telugu)

Currently translated at 40.0% (12 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/te/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/de/

* Translated using Weblate (German)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/de/

* Translated using Weblate (Polish)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/pl/

* Translated using Weblate (Latvian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/lv/

* Translated using Weblate (Lithuanian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/lt/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/uk/

* Translated using Weblate (Slovenian)

Currently translated at 96.6% (29 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/sl/

* Translated using Weblate (Russian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/ru/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (30 of 30 strings)

Translation: Element Web/element-web
Translate-URL: https://translate.element.io/projects/element-web/element-web/id/

Co-authored-by: Xose M <xosem@disroot.org>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
Co-authored-by: oleg-fiksel <github-oleg-fiksel@spam.fiksel.info>
Co-authored-by: Dimitriy Ryazantcev <dimitriy.ryazantcev@gmail.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: LinAGKar <linus.kardell@gmail.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: iaiz <git@iapellaniz.com>
Co-authored-by: Besnik Bleta <besnik@programeshqip.org>
Co-authored-by: Johan Smits <johan@smitsmail.net>
Co-authored-by: Weblate <translate@riot.im>
Co-authored-by: FIONover <overfion@protonmail.com>
Co-authored-by: Lucas <lucasdupanne@gmail.com>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: joshua <weblate-p5yaa1sp@dc7ia.eu>
Co-authored-by: Metehan Özyürek <metehanc8s9@yandex.com>
Co-authored-by: Padam Ghimire <padamghimire2020@gmail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
Co-authored-by: Vilhelmo Bandito <willibandido@gmail.com>
Co-authored-by: anoloth <anoloth@gmail.com>
Co-authored-by: Sanjar Barakayev <sanjarbarakayev5@gmail.com>
Co-authored-by: Kominak Halalu <kominak310@svcache.com>
Co-authored-by: trongtran810 <trantrong810@gmail.com>
Co-authored-by: escix <preminik@preminik.com>
Co-authored-by: Piotr Strebski <strebski@gmail.com>
Co-authored-by: Simon <simonpmt@gmail.com>
Co-authored-by: a19901201 <a19901201@gmail.com>
Co-authored-by: Theo <tbousiou@gmail.com>
Co-authored-by: AhmedRN <APOP319@gmail.com>
Co-authored-by: NetanelHarris <harris.netanel@gmail.com>
Co-authored-by: Nizami <nizamismidov4@gmail.com>
Co-authored-by: Mya Rohit <element@mailbolt.com>
Co-authored-by: HelaBasa <R45XvezA@protonmail.ch>
Co-authored-by: revblue <neosurpass@gmail.com>
Co-authored-by: Mine_My <me@minemy.me>
Co-authored-by: nafi3h <alireza.zarei@gmail.com>
Co-authored-by: c1bebff3 <if5c3xi6@protonmail.com>
Co-authored-by: phardyle <bradney_ccea@aleeas.com>
Co-authored-by: Corvurius <corvurius@gmail.com>
Co-authored-by: kskarthik <kskarthik@disroot.org>
Co-authored-by: Balázs Meskó <meskobalazs@gmail.com>
Co-authored-by: Oliver Gramberg <oliver.gramberg@gmx.de>
Co-authored-by: Vri <element.io@vrifox.cc>
Co-authored-by: Przemysław Romanik <github@rom4nik.pl>
Co-authored-by: Andrejs <tlpbu@droplar.com>
Co-authored-by: Anonimas <weblate@govindas.net>
Co-authored-by: Robert Eržen <robert@erzen.si>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
2022-10-04 13:54:41 +01:00
Michael Telatynski
d8124d37e4 Improve coverage (#23380) 2022-09-30 18:27:59 +01:00
RiotRobot
8e841be393 Reset matrix-react-sdk back to develop branch 2022-09-28 16:07:23 +01:00
RiotRobot
1b164dbc42 Reset matrix-js-sdk back to develop branch 2022-09-28 16:07:01 +01:00
RiotRobot
5ae4d0b8c9 Merge branch 'master' into develop
# Conflicts:
#	package.json
#	yarn.lock
2022-09-28 16:06:22 +01:00
RiotRobot
72ca95e97d v1.11.8 2022-09-28 16:01:20 +01:00
RiotRobot
151ef4d583 Prepare changelog for v1.11.8 2022-09-28 16:01:19 +01:00
RiotRobot
2d0becc8c5 Upgrade matrix-react-sdk to 3.57.0 2022-09-28 15:59:24 +01:00
RiotRobot
a074636773 Upgrade matrix-js-sdk to 20.0.0 2022-09-28 15:59:01 +01:00
RiotRobot
9009e62cc6 Reset matrix-react-sdk back to develop branch 2022-09-28 14:29:17 +01:00
RiotRobot
dca486ca10 Reset matrix-js-sdk back to develop branch 2022-09-28 14:29:01 +01:00
RiotRobot
fa966348ee Merge branch 'master' into develop
# Conflicts:
#	package.json
#	yarn.lock
2022-09-28 14:27:56 +01:00
RiotRobot
981a64aac4 v1.11.7 2022-09-28 14:22:49 +01:00
RiotRobot
3eba2211ca Prepare changelog for v1.11.7 2022-09-28 14:22:49 +01:00
RiotRobot
a529afc133 Upgrade matrix-react-sdk to 3.56.0 2022-09-28 14:20:52 +01:00
RiotRobot
2ba9c13165 Upgrade matrix-js-sdk to 19.7.0 2022-09-28 14:20:28 +01:00
RiotRobot
40ba9c76b1 Reset deps back to develop branch 2022-09-27 20:15:57 +01:00
RiotRobot
958f4d6b86 Merge branch 'master' into develop 2022-09-27 20:04:30 +01:00
Robin
fd40c7a24a New group call experience: Documentation (#23344)
* Document the use_exclusively Element Call config flag

* Document the new group call experience Labs flag
2022-09-25 10:57:26 -04:00
Robin
c797c06f92 Fix the lockfile, take 4 (#23349) 2022-09-24 07:55:34 +02:00
Michael Telatynski
5ec96f5abe Add test coverage (#23341) 2022-09-23 09:42:03 +01:00
Robin
31b4dae26b Fix the lockfile (#23343) 2022-09-22 15:37:17 -06:00
renovate[bot]
287a73506c Update all (#23325)
* Update all

* Hold some upgrades back

* Fix yarn.lock

* Revert @types/react

* Hold back `typescript`

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2022-09-21 10:59:14 +00:00
renovate[bot]
8de92bb14b Update jest monorepo to v29.0.3 (#23321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-21 08:25:25 +00:00
renovate[bot]
991fee51c5 Update babel monorepo to v7.19.1 (#23320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-21 09:13:09 +01:00
renovate[bot]
d1754559ad Update typescript-eslint monorepo to v5.38.0 (#23322)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-21 08:26:38 +02:00
47 changed files with 2232 additions and 899 deletions

View File

@@ -18,6 +18,7 @@ jobs:
- uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
- name: Install Dependencies
run: "./scripts/layered.sh"

View File

@@ -22,6 +22,7 @@ jobs:
- uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
- name: Install Dependencies
run: "./scripts/layered.sh"

View File

@@ -19,6 +19,7 @@ jobs:
- uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
- name: Install Dependencies
run: "./scripts/layered.sh"
@@ -39,6 +40,7 @@ jobs:
- uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
# Does not need branch matching as only analyses this layer
- name: Install Deps
@@ -56,6 +58,7 @@ jobs:
- uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
# Needs branch matching as it inherits .stylelintrc.js from matrix-react-sdk
- name: Install Dependencies
@@ -73,6 +76,7 @@ jobs:
- uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
- name: Install Deps
run: "scripts/layered.sh"

View File

@@ -21,6 +21,7 @@ jobs:
uses: actions/setup-node@v3
with:
cache: 'yarn'
node-version: 16
- name: Install Dependencies
run: "./scripts/layered.sh"

View File

@@ -1,3 +1,53 @@
Changes in [1.11.10](https://github.com/vector-im/element-web/releases/tag/v1.11.10) (2022-10-11)
=================================================================================================
## 🐛 Bug Fixes
* Use correct default for notification silencing ([\#9388](https://github.com/matrix-org/matrix-react-sdk/pull/9388)). Fixes vector-im/element-web#23456.
Changes in [1.11.9](https://github.com/vector-im/element-web/releases/tag/v1.11.9) (2022-10-11)
===============================================================================================
## Deprecations
* Legacy Piwik config.json option `piwik.policy_url` is deprecated in favour of `privacy_policy_url`. Support will be removed in the next release.
## ✨ Features
* Device manager - select all devices ([\#9330](https://github.com/matrix-org/matrix-react-sdk/pull/9330)). Contributed by @kerryarchibald.
* New group call experience: Call tiles ([\#9332](https://github.com/matrix-org/matrix-react-sdk/pull/9332)).
* Add Shift key to FormatQuote keyboard shortcut ([\#9298](https://github.com/matrix-org/matrix-react-sdk/pull/9298)). Contributed by @owi92.
* Device manager - sign out of multiple sessions ([\#9325](https://github.com/matrix-org/matrix-react-sdk/pull/9325)). Contributed by @kerryarchibald.
* Display push toggle for web sessions (MSC3890) ([\#9327](https://github.com/matrix-org/matrix-react-sdk/pull/9327)).
* Add device notifications enabled switch ([\#9324](https://github.com/matrix-org/matrix-react-sdk/pull/9324)).
* Implement push notification toggle in device detail ([\#9308](https://github.com/matrix-org/matrix-react-sdk/pull/9308)).
* New group call experience: Starting and ending calls ([\#9318](https://github.com/matrix-org/matrix-react-sdk/pull/9318)).
* New group call experience: Room header call buttons ([\#9311](https://github.com/matrix-org/matrix-react-sdk/pull/9311)).
* Make device ID copyable in device list ([\#9297](https://github.com/matrix-org/matrix-react-sdk/pull/9297)). Contributed by @duxovni.
* Use display name instead of user ID when rendering power events ([\#9295](https://github.com/matrix-org/matrix-react-sdk/pull/9295)).
* Read receipts for threads ([\#9239](https://github.com/matrix-org/matrix-react-sdk/pull/9239)). Fixes #23191.
## 🐛 Bug Fixes
* Use the correct sender key when checking shared secret ([\#2730](https://github.com/matrix-org/matrix-js-sdk/pull/2730)). Fixes vector-im/element-web#23374.
* Fix device selection in pre-join screen for Element Call video rooms ([\#9321](https://github.com/matrix-org/matrix-react-sdk/pull/9321)). Fixes #23331.
* Don't render a 1px high room topic if the room topic is empty ([\#9317](https://github.com/matrix-org/matrix-react-sdk/pull/9317)). Contributed by @Arnei.
* Don't show feedback prompts when that UIFeature is disabled ([\#9305](https://github.com/matrix-org/matrix-react-sdk/pull/9305)). Fixes #23327.
* Fix soft crash around unknown room pills ([\#9301](https://github.com/matrix-org/matrix-react-sdk/pull/9301)). Fixes matrix-org/element-web-rageshakes#15465.
* Fix spaces feedback prompt wrongly showing when feedback is disabled ([\#9302](https://github.com/matrix-org/matrix-react-sdk/pull/9302)). Fixes #23314.
* Fix tile soft crash in ReplyInThreadButton ([\#9300](https://github.com/matrix-org/matrix-react-sdk/pull/9300)). Fixes matrix-org/element-web-rageshakes#15493.
Changes in [1.11.8](https://github.com/vector-im/element-web/releases/tag/v1.11.8) (2022-09-28)
===============================================================================================
## 🐛 Bug Fixes
* Bump IDB crypto store version ([\#2705](https://github.com/matrix-org/matrix-js-sdk/pull/2705)).
Changes in [1.11.7](https://github.com/vector-im/element-web/releases/tag/v1.11.7) (2022-09-28)
===============================================================================================
## 🔒 Security
* Fix for [CVE-2022-39249](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D39249)
* Fix for [CVE-2022-39250](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D39250)
* Fix for [CVE-2022-39251](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D39251)
* Fix for [CVE-2022-39236](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D39236)
Changes in [1.11.6](https://github.com/vector-im/element-web/releases/tag/v1.11.6) (2022-09-20)
=========================================================================================================

View File

@@ -45,7 +45,9 @@
"preferred_domain": "meet.element.io"
},
"element_call": {
"url": "https://call.element.io"
"url": "https://call.element.io",
"participant_limit": 8,
"brand": "Element Call"
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
}

View File

@@ -321,6 +321,12 @@ The VoIP and Jitsi options are:
6. `element_call`: Optional configuration for native group calls using Element Call, with the following subkeys:
- `url`: The URL of the Element Call instance to use for native group calls. This option is considered experimental
and may be removed at any time without notice. Defaults to `https://call.element.io`.
- `use_exclusively`: A boolean specifying whether Element Call should be used exclusively as the only VoIP stack in
the app, removing the ability to start legacy 1:1 calls or Jitsi calls. Defaults to `false`.
- `participant_limit`: The maximum number of users who can join a call; if
this number is exceeded, the user will not be able to join a given call.
- `brand`: Optional name for the app. Defaults to `Element Call`. This is
used throughout the application in various strings/locations.
## Bug reporting

View File

@@ -168,6 +168,12 @@ Enables support for video rooms that use Element Call rather than Jitsi, and cau
This flag will not have any effect unless `feature_video_rooms` is also enabled.
## New group call experience (`feature_group_calls`) [In Development]
This feature allows users to place and join native [MSC3401](https://github.com/matrix-org/matrix-spec-proposals/pull/3401) group calls in compatible rooms, using Element Call.
If you're enabling this at the deployment level, you may also want to reference the docs for the `element_call` config section.
## Rich text in room topics (`feature_html_topic`) [In Development]
Enables rendering of MD / HTML in room topics.

View File

@@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.11.6",
"version": "1.11.10",
"description": "A feature-rich client for Matrix.org",
"author": "New Vector Ltd.",
"repository": {
@@ -61,8 +61,8 @@
"gfm.css": "^1.1.2",
"jsrsasign": "^10.5.25",
"katex": "^0.16.0",
"matrix-js-sdk": "19.6.0",
"matrix-react-sdk": "3.55.0",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
"matrix-widget-api": "^1.1.1",
"prop-types": "^15.7.2",
"react": "17.0.2",
@@ -90,6 +90,7 @@
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
"@sentry/webpack-plugin": "^1.18.1",
"@svgr/webpack": "^5.5.0",
"@testing-library/react": "^12.1.5",
"@types/flux": "^3.1.9",
"@types/jest": "^29.0.0",
"@types/modernizr": "^3.5.3",
@@ -109,7 +110,7 @@
"cpx": "^1.5.0",
"css-loader": "^3.6.0",
"dotenv": "^16.0.2",
"eslint": "8.23.0",
"eslint": "8.23.1",
"eslint-config-google": "^0.14.0",
"eslint-plugin-deprecate": "^0.7.0",
"eslint-plugin-import": "^2.25.4",
@@ -118,16 +119,19 @@
"eslint-plugin-react-hooks": "^4.3.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"fake-indexeddb": "^3.1.2",
"fetch-mock-jest": "^1.5.1",
"file-loader": "^5.1.0",
"fs-extra": "^0.30.0",
"html-webpack-plugin": "^4.5.2",
"jest": "^29.0.0",
"jest-canvas-mock": "^2.3.0",
"jest-environment-jsdom": "^29.0.0",
"jest-mock": "^29.0.0",
"jest-raw-loader": "^1.0.1",
"jest-sonar-reporter": "^2.0.0",
"json-loader": "^0.5.7",
"loader-utils": "^1.4.0",
"matrix-mock-request": "^2.0.0",
"matrix-mock-request": "^2.5.0",
"matrix-react-test-utils": "^0.2.3",
"matrix-web-i18n": "^1.3.0",
"mini-css-extract-plugin": "^1",
@@ -174,6 +178,9 @@
"testMatch": [
"<rootDir>/test/**/*-test.[tj]s?(x)"
],
"setupFiles": [
"jest-canvas-mock"
],
"setupFilesAfterEnv": [
"<rootDir>/node_modules/matrix-react-sdk/test/setupTests.js"
],
@@ -182,7 +189,6 @@
"\\.(gif|png|ttf|woff2)$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/imageMock.js",
"\\.svg$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/svg.js",
"\\$webapp/i18n/languages.json": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/languages.json",
"^browser-request$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/browser-request.js",
"^react$": "<rootDir>/node_modules/react",
"^react-dom$": "<rootDir>/node_modules/react-dom",
"^matrix-js-sdk$": "<rootDir>/node_modules/matrix-js-sdk/src",

View File

@@ -36,10 +36,12 @@ limitations under the License.
"Apple Color Emoji",
"Segoe UI Emoji",
"Segoe UI Symbol";
width: 100%;
min-height: 100%;
height: auto;
color: #000;
width: 100%;
height: 100%;
overflow: auto;
padding: 0 20px;
box-sizing: border-box;
.mx_ErrorView_container {
max-width: 680px;

View File

@@ -6,7 +6,7 @@
# the branch the current checkout is on, use that branch. Otherwise,
# use develop.
set -ex
set -x
GIT_CLONE_ARGS=("$@")
[ -z "$defbranch" ] && defbranch="develop"

View File

@@ -25,6 +25,8 @@
# all phonenumber.js-supported country flags (as SVGs) into
# PNGs that can be used by CountryDropdown.js.
set -e
# Allow CTRL+C to terminate the script
trap "echo Exited!; exit;" SIGINT SIGTERM

View File

@@ -3,6 +3,8 @@
# Echoes a version based on the git hashes of the element-web, react-sdk & js-sdk checkouts, for the case where
# these dependencies are git checkouts.
set -e
# Since the deps are fetched from git, we can rev-parse
REACT_SHA=$(git -C node_modules/matrix-react-sdk rev-parse --short=12 HEAD)
JSSDK_SHA=$(git -C node_modules/matrix-js-sdk rev-parse --short=12 HEAD)

View File

@@ -1,6 +1,6 @@
#!/bin/bash
set -x
set -ex
# Creates a layered environment with the full repo for the app and SDKs cloned
# and linked. This gives an element-web dev environment ready to build with

View File

@@ -12,8 +12,7 @@ then
exit
fi
set -e
set -x
set -ex
tmpdir=`mktemp -d 2>/dev/null || mktemp -d -t 'icontmp'`

View File

@@ -1,5 +1,7 @@
#!/bin/bash
set -e
# If $1 looks like v1.2.3 or v1.2.3-foo, strip the leading v, then print it to stdout
if [[ $1 =~ ^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(-.+)?$ ]]; then
echo ${1:1}

View File

@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import * as React from 'react';
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
export default class VectorAuthHeaderLogo extends React.PureComponent {

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { CSSProperties } from 'react';
import * as React from 'react';
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
import VectorAuthFooter from "./VectorAuthFooter";
@@ -47,12 +47,12 @@ export default class VectorAuthPage extends React.PureComponent {
background: `center/cover fixed url(${VectorAuthPage.getWelcomeBackgroundUrl()})`,
};
const modalStyle: CSSProperties = {
const modalStyle: React.CSSProperties = {
position: 'relative',
background: 'initial',
};
const blurStyle: CSSProperties = {
const blurStyle: React.CSSProperties = {
position: 'absolute',
top: 0,
right: 0,
@@ -62,7 +62,7 @@ export default class VectorAuthPage extends React.PureComponent {
background: pageStyle.background,
};
const modalContentStyle: CSSProperties = {
const modalContentStyle: React.CSSProperties = {
display: 'flex',
zIndex: 1,
background: 'rgba(255, 255, 255, 0.59)',

View File

@@ -84,12 +84,19 @@ export default class Favicon {
}
}
private reset() {
private reset(): void {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.drawImage(this.baseImage, 0, 0, this.canvas.width, this.canvas.height);
}
private options(n: number | string, params: IParams) {
private options(n: number | string, params: IParams): {
n: string | number;
len: number;
x: number;
y: number;
w: number;
h: number;
} {
const opt = {
n: ((typeof n) === "number") ? Math.abs(n as number | 0) : n,
len: ("" + n).length,
@@ -124,7 +131,7 @@ export default class Favicon {
return opt;
}
private circle(n: number | string, opts?: Partial<IParams>) {
private circle(n: number | string, opts?: Partial<IParams>): void {
const params = { ...this.params, ...opts };
const opt = this.options(n, params);
@@ -177,19 +184,19 @@ export default class Favicon {
this.context.closePath();
}
private ready() {
private ready(): void {
if (this.isReady) return;
this.isReady = true;
this.readyCb?.();
}
private setIcon(canvas) {
private setIcon(canvas: HTMLCanvasElement): void {
setImmediate(() => {
this.setIconSrc(canvas.toDataURL("image/png"));
});
}
private setIconSrc(url) {
private setIconSrc(url: string): void {
// if is attached to fav icon
if (this.browser.ff || this.browser.opera) {
// for FF we need to "recreate" element, attach to dom and remove old <link>
@@ -200,9 +207,7 @@ export default class Favicon {
newIcon.setAttribute("type", "image/png");
window.document.getElementsByTagName("head")[0].appendChild(newIcon);
newIcon.setAttribute("href", url);
if (old.parentNode) {
old.parentNode.removeChild(old);
}
old.parentNode?.removeChild(old);
} else {
this.icons.forEach(icon => {
icon.setAttribute("href", url);
@@ -210,7 +215,7 @@ export default class Favicon {
}
}
public badge(content: number | string, opts?: Partial<IParams>) {
public badge(content: number | string, opts?: Partial<IParams>): void {
if (!this.isReady) {
this.readyCb = () => {
this.badge(content, opts);
@@ -227,7 +232,7 @@ export default class Favicon {
this.setIcon(this.canvas);
}
private static getLinks() {
private static getLinks(): HTMLLinkElement[] {
const icons: HTMLLinkElement[] = [];
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
for (const link of links) {
@@ -238,7 +243,7 @@ export default class Favicon {
return icons;
}
private static getIcons() {
private static getIcons(): HTMLLinkElement[] {
// get favicon link elements
let elms = Favicon.getLinks();
if (elms.length === 0) {

View File

@@ -10,10 +10,10 @@
"Download Completed": "Download Completed",
"Open": "Open",
"Dismiss": "Dismiss",
"%(brand)s Desktop (%(platformName)s)": "%(brand)s Desktop (%(platformName)s)",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s",
"Go to your browser to complete Sign In": "Go to your browser to complete Sign In",
"Unknown device": "Unknown device",
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s on %(osName)s",
"Powered by Matrix": "Powered by Matrix",
"Use %(brand)s on mobile": "Use %(brand)s on mobile",
"Unsupported browser": "Unsupported browser",

View File

@@ -1,21 +1,21 @@
{
"Dismiss": "Abaikan",
"Unknown device": "Perangkat tidak dikenal",
"Unknown device": "Perangkat tidak diketahui",
"Welcome to Element": "Selamat datang di Element",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Konfigurasi Element Anda berisi JSON yang tidak valid. Mohon perbaiki masalahnya dan muat ulang halamannya.",
"Invalid configuration: no default server specified.": "Konfigurasi tidak valid: server bawaan belum ditentukan.",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Konfigurasi Element Anda berisi JSON yang tidak absah. Mohon perbaiki masalahnya dan muat ulang laman ini.",
"Invalid configuration: no default server specified.": "Konfigurasi tidak absah: server bawaan belum ditentukan.",
"Explore rooms": "Jelajahi ruangan",
"Create Account": "Buat Akun",
"Go to your browser to complete Sign In": "Buka browser Anda untuk menyelesaikan Sign In",
"Go to your browser to complete Sign In": "Buka peramban Anda untuk menyelesaikan Sign In",
"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 yang 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 dapat menjalankan %(brand)s",
"Unsupported browser": "Browser tidak didukung",
"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 melanjutkan menggunakan peramban Anda saat ini, tetapi beberapa atau semua fitur mungkin tidak berfungsi dan tampilan serta nuansa aplikasi mungkin tidak benar.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Silakan instal <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, atau <safariLink>Safari</safariLink> untuk pengalaman yang terbaik.",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s menggunakan fitur peramban tingkat lanjut yang tidak didukung oleh peramban Anda saat ini.",
"Your browser can't run %(brand)s": "Peramban Anda tidak dapat menjalankan %(brand)s",
"Unsupported browser": "Peramban tidak didukung",
"Use %(brand)s on mobile": "Gunakan %(brand)s di ponsel",
"Powered by Matrix": "Diberdayakan oleh Matrix",
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
@@ -23,10 +23,10 @@
"Open": "Buka",
"Download Completed": "Unduhan 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 dapat memuat file konfigurasi: mohon muat ulang halaman ini untuk mencoba lagi.",
"Invalid JSON": "JSON tidak valid",
"Unable to load config file: please refresh the page to try again.": "Tidak dapat memuat file konfigurasi: mohon muat ulang laman ini untuk mencoba lagi.",
"Invalid JSON": "JSON tidak absah",
"The message from the parser is: %(message)s": "Pesan dari pengurai adalah: %(message)s",
"Your Element is misconfigured": "Anda mengatur Element dengan salah",
"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.",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Obrolan &amp; kolaborasi terdesentralisasi dan terenkripsi, diberdayakan oleh $matrixLogo"
"Your Element is misconfigured": "Anda salah mengatur Element",
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Konfigurasi tidak absah: hanya bisa menentukan satu dari default_server_config, default_server_name, atau default_hs_url.",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Obrolan &amp; kolaborasi terdesentralisasi dan terenkripsi diberdayakan oleh $matrixLogo"
}

View File

@@ -4,7 +4,7 @@
"Welcome to Element": "Добро пожаловать в Element",
"Sign In": "Войти",
"Create Account": "Создать учётную запись",
"Explore rooms": "Список комнат",
"Explore rooms": "Обзор комнат",
"Unexpected error preparing the app. See console for details.": "Неожиданная ошибка при подготовке приложения. Подробности см. в консоли.",
"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.": "Неверная конфигурация: сервер по умолчанию не указан.",

View File

@@ -26,5 +26,6 @@
"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.": "Lahko nadaljujete z uporabo vašega trenutnega brskalnika, vendar lahko to privede do manjkajočih funkcionalnosti ali napačnega izgleda aplikacije.",
"I understand the risks and wish to continue": "Razumem riziko in želim vseeno nadaljevati",
"Go to element.io": "Pojdi na element.io",
"Failed to start": "Neuspel zagon"
"Failed to start": "Neuspel zagon",
"Use %(brand)s on mobile": "Uporabi %(brand)s na mobilni napravi"
}

View File

@@ -25,7 +25,7 @@
"Go to your browser to complete Sign In": "Перейдіть у ваш браузер щоб завершити вхід",
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
"Powered by Matrix": "Працює на Matrix",
"Your browser can't run %(brand)s": "Ваш переглядач неспроможний запустити %(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 використовує передові властивості, які ваш браузер не підтримує.",
"Use %(brand)s on mobile": "Користуйтеся %(brand)s на мобільному",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Децентралізована, зашифрована бесіда та співпраця на основі $matrixLogo"

View File

@@ -18,8 +18,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// To ensure we load the browser-request version
import "matrix-js-sdk"; // eslint-disable-line no-restricted-imports
// To ensure we load the browser-matrix version first
import "matrix-js-sdk/src/browser-index";
import React from 'react';
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';

View File

@@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import request from 'browser-request';
import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
// Load the config file. First try to load up a domain-specific config of the
@@ -32,44 +30,28 @@ export async function getVectorConfig(relativeLocation=''): Promise<IConfigOptio
if (Object.keys(configJson).length === 0) {
throw new Error(); // throw to enter the catch
}
return configJson as IConfigOptions;
return configJson;
} catch (e) {
return await generalConfigPromise as IConfigOptions;
return generalConfigPromise;
}
}
function getConfig(configJsonFilename: string): Promise<{}> {
return new Promise(function(resolve, reject) {
request(
{ method: "GET", url: configJsonFilename, qs: { cachebuster: Date.now() } },
(err, response, body) => {
try {
if (err || response.status < 200 || response.status >= 300) {
// Lack of a config isn't an error, we should
// just use the defaults.
// Also treat a blank config as no config, assuming
// the status code is 0, because we don't get 404s
// from file: URIs so this is the only way we can
// not fail if the file doesn't exist when loading
// from a file:// URI.
if (response) {
if (response.status == 404 || (response.status == 0 && body == '')) {
resolve({});
}
}
reject({ err: err, response: response });
return;
}
// We parse the JSON ourselves rather than use the JSON
// parameter, since this throws a parse error on empty
// which breaks if there's no config.json and we're
// loading from the filesystem (see above).
resolve(JSON.parse(body));
} catch (e) {
reject({ err: e });
}
},
);
async function getConfig(configJsonFilename: string): Promise<IConfigOptions> {
const url = new URL(configJsonFilename, window.location.href);
url.searchParams.set("cachebuster", Date.now().toString());
const res = await fetch(url, {
cache: "no-cache",
method: "GET",
});
if (res.status === 404 || res.status === 0) {
// Lack of a config isn't an error, we should just use the defaults.
// Also treat a blank config as no config, assuming the status code is 0, because we don't get 404s from file:
// URIs so this is the only way we can not fail if the file doesn't exist when loading from a file:// URI.
return {} as IConfigOptions;
}
if (res.ok) {
return res.json();
}
}

View File

@@ -45,7 +45,6 @@ import VectorBasePlatform from './VectorBasePlatform';
import { SeshatIndexManager } from "./SeshatIndexManager";
import { IPCManager } from "./IPCManager";
const electron = window.electron;
const isMac = navigator.platform.toUpperCase().includes('MAC');
function platformFriendlyName(): string {
@@ -70,7 +69,7 @@ function platformFriendlyName(): string {
function onAction(payload: ActionPayload): void {
// Whitelist payload actions, no point sending most across
if (['call_state'].includes(payload.action)) {
electron.send('app_onAction', payload);
window.electron.send('app_onAction', payload);
}
}
@@ -103,7 +102,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
false if there is not
or the error if one is encountered
*/
electron.on('check_updates', (event, status) => {
window.electron.on('check_updates', (event, status) => {
dis.dispatch<CheckUpdatesPayload>({
action: Action.CheckUpdates,
...getUpdateCheckStatus(status),
@@ -111,27 +110,27 @@ export default class ElectronPlatform extends VectorBasePlatform {
});
// try to flush the rageshake logs to indexeddb before quit.
electron.on('before-quit', function() {
window.electron.on('before-quit', function() {
logger.log('element-desktop closing');
rageshake.flush();
});
electron.on('update-downloaded', this.onUpdateDownloaded);
window.electron.on('update-downloaded', this.onUpdateDownloaded);
electron.on('preferences', () => {
window.electron.on('preferences', () => {
dis.fire(Action.ViewUserSettings);
});
electron.on('userDownloadCompleted', (ev, { id, name }) => {
window.electron.on('userDownloadCompleted', (ev, { id, name }) => {
const key = `DOWNLOAD_TOAST_${id}`;
const onAccept = () => {
electron.send('userDownloadAction', { id, open: true });
window.electron.send('userDownloadAction', { id, open: true });
ToastStore.sharedInstance().dismissToast(key);
};
const onDismiss = () => {
electron.send('userDownloadAction', { id });
window.electron.send('userDownloadAction', { id });
};
ToastStore.sharedInstance().addOrReplaceToast({
@@ -187,7 +186,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
if (this.notificationCount === count) return;
super.setNotificationCount(count);
electron.send('setBadgeCount', count);
window.electron.send('setBadgeCount', count);
}
public supportsNotifications(): boolean {
@@ -233,7 +232,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
public loudNotification(ev: MatrixEvent, room: Room) {
electron.send('loudNotification');
window.electron.send('loudNotification');
}
public needsUrlTooltips(): boolean {
@@ -269,19 +268,19 @@ export default class ElectronPlatform extends VectorBasePlatform {
public startUpdateCheck() {
super.startUpdateCheck();
electron.send('check_updates');
window.electron.send('check_updates');
}
public installUpdate() {
// IPC to the main process to install the update, since quitAndInstall
// doesn't fire the before-quit event so the main process needs to know
// it should exit.
electron.send('install_update');
window.electron.send('install_update');
}
public getDefaultDeviceDisplayName(): string {
const brand = SdkConfig.get().brand;
return _t('%(brand)s Desktop (%(platformName)s)', {
return _t('%(brand)s Desktop: %(platformName)s', {
brand,
platformName: platformFriendlyName(),
});

View File

@@ -19,8 +19,6 @@ import { logger } from "matrix-js-sdk/src/logger";
import { ElectronChannel } from "../../@types/global";
const electron = window.electron;
interface IPCPayload {
id?: number;
error?: string;
@@ -35,7 +33,7 @@ export class IPCManager {
private readonly sendChannel: ElectronChannel = "ipcCall",
private readonly recvChannel: ElectronChannel = "ipcReply",
) {
electron.on(this.recvChannel, this.onIpcReply);
window.electron.on(this.recvChannel, this.onIpcReply);
}
public async call(name: string, ...args: any[]): Promise<any> {

View File

@@ -17,7 +17,6 @@ limitations under the License.
*/
import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform";
import request from 'browser-request';
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
import { _t } from 'matrix-react-sdk/src/languageHandler';
import { hideToast as hideUpdateToast, showToast as showUpdateToast } from "matrix-react-sdk/src/toasts/UpdateToast";
@@ -87,31 +86,18 @@ export default class WebPlatform extends VectorBasePlatform {
});
}
private getMostRecentVersion(): 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
// presence of intermediate caching proxies), but still: we're trying
// to tell the user that there is a new version.
return new Promise((resolve, reject) => {
request(
{
method: "GET",
url: "version",
qs: { cachebuster: Date.now() },
},
(err, response, body) => {
if (err || response.status < 200 || response.status >= 300) {
if (err === null) err = { status: response.status };
reject(err);
return;
}
resolve(getNormalizedAppVersion(body.trim()));
},
);
private async getMostRecentVersion(): Promise<string> {
const res = await fetch("version", {
method: "GET",
cache: "no-cache",
});
if (res.ok) {
const text = await res.text();
return getNormalizedAppVersion(text.trim());
}
return Promise.reject({ status: res.status });
}
public getAppVersion(): Promise<string> {
@@ -210,7 +196,7 @@ export default class WebPlatform extends VectorBasePlatform {
let osName = ua.getOS().name || "unknown OS";
// Stylise the value from the parser to match Apple's current branding.
if (osName === "Mac OS") osName = "macOS";
return _t('%(appName)s (%(browserName)s, %(osName)s)', {
return _t('%(appName)s: %(browserName)s on %(osName)s', {
appName,
browserName,
osName,

View File

@@ -41,6 +41,7 @@ import { RoomView as RoomViewClass } from 'matrix-react-sdk/src/components/struc
import LoginComponent from 'matrix-react-sdk/src/components/structures/auth/Login';
import WelcomeComponent from "matrix-react-sdk/src/components/views/auth/Welcome";
import EmbeddedPage from "matrix-react-sdk/src/components/structures/EmbeddedPage";
import { AutoDiscovery } from 'matrix-js-sdk/src/matrix';
const DEFAULT_HS_URL='http://my_server';
const DEFAULT_IS_URL='http://my_is';
@@ -60,7 +61,7 @@ describe('loading:', function() {
beforeEach(function() {
httpBackend = new MockHttpBackend();
jssdk.request(httpBackend.requestFn);
window.fetch = httpBackend.fetchFn;
parentDiv = document.createElement('div');
// uncomment this to actually add the div to the UI, to help with
@@ -73,21 +74,25 @@ describe('loading:', function() {
afterEach(async function() {
console.log(`${Date.now()}: loading: afterEach`);
if (parentDiv) {
ReactDOM.unmountComponentAtNode(parentDiv);
parentDiv.remove();
parentDiv = null;
try {
if (matrixChat) {
ReactDOM.unmountComponentAtNode(parentDiv);
parentDiv.remove();
parentDiv = null;
}
// unmounting should have cleared the MatrixClientPeg
expect(MatrixClientPeg.get()).toBe(null);
// clear the indexeddbs so we can start from a clean slate next time.
await Promise.all([
test_utils.deleteIndexedDB('matrix-js-sdk:crypto'),
test_utils.deleteIndexedDB('matrix-js-sdk:riot-web-sync'),
]);
cleanLocalstorage();
} catch (e) {
console.error(e);
}
// unmounting should have cleared the MatrixClientPeg
expect(MatrixClientPeg.get()).toBe(null);
// clear the indexeddbs so we can start from a clean slate next time.
await Promise.all([
test_utils.deleteIndexedDB('matrix-js-sdk:crypto'),
test_utils.deleteIndexedDB('matrix-js-sdk:riot-web-sync'),
]);
cleanLocalstorage();
console.log(`${Date.now()}: loading: afterEach complete`);
});
@@ -421,7 +426,6 @@ describe('loading:', function() {
describe('Guest auto-registration:', function() {
it('shows a welcome page by default', function() {
loadApp();
return sleep(1).then(() => {

View File

@@ -0,0 +1,431 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Favicon should clear a badge if called with a zero value 1`] = `
[
{
"props": {
"height": 32,
"width": 32,
"x": 0,
"y": 0,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "clearRect",
},
{
"props": {
"dHeight": 32,
"dWidth": 32,
"dx": 0,
"dy": 0,
"img": <img
height="32"
width="32"
/>,
"sHeight": 32,
"sWidth": 32,
"sx": 0,
"sy": 0,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "drawImage",
},
{
"props": {
"fillRule": "nonzero",
"path": [
{
"props": {},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "beginPath",
},
{
"props": {
"x": 16.159999999999997,
"y": 12.8,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "moveTo",
},
{
"props": {
"x": 22.4,
"y": 12.8,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
{
"props": {
"x": 31.999999999999996,
"y": 22.4,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
{
"props": {
"x": 9.92,
"y": 32,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
{
"props": {
"x": 0.3200000000000003,
"y": 22.4,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
],
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "fill",
},
{
"props": {
"path": [
{
"props": {},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "beginPath",
},
],
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "stroke",
},
{
"props": {
"maxWidth": null,
"text": "123",
"x": 16,
"y": 29,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "fillText",
},
{
"props": {
"height": 32,
"width": 32,
"x": 0,
"y": 0,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "clearRect",
},
{
"props": {
"dHeight": 32,
"dWidth": 32,
"dx": 0,
"dy": 0,
"img": <img
height="32"
width="32"
/>,
"sHeight": 32,
"sWidth": 32,
"sx": 0,
"sy": 0,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "drawImage",
},
]
`;
exports[`Favicon should draw a badge if called with a non-zero value 1`] = `
[
{
"props": {
"height": 32,
"width": 32,
"x": 0,
"y": 0,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "clearRect",
},
{
"props": {
"dHeight": 32,
"dWidth": 32,
"dx": 0,
"dy": 0,
"img": <img
height="32"
width="32"
/>,
"sHeight": 32,
"sWidth": 32,
"sx": 0,
"sy": 0,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "drawImage",
},
{
"props": {
"fillRule": "nonzero",
"path": [
{
"props": {},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "beginPath",
},
{
"props": {
"x": 16.159999999999997,
"y": 12.8,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "moveTo",
},
{
"props": {
"x": 22.4,
"y": 12.8,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
{
"props": {
"x": 31.999999999999996,
"y": 22.4,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
{
"props": {
"x": 9.92,
"y": 32,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
{
"props": {
"x": 0.3200000000000003,
"y": 22.4,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "lineTo",
},
],
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "fill",
},
{
"props": {
"path": [
{
"props": {},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "beginPath",
},
],
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "stroke",
},
{
"props": {
"maxWidth": null,
"text": "123",
"x": 16,
"y": 29,
},
"transform": [
1,
0,
0,
1,
0,
0,
],
"type": "fillText",
},
]
`;

View File

@@ -0,0 +1,27 @@
/*
Copyright 2022 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 * as React from "react";
import { render } from "@testing-library/react";
import ErrorView from "../../../../src/async-components/structures/ErrorView";
describe("<ErrorView />", () => {
it("should match snapshot", () => {
const { asFragment } = render(<ErrorView title="TITLE" messages={["MSG1", "MSG2"]} />);
expect(asFragment()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,66 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<ErrorView /> should match snapshot 1`] = `
<DocumentFragment>
<div
class="mx_ErrorView"
>
<div
class="mx_ErrorView_container"
>
<div
class="mx_HomePage_header"
>
<span
class="mx_HomePage_logo"
>
<img
alt="Element"
height="42"
src="themes/element/img/logos/element-logo.svg"
/>
</span>
<h1>
Failed to start
</h1>
</div>
<div
class="mx_HomePage_col"
>
<div
class="mx_HomePage_row"
>
<div>
<h2
id="step1_heading"
>
TITLE
</h2>
<p>
MSG1
</p>
<p>
MSG2
</p>
</div>
</div>
</div>
<div
class="mx_HomePage_row mx_Center mx_Spacer"
>
<p
class="mx_Spacer"
>
<a
class="mx_FooterLink"
href="https://element.io"
target="_blank"
>
Go to element.io
</a>
</p>
</div>
</div>
</div>
</DocumentFragment>
`;

View File

@@ -0,0 +1,27 @@
/*
Copyright 2022 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 * as React from "react";
import { render } from "@testing-library/react";
import VectorAuthFooter from "../../../../../src/components/views/auth/VectorAuthFooter";
describe("<VectorAuthFooter />", () => {
it("should match snapshot", () => {
const { asFragment } = render(<VectorAuthFooter />);
expect(asFragment()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,27 @@
/*
Copyright 2022 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 * as React from "react";
import { render } from "@testing-library/react";
import VectorAuthHeaderLogo from "../../../../../src/components/views/auth/VectorAuthHeaderLogo";
describe("<VectorAuthHeaderLogo />", () => {
it("should match snapshot", () => {
const { asFragment } = render(<VectorAuthHeaderLogo />);
expect(asFragment()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,27 @@
/*
Copyright 2022 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 * as React from "react";
import { render } from "@testing-library/react";
import VectorAuthPage from "../../../../../src/components/views/auth/VectorAuthPage";
describe("<VectorAuthPage />", () => {
it("should match snapshot", () => {
const { asFragment } = render(<VectorAuthPage />);
expect(asFragment()).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<VectorAuthFooter /> should match snapshot 1`] = `
<DocumentFragment>
<footer
class="mx_AuthFooter"
role="contentinfo"
>
<a
href="https://element.io/blog"
rel="noreferrer noopener"
target="_blank"
>
Blog
</a>
<a
href="https://twitter.com/element_hq"
rel="noreferrer noopener"
target="_blank"
>
Twitter
</a>
<a
href="https://github.com/vector-im/element-web"
rel="noreferrer noopener"
target="_blank"
>
GitHub
</a>
<a
href="https://matrix.org"
rel="noreferrer noopener"
target="_blank"
>
Powered by Matrix
</a>
</footer>
</DocumentFragment>
`;

View File

@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<VectorAuthHeaderLogo /> should match snapshot 1`] = `
<DocumentFragment>
<aside
class="mx_AuthHeaderLogo"
>
<img
alt="Element"
src="themes/element/img/logos/element-logo.svg"
/>
</aside>
</DocumentFragment>
`;

View File

@@ -0,0 +1,56 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<VectorAuthPage /> should match snapshot 1`] = `
<DocumentFragment>
<div
class="mx_AuthPage"
>
<div
class="mx_AuthPage_modal"
style="position: relative;"
>
<div
class="mx_AuthPage_modalBlur"
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; filter: blur(40px);"
/>
<div
class="mx_AuthPage_modalContent"
style="display: flex; z-index: 1; background: rgba(255, 255, 255, 0.59); border-radius: 8px;"
/>
</div>
<footer
class="mx_AuthFooter"
role="contentinfo"
>
<a
href="https://element.io/blog"
rel="noreferrer noopener"
target="_blank"
>
Blog
</a>
<a
href="https://twitter.com/element_hq"
rel="noreferrer noopener"
target="_blank"
>
Twitter
</a>
<a
href="https://github.com/vector-im/element-web"
rel="noreferrer noopener"
target="_blank"
>
GitHub
</a>
<a
href="https://matrix.org"
rel="noreferrer noopener"
target="_blank"
>
Powered by Matrix
</a>
</footer>
</div>
</DocumentFragment>
`;

View File

@@ -0,0 +1,61 @@
/*
Copyright 2022 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 "jest-canvas-mock";
import Favicon from "../../src/favicon";
jest.useFakeTimers();
describe("Favicon", () => {
beforeEach(() => {
const head = document.createElement("head");
window.document.documentElement.prepend(head);
});
it("should create a link element if one doesn't yet exist", () => {
const favicon = new Favicon();
expect(favicon).toBeTruthy();
const link = window.document.querySelector("link");
expect(link.rel).toContain("icon");
});
it("should draw a badge if called with a non-zero value", () => {
const favicon = new Favicon();
favicon.badge(123);
jest.runAllTimers();
expect(favicon["context"].__getDrawCalls()).toMatchSnapshot();
});
it("should clear a badge if called with a zero value", () => {
const favicon = new Favicon();
favicon.badge(123);
jest.runAllTimers();
favicon.badge(0);
expect(favicon["context"].__getDrawCalls()).toMatchSnapshot();
});
it("should recreate link element for firefox and opera", () => {
window["InstallTrigger"] = {};
window["opera"] = {};
const favicon = new Favicon();
const originalLink = window.document.querySelector("link");
favicon.badge(123);
jest.runAllTimers();
const newLink = window.document.querySelector("link");
expect(originalLink).not.toStrictEqual(newLink);
});
});

View File

@@ -14,14 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import request from 'browser-request';
import fetchMock from "fetch-mock-jest";
import { getVectorConfig } from "../../../src/vector/getconfig";
describe('getVectorConfig()', () => {
const setRequestMockImplementationOnce = (err?: unknown, response?: { status: number }, body?: string) =>
request.mockImplementationOnce((_opts, callback) => callback(err, response, body));
fetchMock.config.overwriteRoutes = true;
describe('getVectorConfig()', () => {
const prevDocumentDomain = document.domain;
const elementDomain = 'app.element.io';
const now = 1234567890;
@@ -38,6 +37,7 @@ describe('getVectorConfig()', () => {
// stable value for cachebuster
jest.spyOn(Date, 'now').mockReturnValue(now);
jest.clearAllMocks();
fetchMock.mockClear();
});
afterAll(() => {
@@ -46,85 +46,67 @@ describe('getVectorConfig()', () => {
});
it('requests specific config for document domain', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:/config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:/config.json", generalConfig);
await getVectorConfig();
expect(request.mock.calls[0][0]).toEqual({ method: "GET", url: 'config.app.element.io.json', qs: { cachebuster: now } })
await expect(getVectorConfig()).resolves.toEqual(specificConfig);
});
it('adds trailing slash to relativeLocation when not an empty string', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:../config.app.element.io.json", specificConfig);
fetchMock.getOnce("express:../config.json", generalConfig);
await getVectorConfig('..');
expect(request.mock.calls[0][0]).toEqual(expect.objectContaining({ url: '../config.app.element.io.json' }))
expect(request.mock.calls[1][0]).toEqual(expect.objectContaining({ url: '../config.json' }))
});
it('returns parsed specific config when it is non-empty', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
const result = await getVectorConfig();
expect(result).toEqual(specificConfig);
await expect(getVectorConfig("..")).resolves.toEqual(specificConfig);
});
it('returns general config when specific config succeeds but is empty', async () => {
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify({}))
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:/config.app.element.io.json", {});
fetchMock.getOnce("express:/config.json", generalConfig);
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config 404s', async () => {
setRequestMockImplementationOnce(undefined, { status: 404 })
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:/config.app.element.io.json", { status: 404 });
fetchMock.getOnce("express:/config.json", generalConfig);
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config is fetched from a file and is empty', async () => {
setRequestMockImplementationOnce(undefined, { status: 0 }, '')
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:/config.app.element.io.json", 0);
fetchMock.getOnce("express:/config.json", generalConfig);
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config returns a non-200 status', async () => {
setRequestMockImplementationOnce(undefined, { status: 401 })
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:/config.app.element.io.json", { status: 401 });
fetchMock.getOnce("express:/config.json", generalConfig);
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('returns general config when specific config returns an error', async () => {
setRequestMockImplementationOnce('err1')
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err1" });
fetchMock.getOnce("express:/config.json", generalConfig);
const result = await getVectorConfig();
expect(result).toEqual(generalConfig);
await expect(getVectorConfig()).resolves.toEqual(generalConfig);
});
it('rejects with an error when general config rejects', async () => {
setRequestMockImplementationOnce('err-specific');
setRequestMockImplementationOnce('err-general');
fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" });
fetchMock.getOnce("express:/config.json", { throws: "err-general" });
await expect(() => getVectorConfig()).rejects.toEqual({"err": "err-general", "response": undefined});
await expect(getVectorConfig()).rejects.toBe("err-general");
});
it('rejects with an error when config is invalid JSON', async () => {
setRequestMockImplementationOnce('err-specific');
setRequestMockImplementationOnce(undefined, { status: 200 }, '{"invalid": "json",}');
fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" });
fetchMock.getOnce("express:/config.json", '{"invalid": "json",}');
await expect(() => getVectorConfig()).rejects.toEqual({
err: new SyntaxError("Unexpected token } in JSON at position 19"),
});
// We can't assert it'll be a SyntaxError as node-fetch behaves differently
// https://github.com/wheresrhys/fetch-mock/issues/270
await expect(getVectorConfig()).rejects.toThrow("Unexpected token } in JSON at position 19");
});
});

View File

@@ -0,0 +1,279 @@
/*
Copyright 2022 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 request from 'browser-request';
import EventEmitter from 'events';
import { logger } from 'matrix-js-sdk/src/logger';
import { MatrixClient, MatrixEvent, Room } from 'matrix-js-sdk/src/matrix';
import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform';
import { Action } from 'matrix-react-sdk/src/dispatcher/actions';
import dispatcher from 'matrix-react-sdk/src/dispatcher/dispatcher';
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake';
import ElectronPlatform from '../../../../src/vector/platform/ElectronPlatform';
jest.mock('matrix-react-sdk/src/rageshake/rageshake', () => ({
flush: jest.fn()
}))
describe('ElectronPlatform', () => {
const defaultUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36';
const mockElectron = {
on: jest.fn(),
send: jest.fn()
};
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
const dispatchFireSpy = jest.spyOn(dispatcher, 'fire');
const logSpy = jest.spyOn(logger, 'log').mockImplementation(() => {});
const userId = '@alice:server.org';
const deviceId = 'device-id';
window.electron = mockElectron;
beforeEach(() => {
window.electron = mockElectron;
jest.clearAllMocks();
delete window.navigator;
window.navigator = { userAgent: defaultUserAgent } as unknown as Navigator;
});
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
mockElectron.on.mock.calls.find(([type]) => type === eventType);
it('flushes rageshake before quitting', () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall('before-quit');
// correct event bound
expect(event).toBeTruthy();
handler();
expect(logSpy).toHaveBeenCalled();
expect(rageshake.flush).toHaveBeenCalled();
});
it('dispatches view settings action on preferences event', () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall('preferences');
// correct event bound
expect(event).toBeTruthy();
handler();
expect(dispatchFireSpy).toHaveBeenCalledWith(Action.ViewUserSettings);
});
describe('updates', () => {
it('dispatches on check updates action', () => {
new ElectronPlatform();
const [event, handler] = getElectronEventHandlerCall('check_updates');
// correct event bound
expect(event).toBeTruthy();
handler({}, true);
expect(dispatchSpy).toHaveBeenCalledWith({
action: Action.CheckUpdates,
status: UpdateCheckStatus.Downloading
})
});
it('dispatches on check updates action when update not available', () => {
new ElectronPlatform();
const [, handler] = getElectronEventHandlerCall('check_updates');
handler({}, false);
expect(dispatchSpy).toHaveBeenCalledWith({
action: Action.CheckUpdates,
status: UpdateCheckStatus.NotAvailable
})
});
it('starts update check', () => {
const platform = new ElectronPlatform();
platform.startUpdateCheck();
expect(mockElectron.send).toHaveBeenCalledWith('check_updates')
});
it('installs update', () => {
const platform = new ElectronPlatform();
platform.installUpdate();
expect(mockElectron.send).toHaveBeenCalledWith('install_update')
});
});
it('returns human readable name', () => {
const platform = new ElectronPlatform();
expect(platform.getHumanReadableName()).toEqual('Electron Platform');
});
describe("getDefaultDeviceDisplayName", () => {
it.each([[
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"Element Desktop: macOS",
],
[
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) electron/1.0.0 Chrome/53.0.2785.113 Electron/1.4.3 Safari/537.36",
"Element Desktop: Windows",
],
[
"Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: Linux",
],
[
"Mozilla/5.0 (X11; FreeBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: FreeBSD",
],
[
"Mozilla/5.0 (X11; OpenBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: OpenBSD",
],
[
"Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0",
"Element Desktop: SunOS",
],
[
"custom user agent",
"Element Desktop: Unknown",
],
])("%s = %s", (userAgent, result) => {
delete window.navigator;
window.navigator = { userAgent } as unknown as Navigator;
const platform = new ElectronPlatform();
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
});
});
it('returns true for needsUrlTooltips', () => {
const platform = new ElectronPlatform();
expect(platform.needsUrlTooltips()).toBe(true);
});
it('should override browser shortcuts', () => {
const platform = new ElectronPlatform();
expect(platform.overrideBrowserShortcuts()).toBe(true);
});
it('allows overriding native context menus', () => {
const platform = new ElectronPlatform();
expect(platform.allowOverridingNativeContextMenus()).toBe(true);
});
it('indicates support for desktop capturer', () => {
const platform = new ElectronPlatform();
expect(platform.supportsDesktopCapturer()).toBe(true);
});
it('indicates no support for jitsi screensharing', () => {
const platform = new ElectronPlatform();
expect(platform.supportsJitsiScreensharing()).toBe(false);
});
describe('notifications', () => {
it('indicates support for notifications', () => {
const platform = new ElectronPlatform();
expect(platform.supportsNotifications()).toBe(true);
});
it('may send notifications', () => {
const platform = new ElectronPlatform();
expect(platform.maySendNotifications()).toBe(true);
});
it('pretends to request notification permission', async () => {
const platform = new ElectronPlatform();
const result = await platform.requestNotificationPermission();
expect(result).toEqual('granted');
});
it('creates a loud notification', async () => {
const platform = new ElectronPlatform();
platform.loudNotification(new MatrixEvent(), new Room('!room:server', {} as any, userId));
expect(mockElectron.send).toHaveBeenCalledWith('loudNotification');
});
it('sets notification count when count is changing', async () => {
const platform = new ElectronPlatform();
platform.setNotificationCount(0);
// not called because matches internal notificaiton count
expect(mockElectron.send).not.toHaveBeenCalledWith('setBadgeCount', 0);
platform.setNotificationCount(1);
expect(mockElectron.send).toHaveBeenCalledWith('setBadgeCount', 1);
});
});
describe('spellcheck', () => {
it('indicates support for spellcheck settings', () => {
const platform = new ElectronPlatform();
expect(platform.supportsSpellCheckSettings()).toBe(true);
});
it('gets available spellcheck languages', () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.getAvailableSpellCheckLanguages();
const [channel, { name }] = mockElectron.send.mock.calls[0];
expect(channel).toEqual("ipcCall");
expect(name).toEqual('getAvailableSpellCheckLanguages')
});
});
describe('pickle key', () => {
it('makes correct ipc call to get pickle key', () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.getPickleKey(userId, deviceId);
const [, { name, args }] = mockElectron.send.mock.calls[0];
expect(name).toEqual('getPickleKey')
expect(args).toEqual([userId, deviceId])
});
it('makes correct ipc call to create pickle key', () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.createPickleKey(userId, deviceId);
const [, { name, args }] = mockElectron.send.mock.calls[0];
expect(name).toEqual('createPickleKey')
expect(args).toEqual([userId, deviceId])
});
it('makes correct ipc call to destroy pickle key', () => {
const platform = new ElectronPlatform();
mockElectron.send.mockClear();
platform.destroyPickleKey(userId, deviceId);
const [, { name, args }] = mockElectron.send.mock.calls[0];
expect(name).toEqual('destroyPickleKey')
expect(args).toEqual([userId, deviceId])
});
});
describe('versions', () => {
it('calls install update', () => {
const platform = new ElectronPlatform();
platform.installUpdate();
expect(mockElectron.send).toHaveBeenCalledWith('install_update');
});
});
});

View File

@@ -14,7 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { mocked } from "jest-mock";
import PWAPlatform from "../../../../src/vector/platform/PWAPlatform";
import WebPlatform from "../../../../src/vector/platform/WebPlatform";
jest.mock("../../../../src/vector/platform/WebPlatform");
describe('PWAPlatform', () => {
beforeEach(() => {
@@ -29,5 +34,29 @@ describe('PWAPlatform', () => {
platform.setNotificationCount(123);
expect(navigator.setAppBadge).toHaveBeenCalledWith(123);
});
it("should no-op if the badge count isn't changing", () => {
navigator.setAppBadge = jest.fn().mockResolvedValue(undefined);
const platform = new PWAPlatform();
platform.setNotificationCount(123);
expect(navigator.setAppBadge).toHaveBeenCalledTimes(1);
platform.setNotificationCount(123);
expect(navigator.setAppBadge).toHaveBeenCalledTimes(1);
});
it("should fall back to WebPlatform::setNotificationCount if no Navigator::setAppBadge", () => {
navigator.setAppBadge = undefined;
const platform = new PWAPlatform();
const superMethod = mocked(WebPlatform.prototype.setNotificationCount);
expect(superMethod).not.toHaveBeenCalled();
platform.setNotificationCount(123);
expect(superMethod).toHaveBeenCalledWith(123);
});
it("should handle Navigator::setAppBadge rejecting gracefully", () => {
navigator.setAppBadge = jest.fn().mockRejectedValue(new Error);
const platform = new PWAPlatform();
expect(() => platform.setNotificationCount(123)).not.toThrow();
});
});
});

View File

@@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import request from 'browser-request';
import fetchMock from "fetch-mock-jest";
import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform';
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
import WebPlatform from '../../../../src/vector/platform/WebPlatform';
fetchMock.config.overwriteRoutes = true;
describe('WebPlatform', () => {
beforeEach(() => {
jest.clearAllMocks();
@@ -37,6 +39,45 @@ describe('WebPlatform', () => {
expect(navigator.serviceWorker.register).toHaveBeenCalled();
});
it("should call reload on window location object", () => {
delete window.location;
window.location = {
reload: jest.fn(),
} as unknown as Location;
const platform = new WebPlatform();
expect(window.location.reload).not.toHaveBeenCalled();
platform.reload();
expect(window.location.reload).toHaveBeenCalled();
});
it("should call reload to install update", () => {
delete window.location;
window.location = {
reload: jest.fn(),
} as unknown as Location;
const platform = new WebPlatform();
expect(window.location.reload).not.toHaveBeenCalled();
platform.installUpdate();
expect(window.location.reload).toHaveBeenCalled();
});
describe("getDefaultDeviceDisplayName", () => {
it.each([[
"https://develop.element.io/#/room/!foo:bar",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
"develop.element.io: Chrome on macOS",
]])("%s & %s = %s", (url, userAgent, result) => {
delete window.navigator;
window.navigator = { userAgent } as unknown as Navigator;
delete window.location;
window.location = { href: url } as unknown as Location;
const platform = new WebPlatform();
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
});
});
describe('notification support', () => {
const mockNotification = {
requestPermission: jest.fn(),
@@ -81,9 +122,6 @@ describe('WebPlatform', () => {
const envVersion = process.env.VERSION;
const prodVersion = '1.10.13';
const setRequestMockImplementation = (err?: unknown, response?: { status: number }, body?: string) =>
request.mockImplementation((_opts, callback) => callback(err, response, body));
beforeEach(() => {
jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(false);
})
@@ -118,7 +156,7 @@ describe('WebPlatform', () => {
describe('pollForUpdate()', () => {
it('should return not available and call showNoUpdate when current version matches most recent version', async () => {
process.env.VERSION = prodVersion;
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@@ -132,7 +170,7 @@ describe('WebPlatform', () => {
it('should strip v prefix from versions before comparing', async () => {
process.env.VERSION = prodVersion;
setRequestMockImplementation(undefined, { status: 200}, `v${prodVersion}`);
fetchMock.getOnce("/version", `v${prodVersion}`);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@@ -147,7 +185,7 @@ describe('WebPlatform', () => {
it('should return ready and call showUpdate when current version differs from most recent version', async () => {
process.env.VERSION = '0.0.0'; // old version
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@@ -162,7 +200,7 @@ describe('WebPlatform', () => {
it('should return ready without showing update when user registered in last 24', async () => {
process.env.VERSION = '0.0.0'; // old version
jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(true);
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
fetchMock.getOnce("/version", prodVersion);
const platform = new WebPlatform();
const showUpdate = jest.fn();
@@ -175,7 +213,7 @@ describe('WebPlatform', () => {
});
it('should return error when version check fails', async () => {
setRequestMockImplementation('oups');
fetchMock.getOnce("/version", { throws: "oups" });
const platform = new WebPlatform();
const showUpdate = jest.fn();

View File

@@ -0,0 +1,43 @@
/*
Copyright 2022 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 { onNewScreen } from "../../../src/vector/routing";
describe("onNewScreen", () => {
it("should replace history if stripping via fields", () => {
delete window.location;
window.location = {
hash: "#/room/!room:server?via=abc",
replace: jest.fn(),
assign: jest.fn(),
} as unknown as Location;
onNewScreen("room/!room:server");
expect(window.location.assign).not.toHaveBeenCalled();
expect(window.location.replace).toHaveBeenCalled();
});
it("should not replace history if changing rooms", () => {
delete window.location;
window.location = {
hash: "#/room/!room1:server?via=abc",
replace: jest.fn(),
assign: jest.fn(),
} as unknown as Location;
onNewScreen("room/!room2:server");
expect(window.location.assign).toHaveBeenCalled();
expect(window.location.replace).not.toHaveBeenCalled();
});
});

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { parseQsFromFragment, parseQs } from "../../src/vector/url_utils";
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
describe("url_utils.ts", function() {
// @ts-ignore

1508
yarn.lock

File diff suppressed because it is too large Load Diff