Compare commits

..

81 Commits

Author SHA1 Message Date
RiotRobot
08cb450d25 v1.11.86-rc.0 2024-11-12 14:05:01 +00:00
RiotRobot
4bdd4f4f49 Upgrade dependency to matrix-js-sdk@34.12.0-rc.0 2024-11-12 14:01:37 +00:00
RiotRobot
6d1b702214 Merge branch 'master' into develop 2024-11-12 09:46:03 +00:00
RiotRobot
bbe474ae57 v1.11.85 2024-11-12 09:43:10 +00:00
RiotRobot
bebf44d9ee Upgrade dependency to matrix-js-sdk@34.11.1 2024-11-12 09:31:49 +00:00
Michael Telatynski
231073c578 Merge commit from fork
Handle rendering of invalid date errors
2024-11-12 09:08:32 +00:00
Michael Telatynski
a00c343435 Merge commit from fork
Add mimetype checks to stickers, image and video messages
2024-11-12 09:07:39 +00:00
ElementRobot
1e2e8844e5 [create-pull-request] automated change (#28424)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-12 09:03:39 +00:00
Michael Telatynski
6bc8080ec5 Merge pull request #28387 from element-hq/t3chguy/knip2 2024-11-12 09:01:24 +00:00
David Langley
bff17ff470 Make case consistent 2024-11-11 19:54:05 +00:00
David Langley
c0a313abae Make logic more DRY, simplify logic, improve naming. 2024-11-11 19:38:58 +00:00
Michael Telatynski
db30bc51a1 Deduplicate icons using Compound Design Tokens (#28419)
* Deduplicate icons using Compound Design Tokens

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshots & snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-11 17:27:22 +00:00
Andrew Ferrazzutti
c8c107405f Let widget driver send error details (#28357)
* Let widget driver send error details

* Match new widget API types

* Don't @link across packages

in case web documentation generation disallows it.

* Update matrix-widget-api

* Update matrix-js-sdk

Include matrix-org/matrix-js-sdk#4507 to fix playwright CI
2024-11-11 16:39:17 +00:00
David Langley
6134cfd9c4 Add mimetype checks
Add checks to validate the advertised mimetype and file extension of stickers, videos and images are coherent.
2024-11-11 15:44:33 +00:00
David Langley
3f70105204 lint 2024-11-11 11:42:18 +00:00
David Langley
4ff08f942d lint 2024-11-11 11:41:50 +00:00
David Langley
29b75385a3 handle rendering of invalid date errors 2024-11-11 11:41:05 +00:00
ElementRobot
17de66140d [create-pull-request] automated change (#28411)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-09 06:20:13 +00:00
Robin
d0cddc5b66 Feed events to widgets as they are decrypted (even if out of order) (#28376)
* Refactor feeding of events to widgets

This is a pure refactor with (hopefully) no behavior changes.

* Feed events to widgets as they are decrypted (even if out of order)

The code that feeds events to widgets tries to enforce that only events from the end of the timeline will be passed through. This is to prevent old, irrelevant events from being passed to widgets as the timeline is back-filled. However, since encrypted events need to be decrypted asynchronously, it's not possible to feed them to a widget in a strictly linear order without introducing some kind of blocking or unreliable delivery. This code has been dropping events when they're decrypted out of order, which we consider to be an undesirable behavior.

The solution provided here is that, to reflect the asynchronous nature of decryption, encrypted events that arrive at the end of the timeline will be fed to a widget whenever they finish decrypting, even if this means feeding them out of order. For now we're not aware of any widgets that care about knowing the exact order of events in the timeline, but if such a need reveals itself later, we can explore adding ordering information to this part of the widget API.

* Add braces to if
2024-11-08 14:35:47 +00:00
ElementRobot
9a6be72c10 [create-pull-request] automated change (#28408)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-08 09:31:14 +00:00
ElementRobot
536d6ad360 [create-pull-request] automated change (#28409)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-11-08 06:24:40 +00:00
Michael Telatynski
b604a6ea8c Enable enable_authenticated_media in Playwright tests (#28395)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-07 11:58:17 +00:00
Michael Telatynski
da4672d715 Handle authenticated media when downloading from ImageView (#28379)
* Handle authenticated media when downloading from ImageView

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-07 11:43:33 +00:00
Michael Telatynski
74a919cb65 Deduplicate icons using Compound Design Tokens (#28381)
* Deduplicate icons using Compound Design Tokens

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove unused test images

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots & screenshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-07 11:39:18 +00:00
ElementRobot
b92101a3da [create-pull-request] automated change (#28403)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-07 09:34:13 +00:00
Timo
5057668705 Auto approvoce io.element.call.reaction capability for element call widgets (#28401) 2024-11-06 20:15:19 +00:00
Michael Telatynski
88c72a1514 Ignore m.3pid_changes for Identity service 3PID changes (#28375)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-06 14:33:50 +00:00
Michael Telatynski
d06cf09bf0 Switch secondary React trees to the createRoot API (#28296)
* Switch secondary React trees to the createRoot API

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add comment

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-06 12:44:54 +00:00
ElementRobot
2f8e98242c [create-pull-request] automated change (#28391)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-06 09:02:22 +00:00
Michael Telatynski
1c6408081f Add back maplibre-gl.js
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-06 08:58:30 +00:00
ElementRobot
3ff5a511e5 [create-pull-request] automated change (#28392)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-11-06 06:23:22 +00:00
Michael Telatynski
f3e976d67d Iterate
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 19:12:43 +00:00
Michael Telatynski
21930a10ae Iterate workflow
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:40:20 +00:00
Michael Telatynski
82a6826a02 Install knip to keep the codebase in check
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:36:34 +00:00
Michael Telatynski
1c06ebadcd Avoid double exports
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:36:26 +00:00
Michael Telatynski
458b4a45e5 Avoid use of transitive deps
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:35:53 +00:00
Michael Telatynski
01039137b5 Remove unused types & imports
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:35:31 +00:00
Michael Telatynski
f86099c76e Remove unused mocks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:34:54 +00:00
Michael Telatynski
464be37815 Remove unused enums
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:34:47 +00:00
Michael Telatynski
f919d1654a Simplify eslint configs
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:33:42 +00:00
Michael Telatynski
7093c9a61b Delete empty unused files
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:23:57 +00:00
Michael Telatynski
9ef3b0b994 Remove unused dependencies
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 18:17:11 +00:00
Richard van der Hoff
6042625198 Remove redundant switch_package_to_release call (#28384)
`switch_package_to_release` is now a no-op (there are no fields called
`matrix_lib_*` in matrix-js-sdk's package.json), so everything after that is
redundant.
2024-11-05 16:16:38 +00:00
Michael Telatynski
9d79a934bf Flatten Vector-override components (#28346)
* Flatten Vector-override components

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve coverage

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Ie

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 15:41:00 +00:00
Michael Telatynski
a355292a7f Merge remote-tracking branch 'origin/develop' into develop 2024-11-05 14:03:17 +00:00
Michael Telatynski
24fabfff89 Fix post release docker check
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 14:02:25 +00:00
RiotRobot
0b6ed44390 Reset matrix-js-sdk back to develop branch 2024-11-05 13:59:24 +00:00
RiotRobot
a6ce6dc7ab Merge branch 'master' into develop 2024-11-05 13:59:15 +00:00
RiotRobot
15984455af v1.11.84 2024-11-05 13:55:39 +00:00
RiotRobot
6e4bd564d5 Upgrade dependency to matrix-js-sdk@34.10.0 2024-11-05 13:50:59 +00:00
Michael Telatynski
aeabf3b188 Show message type prefix in thread root & reply previews (#28361)
* Extract EventPreview from PinnedMessageBanner

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Show message type prefix in thread root previews

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Show message type prefix in thread reply preview

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 11:34:42 +00:00
Michael Telatynski
c9d9c421bc Move navigator message listener registration to be synchronously attached (#28340)
to silence Chrome warning

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-05 11:04:14 +00:00
ElementRobot
d7d96b6b8b [create-pull-request] automated change (#28377)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-05 06:19:43 +00:00
Hubert Chathi
2631b908b6 Update the display of decryption failures due to failed trust requirement (#28300)
* update the display of decryption failures due to failed trust requirement

* add test for not showing shield
2024-11-04 12:46:38 +00:00
Michael Telatynski
502cc91dfe Switch ModalManager to the React 18 createRoot API (#28336)
* Remove boilerplate around dispatcher and settings watchers

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Move state update listeners from constructor to componentDidMount

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Switch ModalManager to the React 18 createRoot API

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-04 11:34:00 +00:00
Michael Telatynski
38e5eeea00 Fix markdown escaping wrongly passing html through (#28363)
* Fix markdown escaping wrongly passing html through

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add comment

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-04 11:31:44 +00:00
Michael Telatynski
1ccbdb21e9 Wire up analytics for Legacy/EC/Jitsi voip options (#28348)
* Wire up analytics for Legacy/EC/Jitsi voip options

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update @matrix-org/analytics-events

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-04 09:49:41 +00:00
Michael Telatynski
b1ef099cd6 Show developer jump to event button on all events with associations (#28351)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-04 09:49:35 +00:00
Michael Telatynski
00d46f1c8f Remove unused Security customisations module (#28350)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-04 09:49:20 +00:00
ElementRobot
8e304713a2 [create-pull-request] automated change (#28372)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-11-04 06:25:18 +00:00
Michael Telatynski
0899165d9e Move state update listeners from constructor to componentDidMount (#28341)
* Move state update listeners from constructor to componentDidMount

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-01 17:39:08 +00:00
Michael Telatynski
2d9982f9f0 Remove boilerplate around dispatcher and settings watchers (#28338)
* Remove boilerplate around dispatcher and settings watchers

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-01 15:15:04 +00:00
Richard Gibson
b8fd98ab3c Specify a local address when exposing ports with Docker (#20891)
* Specify a local address when exposing ports with Docker

cf. https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p---expose

Signed-off-by: Richard Gibson <richard.gibson@gmail.com>

* Provide and explain docker run examples with and without confinement to localhost

Signed-off-by: Richard Gibson <richard.gibson@gmail.com>

* Update README.md

Co-authored-by: Richard Gibson <richard.gibson@gmail.com>

---------

Signed-off-by: Richard Gibson <richard.gibson@gmail.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-01 12:59:16 +00:00
ElementRobot
a27dfa1825 [create-pull-request] automated change (#28360)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-11-01 06:26:53 +00:00
ElementRobot
a3ece9d902 [create-pull-request] automated change (#28359)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-11-01 06:22:26 +00:00
ElementRobot
23613ac37e [create-pull-request] automated change (#28119)
Co-authored-by: Johennes <1137962+Johennes@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-10-31 13:56:41 +00:00
Michael Telatynski
195337d865 Pass nodeRef to CSSTransition to avoid ReactDOM.findDOMNode (#28339)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-10-31 11:21:19 +00:00
chagai95
4bb9f2ed7b Documentation fix: missing comma and I would also take the ellipsis away (#17233)
* Missing coma and I would also take the ellipsis away

took me a while to find the mistake and "comment" and I think it'll take other even longer...

* use json5

---------

Co-authored-by: Travis Ralston <travisr@matrix.org>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-10-31 10:54:46 +00:00
ElementRobot
e0ffddf3eb [create-pull-request] automated change (#28345)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-10-31 06:21:39 +00:00
RiotRobot
849f2c9818 v1.11.84-rc.0 2024-10-29 13:02:40 +00:00
RiotRobot
8ebfaadeed Upgrade dependency to matrix-js-sdk@34.10.0-rc.0 2024-10-29 12:57:26 +00:00
Michael Telatynski
1d49a46dd2 Merge branch 'develop' into staging
# Conflicts:
#	package.json
#	yarn.lock
2024-10-29 12:49:13 +00:00
RiotRobot
dabe6722aa v1.11.83 2024-10-29 09:23:20 +00:00
Michael Telatynski
10a63b3c23 Merge pull request #28314 from element-hq/backport-28313-to-staging 2024-10-29 09:20:38 +00:00
Michael Telatynski
9ce515a646 Enable Element Call by default on release instances (#28313)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

(cherry picked from commit 79c956388f)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-10-28 15:45:59 +00:00
RiotRobot
1df72ce2d0 v1.11.82 2024-10-22 12:10:29 +00:00
RiotRobot
6a960204b3 Upgrade dependency to matrix-react-sdk@3.114.0 2024-10-22 12:07:41 +00:00
RiotRobot
26cd13ae3c Upgrade dependency to matrix-js-sdk@34.9.0 2024-10-22 11:59:48 +00:00
RiotRobot
3793c6daca v1.11.82-rc.0 2024-10-15 14:57:48 +00:00
RiotRobot
65f0d7930a Upgrade dependency to matrix-react-sdk@3.114.0-rc.0 2024-10-15 14:43:13 +00:00
RiotRobot
8b914c02d0 Upgrade dependency to matrix-js-sdk@34.9.0-rc.0 2024-10-15 14:33:13 +00:00
327 changed files with 2779 additions and 2565 deletions

View File

@@ -1,60 +0,0 @@
module.exports = {
plugins: ["matrix-org"],
extends: ["./.eslintrc.js"],
parserOptions: {
project: ["./tsconfig.module_system.json"],
},
overrides: [
{
files: ["module_system/**/*.{ts,tsx}"],
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
// NOTE: These rules are frozen and new rules should not be added here.
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/
rules: {
// Things we do that break the ideal style
"prefer-promise-reject-errors": "off",
"quotes": "off",
// We disable this while we're transitioning
"@typescript-eslint/no-explicit-any": "off",
// We're okay with assertion errors when we ask for them
"@typescript-eslint/no-non-null-assertion": "off",
// Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell.
"no-restricted-imports": [
"error",
{
paths: [
{
name: "matrix-js-sdk",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/src",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/src/",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/src/index",
message: "Please use matrix-js-sdk/src/matrix instead",
},
],
patterns: [
{
group: ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"],
message: "Please use matrix-js-sdk/src/* instead",
},
],
},
],
},
},
],
};

View File

@@ -270,6 +270,60 @@ module.exports = {
"react-hooks/rules-of-hooks": ["off"],
},
},
{
files: ["module_system/**/*.{ts,tsx}"],
parserOptions: {
project: ["./tsconfig.module_system.json"],
},
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
// NOTE: These rules are frozen and new rules should not be added here.
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/
rules: {
// Things we do that break the ideal style
"prefer-promise-reject-errors": "off",
"quotes": "off",
// We disable this while we're transitioning
"@typescript-eslint/no-explicit-any": "off",
// We're okay with assertion errors when we ask for them
"@typescript-eslint/no-non-null-assertion": "off",
// Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell.
"no-restricted-imports": [
"error",
{
paths: [
{
name: "matrix-js-sdk",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/src",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/src/",
message: "Please use matrix-js-sdk/src/matrix instead",
},
{
name: "matrix-js-sdk/src/index",
message: "Please use matrix-js-sdk/src/matrix instead",
},
],
patterns: [
{
group: ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"],
message: "Please use matrix-js-sdk/src/* instead",
},
],
},
],
},
},
],
settings: {
react: {

View File

@@ -49,7 +49,7 @@ jobs:
ref: master
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-name: "Docker Buildx (vanilla)"
check-name: "Docker Buildx"
allowed-conclusions: success
- name: Wait for debian package

View File

@@ -34,27 +34,6 @@ jobs:
- name: Typecheck
run: "yarn run lint:types"
- name: Switch js-sdk to release mode
working-directory: node_modules/matrix-js-sdk
run: |
scripts/switch_package_to_release.cjs
yarn install
yarn run build:compile
yarn run build:types
- name: Typecheck (release mode)
run: "yarn run lint:types"
# Temporary while we directly import matrix-js-sdk/src/* which means we need
# certain @types/* packages to make sense of matrix-js-sdk types.
#- name: Typecheck (release mode; no yarn link)
# if: github.event_name != 'pull_request' && github.ref_name != 'master'
# run: |
# yarn unlink matrix-js-sdk
# yarn add github:matrix-org/matrix-js-sdk#develop
# yarn install --force
# yarn run lint:types
i18n_lint:
name: "i18n Check"
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
@@ -144,6 +123,12 @@ jobs:
cache: "yarn"
node-version: "lts/*"
- name: Install Deps
run: "yarn install --frozen-lockfile"
- name: Run linter
run: "yarn run lint:knip"
- name: Install Deps
run: "scripts/layered.sh"

View File

@@ -2,6 +2,6 @@
"*": "prettier --write",
"src/**/*.(ts|tsx)": ["eslint --fix"],
"scripts/**/*.(ts|tsx)": ["eslint --fix"],
"module_system/**/*.(ts|tsx)": ["eslint --fix --config .eslintrc-module_system.js module_system"],
"module_system/**/*.(ts|tsx)": ["eslint --fix"],
"*.pcss": ["stylelint --fix"]
}

View File

@@ -1,6 +1,6 @@
module.exports = {
extends: ["stylelint-config-standard"],
customSyntax: require("postcss-scss"),
customSyntax: "postcss-scss",
plugins: ["stylelint-scss"],
rules: {
"comment-empty-line-before": null,

View File

@@ -1,3 +1,76 @@
Changes in [1.11.85](https://github.com/element-hq/element-web/releases/tag/v1.11.85) (2024-11-12)
==================================================================================================
# Security
- Fixes for [CVE-2024-51750](https://www.cve.org/CVERecord?id=CVE-2024-51750) / [GHSA-w36j-v56h-q9pc](https://github.com/element-hq/element-web/security/advisories/GHSA-w36j-v56h-q9pc)
- Fixes for [CVE-2024-51749](https://www.cve.org/CVERecord?id=CVE-2024-51749) / [GHSA-5486-384g-mcx2](https://github.com/element-hq/element-web/security/advisories/GHSA-5486-384g-mcx2)
- Update JS SDK with the fixes for [CVE-2024-50336](https://www.cve.org/CVERecord?id=CVE-2024-50336) / [GHSA-xvg8-m4x3-w6xr](https://github.com/matrix-org/matrix-js-sdk/security/advisories/GHSA-xvg8-m4x3-w6xr)
Changes in [1.11.84](https://github.com/element-hq/element-web/releases/tag/v1.11.84) (2024-11-05)
==================================================================================================
## ✨ Features
* Remove abandoned MSC3886, MSC3903, MSC3906 implementations ([#28274](https://github.com/element-hq/element-web/pull/28274)). Contributed by @t3chguy.
* Update to React 18 ([#24763](https://github.com/element-hq/element-web/pull/24763)). Contributed by @t3chguy.
* Deduplicate icons using Compound ([#28239](https://github.com/element-hq/element-web/pull/28239)). Contributed by @t3chguy.
* Replace legacy Tooltips with Compound tooltips ([#28231](https://github.com/element-hq/element-web/pull/28231)). Contributed by @t3chguy.
* Deduplicate icons using Compound Design Tokens ([#28219](https://github.com/element-hq/element-web/pull/28219)). Contributed by @t3chguy.
* Add reactions to html export ([#28210](https://github.com/element-hq/element-web/pull/28210)). Contributed by @langleyd.
* Remove feature\_dehydration ([#28173](https://github.com/element-hq/element-web/pull/28173)). Contributed by @florianduros.
## 🐛 Bug Fixes
* Remove upgrade encryption in `DeviceListener` and `SetupEncryptionToast` ([#28299](https://github.com/element-hq/element-web/pull/28299)). Contributed by @florianduros.
* Fix 'remove alias' button in room settings ([#28269](https://github.com/element-hq/element-web/pull/28269)). Contributed by @Dev-Gurjar.
* Add back unencrypted path in `StopGapWidgetDriver.sendToDevice` ([#28295](https://github.com/element-hq/element-web/pull/28295)). Contributed by @florianduros.
* Fix other devices not being decorated as such ([#28279](https://github.com/element-hq/element-web/pull/28279)). Contributed by @t3chguy.
* Fix pill contrast in invitation dialog ([#28250](https://github.com/element-hq/element-web/pull/28250)). Contributed by @florianduros.
* Close right panel chat when minimising maximised voip widget ([#28241](https://github.com/element-hq/element-web/pull/28241)). Contributed by @t3chguy.
* Fix develop changelog parsing ([#28232](https://github.com/element-hq/element-web/pull/28232)). Contributed by @t3chguy.
* Fix Ctrl+F shortcut not working with minimised room summary card ([#28223](https://github.com/element-hq/element-web/pull/28223)). Contributed by @t3chguy.
* Fix network dropdown missing checkbox \& aria-checked ([#28220](https://github.com/element-hq/element-web/pull/28220)). Contributed by @t3chguy.
Changes in [1.11.83](https://github.com/element-hq/element-web/releases/tag/v1.11.83) (2024-10-29)
==================================================================================================
## ✨ Features
* Enable Element Call by default on release instances ([#28314](https://github.com/element-hq/element-web/pull/28314)). Contributed by @t3chguy.
Changes in [1.11.82](https://github.com/element-hq/element-web/releases/tag/v1.11.82) (2024-10-22)
==================================================================================================
## ✨ Features
* Deduplicate more icons using Compound Design Tokens ([#132](https://github.com/element-hq/matrix-react-sdk/pull/132)). Contributed by @t3chguy.
* Always show link new device flow even if unsupported ([#147](https://github.com/element-hq/matrix-react-sdk/pull/147)). Contributed by @t3chguy.
* Update design of files list in right panel ([#144](https://github.com/element-hq/matrix-react-sdk/pull/144)). Contributed by @t3chguy.
* Remove feature\_dehydration ([#138](https://github.com/element-hq/matrix-react-sdk/pull/138)). Contributed by @florianduros.
* Upgrade emojibase-bindings and remove local handling of emoticon variations ([#127](https://github.com/element-hq/matrix-react-sdk/pull/127)). Contributed by @langleyd.
* Add support for rendering media captions ([#43](https://github.com/element-hq/matrix-react-sdk/pull/43)). Contributed by @tulir.
* Replace composer icons with Compound variants ([#123](https://github.com/element-hq/matrix-react-sdk/pull/123)). Contributed by @t3chguy.
* Tweak default right panel size to be 320px except for maximised widgets at 420px ([#110](https://github.com/element-hq/matrix-react-sdk/pull/110)). Contributed by @t3chguy.
* Add a pinned message badge under a pinned message ([#118](https://github.com/element-hq/matrix-react-sdk/pull/118)). Contributed by @florianduros.
* Ditch right panel tabs and re-add close button ([#99](https://github.com/element-hq/matrix-react-sdk/pull/99)). Contributed by @t3chguy.
* Force verification even for refreshed clients ([#44](https://github.com/element-hq/matrix-react-sdk/pull/44)). Contributed by @dbkr.
* Update emoji text, border and background colour in timeline ([#119](https://github.com/element-hq/matrix-react-sdk/pull/119)). Contributed by @florianduros.
* Disable ICE fallback based on well-known configuration ([#111](https://github.com/element-hq/matrix-react-sdk/pull/111)). Contributed by @t3chguy.
* Remove legacy room header and promote beta room header ([#105](https://github.com/element-hq/matrix-react-sdk/pull/105)). Contributed by @t3chguy.
* Respect `io.element.jitsi` `useFor1To1Calls` in well-known ([#112](https://github.com/element-hq/matrix-react-sdk/pull/112)). Contributed by @t3chguy.
* Use Compound close icon in favour of mishmash of x/close icons ([#108](https://github.com/element-hq/matrix-react-sdk/pull/108)). Contributed by @t3chguy.
## 🐛 Bug Fixes
* Correct typo in option documentation ([#28148](https://github.com/element-hq/element-web/pull/28148)). Contributed by @AndrewKvalheim.
* Revert #124 and #135 ([#139](https://github.com/element-hq/matrix-react-sdk/pull/139)). Contributed by @dbkr.
* Add aria-label to e2e icon ([#136](https://github.com/element-hq/matrix-react-sdk/pull/136)). Contributed by @florianduros.
* Fix bell icons on room list hover being black squares ([#135](https://github.com/element-hq/matrix-react-sdk/pull/135)). Contributed by @dbkr.
* Fix vertical overflow on the mobile register screen ([#137](https://github.com/element-hq/matrix-react-sdk/pull/137)). Contributed by @langleyd.
* Allow to unpin redacted event ([#98](https://github.com/element-hq/matrix-react-sdk/pull/98)). Contributed by @florianduros.
Changes in [1.11.81](https://github.com/element-hq/element-web/releases/tag/v1.11.81) (2024-10-15)
==================================================================================================
This release fixes High severity vulnerability CVE-2024-47771 / GHSA-963w-49j9-gxj6

View File

@@ -1,5 +1 @@
{
"src/components/views/auth/AuthFooter.tsx": "src/components/views/auth/VectorAuthFooter.tsx",
"src/components/views/auth/AuthHeaderLogo.tsx": "src/components/views/auth/VectorAuthHeaderLogo.tsx",
"src/components/views/auth/AuthPage.tsx": "src/components/views/auth/VectorAuthPage.tsx"
}
{}

View File

@@ -11,8 +11,8 @@ Customisations will be removed from the codebase in a future release.
Element Web and the React SDK support "customisation points" that can be used to
easily add custom logic specific to a particular deployment of Element Web.
An example of this is the [security customisations
module](https://github.com/element-hq/element-web/blob/develop/src/customisations/Security.ts).
An example of this is the [media customisations
module](https://github.com/element-hq/element-web/blob/develop/src/customisations/Media.ts).
This module in the React SDK only defines some empty functions and their types:
it does not do anything by default.
@@ -21,14 +21,14 @@ Web so that you can add your own code. Even though the default module is part of
the React SDK, you can still override it from the Element Web layer:
1. Copy the default customisation module to
`element-web/src/customisations/YourNameSecurity.ts`
`element-web/src/customisations/YourNameMedia.ts`
2. Edit customisations points and make sure export the ones you actually want to
activate
3. Create/add an entry to `customisations.json` next to the webpack config:
```json
{
"src/customisations/Security.ts": "src/customisations/YourNameSecurity.ts"
"src/customisations/Media.ts": "src/customisations/YourNameMedia.ts"
}
```

View File

@@ -41,7 +41,15 @@ The Docker image can be used to serve element-web as a web server. The easiest w
it is to use the prebuilt image:
```bash
docker run -p 80:80 vectorim/element-web
docker run --rm -p 127.0.0.1:80:80 vectorim/element-web
```
A server can also be made available to clients outside the local host by omitting the
explicit local address as described in
[docker run documentation](https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p---expose):
```bash
docker run --rm -p 80:80 vectorim/element-web
```
To supply your own custom `config.json`, map a volume to `/app/config.json`. For example,
@@ -49,7 +57,7 @@ if your custom config was located at `/etc/element-web/config.json` then your Do
would be:
```bash
docker run -p 80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
docker run --rm -p 127.0.0.1:80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
```
To build the image yourself:

View File

@@ -29,7 +29,7 @@ default theme, you would use `default_theme: "custom-Electric Blue"`.
e.g. in config.json:
```
```json5
"setting_defaults": {
"custom_themes": [
{
@@ -59,6 +59,10 @@ e.g. in config.json:
"timeline-text-color": "#2e2f32",
"timeline-text-secondary-color": "#61708b",
"timeline-highlights-color": "#f3f8fd",
// These should both be 8 values long
"username-colors": ["#ff0000", /*...*/],
"avatar-background-colors": ["#cc0000", /*...*/]
},
"compound": {
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",

View File

@@ -38,7 +38,7 @@ const config: Config = {
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
},
transformIgnorePatterns: ["/node_modules/(?!matrix-js-sdk).+$"],
transformIgnorePatterns: ["/node_modules/(?!(mime|matrix-js-sdk)).+$"],
collectCoverageFrom: [
"<rootDir>/src/**/*.{js,ts,tsx}",
// getSessionLock is piped into a different JS context via stringification, and the coverage functionality is

53
knip.ts Normal file
View File

@@ -0,0 +1,53 @@
import { KnipConfig } from "knip";
export default {
entry: [
"src/vector/index.ts",
"src/serviceworker/index.ts",
"src/workers/*.worker.ts",
"src/utils/exportUtils/exportJS.js",
"scripts/**",
"playwright/**",
"test/**",
"res/decoder-ring/**",
],
project: ["**/*.{js,ts,jsx,tsx}"],
ignore: [
"docs/**",
"res/jitsi_external_api.min.js",
// Used by jest
"__mocks__/maplibre-gl.js",
// Keep for now
"src/hooks/useLocalStorageState.ts",
"src/components/views/elements/InfoTooltip.tsx",
"src/components/views/elements/StyledCheckbox.tsx",
],
ignoreDependencies: [
// Required for `action-validator`
"@action-validator/*",
// Used for git pre-commit hooks
"husky",
// Used by jest
"babel-jest",
// Used by babel
"@babel/runtime",
"@babel/plugin-transform-class-properties",
// Referenced in PCSS
"github-markdown-css",
// False positive
"sw.js",
// Used by webpack
"buffer",
"process",
"util",
// Used by workflows
"ts-prune",
// Required due to bug in bloom-filters https://github.com/Callidon/bloom-filters/issues/75
"@types/seedrandom",
],
ignoreBinaries: [
// Used in scripts & workflows
"jq",
],
ignoreExportsUsedInFile: true,
} satisfies KnipConfig;

View File

@@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.11.81",
"version": "1.11.86-rc.0",
"description": "A feature-rich client for Matrix.org",
"author": "New Vector Ltd.",
"repository": {
@@ -35,7 +35,7 @@
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
"make-component": "node scripts/make-react-component.js",
"rethemendex": "res/css/rethemendex.sh",
"rethemendex": "./res/css/rethemendex.sh",
"clean": "rimraf lib webapp",
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
@@ -45,23 +45,20 @@
"build:bundle": "webpack --progress --mode production",
"build:bundle-stats": "webpack --progress --mode production --json > webpack-stats.json",
"build:module_system": "ts-node --project ./tsconfig.module_system.json module_system/scripts/install.ts",
"dist": "scripts/package.sh",
"dist": "./scripts/package.sh",
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n modules,res \"yarn build:module_system\" \"yarn build:res\" && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --server-type https\"",
"start:res": "ts-node scripts/copy-res.ts -w",
"start:js": "webpack serve --output-path webapp --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js --mode development",
"lint": "yarn lint:types && yarn lint:js && yarn lint:style && yarn lint:workflows",
"lint:js": "yarn lint:js:src && yarn lint:js:module_system",
"lint:js:src": "eslint --max-warnings 0 src test playwright && prettier --check .",
"lint:js:module_system": "eslint --max-warnings 0 --config .eslintrc-module_system.js module_system",
"lint:js-fix": "yarn lint:js-fix:src && yarn lint:js-fix:module_system",
"lint:js-fix:src": "prettier --log-level=warn --write . && eslint --fix src test playwright",
"lint:js-fix:module_system": "eslint --fix --config .eslintrc-module_system.js module_system",
"lint:js": "eslint --max-warnings 0 src test playwright module_system && prettier --check .",
"lint:js-fix": "prettier --log-level=warn --write . && eslint --fix src test playwright module_system",
"lint:types": "yarn lint:types:src && yarn lint:types:module_system",
"lint:types:src": "tsc --noEmit --jsx react && tsc --noEmit --jsx react -p playwright",
"lint:types:module_system": "tsc --noEmit --project ./tsconfig.module_system.json",
"lint:style": "stylelint \"res/css/**/*.pcss\"",
"lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'",
"lint:knip": "knip",
"test": "jest",
"test:playwright": "playwright test",
"test:playwright:open": "yarn test:playwright --ui",
@@ -74,7 +71,6 @@
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js"
},
"resolutions": {
"@types/seedrandom": "3.0.8",
"oidc-client-ts": "3.1.0",
"jwt-decode": "4.0.0",
"caniuse-lite": "1.0.30001668",
@@ -84,19 +80,19 @@
"dependencies": {
"@babel/runtime": "^7.12.5",
"@formatjs/intl-segmenter": "^11.5.7",
"@matrix-org/analytics-events": "^0.28.0",
"@matrix-org/analytics-events": "^0.29.0",
"@matrix-org/emojibase-bindings": "^1.3.3",
"@vector-im/matrix-wysiwyg": "2.37.13",
"@matrix-org/react-sdk-module-api": "^2.4.0",
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^8.0.0",
"@vector-im/compound-design-tokens": "^1.8.0",
"@vector-im/compound-web": "^7.1.0",
"@vector-im/matrix-wysiwyg": "2.37.13",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2",
"await-lock": "^2.1.0",
"bloom-filters": "^3.0.1",
"bloom-filters": "^3.0.2",
"blurhash": "^2.0.3",
"browserslist": "^4.23.2",
"classnames": "^2.2.6",
@@ -114,8 +110,8 @@
"highlight.js": "^11.3.1",
"html-entities": "^2.0.0",
"is-ip": "^3.1.0",
"jsrsasign": "^11.0.0",
"js-xxhash": "^4.0.0",
"jsrsasign": "^11.0.0",
"jszip": "^3.7.0",
"katex": "^0.16.0",
"linkify-element": "4.1.3",
@@ -126,9 +122,10 @@
"maplibre-gl": "^2.0.0",
"matrix-encrypt-attachment": "^1.0.3",
"matrix-events-sdk": "0.0.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^1.9.0",
"matrix-js-sdk": "34.12.0-rc.0",
"matrix-widget-api": "^1.10.0",
"memoize-one": "^6.0.0",
"mime": "^4.0.4",
"oidc-client-ts": "^3.0.1",
"opus-recorder": "^8.0.3",
"pako": "^2.0.3",
@@ -155,11 +152,9 @@
"@action-validator/cli": "^0.6.0",
"@action-validator/core": "^0.6.0",
"@axe-core/playwright": "^4.8.1",
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.12.10",
"@babel/eslint-plugin": "^7.12.10",
"@babel/parser": "^7.12.11",
"@babel/plugin-proposal-export-default-from": "^7.12.1",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-class-properties": "^7.12.1",
@@ -172,7 +167,6 @@
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.12.10",
"@babel/runtime": "^7.12.5",
"@casualbot/jest-sonar-reporter": "2.2.7",
"@peculiar/webcrypto": "^1.4.3",
@@ -186,7 +180,6 @@
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/commonmark": "^0.27.4",
"@types/content-type": "^1.1.5",
"@types/counterpart": "^0.18.1",
"@types/css-tree": "^2.3.8",
"@types/diff-match-patch": "^1.0.32",
@@ -203,6 +196,7 @@
"@types/minimist": "^1.2.5",
"@types/modernizr": "^3.5.3",
"@types/node": "18",
"@types/node-fetch": "^2.6.2",
"@types/pako": "^2.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "18.3.3",
@@ -210,7 +204,6 @@
"@types/react-dom": "18.3.1",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "2.13.0",
"@types/sdp-transform": "^2.4.6",
"@types/seedrandom": "3.0.8",
"@types/semver": "^7.5.8",
"@types/tar-js": "^0.3.5",
@@ -218,7 +211,6 @@
"@types/uuid": "^10.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"axe-core": "4.10.2",
"babel-jest": "^29.0.0",
"babel-loader": "^9.0.0",
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
@@ -258,15 +250,14 @@
"jest-mock": "^29.6.2",
"jest-raw-loader": "^1.0.1",
"jsqr": "^1.4.0",
"knip": "^5.36.2",
"lint-staged": "^15.0.2",
"mailhog": "^4.16.0",
"matrix-mock-request": "^2.5.0",
"matrix-web-i18n": "^3.2.1",
"mini-css-extract-plugin": "2.9.0",
"minimist": "^1.2.6",
"mkdirp": "^3.0.0",
"mocha-junit-reporter": "^2.2.0",
"modernizr": "^3.12.0",
"node-fetch": "^2.6.7",
"playwright-core": "^1.45.1",
"postcss": "8.4.38",
"postcss-easings": "^4.0.0",

View File

@@ -51,6 +51,6 @@ test.describe("Invisible cryptography", () => {
/* should show an error for a message from a previously verified device */
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
const lastTile = page.locator(".mx_EventTile_last");
await expect(lastTile).toContainText("Verified identity has changed");
await expect(lastTile).toContainText("Sender's verified identity has changed");
});
});

View File

@@ -6,32 +6,48 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import * as fs from "node:fs";
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { ElementAppPage } from "../../pages/ElementAppPage";
import { Credentials } from "../../plugins/homeserver";
const STICKER_PICKER_WIDGET_ID = "fake-sticker-picker";
const STICKER_PICKER_WIDGET_NAME = "Fake Stickers";
const STICKER_NAME = "Test Sticker";
const ROOM_NAME_1 = "Sticker Test";
const ROOM_NAME_2 = "Sticker Test Two";
const STICKER_MESSAGE = JSON.stringify({
action: "m.sticker",
api: "fromWidget",
data: {
name: "teststicker",
description: STICKER_NAME,
file: "test.png",
content: {
body: STICKER_NAME,
msgtype: "m.sticker",
url: "mxc://localhost/somewhere",
const STICKER_IMAGE = fs.readFileSync("playwright/sample-files/riot.png");
function getStickerMessage(contentUri: string, mimetype: string): string {
return JSON.stringify({
action: "m.sticker",
api: "fromWidget",
data: {
name: "teststicker",
description: STICKER_NAME,
file: "test.png",
content: {
body: STICKER_NAME,
info: {
h: 480,
mimetype: mimetype,
size: 13818,
w: 480,
},
msgtype: "m.sticker",
url: contentUri,
},
},
},
requestId: "1",
widgetId: STICKER_PICKER_WIDGET_ID,
});
const WIDGET_HTML = `
requestId: "1",
widgetId: STICKER_PICKER_WIDGET_ID,
});
}
function getWidgetHtml(contentUri: string, mimetype: string) {
const stickerMessage = getStickerMessage(contentUri, mimetype);
return `
<html lang="en">
<head>
<title>Fake Sticker Picker</title>
@@ -51,13 +67,13 @@ const WIDGET_HTML = `
<button name="Send" id="sendsticker">Press for sticker</button>
<script>
document.getElementById('sendsticker').onclick = () => {
window.parent.postMessage(${STICKER_MESSAGE}, '*')
window.parent.postMessage(${stickerMessage}, '*')
};
</script>
</body>
</html>
`;
}
async function openStickerPicker(app: ElementAppPage) {
const options = await app.openMessageComposerOptions();
await options.getByRole("menuitem", { name: "Sticker" }).click();
@@ -71,7 +87,8 @@ async function sendStickerFromPicker(page: Page) {
await expect(page.locator(".mx_AppTileFullWidth#stickers")).not.toBeVisible();
}
async function expectTimelineSticker(page: Page, roomId: string) {
async function expectTimelineSticker(page: Page, roomId: string, contentUri: string) {
const contentId = contentUri.split("/").slice(-1)[0];
// Make sure it's in the right room
await expect(page.locator(".mx_EventTile_sticker > a")).toHaveAttribute("href", new RegExp(`/${roomId}/`));
@@ -80,13 +97,43 @@ async function expectTimelineSticker(page: Page, roomId: string) {
// download URL.
await expect(page.locator(`img[alt="${STICKER_NAME}"]`)).toHaveAttribute(
"src",
new RegExp("/download/localhost/somewhere"),
new RegExp(`/localhost/${contentId}`),
);
}
async function expectFileTile(page: Page, roomId: string, contentUri: string) {
await expect(page.locator(".mx_MFileBody_info_filename")).toContainText(STICKER_NAME);
}
async function setWidgetAccountData(
app: ElementAppPage,
user: Credentials,
stickerPickerUrl: string,
provideCreatorUserId: boolean = true,
) {
await app.client.setAccountData("m.widgets", {
[STICKER_PICKER_WIDGET_ID]: {
content: {
type: "m.stickerpicker",
name: STICKER_PICKER_WIDGET_NAME,
url: stickerPickerUrl,
creatorUserId: provideCreatorUserId ? user.userId : undefined,
},
sender: user.userId,
state_key: STICKER_PICKER_WIDGET_ID,
type: "m.widget",
id: STICKER_PICKER_WIDGET_ID,
},
});
}
test.describe("Stickers", () => {
test.use({
displayName: "Sally",
room: async ({ app }, use) => {
const roomId = await app.client.createRoom({ name: ROOM_NAME_1 });
await use({ roomId });
},
});
// We spin up a web server for the sticker picker so that we're not testing to see if
@@ -96,34 +143,19 @@ test.describe("Stickers", () => {
//
// See sendStickerFromPicker() for more detail on iframe comms.
let stickerPickerUrl: string;
test.beforeEach(async ({ webserver }) => {
stickerPickerUrl = webserver.start(WIDGET_HTML);
});
test("should send a sticker to multiple rooms", async ({ page, app, user }) => {
const roomId1 = await app.client.createRoom({ name: ROOM_NAME_1 });
test("should send a sticker to multiple rooms", async ({ webserver, page, app, user, room }) => {
const roomId2 = await app.client.createRoom({ name: ROOM_NAME_2 });
await app.client.setAccountData("m.widgets", {
[STICKER_PICKER_WIDGET_ID]: {
content: {
type: "m.stickerpicker",
name: STICKER_PICKER_WIDGET_NAME,
url: stickerPickerUrl,
creatorUserId: user.userId,
},
sender: user.userId,
state_key: STICKER_PICKER_WIDGET_ID,
type: "m.widget",
id: STICKER_PICKER_WIDGET_ID,
},
});
const { content_uri: contentUri } = await app.client.uploadContent(STICKER_IMAGE, { type: "image/png" });
const widgetHtml = getWidgetHtml(contentUri, "image/png");
stickerPickerUrl = webserver.start(widgetHtml);
setWidgetAccountData(app, user, stickerPickerUrl);
await app.viewRoomByName(ROOM_NAME_1);
await expect(page).toHaveURL(`/#/room/${roomId1}`);
await expect(page).toHaveURL(`/#/room/${room.roomId}`);
await openStickerPicker(app);
await sendStickerFromPicker(page);
await expectTimelineSticker(page, roomId1);
await expectTimelineSticker(page, room.roomId, contentUri);
// Ensure that when we switch to a different room that the sticker
// goes to the right place
@@ -131,31 +163,40 @@ test.describe("Stickers", () => {
await expect(page).toHaveURL(`/#/room/${roomId2}`);
await openStickerPicker(app);
await sendStickerFromPicker(page);
await expectTimelineSticker(page, roomId2);
await expectTimelineSticker(page, roomId2, contentUri);
});
test("should handle a sticker picker widget missing creatorUserId", async ({ page, app, user }) => {
const roomId1 = await app.client.createRoom({ name: ROOM_NAME_1 });
await app.client.setAccountData("m.widgets", {
[STICKER_PICKER_WIDGET_ID]: {
content: {
type: "m.stickerpicker",
name: STICKER_PICKER_WIDGET_NAME,
url: stickerPickerUrl,
// No creatorUserId
},
sender: user.userId,
state_key: STICKER_PICKER_WIDGET_ID,
type: "m.widget",
id: STICKER_PICKER_WIDGET_ID,
},
});
test("should handle a sticker picker widget missing creatorUserId", async ({
webserver,
page,
app,
user,
room,
}) => {
const { content_uri: contentUri } = await app.client.uploadContent(STICKER_IMAGE, { type: "image/png" });
const widgetHtml = getWidgetHtml(contentUri, "image/png");
stickerPickerUrl = webserver.start(widgetHtml);
setWidgetAccountData(app, user, stickerPickerUrl, false);
await app.viewRoomByName(ROOM_NAME_1);
await expect(page).toHaveURL(`/#/room/${roomId1}`);
await expect(page).toHaveURL(`/#/room/${room.roomId}`);
await openStickerPicker(app);
await sendStickerFromPicker(page);
await expectTimelineSticker(page, roomId1);
await expectTimelineSticker(page, room.roomId, contentUri);
});
test("should render invalid mimetype as a file", async ({ webserver, page, app, user, room }) => {
const { content_uri: contentUri } = await app.client.uploadContent(STICKER_IMAGE, {
type: "application/octet-stream",
});
const widgetHtml = getWidgetHtml(contentUri, "application/octet-stream");
stickerPickerUrl = webserver.start(widgetHtml);
setWidgetAccountData(app, user, stickerPickerUrl);
await app.viewRoomByName(ROOM_NAME_1);
await expect(page).toHaveURL(`/#/room/${room.roomId}`);
await openStickerPicker(app);
await sendStickerFromPicker(page);
await expectFileTile(page, room.roomId, contentUri);
});
});

View File

@@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
// Docker tag to use for synapse docker image.
// We target a specific digest as every now and then a Synapse update will break our CI.
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
const DOCKER_TAG = "develop@sha256:eee8e1551640da28f017b93dbf2264a89a092709d12475c785a52c91cf580c6a";
const DOCKER_TAG = "develop@sha256:7ab3cd9f4d167b47288c6704ed455046b67b5a5097c843ddfbcbc6d3a03ff76f";
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
const templateDir = path.join(__dirname, "templates", opts.template);

View File

@@ -102,3 +102,5 @@ experimental_features:
# messages > non-joined historical messages.
# Can be removed after Synapse enables it by default
msc4115_membership_on_events: true
enable_authenticated_media: true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -186,7 +186,7 @@ input[type="search"].mx_textinput_icon {
/* FIXME THEME - Tint by CSS rather than referencing a duplicate asset */
input[type="text"].mx_textinput_icon.mx_textinput_search,
input[type="search"].mx_textinput_icon.mx_textinput_search {
background-image: url("$(res)/img/feather-customised/search-input.svg");
background-image: url("@vector-im/compound-design-tokens/icons/search.svg");
}
/* dont search UI as not all browsers support it, */

View File

@@ -282,6 +282,7 @@
@import "./views/rooms/_EmojiButton.pcss";
@import "./views/rooms/_EntityTile.pcss";
@import "./views/rooms/_EventBubbleTile.pcss";
@import "./views/rooms/_EventPreview.pcss";
@import "./views/rooms/_EventTile.pcss";
@import "./views/rooms/_HistoryTile.pcss";
@import "./views/rooms/_IRCLayout.pcss";

View File

@@ -32,8 +32,8 @@ Please see LICENSE files in the repository root for full details.
}
.mx_DeviceExpandDetailsButton_icon {
height: 16px;
width: 16px;
height: 24px;
width: 24px;
transition: all 0.3s;
transform: var(--icon-transform);

View File

@@ -25,7 +25,7 @@ Please see LICENSE files in the repository root for full details.
width: 18px;
height: 18px;
background: currentColor;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
mask-size: 100%;
mask-repeat: no-repeat;
float: right;

View File

@@ -125,7 +125,7 @@ Please see LICENSE files in the repository root for full details.
padding-left: 34px; /* 28px from above, but +6px to account for the wider icon */
&::before {
mask-image: url("$(res)/img/element-icons/retry.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg");
}
}
}

View File

@@ -62,7 +62,7 @@ Please see LICENSE files in the repository root for full details.
&::before {
background-color: $info-plinth-fg-color;
mask: url("$(res)/img/feather-customised/search-input.svg");
mask: url("@vector-im/compound-design-tokens/icons/search.svg");
mask-repeat: no-repeat;
mask-position: center;
mask-size: 50px;

View File

@@ -77,7 +77,7 @@ Please see LICENSE files in the repository root for full details.
height: 16px;
width: 16px;
left: 0;
background-image: url("$(res)/img/element-icons/warning-badge.svg");
background-image: url("@vector-im/compound-design-tokens/icons/error.svg");
background-size: cover;
background-repeat: no-repeat;
}
@@ -121,7 +121,7 @@ Please see LICENSE files in the repository root for full details.
background-color: $tertiary-content;
mask-size: 16px;
transform: rotate(270deg);
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
&.mx_SpaceHierarchy_subspace_toggle_shown::before {

View File

@@ -48,7 +48,7 @@ Please see LICENSE files in the repository root for full details.
mask-size: contain;
mask-repeat: no-repeat;
background-color: $background;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
transform: rotate(270deg);
}
@@ -169,7 +169,7 @@ Please see LICENSE files in the repository root for full details.
mask-size: 20px;
mask-repeat: no-repeat;
background-color: $tertiary-content;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
.mx_SpaceButton_icon {

View File

@@ -29,7 +29,7 @@ Please see LICENSE files in the repository root for full details.
}
.mx_MessageContextMenu_iconReport::before {
mask-image: url("$(res)/img/element-icons/warning-badge.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/error.svg");
}
.mx_MessageContextMenu_iconLink::before {
@@ -61,7 +61,7 @@ Please see LICENSE files in the repository root for full details.
}
.mx_MessageContextMenu_iconResend::before {
mask-image: url("$(res)/img/element-icons/retry.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg");
}
.mx_MessageContextMenu_iconSource::before {

View File

@@ -125,7 +125,7 @@ Please see LICENSE files in the repository root for full details.
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url("$(res)/img/element-icons/retry.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg");
width: 18px;
height: 18px;
left: 0;

View File

@@ -36,9 +36,24 @@ Please see LICENSE files in the repository root for full details.
}
.mx_AnalyticsLearnMore_bullets li {
background: url("$(res)/img/tick-circle.svg") no-repeat;
list-style-type: none;
padding: 2px 0px 20px 32px;
padding: 2px 0 0 32px;
margin-bottom: 20px;
vertical-align: middle;
position: relative;
&::before {
content: "";
position: absolute;
width: 26px;
height: 26px;
left: 0;
top: 0;
background-color: #0dbd8b;
mask-image: url("@vector-im/compound-design-tokens/icons/check-circle.svg");
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
}
}
}

View File

@@ -21,7 +21,7 @@ Please see LICENSE files in the repository root for full details.
&.mx_AccessSecretStorageDialog_resetBadge::before {
/* The image isn't capable of masking, so we use a background instead. */
background-image: url("$(res)/img/element-icons/warning-badge.svg");
background-image: url("@vector-im/compound-design-tokens/icons/error.svg");
background-size: 24px;
background-color: transparent;
}
@@ -120,7 +120,7 @@ Please see LICENSE files in the repository root for full details.
width: 16px;
left: 0;
top: 2px; /* alignment */
background-image: url("$(res)/img/element-icons/warning-badge.svg");
background-image: url("@vector-im/compound-design-tokens/icons/error.svg");
background-size: contain;
}

View File

@@ -39,11 +39,13 @@ Please see LICENSE files in the repository root for full details.
}
.mx_Dropdown_arrow {
width: 10px;
height: 6px;
padding-right: 9px;
mask: url("$(res)/img/feather-customised/dropdown-arrow.svg");
width: 16px;
height: 16px;
margin-right: 4px;
mask: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
mask-repeat: no-repeat;
mask-position: center;
mask-size: 18px;
background: $primary-content;
}

View File

@@ -51,12 +51,15 @@ Please see LICENSE files in the repository root for full details.
.mx_Field_select::before {
content: "";
position: absolute;
top: 15px;
right: 10px;
width: 10px;
height: 6px;
mask: url("$(res)/img/feather-customised/dropdown-arrow.svg");
top: 50%;
transform: translateY(-50%);
right: 4px;
width: 18px;
height: 18px;
mask: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
background-color: $primary-content;
z-index: 1;
pointer-events: none;

View File

@@ -29,5 +29,5 @@ Please see LICENSE files in the repository root for full details.
}
.mx_InfoTooltip_icon_warning::before {
mask-image: url("$(res)/img/element-icons/warning.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/error.svg");
}

View File

@@ -30,6 +30,6 @@ Please see LICENSE files in the repository root for full details.
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
background-color: var(--cpd-color-icon-secondary);
}

View File

@@ -11,22 +11,11 @@ Please see LICENSE files in the repository root for full details.
font-style: italic;
}
/* Formatting for the "Verified identity has changed" error */
.mx_DecryptionFailureVerifiedIdentityChanged > span {
/* Show it in red */
color: var(--cpd-color-text-critical-primary);
background-color: var(--cpd-color-bg-critical-subtle);
/* With a red border */
border: 1px solid var(--cpd-color-border-critical-subtle);
border-radius: $font-16px;
/* Some space inside the border */
padding: var(--cpd-space-1x) var(--cpd-space-3x) var(--cpd-space-1x) var(--cpd-space-2x);
/* some space between the (!) icon and text */
/* Formatting for errors due to sender trust requirement failures */
.mx_DecryptionFailureSenderTrustRequirement > span {
/* some space between the (/) icon and text */
display: inline-flex;
gap: var(--cpd-space-2x);
gap: var(--cpd-space-1x);
/* Center vertically */
align-items: center;

View File

@@ -108,6 +108,10 @@ Please see LICENSE files in the repository root for full details.
color: var(--cpd-color-icon-primary);
}
&.mx_MessageActionBar_retryButton {
--MessageActionBar-icon-size: 16px;
}
&.mx_MessageActionBar_downloadButton {
--MessageActionBar-icon-size: 14px;

View File

@@ -45,7 +45,7 @@ Please see LICENSE files in the repository root for full details.
width: 18px;
height: 18px;
background: currentColor;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
mask-size: 100%;
mask-repeat: no-repeat;
float: right;

View File

@@ -26,9 +26,9 @@ Please see LICENSE files in the repository root for full details.
height: 16px;
width: 16px;
padding: 4px;
mask-image: url("$(res)/img/minimise.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-left.svg");
mask-repeat: no-repeat;
mask-position: 7px center;
mask-position: center;
background-color: $header-panel-text-primary-color;
}
}

View File

@@ -31,8 +31,9 @@ Please see LICENSE files in the repository root for full details.
position: absolute;
top: calc(50% - 8px); /* center */
right: -8px;
mask: url("$(res)/img/member_chevron.png");
mask: url("@vector-im/compound-design-tokens/icons/chevron-right.svg");
mask-repeat: no-repeat;
mask-position: center;
width: 16px;
height: 16px;
background-color: $header-panel-text-primary-color;

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright 2024 The Matrix.org Foundation C.I.C.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
* Please see LICENSE files in the repository root for full details.
*/
.mx_EventPreview {
font: var(--cpd-font-body-sm-regular);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.mx_EventPreview_prefix {
font: var(--cpd-font-body-sm-semibold);
}
}

View File

@@ -18,7 +18,7 @@ Please see LICENSE files in the repository root for full details.
}
}
&:hover .mx_LinkPreviewGroup_hide img,
&:hover .mx_LinkPreviewGroup_hide svg,
.mx_LinkPreviewGroup_hide:focus-visible:focus svg {
visibility: visible;
}

View File

@@ -81,15 +81,7 @@
.mx_PinnedMessageBanner_message {
grid-area: message;
font: var(--cpd-font-body-sm-regular);
line-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.mx_PinnedMessageBanner_prefix {
font: var(--cpd-font-body-sm-semibold);
}
}
.mx_PinnedMessageBanner_redactedMessage {

View File

@@ -42,7 +42,7 @@ Please see LICENSE files in the repository root for full details.
mask-size: contain;
mask-repeat: no-repeat;
background-color: $tertiary-content;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
&[aria-expanded="true"] {

View File

@@ -160,7 +160,7 @@ Please see LICENSE files in the repository root for full details.
mask-size: contain;
mask-repeat: no-repeat;
background-color: var(--cpd-color-icon-secondary);
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
&.mx_RoomSublist_collapseBtn_collapsed::before {
@@ -276,7 +276,7 @@ Please see LICENSE files in the repository root for full details.
.mx_RoomSublist_showMoreButtonChevron,
.mx_RoomSublist_showLessButtonChevron {
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
.mx_RoomSublist_showLessButtonChevron {

View File

@@ -53,11 +53,11 @@ Please see LICENSE files in the repository root for full details.
content: "";
position: absolute;
top: 50%;
right: $spacing-12;
right: var(--cpd-space-1x);
transform: translateY(-50%);
width: 12px;
height: 12px;
mask-image: url("$(res)/img/compound/chevron-right-12px.svg");
width: 24px;
height: 24px;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-right.svg");
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;

View File

@@ -67,7 +67,7 @@ Please see LICENSE files in the repository root for full details.
mask-repeat: no-repeat;
mask-position: 2px 3px;
mask-size: 24px;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
}

View File

@@ -147,7 +147,7 @@ Please see LICENSE files in the repository root for full details.
&::before {
content: "";
display: inline-block;
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
mask-size: 20px;
mask-position: center;
background-color: $call-primary-content;

View File

@@ -1,10 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1692_80)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.96967 10.7197C3.67678 10.4268 3.67601 9.95114 3.96795 9.6573L7.66823 5.933L3.95592 2.22069C3.66303 1.92779 3.66226 1.45215 3.9542 1.15831C4.24615 0.864473 4.72025 0.863706 5.01315 1.1566L9.25579 5.39924C9.54868 5.69213 9.54945 6.16777 9.2575 6.46161L5.02861 10.718C4.73667 11.0118 4.26256 11.0126 3.96967 10.7197Z" fill="#737D8C"/>
</g>
<defs>
<clipPath id="clip0_1692_80">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 629 B

View File

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.02266 2.96455C5.11589 2.10004 6.49866 1.5835 8 1.5835C11.3187 1.5835 14.049 4.10294 14.3825 7.3335H15.6723C15.9336 7.3335 16.0894 7.62498 15.9445 7.8426L13.9388 10.8543C13.8094 11.0488 13.524 11.0488 13.3945 10.8543L11.3888 7.8426C11.2439 7.62498 11.3997 7.3335 11.661 7.3335H12.8719C12.5465 4.93343 10.4893 3.0835 8 3.0835C6.84828 3.0835 5.79092 3.47857 4.95308 4.14112C4.8969 4.18555 4.84851 4.22129 4.81295 4.24673C4.7951 4.2595 4.78032 4.26979 4.7692 4.27743L4.75529 4.28689L4.75051 4.2901L4.74868 4.29132L4.74791 4.29183L4.74756 4.29206L4.74739 4.29217L4.74731 4.29223L4.33341 3.66694L4.74723 4.29228C4.40181 4.52087 3.93648 4.42616 3.70788 4.08073C3.47976 3.736 3.57362 3.27185 3.91734 3.04277L3.92021 3.04081L3.94013 3.02682C3.95912 3.01323 3.988 2.99197 4.02266 2.96455ZM3.12815 8.66683H4.33901C4.60027 8.66683 4.7561 8.37534 4.61118 8.15772L2.60551 5.14598C2.47603 4.95156 2.19064 4.95156 2.06116 5.14598L0.0554881 8.15772C-0.0894338 8.37534 0.0663988 8.66683 0.327661 8.66683H1.61755C1.95103 11.8974 4.68129 14.4168 8 14.4168C9.56831 14.4168 11.0069 13.8532 12.1215 12.9184C12.4388 12.6522 12.4803 12.1791 12.2141 11.8617C11.9479 11.5444 11.4749 11.5029 11.1575 11.7691C10.303 12.4859 9.20281 12.9168 8 12.9168C5.51071 12.9168 3.4535 11.0669 3.12815 8.66683Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="17" height="16" fill="none">
<path
fill="currentColor"
fill-rule="evenodd"
d="M4.523 2.964a6.418 6.418 0 0 1 10.36 4.369h1.29c.26 0 .416.292.272.51l-2.006 3.011a.327.327 0 0 1-.544 0l-2.006-3.012a.327.327 0 0 1 .272-.509h1.21a4.918 4.918 0 0 0-7.918-3.192 3.684 3.684 0 0 1-.184.136l-.014.01-.004.003-.002.001h-.001v.001l-.415-.625.414.625a.75.75 0 0 1-.83-1.25l.003-.001.02-.014c.02-.014.048-.035.083-.063Zm-.895 5.703H4.84a.327.327 0 0 0 .272-.51L3.106 5.146a.327.327 0 0 0-.545 0L.555 8.157c-.144.218.011.51.273.51h1.29a6.418 6.418 0 0 0 10.503 4.251.75.75 0 0 0-.963-1.15 4.918 4.918 0 0 1-8.03-3.102Z"
clip-rule="evenodd"/>
</svg>

Before

Width:  |  Height:  |  Size: 703 B

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
fill="none"
viewBox="0 0 24 24"
height="24"
width="24">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<path
id="path2"
d="M 12 2 C 6.47715 2 2 6.47715 2 12 C 2 17.5228 6.47715 22 12 22 C 17.5228 22 22 17.5228 22 12 C 22 6.47715 17.5228 2 12 2 z M 11.880859 5.5039062 C 12.720859 5.4439063 13.470547 6.0746875 13.560547 6.9296875 L 13.560547 7.1699219 L 13.080078 13.169922 C 13.035078 13.724922 12.570625 14.144531 12.015625 14.144531 L 11.925781 14.144531 C 11.400781 14.099531 10.996172 13.694922 10.951172 13.169922 L 10.470703 7.1699219 C 10.395703 6.3149219 11.025859 5.5639064 11.880859 5.5039062 z M 12 15.763672 C 12.729 15.763672 13.320312 16.354884 13.320312 17.083984 C 13.320313 17.812984 12.729 18.404297 12 18.404297 C 11.271 18.404297 10.679688 17.812984 10.679688 17.083984 C 10.679688 16.354884 11.271 15.763672 12 15.763672 z "
style="fill:#ff5b55;fill-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM6.9806 4.5101C6.9306 3.9401 7.3506 3.4401 7.9206 3.4001C8.4806 3.3601 8.9806 3.7801 9.0406 4.3501V4.5101L8.7206 8.5101C8.6906 8.8801 8.3806 9.1601 8.0106 9.1601H7.9506C7.6006 9.1301 7.3306 8.8601 7.3006 8.5101L6.9806 4.5101ZM8.88012 11.1202C8.88012 11.6062 8.48613 12.0002 8.00012 12.0002C7.51411 12.0002 7.12012 11.6062 7.12012 11.1202C7.12012 10.6342 7.51411 10.2402 8.00012 10.2402C8.48613 10.2402 8.88012 10.6342 8.88012 11.1202Z" fill="#8D99A5"/>
</svg>

Before

Width:  |  Height:  |  Size: 713 B

View File

@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 7.5L9 10.5L12 7.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 217 B

View File

@@ -1,11 +0,0 @@
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="LifeBuoy" transform="translate(-1378.000000, -91.000000)" stroke="#61708b" stroke-width="1">
<g id="search-copy" transform="translate(1379.000000, 92.000000)">
<circle id="Oval" cx="6.22222222" cy="6.22222222" r="6.22222222"></circle>
<path d="M14,14 L10.6166667,10.6166667" id="Path"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="10px" height="16px" viewBox="-1 -1 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: sketchtool 3.5.1 (25234) - http://www.bohemiancoding.com/sketch -->
<title>minimise</title>
<desc>Created with sketchtool.</desc>
<defs></defs>
<g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="02_1-Chat-collapsed-w-topic" sketch:type="MSArtboardGroup" transform="translate(-176.000000, -27.000000)" stroke-width="2" stroke="#9FA9BA">
<g id="Room-list" sketch:type="MSLayerGroup">
<g id="Room-list/Header" sketch:type="MSShapeGroup">
<g id="minimise" transform="translate(172.000000, 25.000000)">
<path d="M7,5 L15,5 L15,13" id="Path-53-Copy" transform="translate(11.000000, 9.000000) scale(-1, -1) rotate(-315.000000) translate(-11.000000, -9.000000) "></path>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,4 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22C17.5 22 22 17.5 22 12C22 6.5 17.5 2 12 2V2Z" stroke="#0DBD8B" stroke-width="2" stroke-linecap="square"/>
<path d="M6.54549 12.8882L9.80306 16.2426L17.4546 8.36377" stroke="#0DBD8B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 442 B

File diff suppressed because one or more lines are too long

View File

@@ -14,14 +14,6 @@ export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor
export type { Leaves } from "matrix-web-i18n";
export type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
? RecursivePartial<U>[]
: T[P] extends object
? RecursivePartial<T[P]>
: T[P];
};
export type KeysStartingWith<Input extends object, Str extends string> = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X

View File

@@ -37,6 +37,7 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
public state: IState = {};
public componentDidMount(): void {
this.unmounted = false;
this.props.prom
.then((result) => {
if (this.unmounted) return;

View File

@@ -31,6 +31,8 @@ import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
import { IConfigOptions } from "./IConfigOptions";
import SdkConfig from "./SdkConfig";
import { buildAndEncodePickleKey, encryptPickleKey } from "./utils/tokens/pickling";
import Favicon from "./favicon.ts";
import { getVectorConfig } from "./vector/getconfig.ts";
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
@@ -66,14 +68,20 @@ const UPDATE_DEFER_KEY = "mx_defer_update";
export default abstract class BasePlatform {
protected notificationCount = 0;
protected errorDidOccur = false;
protected _favicon?: Favicon;
protected constructor() {
dis.register(this.onAction);
this.startUpdateCheck = this.startUpdateCheck.bind(this);
}
public abstract getConfig(): Promise<IConfigOptions | undefined>;
public async getConfig(): Promise<IConfigOptions | undefined> {
return getVectorConfig();
}
/**
* Get a sensible default display name for the device Element is running on
*/
public abstract getDefaultDeviceDisplayName(): string;
protected onAction = (payload: ActionPayload): void => {
@@ -89,11 +97,15 @@ export default abstract class BasePlatform {
public abstract getHumanReadableName(): string;
public setNotificationCount(count: number): void {
if (this.notificationCount === count) return;
this.notificationCount = count;
this.updateFavicon();
}
public setErrorStatus(errorDidOccur: boolean): void {
if (this.errorDidOccur === errorDidOccur) return;
this.errorDidOccur = errorDidOccur;
this.updateFavicon();
}
/**
@@ -456,4 +468,34 @@ export default abstract class BasePlatform {
url.hash = "";
return url;
}
/**
* Delay creating the `Favicon` instance until first use (on the first notification) as
* it uses canvas, which can trigger a permission prompt in Firefox's resist fingerprinting mode.
* See https://github.com/element-hq/element-web/issues/9605.
*/
public get favicon(): Favicon {
if (this._favicon) {
return this._favicon;
}
this._favicon = new Favicon();
return this._favicon;
}
private updateFavicon(): void {
let bgColor = "#d00";
let notif: string | number = this.notificationCount;
if (this.errorDidOccur) {
notif = notif || "×";
bgColor = "#f00";
}
this.favicon.badge(notif, { bgColor });
}
/**
* Begin update polling, if applicable
*/
public startUpdater(): void {}
}

View File

@@ -6,7 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
// @ts-ignore - `.ts` is needed here to make TS happy
import { Request, Response } from "./workers/blurhash.worker.ts";
import { WorkerManager } from "./WorkerManager";
import blurhashWorkerFactory from "./workers/blurhashWorkerFactory";

View File

@@ -113,13 +113,9 @@ export default class DeviceListener {
this.client.removeListener(ClientEvent.Sync, this.onSync);
this.client.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
}
if (this.deviceClientInformationSettingWatcherRef) {
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
}
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef);
this.dispatcherRef = undefined;
}
SettingsStore.unwatchSetting(this.deviceClientInformationSettingWatcherRef);
dis.unregister(this.dispatcherRef);
this.dispatcherRef = undefined;
this.dismissed.clear();
this.dismissedThisDeviceToast = false;
this.keyBackupInfo = null;

View File

@@ -383,6 +383,9 @@ export default class Markdown {
if (isMultiLine(node) && node.next) this.lit("\n\n");
};
return renderer.render(this.parsed);
// We inhibit the default escape function as we escape the entire output string to correctly handle backslashes
renderer.esc = (input: string) => input;
return escape(renderer.render(this.parsed));
}
}

View File

@@ -8,13 +8,13 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { StrictMode } from "react";
import ReactDOM from "react-dom";
import { createRoot, Root } from "react-dom/client";
import classNames from "classnames";
import { IDeferred, defer, sleep } from "matrix-js-sdk/src/utils";
import { IDeferred, defer } from "matrix-js-sdk/src/utils";
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { Glass, TooltipProvider } from "@vector-im/compound-web";
import dis, { defaultDispatcher } from "./dispatcher/dispatcher";
import defaultDispatcher from "./dispatcher/dispatcher";
import AsyncWrapper from "./AsyncWrapper";
import { Defaultize } from "./@types/common";
import { ActionPayload } from "./dispatcher/payloads";
@@ -69,6 +69,16 @@ type HandlerMap = {
type ModalCloseReason = "backgroundClick";
function getOrCreateContainer(id: string): HTMLDivElement {
let container = document.getElementById(id) as HTMLDivElement | null;
if (!container) {
container = document.createElement("div");
container.id = id;
document.body.appendChild(container);
}
return container;
}
export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMap> {
private counter = 0;
// The modal to prioritise over all others. If this is set, only show
@@ -83,28 +93,22 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
// Neither the static nor priority modal will be in this list.
private modals: IModal<any>[] = [];
private static getOrCreateContainer(): HTMLElement {
let container = document.getElementById(DIALOG_CONTAINER_ID);
if (!container) {
container = document.createElement("div");
container.id = DIALOG_CONTAINER_ID;
document.body.appendChild(container);
private static root?: Root;
private static getOrCreateRoot(): Root {
if (!ModalManager.root) {
const container = getOrCreateContainer(DIALOG_CONTAINER_ID);
ModalManager.root = createRoot(container);
}
return container;
return ModalManager.root;
}
private static getOrCreateStaticContainer(): HTMLElement {
let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID);
if (!container) {
container = document.createElement("div");
container.id = STATIC_DIALOG_CONTAINER_ID;
document.body.appendChild(container);
private static staticRoot?: Root;
private static getOrCreateStaticRoot(): Root {
if (!ModalManager.staticRoot) {
const container = getOrCreateContainer(STATIC_DIALOG_CONTAINER_ID);
ModalManager.staticRoot = createRoot(container);
}
return container;
return ModalManager.staticRoot;
}
public constructor() {
@@ -389,26 +393,21 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
}
private async reRender(): Promise<void> {
// TODO: We should figure out how to remove this weird sleep. It also makes testing harder
//
// await next tick because sometimes ReactDOM can race with itself and cause the modal to wrongly stick around
await sleep(0);
if (this.modals.length === 0 && !this.priorityModal && !this.staticModal) {
// If there is no modal to render, make all of Element available
// to screen reader users again
dis.dispatch({
defaultDispatcher.dispatch({
action: "aria_unhide_main_app",
});
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer());
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateStaticContainer());
ModalManager.getOrCreateRoot().render(<></>);
ModalManager.getOrCreateStaticRoot().render(<></>);
return;
}
// Hide the content outside the modal to screen reader users
// so they won't be able to navigate into it and act on it using
// screen reader specific features
dis.dispatch({
defaultDispatcher.dispatch({
action: "aria_hide_main_app",
});
@@ -432,10 +431,10 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
</StrictMode>
);
ReactDOM.render(staticDialog, ModalManager.getOrCreateStaticContainer());
ModalManager.getOrCreateStaticRoot().render(staticDialog);
} else {
// This is safe to call repeatedly if we happen to do that
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateStaticContainer());
ModalManager.getOrCreateStaticRoot().render(<></>);
}
const modal = this.getCurrentModal();
@@ -461,10 +460,10 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
</StrictMode>
);
setTimeout(() => ReactDOM.render(dialog, ModalManager.getOrCreateContainer()), 0);
ModalManager.getOrCreateRoot().render(dialog);
} else {
// This is safe to call repeatedly if we happen to do that
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer());
ModalManager.getOrCreateRoot().render(<></>);
}
}
}

View File

@@ -6,7 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
// @ts-ignore - `.ts` is needed here to make TS happy
import { Request, Response } from "./workers/playback.worker";
import { WorkerManager } from "./WorkerManager";
import playbackWorkerFactory from "./workers/playbackWorkerFactory";

View File

@@ -326,7 +326,7 @@ export class PosthogAnalytics {
if (this.enabled) {
this.posthog.reset();
}
if (this.watchSettingRef) SettingsStore.unwatchSetting(this.watchSettingRef);
SettingsStore.unwatchSetting(this.watchSettingRef);
this.setAnonymity(Anonymity.Disabled);
}

View File

@@ -20,9 +20,9 @@ import { ActionPayload } from "./dispatcher/payloads";
const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
class Presence {
private unavailableTimer: Timer | null = null;
private dispatcherRef: string | null = null;
private state: SetPresence | null = null;
private unavailableTimer?: Timer;
private dispatcherRef?: string;
private state?: SetPresence;
/**
* Start listening the user activity to evaluate his presence state.
@@ -46,14 +46,10 @@ class Presence {
* Stop tracking user activity
*/
public stop(): void {
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef);
this.dispatcherRef = null;
}
if (this.unavailableTimer) {
this.unavailableTimer.abort();
this.unavailableTimer = null;
}
dis.unregister(this.dispatcherRef);
this.dispatcherRef = undefined;
this.unavailableTimer?.abort();
this.unavailableTimer = undefined;
}
/**
@@ -61,7 +57,7 @@ class Presence {
* @returns {string} the presence state (see PRESENCE enum)
*/
public getState(): SetPresence | null {
return this.state;
return this.state ?? null;
}
private onAction = (payload: ActionPayload): void => {

View File

@@ -117,8 +117,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
// signing key upload as well. This avoids hitting the server to
// test auth flows, which may be slow under high load.
canUploadKeysWithPasswordOnly = true;
} else {
this.queryKeyUploadAuth();
}
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
@@ -140,8 +138,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
passPhraseKeySelected,
accountPassword,
};
}
public componentDidMount(): void {
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
if (keyFromCustomisations) this.initExtension(keyFromCustomisations);
if (this.state.canUploadKeysWithPasswordOnly === null) {
this.queryKeyUploadAuth();
}
}
private initExtension(keyFromCustomisations: Uint8Array): void {

View File

@@ -56,6 +56,10 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
};
}
public componentDidMount(): void {
this.unmounted = false;
}
public componentWillUnmount(): void {
this.unmounted = true;
}

View File

@@ -64,6 +64,10 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
};
}
public componentDidMount(): void {
this.unmounted = false;
}
public componentWillUnmount(): void {
this.unmounted = true;
}

View File

@@ -31,4 +31,3 @@ export const BackdropPanel: React.FC<IProps> = ({ backgroundImage, blurMultiplie
</div>
);
};
export default BackdropPanel;

View File

@@ -38,7 +38,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
public static contextType = MatrixClientContext;
public declare context: React.ContextType<typeof MatrixClientContext>;
private unmounted = false;
private dispatcherRef: string | null = null;
private dispatcherRef?: string;
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
@@ -100,7 +100,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
public componentWillUnmount(): void {
this.unmounted = true;
if (this.dispatcherRef !== null) dis.unregister(this.dispatcherRef);
dis.unregister(this.dispatcherRef);
}
private onAction = (payload: ActionPayload): void => {

View File

@@ -90,8 +90,8 @@ interface IState {
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
private readonly authLogic: InteractiveAuth<T>;
private readonly intervalId: number | null = null;
private readonly stageComponent = createRef<IStageComponent>();
private intervalId: number | null = null;
private unmounted = false;
@@ -126,15 +126,17 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
AuthType.SsoUnstable,
],
});
}
public componentDidMount(): void {
this.unmounted = false;
if (this.props.poll) {
this.intervalId = window.setInterval(() => {
this.authLogic.poll();
}, 2000);
}
}
public componentDidMount(): void {
this.authLogic
.attemptAuth()
.then(async (result) => {

View File

@@ -67,10 +67,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
activeSpace: SpaceStore.instance.activeSpace,
showBreadcrumbs: LeftPanel.breadcrumbsMode,
};
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
}
private static get breadcrumbsMode(): BreadcrumbsMode {
@@ -78,6 +74,10 @@ export default class LeftPanel extends React.Component<IProps, IState> {
}
public componentDidMount(): void {
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
if (this.listContainerRef.current) {
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
// Using the passive option to not block the main thread

View File

@@ -49,11 +49,10 @@ import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../LegacyCallHandl
import AudioFeedArrayForLegacyCall from "../views/voip/AudioFeedArrayForLegacyCall";
import { OwnProfileStore } from "../../stores/OwnProfileStore";
import { UPDATE_EVENT } from "../../stores/AsyncStore";
import RoomView from "./RoomView";
import type { RoomView as RoomViewType } from "./RoomView";
import { RoomView } from "./RoomView";
import ToastContainer from "./ToastContainer";
import UserView from "./UserView";
import BackdropPanel from "./BackdropPanel";
import { BackdropPanel } from "./BackdropPanel";
import { mediaFromMxc } from "../../customisations/Media";
import { UserTab } from "../views/dialogs/UserTab";
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
@@ -125,7 +124,7 @@ class LoggedInView extends React.Component<IProps, IState> {
public static displayName = "LoggedInView";
protected readonly _matrixClient: MatrixClient;
protected readonly _roomView: React.RefObject<RoomViewType>;
protected readonly _roomView: React.RefObject<RoomView>;
protected readonly _resizeContainer: React.RefObject<HTMLDivElement>;
protected readonly resizeHandler: React.RefObject<HTMLDivElement>;
protected layoutWatcherRef?: string;
@@ -228,9 +227,9 @@ class LoggedInView extends React.Component<IProps, IState> {
this._matrixClient.removeListener(ClientEvent.Sync, this.onSync);
this._matrixClient.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
OwnProfileStore.instance.off(UPDATE_EVENT, this.refreshBackgroundImage);
if (this.layoutWatcherRef) SettingsStore.unwatchSetting(this.layoutWatcherRef);
if (this.compactLayoutWatcherRef) SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
if (this.backgroundImageWatcherRef) SettingsStore.unwatchSetting(this.backgroundImageWatcherRef);
SettingsStore.unwatchSetting(this.layoutWatcherRef);
SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
SettingsStore.unwatchSetting(this.backgroundImageWatcherRef);
this.timezoneProfileUpdateRef?.forEach((s) => SettingsStore.unwatchSetting(s));
this.resizer?.detach();
}

View File

@@ -231,10 +231,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
private prevWindowWidth: number;
private voiceBroadcastResumer?: VoiceBroadcastResumer;
private readonly loggedInView: React.RefObject<LoggedInViewType>;
private readonly dispatcherRef: string;
private readonly themeWatcher: ThemeWatcher;
private readonly fontWatcher: FontWatcher;
private readonly loggedInView = createRef<LoggedInViewType>();
private dispatcherRef?: string;
private themeWatcher?: ThemeWatcher;
private fontWatcher?: FontWatcher;
private readonly stores: SdkContextClass;
public constructor(props: IProps) {
@@ -256,8 +256,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
ready: false,
};
this.loggedInView = createRef();
SdkConfig.put(this.props.config);
// Used by _viewRoom before getting state from sync
@@ -282,32 +280,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
this.prevWindowWidth = UIStore.instance.windowWidth || 1000;
UIStore.instance.on(UI_EVENTS.Resize, this.handleResize);
// For PersistentElement
this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize);
RoomNotificationStateStore.instance.on(UPDATE_STATUS_INDICATOR, this.onUpdateStatusIndicator);
this.dispatcherRef = dis.register(this.onAction);
this.themeWatcher = new ThemeWatcher();
this.fontWatcher = new FontWatcher();
this.themeWatcher.start();
this.fontWatcher.start();
// object field used for tracking the status info appended to the title tag.
// we don't do it as react state as i'm scared about triggering needless react refreshes.
this.subTitleStatus = "";
initSentry(SdkConfig.get("sentry"));
if (!checkSessionLockFree()) {
// another instance holds the lock; confirm its theft before proceeding
setTimeout(() => this.setState({ view: Views.CONFIRM_LOCK_THEFT }), 0);
} else {
this.startInitSession();
}
}
/**
@@ -476,6 +452,29 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}
public componentDidMount(): void {
UIStore.instance.on(UI_EVENTS.Resize, this.handleResize);
// For PersistentElement
this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize);
RoomNotificationStateStore.instance.on(UPDATE_STATUS_INDICATOR, this.onUpdateStatusIndicator);
this.dispatcherRef = dis.register(this.onAction);
this.themeWatcher = new ThemeWatcher();
this.fontWatcher = new FontWatcher();
this.themeWatcher.start();
this.fontWatcher.start();
initSentry(SdkConfig.get("sentry"));
if (!checkSessionLockFree()) {
// another instance holds the lock; confirm its theft before proceeding
setTimeout(() => this.setState({ view: Views.CONFIRM_LOCK_THEFT }), 0);
} else {
this.startInitSession();
}
window.addEventListener("resize", this.onWindowResized);
}
@@ -497,8 +496,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
public componentWillUnmount(): void {
Lifecycle.stopMatrixClient();
dis.unregister(this.dispatcherRef);
this.themeWatcher.stop();
this.fontWatcher.stop();
this.themeWatcher?.stop();
this.fontWatcher?.stop();
UIStore.destroy();
this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize);
window.removeEventListener("resize", this.onWindowResized);
@@ -1011,7 +1010,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.setStateForNewView(newState);
ThemeController.isLogin = true;
this.themeWatcher.recheck();
this.themeWatcher?.recheck();
this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register");
}
@@ -1088,7 +1087,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
},
() => {
ThemeController.isLogin = false;
this.themeWatcher.recheck();
this.themeWatcher?.recheck();
this.notifyNewScreen("room/" + presentedId, replaceLast);
},
);
@@ -1113,7 +1112,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
});
this.notifyNewScreen("welcome");
ThemeController.isLogin = true;
this.themeWatcher.recheck();
this.themeWatcher?.recheck();
}
private viewLogin(otherState?: any): void {
@@ -1123,7 +1122,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
});
this.notifyNewScreen("login");
ThemeController.isLogin = true;
this.themeWatcher.recheck();
this.themeWatcher?.recheck();
}
private viewHome(justRegistered = false): void {
@@ -1136,7 +1135,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.setPage(PageType.HomePage);
this.notifyNewScreen("home");
ThemeController.isLogin = false;
this.themeWatcher.recheck();
this.themeWatcher?.recheck();
}
private viewUser(userId: string, subAction: string): void {
@@ -1357,7 +1356,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
*/
private async onLoggedIn(): Promise<void> {
ThemeController.isLogin = false;
this.themeWatcher.recheck();
this.themeWatcher?.recheck();
StorageManager.tryPersistStorage();
await this.onShowPostLoginScreen();

View File

@@ -240,13 +240,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
private readReceiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
private readonly _showHiddenEvents: boolean;
private isMounted = false;
private unmounted = false;
private readMarkerNode = createRef<HTMLLIElement>();
private whoIsTyping = createRef<WhoIsTypingTile>();
public scrollPanel = createRef<ScrollPanel>();
private readonly showTypingNotificationsWatcherRef: string;
private showTypingNotificationsWatcherRef?: string;
private eventTiles: Record<string, UnwrappedEventTile> = {};
// A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
@@ -267,22 +267,21 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// and we check this in a hot code path. This is also cached in our
// RoomContext, however we still need a fallback for roomless MessagePanels.
this._showHiddenEvents = SettingsStore.getValue("showHiddenEventsInTimeline");
}
public componentDidMount(): void {
this.unmounted = false;
this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting(
"showTypingNotifications",
null,
this.onShowTypingNotificationsChange,
);
}
public componentDidMount(): void {
this.calculateRoomMembersCount();
this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount);
this.isMounted = true;
}
public componentWillUnmount(): void {
this.isMounted = false;
this.unmounted = true;
this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount);
SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
this.readReceiptMap = {};
@@ -441,7 +440,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
}
private isUnmounting = (): boolean => {
return !this.isMounted;
return this.unmounted;
};
public get showHiddenEvents(): boolean {

View File

@@ -25,7 +25,9 @@ export default class NonUrgentToastContainer extends React.PureComponent<IProps,
this.state = {
toasts: NonUrgentToastStore.instance.components,
};
}
public componentDidMount(): void {
NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
}

View File

@@ -22,11 +22,9 @@ interface IProps {
}
export default class RoomSearch extends React.PureComponent<IProps> {
private readonly dispatcherRef: string;
public constructor(props: IProps) {
super(props);
private dispatcherRef?: string;
public componentDidMount(): void {
this.dispatcherRef = defaultDispatcher.register(this.onAction);
}

View File

@@ -103,6 +103,8 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
}
public componentDidMount(): void {
this.unmounted = false;
const client = this.context;
client.on(ClientEvent.Sync, this.onSyncStateChange);
client.on(RoomEvent.LocalEchoUpdated, this.onRoomLocalEchoUpdated);

View File

@@ -45,7 +45,7 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
import ContentMessages from "../../ContentMessages";
import Modal from "../../Modal";
import { LegacyCallHandlerEvent } from "../../LegacyCallHandler";
import dis, { defaultDispatcher } from "../../dispatcher/dispatcher";
import defaultDispatcher from "../../dispatcher/dispatcher";
import * as Rooms from "../../Rooms";
import MainSplit from "./MainSplit";
import RightPanel from "./RightPanel";
@@ -351,8 +351,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private static e2eStatusCache = new Map<string, E2EStatus>();
private readonly askToJoinEnabled: boolean;
private readonly dispatcherRef: string;
private settingWatchers: string[];
private dispatcherRef?: string;
private settingWatchers: string[] = [];
private unmounted = false;
private permalinkCreators: Record<string, RoomPermalinkCreator> = {};
@@ -418,62 +418,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
promptAskToJoin: false,
viewRoomOpts: { buttons: [] },
};
this.dispatcherRef = dis.register(this.onAction);
context.client.on(ClientEvent.Room, this.onRoom);
context.client.on(RoomEvent.Timeline, this.onRoomTimeline);
context.client.on(RoomEvent.TimelineReset, this.onRoomTimelineReset);
context.client.on(RoomEvent.Name, this.onRoomName);
context.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
context.client.on(RoomStateEvent.Update, this.onRoomStateUpdate);
context.client.on(RoomEvent.MyMembership, this.onMyMembership);
context.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
context.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
context.client.on(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged);
context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
// Start listening for RoomViewStore updates
context.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
context.widgetStore.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
CallStore.instance.on(CallStoreEvent.ConnectedCalls, this.onConnectedCalls);
this.props.resizeNotifier.on("isResizing", this.onIsResizing);
this.settingWatchers = [
SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
this.setState({ layout: value as Layout }),
),
SettingsStore.watchSetting("lowBandwidth", null, (...[, , , value]) =>
this.setState({ lowBandwidth: value as boolean }),
),
SettingsStore.watchSetting("alwaysShowTimestamps", null, (...[, , , value]) =>
this.setState({ alwaysShowTimestamps: value as boolean }),
),
SettingsStore.watchSetting("showTwelveHourTimestamps", null, (...[, , , value]) =>
this.setState({ showTwelveHourTimestamps: value as boolean }),
),
SettingsStore.watchSetting(TimezoneHandler.USER_TIMEZONE_KEY, null, (...[, , , value]) =>
this.setState({ userTimezone: value as string }),
),
SettingsStore.watchSetting("readMarkerInViewThresholdMs", null, (...[, , , value]) =>
this.setState({ readMarkerInViewThresholdMs: value as number }),
),
SettingsStore.watchSetting("readMarkerOutOfViewThresholdMs", null, (...[, , , value]) =>
this.setState({ readMarkerOutOfViewThresholdMs: value as number }),
),
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, (...[, , , value]) =>
this.setState({ showHiddenEvents: value as boolean }),
),
SettingsStore.watchSetting("urlPreviewsEnabled", null, this.onUrlPreviewsEnabledChange),
SettingsStore.watchSetting("urlPreviewsEnabled_e2ee", null, this.onUrlPreviewsEnabledChange),
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, (...[, , , value]) =>
this.setState({ msc3946ProcessDynamicPredecessor: value as boolean }),
),
];
}
private onIsResizing = (resizing: boolean): void => {
@@ -493,7 +437,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private onWidgetLayoutChange = (): void => {
if (!this.state.room) return;
dis.dispatch({
defaultDispatcher.dispatch({
action: "appsDrawer",
show: true,
});
@@ -654,7 +598,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// Handle the use case of a link to a thread message
// ie: #/room/roomId/eventId (eventId of a thread message)
if (thread?.rootEvent && !initialEvent?.isThreadRoot) {
dis.dispatch<ShowThreadPayload>({
defaultDispatcher.dispatch<ShowThreadPayload>({
action: Action.ShowThread,
rootEvent: thread.rootEvent,
initialEvent,
@@ -760,7 +704,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
const activeCall = CallStore.instance.getActiveCall(this.state.roomId);
if (activeCall === null) {
// We disconnected from the call, so stop viewing it
dis.dispatch<ViewRoomPayload>(
defaultDispatcher.dispatch<ViewRoomPayload>(
{
action: Action.ViewRoom,
room_id: this.state.roomId,
@@ -904,6 +848,66 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
public componentDidMount(): void {
this.unmounted = false;
this.dispatcherRef = defaultDispatcher.register(this.onAction);
if (this.context.client) {
this.context.client.on(ClientEvent.Room, this.onRoom);
this.context.client.on(RoomEvent.Timeline, this.onRoomTimeline);
this.context.client.on(RoomEvent.TimelineReset, this.onRoomTimelineReset);
this.context.client.on(RoomEvent.Name, this.onRoomName);
this.context.client.on(RoomStateEvent.Events, this.onRoomStateEvents);
this.context.client.on(RoomStateEvent.Update, this.onRoomStateUpdate);
this.context.client.on(RoomEvent.MyMembership, this.onMyMembership);
this.context.client.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus);
this.context.client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged);
this.context.client.on(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged);
this.context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
}
// Start listening for RoomViewStore updates
this.context.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
this.context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
this.context.widgetStore.on(UPDATE_EVENT, this.onWidgetStoreUpdate);
CallStore.instance.on(CallStoreEvent.ConnectedCalls, this.onConnectedCalls);
this.props.resizeNotifier.on("isResizing", this.onIsResizing);
this.settingWatchers = [
SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
this.setState({ layout: value as Layout }),
),
SettingsStore.watchSetting("lowBandwidth", null, (...[, , , value]) =>
this.setState({ lowBandwidth: value as boolean }),
),
SettingsStore.watchSetting("alwaysShowTimestamps", null, (...[, , , value]) =>
this.setState({ alwaysShowTimestamps: value as boolean }),
),
SettingsStore.watchSetting("showTwelveHourTimestamps", null, (...[, , , value]) =>
this.setState({ showTwelveHourTimestamps: value as boolean }),
),
SettingsStore.watchSetting(TimezoneHandler.USER_TIMEZONE_KEY, null, (...[, , , value]) =>
this.setState({ userTimezone: value as string }),
),
SettingsStore.watchSetting("readMarkerInViewThresholdMs", null, (...[, , , value]) =>
this.setState({ readMarkerInViewThresholdMs: value as number }),
),
SettingsStore.watchSetting("readMarkerOutOfViewThresholdMs", null, (...[, , , value]) =>
this.setState({ readMarkerOutOfViewThresholdMs: value as number }),
),
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, (...[, , , value]) =>
this.setState({ showHiddenEvents: value as boolean }),
),
SettingsStore.watchSetting("urlPreviewsEnabled", null, this.onUrlPreviewsEnabledChange),
SettingsStore.watchSetting("urlPreviewsEnabled_e2ee", null, this.onUrlPreviewsEnabledChange),
SettingsStore.watchSetting("feature_dynamic_room_predecessors", null, (...[, , , value]) =>
this.setState({ msc3946ProcessDynamicPredecessor: value as boolean }),
),
];
this.onRoomViewStoreUpdate(true);
const call = this.getCallForRoom();
@@ -963,7 +967,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// stop tracking room changes to format permalinks
this.stopAllPermalinkCreators();
dis.unregister(this.dispatcherRef);
defaultDispatcher.unregister(this.dispatcherRef);
if (this.context.client) {
this.context.client.removeListener(ClientEvent.Room, this.onRoom);
this.context.client.removeListener(RoomEvent.Timeline, this.onRoomTimeline);
@@ -1041,7 +1045,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
handled = true;
break;
case KeyBindingAction.UploadFile: {
dis.dispatch(
defaultDispatcher.dispatch(
{
action: "upload_file",
context: TimelineRenderingType.Room,
@@ -1141,7 +1145,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (payload.event && payload.event.getRoomId() !== this.state.roomId) {
// If the event is in a different room (e.g. because the event to be edited is being displayed
// in the results of an all-rooms search), we need to view that room first.
dis.dispatch<ViewRoomPayload>({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: payload.event.getRoomId(),
metricsTrigger: undefined,
@@ -1184,7 +1188,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
// re-dispatch to the correct composer
dis.dispatch<ComposerInsertPayload>({
defaultDispatcher.dispatch<ComposerInsertPayload>({
...(payload as ComposerInsertPayload),
timelineRenderingType,
composerType: this.state.editState ? ComposerType.Edit : ComposerType.Send,
@@ -1193,7 +1197,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
case Action.FocusAComposer: {
dis.dispatch<FocusComposerPayload>({
defaultDispatcher.dispatch<FocusComposerPayload>({
...(payload as FocusComposerPayload),
// re-dispatch to the correct composer (the send message will still be on screen even when editing a message)
action: this.state.editState ? Action.FocusEditMessageComposer : Action.FocusSendMessageComposer,
@@ -1299,7 +1303,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
// For initial threads launch, chat effects are disabled see #19731
if (!ev.isRelation(THREAD_RELATION_TYPE.name)) {
dis.dispatch({ action: `effects.${effect.command}`, event: ev });
defaultDispatcher.dispatch({ action: `effects.${effect.command}`, event: ev });
}
}
});
@@ -1359,7 +1363,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
liveTimeline: room.getLiveTimeline(),
});
dis.dispatch<ActionPayload>({ action: Action.RoomLoaded });
defaultDispatcher.dispatch<ActionPayload>({ action: Action.RoomLoaded });
};
private onRoomTimelineReset = (room?: Room): void => {
@@ -1557,7 +1561,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private onInviteClick = (): void => {
// open the room inviter
dis.dispatch({
defaultDispatcher.dispatch({
action: "view_invite",
roomId: this.getRoomId(),
});
@@ -1568,7 +1572,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (this.context.client?.isGuest()) {
// Join this room once the user has registered and logged in
// (If we failed to peek, we may not have a valid room object.)
dis.dispatch<DoAfterSyncPreparedPayload<ViewRoomPayload>>({
defaultDispatcher.dispatch<DoAfterSyncPreparedPayload<ViewRoomPayload>>({
action: Action.DoAfterSyncPrepared,
deferred_action: {
action: Action.ViewRoom,
@@ -1576,13 +1580,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
metricsTrigger: undefined,
},
});
dis.dispatch({ action: "require_registration" });
defaultDispatcher.dispatch({ action: "require_registration" });
} else {
Promise.resolve().then(() => {
const signUrl = this.props.threepidInvite?.signUrl;
const roomId = this.getRoomId();
if (isNotUndefined(roomId)) {
dis.dispatch<JoinRoomPayload>({
defaultDispatcher.dispatch<JoinRoomPayload>({
action: Action.JoinRoom,
roomId,
opts: { inviteSignUrl: signUrl },
@@ -1618,7 +1622,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
this.state.initialEventId === eventId
) {
debuglog("Removing scroll_into_view flag from initial event");
dis.dispatch<ViewRoomPayload>({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.getRoomId(),
event_id: this.state.initialEventId,
@@ -1634,7 +1638,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
const roomId = this.getRoomId();
if (!this.context.client || !roomId) return;
if (this.context.client.isGuest()) {
dis.dispatch({ action: "require_registration" });
defaultDispatcher.dispatch({ action: "require_registration" });
return;
}
@@ -1684,7 +1688,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
};
private onForgetClick = (): void => {
dis.dispatch({
defaultDispatcher.dispatch({
action: "forget_room",
room_id: this.getRoomId(),
});
@@ -1698,7 +1702,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
});
this.context.client?.leave(roomId).then(
() => {
dis.dispatch({ action: Action.ViewHomePage });
defaultDispatcher.dispatch({ action: Action.ViewHomePage });
this.setState({
rejecting: false,
});
@@ -1732,7 +1736,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
await this.context.client!.setIgnoredUsers(ignoredUsers);
await this.context.client!.leave(this.state.roomId!);
dis.dispatch({ action: Action.ViewHomePage });
defaultDispatcher.dispatch({ action: Action.ViewHomePage });
this.setState({
rejecting: false,
});
@@ -1756,7 +1760,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// using /leave rather than /join. In the short term though, we
// just ignore them.
// https://github.com/vector-im/vector-web/issues/1134
dis.fire(Action.ViewRoomDirectory);
defaultDispatcher.fire(Action.ViewRoomDirectory);
};
private onSearchChange = debounce((e: ChangeEvent): void => {
@@ -1782,7 +1786,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
// If we were viewing a highlighted event, firing view_room without
// an event will take care of both clearing the URL fragment and
// jumping to the bottom
dis.dispatch<ViewRoomPayload>({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: this.getRoomId(),
metricsTrigger: undefined, // room doesn't change
@@ -1790,7 +1794,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
} else {
// Otherwise we have to jump manually
this.messagePanel?.jumpToLiveTimeline();
dis.fire(Action.FocusSendMessageComposer);
defaultDispatcher.fire(Action.FocusSendMessageComposer);
}
};
@@ -1914,7 +1918,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
public onHiddenHighlightsClick = (): void => {
const oldRoom = this.getOldRoom();
if (!oldRoom) return;
dis.dispatch<ViewRoomPayload>({
defaultDispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: oldRoom.roomId,
metricsTrigger: "Predecessor",
@@ -1997,7 +2001,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
const roomId = this.getRoomId();
if (isNotUndefined(roomId)) {
dis.dispatch<SubmitAskToJoinPayload>({
defaultDispatcher.dispatch<SubmitAskToJoinPayload>({
action: Action.SubmitAskToJoin,
roomId,
opts: { reason },
@@ -2014,7 +2018,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
const roomId = this.getRoomId();
if (isNotUndefined(roomId)) {
dis.dispatch<CancelAskToJoinPayload>({
defaultDispatcher.dispatch<CancelAskToJoinPayload>({
action: Action.CancelAskToJoin,
roomId,
});
@@ -2543,5 +2547,3 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
);
}
}
export default RoomView;

View File

@@ -191,12 +191,12 @@ export default class ScrollPanel extends React.Component<IProps> {
public constructor(props: IProps) {
super(props);
this.props.resizeNotifier?.on("middlePanelResizedNoisy", this.onResize);
this.resetScrollState();
}
public componentDidMount(): void {
this.unmounted = false;
this.props.resizeNotifier?.on("middlePanelResizedNoisy", this.onResize);
this.checkScroll();
}

View File

@@ -599,7 +599,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
public static contextType = MatrixClientContext;
public declare context: React.ContextType<typeof MatrixClientContext>;
private readonly dispatcherRef: string;
private dispatcherRef?: string;
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
@@ -621,12 +621,11 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
myMembership: this.props.space.getMyMembership(),
};
this.dispatcherRef = defaultDispatcher.register(this.onAction);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
}
public componentDidMount(): void {
this.dispatcherRef = defaultDispatcher.register(this.onAction);
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
this.context.on(RoomEvent.MyMembership, this.onMyMembership);
}

View File

@@ -77,8 +77,8 @@ export default class ThreadView extends React.Component<IProps, IState> {
public static contextType = RoomContext;
public declare context: React.ContextType<typeof RoomContext>;
private dispatcherRef: string | null = null;
private readonly layoutWatcherRef: string;
private dispatcherRef?: string;
private layoutWatcherRef?: string;
private timelinePanel = createRef<TimelinePanel>();
private card = createRef<HTMLDivElement>();
@@ -91,7 +91,6 @@ export default class ThreadView extends React.Component<IProps, IState> {
this.setEventId(this.props.mxEvent);
const thread = this.props.room.getThread(this.eventId) ?? undefined;
this.setupThreadListeners(thread);
this.state = {
layout: SettingsStore.getValue("layout"),
narrow: false,
@@ -100,13 +99,15 @@ export default class ThreadView extends React.Component<IProps, IState> {
return ev.isRelation(THREAD_RELATION_TYPE.name) && !ev.status;
}),
};
}
public componentDidMount(): void {
this.setupThreadListeners(this.state.thread);
this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, (...[, , , value]) =>
this.setState({ layout: value as Layout }),
);
}
public componentDidMount(): void {
if (this.state.thread) {
this.postThreadUpdate(this.state.thread);
}
@@ -118,7 +119,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
}
public componentWillUnmount(): void {
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
dis.unregister(this.dispatcherRef);
const roomId = this.props.mxEvent.getRoomId();
SettingsStore.unwatchSetting(this.layoutWatcherRef);

View File

@@ -248,7 +248,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
private lastRMSentEventId: string | null | undefined = undefined;
private readonly messagePanel = createRef<MessagePanel>();
private readonly dispatcherRef: string;
private dispatcherRef?: string;
private timelineWindow?: TimelineWindow;
private overlayTimelineWindow?: TimelineWindow;
private unmounted = false;
@@ -291,6 +291,10 @@ class TimelinePanel extends React.Component<IProps, IState> {
readMarkerInViewThresholdMs: SettingsStore.getValue("readMarkerInViewThresholdMs"),
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
};
}
public componentDidMount(): void {
this.unmounted = false;
this.dispatcherRef = dis.register(this.onAction);
const cli = MatrixClientPeg.safeGet();
@@ -312,9 +316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
cli.on(ClientEvent.Sync, this.onSync);
this.props.timelineSet.room?.on(ThreadEvent.Update, this.onThreadUpdate);
}
public componentDidMount(): void {
if (this.props.manageReadReceipts) {
this.updateReadReceiptOnUserActivity();
}

View File

@@ -24,12 +24,11 @@ export default class ToastContainer extends React.Component<{}, IState> {
toasts: ToastStore.sharedInstance().getToasts(),
countSeen: ToastStore.sharedInstance().getCountSeen(),
};
}
// Start listening here rather than in componentDidMount because
// toasts may dismiss themselves in their didMount if they find
// they're already irrelevant by the time they're mounted, and
// our own componentDidMount is too late.
public componentDidMount(): void {
ToastStore.sharedInstance().on("update", this.onToastStoreUpdate);
this.onToastStoreUpdate();
}
public componentWillUnmount(): void {

View File

@@ -46,7 +46,7 @@ function isUploadPayload(payload: ActionPayload): payload is UploadPayload {
export default class UploadBar extends React.PureComponent<IProps, IState> {
private dispatcherRef: Optional<string>;
private mounted = false;
private unmounted = false;
public constructor(props: IProps) {
super(props);
@@ -57,12 +57,12 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
}
public componentDidMount(): void {
this.unmounted = false;
this.dispatcherRef = dis.register(this.onAction);
this.mounted = true;
}
public componentWillUnmount(): void {
this.mounted = false;
this.unmounted = true;
dis.unregister(this.dispatcherRef!);
}
@@ -83,7 +83,7 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
}
private onAction = (payload: ActionPayload): void => {
if (!this.mounted) return;
if (this.unmounted) return;
if (isUploadPayload(payload)) {
this.setState(this.calculateState());
}

View File

@@ -96,9 +96,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
selectedSpace: SpaceStore.instance.activeSpaceRoom,
showLiveAvatarAddon: this.context.voiceBroadcastRecordingsStore.hasCurrent(),
};
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
}
private get hasHomePage(): boolean {
@@ -112,6 +109,8 @@ export default class UserMenu extends React.Component<IProps, IState> {
};
public componentDidMount(): void {
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
this.context.voiceBroadcastRecordingsStore.on(
VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
this.onCurrentVoiceBroadcastRecordingChanged,
@@ -121,9 +120,9 @@ export default class UserMenu extends React.Component<IProps, IState> {
}
public componentWillUnmount(): void {
if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef);
if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
SettingsStore.unwatchSetting(this.themeWatcherRef);
SettingsStore.unwatchSetting(this.dndWatcherRef);
defaultDispatcher.unregister(this.dispatcherRef);
OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate);
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
this.context.voiceBroadcastRecordingsStore.off(

View File

@@ -29,11 +29,15 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
public constructor(props: IProps) {
super(props);
const store = SetupEncryptionStore.sharedInstance();
store.on("update", this.onStoreUpdate);
store.start();
this.state = { phase: store.phase, lostKeys: store.lostKeys() };
}
public componentDidMount(): void {
const store = SetupEncryptionStore.sharedInstance();
store.on("update", this.onStoreUpdate);
}
private onStoreUpdate = (): void => {
const store = SetupEncryptionStore.sharedInstance();
this.setState({ phase: store.phase, lostKeys: store.lostKeys() });

Some files were not shown because too many files have changed in this diff Show More