Compare commits

..

59 Commits

Author SHA1 Message Date
Michael Telatynski
24d4ecf912 Iterate
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-19 12:40:01 +00:00
Michael Telatynski
9ef3ea630c Merge branch 'develop' of https://github.com/vector-im/element-web into t3chguy/react-compiler-eslint
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

# Conflicts:
#	yarn.lock
2024-12-19 11:57:34 +00:00
ElementRobot
2c4a079153 [create-pull-request] automated change (#28772)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-19 09:26:31 +00:00
renovate[bot]
9099338af8 Update dependency typescript to v5.7.2 (#28565)
* Update dependency typescript to v5.7.2

* Fix types

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

* Fix types

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-19 00:09:12 +00:00
Michael Telatynski
f621c342ff Update deploy.yml 2024-12-18 18:34:51 +00:00
Michael Telatynski
4c1924311f Add skip-checks 2024-12-18 18:34:22 +00:00
RiotRobot
a7e3764c27 Merge branch 'master' into develop 2024-12-18 17:21:34 +00:00
RiotRobot
07f1680ba0 v1.11.89 2024-12-18 17:18:31 +00:00
ElementRobot
3fbc9e6de6 Fix url preview display (#28765) (#28766)
(cherry picked from commit 117bee787f)

Co-authored-by: Florian Duros <florianduros@element.io>
2024-12-18 17:09:08 +00:00
Florian Duros
117bee787f Fix url preview display (#28765) 2024-12-18 16:42:37 +00:00
dependabot[bot]
580213da5d Bump nanoid from 3.3.7 to 3.3.8 (#28759)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-18 15:55:48 +00:00
ElementRobot
22530d6ea5 [create-pull-request] automated change (#28764)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-12-18 15:55:29 +00:00
RiotRobot
e7d9df24e2 Upgrade dependency to matrix-js-sdk@35.1.0 2024-12-18 14:12:15 +00:00
Michael Telatynski
95c879c9e5 Move room header info button to right-most position (#28754)
* Move room header info button to right-most position

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

* Update screenshots

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

* Iterate

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

* Update snapshot

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

* Update screenshots

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-18 09:55:59 +00:00
ElementRobot
cbc1838755 [create-pull-request] automated change (#28762)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-12-18 09:44:29 +00:00
ElementRobot
c2799a1812 [create-pull-request] automated change (#28761)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-18 06:16:59 +00:00
David Baker
980b922348 Enable key backup by default (#28691)
* Factor out crypto setup process into a store

To make components pure and avoid react 18 dev mode problems due
to components making requests when mounted.

* fix test

* test for the store

* Add comment

* Enable key backup by default

When we set up cross signing, so the key backup key will be stored locally along with the cross signing keys until the user sets up recovery (4s). This will mean that a user can restore their backup if they log in on a new device as long as they verify with the one they registered on.

Replaces https://github.com/element-hq/element-web/pull/28267

* Fix test

* Prompt user to set up 4S on logout

* Fix test

* Add playwright test for key backup by default

* Fix imports

* This isn't unexpected anymore

* Update doc

* Fix docs and function name on renderSetupBackupDialog()

* Use checkKeyBackupAndEnable

* Docs for setup encryption toast

* Also test the toast appears

* Update mock for the method we use now

* Okay fine I guess we need both

* Swap here too

* Fix comment & doc comments
2024-12-17 14:50:48 +00:00
RiotRobot
ad77f7943b v1.11.88 2024-12-17 13:32:35 +00:00
RiotRobot
89d7dca464 Upgrade dependency to matrix-js-sdk@35.0.0 2024-12-17 13:28:57 +00:00
Florian Duros
aa44cadb02 Enable message layout (#28753) 2024-12-17 13:25:04 +00:00
RiotRobot
941f4e1005 Reset matrix-js-sdk back to develop branch 2024-12-17 13:35:41 +00:00
RiotRobot
9b85c2d0fd Merge branch 'master' into develop 2024-12-17 13:35:32 +00:00
ElementRobot
1e0dfd0241 [create-pull-request] automated change (#28747)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-17 06:17:03 +00:00
Richard van der Hoff
bea1b8eb85 Add some logging to UserIdenitityWarning (#28734)
We had some reports of misbehaviour here, so adding a bit of looging to try to
track it down.
2024-12-16 11:11:29 +00:00
ElementRobot
d5db16ca24 [create-pull-request] automated change (#28740)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-14 06:17:53 +00:00
ElementRobot
edaf9773c0 [create-pull-request] automated change (#28725)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-12 10:21:33 +00:00
renovate[bot]
7ea188cf89 Update dependency @testing-library/react to v16.1.0 (#28716)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-12 10:26:58 +00:00
renovate[bot]
a581e776a8 Update dependency @sentry/browser to v8.42.0 (#28715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-12 00:53:43 +00:00
renovate[bot]
8d261d9819 Update mcr.microsoft.com/playwright Docker tag to v1.49.1 (#28706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-12 00:52:48 +00:00
Richard van der Hoff
299270e52d Move rust-sdk wasm artifact into bundles directory (#28718)
As of https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm/pull/167, the
wasm build of matrix-sdk-crypto is actually shipped as a `.wasm` file, rather
than base64-ed into Javascript. Our current webpack config then dumps it into
the top level of the build directory, which will be a problem on redeployment
(specifically, if you try to fetch the wasm artifact for vN after vN+1 has been
deployed, you'll get a 404 and sadness).

So, instead we use Webpack's experimental support for WASM-as-ES-module, which
makes Webpack put the wasm file in the bundle, along with everything else.

Fixes: https://github.com/element-hq/element-web/issues/28632
2024-12-11 22:49:41 +00:00
renovate[bot]
943b817194 Update dependency @babel/preset-react to v7.26.3 (#28714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 20:24:53 +00:00
Florian Duros
2aa72bb40b Update @vector-im/compound-web to 7.5.0 (#28700)
Co-authored-by: David Baker <dbkr@users.noreply.github.com>
2024-12-11 19:35:59 +00:00
David Baker
a755e399cf Change to en-US locale for date tests (#28723)
* Change to en-US locale for date tests

As per comment. Fixes the tests.

* Spell locale right

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-12-11 19:00:24 +00:00
renovate[bot]
8dff758153 Update definitelyTyped (#28704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 15:02:47 +00:00
David Baker
cf3bdbdc7a Fix Read Receipt Test (#28719)
* Update test snapshot

as the date formatting appears to have gained a comma, and somehow
got through the merge tests on the dependency bump.

* Actually this was the problem
2024-12-11 14:25:56 +00:00
renovate[bot]
ba98c2085d Update dependency @formatjs/intl-segmenter to v11.7.5 (#28713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 13:13:09 +00:00
David Baker
b330de5d6e Factor out crypto setup process into a store (#28675)
* Factor out crypto setup process into a store

To make components pure and avoid react 18 dev mode problems due
to components making requests when mounted.

* fix test

* test for the store

* Add comment
2024-12-11 13:10:27 +00:00
renovate[bot]
b86bb5cc2f Update guibranco/github-status-action-v2 digest to d469d49 (#28702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 02:01:31 +00:00
renovate[bot]
e835cab139 Update all non-major dependencies (#28703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 17:20:18 +00:00
RiotRobot
af3040fb62 v1.11.88-rc.0 2024-12-10 15:54:01 +00:00
RiotRobot
b6ba3335ec Upgrade dependency to matrix-js-sdk@35.0.0-rc.0 2024-12-10 15:50:25 +00:00
Hugh Nimmo-Smith
6b7c94905f Allow trusted Element Call widget to send and receive media encryption key to-device messages (#28316) 2024-12-10 12:05:30 +00:00
ElementRobot
a4e8bb3f9a [create-pull-request] automated change (#28696)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-10 06:17:13 +00:00
Hubert Chathi
2b4000d47f Add delay in test to allow Alice to fetch Bob's device keys (#28668)
* add delay in test to allow Alice to fetch Bob's device keys

* wait until we see bob's device, rather than hard-coding a timeout

* Fix comment

Co-authored-by: Florian Duros <florianduros@element.io>

* fix lint

---------

Co-authored-by: Florian Duros <florianduros@element.io>
2024-12-09 21:02:29 +00:00
Michael Telatynski
01304439ee Make tsc faster again (#28678)
* Stash initial work to bring TSC from over 6 mins to under 1 minute

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

* Stabilise types

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

* Fix incorrect props to AccessibleButton

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

* Swap AccessibleButton element types to match the props they provide

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

* Changed my mind, remove spurious previously ignored props

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

* Update snapshots

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-06 17:49:32 +00:00
David Baker
c659afa8db Rename CreateCrossSigningDialog to InitialCryptoSetupDialog (#28658)
* Rename CreateCrossSigningDialog to InitialCryptoSetup

because it will soon encompass things other than just creating cross
signing.

* Fix name & tests

* Fix import

* Remove code creating key backup

Because this was split out from my key backup by default PR

* Fix comment

* Convert to named export
2024-12-06 10:26:26 +00:00
ElementRobot
9cc5564d50 [create-pull-request] automated change (#28670)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-12-06 09:38:58 +00:00
Michael Telatynski
549300726f Update CODEOWNERS 2024-12-06 09:18:33 +00:00
ElementRobot
319dab5920 [create-pull-request] automated change (#28669)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-06 06:17:19 +00:00
Michael Telatynski
6946b90b11 Iterate
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 13:27:34 +00:00
Michael Telatynski
c3f3c9364f Fix MapMock
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 13:14:13 +00:00
Michael Telatynski
72f155640d Make React compiler happy about some frankly non-issues
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:59:53 +00:00
Michael Telatynski
b597abf567 Fix mutation of external values in hooks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:59:35 +00:00
Michael Telatynski
9443426edb Fix usage of useRef as memoization
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:57:39 +00:00
Michael Telatynski
6c2334c029 Merge branch 't3chguy/useId' of https://github.com/vector-im/element-web into t3chguy/react-compiler-eslint 2024-12-05 12:37:04 +00:00
Michael Telatynski
eb1a09a912 Disable in tests
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:32:36 +00:00
Michael Telatynski
e730074e1b Fix an easy one
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:31:33 +00:00
Michael Telatynski
c8c5ef5e6e Enable react-compiler eslint
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:31:18 +00:00
Michael Telatynski
b61a2225b7 Switch to React18 useId
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:28:27 +00:00
412 changed files with 2787 additions and 2443 deletions

View File

@@ -1,5 +1,5 @@
module.exports = {
plugins: ["matrix-org"],
plugins: ["matrix-org", "eslint-plugin-react-compiler"],
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"],
parserOptions: {
project: ["./tsconfig.json"],
@@ -170,6 +170,8 @@ module.exports = {
"jsx-a11y/role-supports-aria-props": "off",
"matrix-org/require-copyright-header": "error",
"react-compiler/react-compiler": "error",
},
overrides: [
{
@@ -262,6 +264,7 @@ module.exports = {
// These are fine in tests
"no-restricted-globals": "off",
"react-compiler/react-compiler": "off",
},
},
{

1
.github/CODEOWNERS vendored
View File

@@ -13,6 +13,7 @@
# Ignore translations as those will be updated by GHA for Localazy download
/src/i18n/strings
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
# Ignore the synapse plugin as this is updated by GHA for docker image updating
/playwright/plugins/homeserver/synapse/index.ts

View File

@@ -16,6 +16,11 @@ on:
options:
- staging.element.io
- app.element.io
skip-checks:
description: Skip CI on the tagged commit
required: true
default: false
type: boolean
concurrency: ${{ inputs.site || 'staging.element.io' }}
permissions: {}
jobs:
@@ -75,6 +80,7 @@ jobs:
- name: Wait for other steps to succeed
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
if: inputs.skip-checks != true
with:
ref: ${{ github.sha }}
running-workflow-name: "Deploy to Cloudflare Pages"

View File

@@ -104,7 +104,7 @@ jobs:
- name: Skip SonarCloud in merge queue
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
uses: guibranco/github-status-action-v2@66088c44e212a906c32a047529a213d81809ec1c
uses: guibranco/github-status-action-v2@d469d49426f5a7b8a1fbcac20ad274d3e4892321
with:
authToken: ${{ secrets.GITHUB_TOKEN }}
state: success

View File

@@ -1,3 +1,38 @@
Changes in [1.11.89](https://github.com/element-hq/element-web/releases/tag/v1.11.89) (2024-12-18)
==================================================================================================
This is a patch release to fix a bug which could prevent loading stored crypto state from storage, and also to fix URL previews when switching back to a room.
## 🐛 Bug Fixes
* Upgrade matrix-sdk-crypto-wasm to 1.11.0 (https://github.com/matrix-org/matrix-js-sdk/pull/4593)
* Fix url preview display ([#28766](https://github.com/element-hq/element-web/pull/28766)).
Changes in [1.11.88](https://github.com/element-hq/element-web/releases/tag/v1.11.88) (2024-12-17)
==================================================================================================
## ✨ Features
* Allow trusted Element Call widget to send and receive media encryption key to-device messages ([#28316](https://github.com/element-hq/element-web/pull/28316)). Contributed by @hughns.
* increase ringing timeout from 10 seconds to 90 seconds ([#28630](https://github.com/element-hq/element-web/pull/28630)). Contributed by @fkwp.
* Add `Close` tooltip to dialog ([#28617](https://github.com/element-hq/element-web/pull/28617)). Contributed by @florianduros.
* New UX for Share dialog ([#28598](https://github.com/element-hq/element-web/pull/28598)). Contributed by @florianduros.
* Improve performance of RoomContext in RoomHeader ([#28574](https://github.com/element-hq/element-web/pull/28574)). Contributed by @t3chguy.
* Remove `Features.RustCrypto` flag ([#28582](https://github.com/element-hq/element-web/pull/28582)). Contributed by @florianduros.
* Add Modernizr warning when running in non-secure context ([#28581](https://github.com/element-hq/element-web/pull/28581)). Contributed by @t3chguy.
## 🐛 Bug Fixes
* Fix jumpy timeline when the pinned message banner is displayed ([#28654](https://github.com/element-hq/element-web/pull/28654)). Contributed by @florianduros.
* Fix font \& spaces in settings subsection ([#28631](https://github.com/element-hq/element-web/pull/28631)). Contributed by @florianduros.
* Remove manual device verification which is not supported by the new cryptography stack ([#28588](https://github.com/element-hq/element-web/pull/28588)). Contributed by @florianduros.
* Fix code block highlighting not working reliably with many code blocks ([#28613](https://github.com/element-hq/element-web/pull/28613)). Contributed by @t3chguy.
* Remove remaining reply fallbacks code ([#28610](https://github.com/element-hq/element-web/pull/28610)). Contributed by @t3chguy.
* Provide a way to activate GIFs via the keyboard for a11y ([#28611](https://github.com/element-hq/element-web/pull/28611)). Contributed by @t3chguy.
* Fix format bar position ([#28591](https://github.com/element-hq/element-web/pull/28591)). Contributed by @florianduros.
* Fix room taking long time to load ([#28579](https://github.com/element-hq/element-web/pull/28579)). Contributed by @florianduros.
* Show the correct shield status in tooltip for more conditions ([#28476](https://github.com/element-hq/element-web/pull/28476)). Contributed by @uhoreg.
Changes in [1.11.87](https://github.com/element-hq/element-web/releases/tag/v1.11.87) (2024-12-03)
==================================================================================================
## ✨ Features

View File

@@ -17,6 +17,7 @@ class MockMap extends EventEmitter {
setCenter = jest.fn();
setStyle = jest.fn();
fitBounds = jest.fn();
remove = jest.fn();
}
const MockMapInstance = new MockMap();

View File

@@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.11.87",
"version": "1.11.89",
"description": "A feature-rich client for Matrix.org",
"author": "New Vector Ltd.",
"repository": {
@@ -71,13 +71,9 @@
"update:jitsi": "curl -s https://meet.element.io/libs/external_api.min.js > ./res/jitsi_external_api.min.js"
},
"resolutions": {
"@types/react": "19.0.0",
"@types/react-dom": "19.0.0",
"oidc-client-ts": "3.1.0",
"jwt-decode": "4.0.0",
"caniuse-lite": "1.0.30001684",
"react": "19.0.0",
"react-dom": "19.0.0",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
},
@@ -92,7 +88,7 @@
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^8.0.0",
"@vector-im/compound-design-tokens": "^2.0.1",
"@vector-im/compound-web": "^7.4.0",
"@vector-im/compound-web": "^7.5.0",
"@vector-im/matrix-wysiwyg": "2.37.13",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
@@ -139,10 +135,10 @@
"posthog-js": "1.157.2",
"qrcode": "1.5.4",
"re-resizable": "6.10.1",
"react": "^19",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.0",
"react-blurhash": "^0.3.0",
"react-dom": "^19",
"react-dom": "^18.3.1",
"react-focus-lock": "^2.5.1",
"react-transition-group": "^4.4.1",
"rfc4648": "^1.4.0",
@@ -183,7 +179,7 @@
"@svgr/webpack": "^8.0.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.1.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/commonmark": "^0.27.4",
"@types/counterpart": "^0.18.1",
@@ -205,9 +201,9 @@
"@types/node-fetch": "^2.6.2",
"@types/pako": "^2.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "^19",
"@types/react": "18.3.3",
"@types/react-beautiful-dnd": "^13.0.0",
"@types/react-dom": "^19",
"@types/react-dom": "18.3.1",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "2.13.0",
"@types/semver": "^7.5.8",
@@ -237,6 +233,7 @@
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-matrix-org": "^2.0.2",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-unicorn": "^56.0.0",
"express": "^4.18.2",
@@ -273,7 +270,7 @@
"postcss-preset-env": "^10.0.0",
"postcss-scss": "^4.0.4",
"postcss-simple-vars": "^7.0.1",
"prettier": "3.4.1",
"prettier": "3.4.2",
"process": "^0.11.10",
"raw-loader": "^4.0.2",
"rimraf": "^6.0.0",
@@ -286,7 +283,7 @@
"terser-webpack-plugin": "^5.3.9",
"ts-node": "^10.9.1",
"ts-prune": "^0.10.3",
"typescript": "5.6.3",
"typescript": "5.7.2",
"util": "^0.12.5",
"web-streams-polyfill": "^4.0.0",
"webpack": "^5.89.0",

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/playwright:v1.49.0-noble
FROM mcr.microsoft.com/playwright:v1.49.1-noble
WORKDIR /work

View File

@@ -9,6 +9,8 @@ Please see LICENSE files in the repository root for full details.
import { type Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { test as masTest, registerAccountMas } from "../oidc";
import { isDendrite } from "../../plugins/homeserver/dendrite";
async function expectBackupVersionToBe(page: Page, version: string) {
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText(
@@ -18,6 +20,32 @@ async function expectBackupVersionToBe(page: Page, version: string) {
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version);
}
masTest.describe("Encryption state after registration", () => {
masTest.skip(isDendrite, "does not yet support MAS");
masTest("Key backup is enabled by default", async ({ page, mailhog, app }) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
await app.settings.openUserSettings("Security & Privacy");
expect(page.getByText("This session is backing up your keys.")).toBeVisible();
});
masTest("user is prompted to set up recovery", async ({ page, mailhog, app }) => {
await page.goto("/#/login");
await page.getByRole("button", { name: "Continue" }).click();
await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!");
await page.getByRole("button", { name: "Add room" }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("test room");
await page.getByRole("button", { name: "Create room" }).click();
await expect(page.getByRole("heading", { name: "Set up recovery" })).toBeVisible();
});
});
test.describe("Backups", () => {
test.use({
displayName: "Hanako",

View File

@@ -8,11 +8,11 @@ Please see LICENSE files in the repository root for full details.
import { Locator, type Page } from "@playwright/test";
import { test as base, expect } from "../../element-web-test";
import { test as base, expect, Fixtures } from "../../element-web-test";
import { viewRoomSummaryByName } from "../right-panel/utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
const test = base.extend({
const test = base.extend<Fixtures>({
// eslint-disable-next-line no-empty-pattern
startHomeserverOpts: async ({}, use) => {
await use("dehydration");

View File

@@ -9,9 +9,9 @@ Please see LICENSE files in the repository root for full details.
import path from "path";
import { readFile } from "node:fs/promises";
import { expect, test as base } from "../../element-web-test";
import { expect, Fixtures, test as base } from "../../element-web-test";
const test = base.extend({
const test = base.extend<Fixtures>({
// Replace the `user` fixture with one which populates the indexeddb data before starting the app.
user: async ({ context, pageWithCredentials: page, credentials }, use) => {
await page.route(`/test_indexeddb_cryptostore_dump/*`, async (route, request) => {

View File

@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { doTwoWaySasVerification, awaitVerifier } from "./utils";
import { Client } from "../../pages/client";
@@ -38,6 +39,8 @@ test.describe("User verification", () => {
toasts,
room: { roomId: dmRoomId },
}) => {
await waitForDeviceKeys(page);
// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => {
@@ -87,6 +90,8 @@ test.describe("User verification", () => {
toasts,
room: { roomId: dmRoomId },
}) => {
await waitForDeviceKeys(page);
// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => {
@@ -149,3 +154,15 @@ async function createDMRoom(client: Client, userId: string): Promise<string> {
],
});
}
/**
* Wait until we get the other user's device keys.
* In newer rust-crypto versions, the verification request will be ignored if we
* don't have the sender's device keys.
*/
async function waitForDeviceKeys(page: Page): Promise<void> {
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = await page.getByRole("button", { name: "Avatar" });
await avatar.click();
await expect(page.getByText("1 session")).toBeVisible();
}

View File

@@ -71,7 +71,9 @@ test.describe("Room Header", () => {
// Assert the size of buttons on RoomHeader are specified and the buttons are not compressed
// Note these assertions do not check the size of mx_LegacyRoomHeader_name button
const buttons = header.locator(".mx_Flex").getByRole("button");
const buttons = header.getByRole("button").filter({
has: page.locator("svg"),
});
await expect(buttons).toHaveCount(5);
for (const button of await buttons.all()) {

View File

@@ -60,7 +60,7 @@ interface CredentialsWithDisplayName extends Credentials {
displayName: string;
}
export const test = base.extend<{
export interface Fixtures {
axe: AxeBuilder;
checkA11y: () => Promise<void>;
@@ -124,7 +124,9 @@ export const test = base.extend<{
slidingSyncProxy: ProxyInstance;
labsFlags: string[];
webserver: Webserver;
}>({
}
export const test = base.extend<Fixtures>({
config: CONFIG_JSON,
page: async ({ context, page, config, labsFlags }, use) => {
await context.route(`http://localhost:8080/config.json*`, async (route) => {

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:48308e18c5b3ad20bc0d090119618f45b6be4ba727522e37fbf7827d1a109531";
const DOCKER_TAG = "develop@sha256:c965896a4865479ab2628807ebf6d9c742586f3b6185a56f10077a408f1c7c3b";
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
const templateDir = path.join(__dirname, "templates", opts.template);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 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: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { JSXElementConstructor, type JSX } from "react";
import { JSXElementConstructor } from "react";
export type { NonEmptyArray, XOR, Writeable } from "matrix-js-sdk/src/matrix";

View File

@@ -44,6 +44,7 @@ import { IConfigOptions } from "../IConfigOptions";
import { MatrixDispatcher } from "../dispatcher/dispatcher";
import { DeepReadonly } from "./common";
import MatrixChat from "../components/structures/MatrixChat";
import { InitialCryptoSetupStore } from "../stores/InitialCryptoSetupStore";
/* eslint-disable @typescript-eslint/naming-convention */
@@ -117,6 +118,7 @@ declare global {
mxPerformanceEntryNames: any;
mxUIStore: UIStore;
mxSetupEncryptionStore?: SetupEncryptionStore;
mxInitialCryptoStore?: InitialCryptoSetupStore;
mxRoomScrollStateStore?: RoomScrollStateStore;
mxActiveWidgetStore?: ActiveWidgetStore;
mxOnRecaptchaLoaded?: () => void;

11
src/@types/react.d.ts vendored
View File

@@ -11,14 +11,9 @@ import React, { PropsWithChildren } from "react";
declare module "react" {
// Fix forwardRef types for Generic components - https://stackoverflow.com/a/58473012
function forwardRef<T, P = {}>(
render: (props: PropsWithChildren<P>, ref: React.ForwardedRef<T>) => React.ReactElement<any> | null,
): (props: P & React.RefAttributes<T>) => React.ReactElement<any> | null;
render: (props: PropsWithChildren<P>, ref: React.ForwardedRef<T>) => React.ReactElement | null,
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
// Fix lazy types - https://stackoverflow.com/a/71017028
// function lazy<T extends ComponentType<any>>(factory: () => Promise<{ default: T }>): T;
// Workaround for generics in React 19
interface FunctionComponent {
defaultProps?: {};
}
function lazy<T extends ComponentType<any>>(factory: () => Promise<{ default: T }>): T;
}

View File

@@ -16,18 +16,19 @@ import { _t } from "./languageHandler";
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";
/**
* Determine if the homeserver allows uploading device keys with only password auth.
* Determine if the homeserver allows uploading device keys with only password auth, or with no auth at
* all (ie. if the homeserver supports MSC3967).
* @param cli The Matrix Client to use
* @returns True if the homeserver allows uploading device keys with only password auth, otherwise false
* @returns True if the homeserver allows uploading device keys with only password auth or with no auth
* at all, otherwise false
*/
async function canUploadKeysWithPasswordOnly(cli: MatrixClient): Promise<boolean> {
try {
await cli.uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys);
// We should never get here: the server should always require
// UI auth to upload device signing keys. If we do, we upload
// no keys which would be a no-op.
logger.log("uploadDeviceSigningKeys unexpectedly succeeded without UI auth!");
return false;
// If we get here, it's because the server is allowing us to upload keys without
// auth the first time due to MSC3967. Therefore, yes, we can upload keys
// (with or without password, technically, but that's fine).
return true;
} catch (error) {
if (!(error instanceof MatrixError) || !error.data || !error.data.flows) {
logger.log("uploadDeviceSigningKeys advertised no flows!");

View File

@@ -295,21 +295,29 @@ export default class DeviceListener {
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
// cross signing isn't enabled - nag to enable it
// There are 2 different toasts for:
// There are 3 different toasts for:
if (!(await crypto.getCrossSigningKeyId()) && (await crypto.userHasCrossSigningKeys())) {
// Cross-signing on account but this device doesn't trust the master key (verify this session)
// Toast 1. Cross-signing on account but this device doesn't trust the master key (verify this session)
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
this.checkKeyBackupStatus();
} else {
// No cross-signing or key backup on account (set up encryption)
await cli.waitForClientWellKnown();
if (isSecureBackupRequired(cli) && isLoggedIn()) {
// If we're meant to set up, and Secure Backup is required,
// trigger the flow directly without a toast once logged in.
hideSetupEncryptionToast();
accessSecretStorage();
const backupInfo = await this.getKeyBackupInfo();
if (backupInfo) {
// Toast 2: Key backup is enabled but recovery (4S) is not set up: prompt user to set up recovery.
// Since we now enable key backup at registration time, this will be the common case for
// new users.
showSetupEncryptionToast(SetupKind.SET_UP_RECOVERY);
} else {
showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
// Toast 3: No cross-signing or key backup on account (set up encryption)
await cli.waitForClientWellKnown();
if (isSecureBackupRequired(cli) && isLoggedIn()) {
// If we're meant to set up, and Secure Backup is required,
// trigger the flow directly without a toast once logged in.
hideSetupEncryptionToast();
accessSecretStorage();
} else {
showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
}
}
}
}

View File

@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { Ref, ReactNode, type JSX } from "react";
import React, { LegacyRef, ReactNode } from "react";
import sanitizeHtml from "sanitize-html";
import classNames from "classnames";
import katex from "katex";
@@ -503,7 +503,7 @@ export function bodyToHtml(content: IContent, highlights: Optional<string[]>, op
export function topicToHtml(
topic?: string,
htmlTopic?: string,
ref?: Ref<HTMLSpanElement>,
ref?: LegacyRef<HTMLSpanElement>,
allowExtendedHtml = false,
): ReactNode {
if (!SettingsStore.getValue("feature_html_topic")) {

View File

@@ -205,7 +205,7 @@ export const sanitizeHtmlParams: IExtendedSanitizeOptions = {
};
/* Wrapper around linkify-react merging in our default linkify options */
export function Linkify({ as, options, children }: React.ComponentProps<typeof _Linkify>): ReactElement<any> {
export function Linkify({ as, options, children }: React.ComponentProps<typeof _Linkify>): ReactElement {
return (
<_Linkify as={as} options={merge({}, linkifyMatrixOptions, options)}>
{children}

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { HTMLAttributes, Key, MutableRefObject, ReactElement, RefCallback } from "react";
import React, { Key, MutableRefObject, ReactElement, RefCallback } from "react";
interface IChildProps {
style: React.CSSProperties;
@@ -23,7 +23,7 @@ interface IProps {
innerRef?: MutableRefObject<any>;
}
function isReactElement(c: ReturnType<(typeof React.Children)["toArray"]>[number]): c is ReactElement<any> {
function isReactElement(c: ReturnType<(typeof React.Children)["toArray"]>[number]): c is ReactElement {
return typeof c === "object" && "type" in c;
}
@@ -36,7 +36,7 @@ function isReactElement(c: ReturnType<(typeof React.Children)["toArray"]>[number
*/
export default class NodeAnimator extends React.Component<IProps> {
private nodes: Record<string, HTMLElement> = {};
private children: { [key: string]: ReactElement<any> } = {};
private children: { [key: string]: ReactElement } = {};
public static defaultProps: Partial<IProps> = {
startStyles: [],
};
@@ -68,22 +68,21 @@ export default class NodeAnimator extends React.Component<IProps> {
this.children = {};
React.Children.toArray(newChildren).forEach((c) => {
if (!isReactElement(c)) return;
const props = c.props as HTMLAttributes<HTMLElement>;
if (oldChildren[c.key!]) {
const old = oldChildren[c.key!];
const oldNode = this.nodes[old.key!];
if (oldNode && oldNode.style.left !== props.style!.left) {
this.applyStyles(oldNode, { left: props.style!.left });
if (oldNode && oldNode.style.left !== c.props.style.left) {
this.applyStyles(oldNode, { left: c.props.style.left });
}
// clone the old element with the props (and children) of the new element
// so prop updates are still received by the children.
this.children[c.key!] = React.cloneElement(old, props, props.children);
this.children[c.key!] = React.cloneElement(old, c.props, c.props.children);
} else {
// new element. If we have a startStyle, use that as the style and go through
// the enter animations
const newProps: Partial<IChildProps> = {};
const restingStyle = props.style!;
const restingStyle = c.props.style;
const startStyles = this.props.startStyles;
if (startStyles.length > 0) {

View File

@@ -212,7 +212,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
scrollIntoView,
onKeyDown,
}) => {
const [state, dispatch] = useReducer(reducer, {
const [state, dispatch] = useReducer<Reducer<IState, Action>>(reducer, {
nodes: [],
});
@@ -354,7 +354,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
* nodeRef = inputRef when inputRef argument is provided.
*/
export const useRovingTabIndex = <T extends HTMLElement>(
inputRef?: RefObject<T | null>,
inputRef?: RefObject<T>,
): [FocusHandler, boolean, RefCallback<T>, RefObject<T | null>] => {
const context = useContext(RovingTabIndexContext);
@@ -392,6 +392,7 @@ export const useRovingTabIndex = <T extends HTMLElement>(
});
}, []); // eslint-disable-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-compiler/react-compiler
const isActive = context.state.activeNode === nodeRef.current;
return [onFocus, isActive, ref, nodeRef];
};

View File

@@ -8,25 +8,24 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ComponentProps, forwardRef, Ref } from "react";
import React, { forwardRef, Ref } from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton";
type Props<T extends React.ElementType> = ComponentProps<typeof AccessibleButton<T>> & {
type Props<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & {
label?: string;
// whether the context menu is currently open
isExpanded: boolean;
};
// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuButton = forwardRef(function <T extends React.ElementType>(
{ label, isExpanded, children, onClick, onContextMenu, element, ...props }: Props<T>,
ref: Ref<HTMLElement>,
export const ContextMenuButton = forwardRef(function <T extends keyof HTMLElementTagNameMap>(
{ label, isExpanded, children, onClick, onContextMenu, ...props }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>,
) {
return (
<AccessibleButton
{...props}
element={element as React.ElementType}
onClick={onClick}
onContextMenu={onContextMenu ?? onClick ?? undefined}
aria-label={label}

View File

@@ -8,24 +8,23 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ComponentProps, forwardRef, Ref } from "react";
import React, { forwardRef, Ref } from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton";
type Props<T extends React.ElementType> = ComponentProps<typeof AccessibleButton<T>> & {
type Props<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & {
// whether the context menu is currently open
isExpanded: boolean;
};
// Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuTooltipButton = forwardRef(function <T extends React.ElementType>(
{ isExpanded, children, onClick, onContextMenu, element, ...props }: Props<T>,
ref: Ref<HTMLElement>,
export const ContextMenuTooltipButton = forwardRef(function <T extends keyof HTMLElementTagNameMap>(
{ isExpanded, children, onClick, onContextMenu, ...props }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>,
) {
return (
<AccessibleButton
{...props}
element={element as React.ElementType}
onClick={onClick}
onContextMenu={onContextMenu ?? onClick ?? undefined}
aria-haspopup={true}

View File

@@ -8,20 +8,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { RovingAccessibleButton } from "../RovingTabIndex";
type IProps<T extends keyof JSX.IntrinsicElements> = React.ComponentProps<typeof RovingAccessibleButton<T>> & {
interface IProps extends React.ComponentProps<typeof RovingAccessibleButton> {
label?: string;
};
}
// Semantic component for representing a role=menuitem
export const MenuItem = <T extends keyof JSX.IntrinsicElements>({
children,
label,
...props
}: IProps<T>): JSX.Element => {
export const MenuItem: React.FC<IProps> = ({ children, label, ...props }) => {
const ariaLabel = props["aria-label"] || label;
return (

View File

@@ -6,36 +6,33 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ComponentProps, type JSX } from "react";
import React, { RefObject } from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton";
import { useRovingTabIndex } from "../RovingTabIndex";
import { Ref } from "./types";
type Props<T extends React.ElementType> = Omit<ComponentProps<typeof AccessibleButton<T>>, "inputRef" | "tabIndex"> & {
inputRef?: Ref;
type Props<T extends keyof HTMLElementTagNameMap> = Omit<ButtonProps<T>, "tabIndex"> & {
inputRef?: RefObject<HTMLElementTagNameMap[T]>;
focusOnMouseOver?: boolean;
};
// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
export const RovingAccessibleButton = <T extends React.ElementType>({
export const RovingAccessibleButton = <T extends keyof HTMLElementTagNameMap>({
inputRef,
onFocus,
onMouseOver,
focusOnMouseOver,
element,
...props
}: Props<T>): JSX.Element => {
const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef);
const [onFocusInternal, isActive, ref] = useRovingTabIndex<HTMLElementTagNameMap[T]>(inputRef);
return (
<AccessibleButton
{...props}
element={element as React.ElementType}
onFocus={(event: React.FocusEvent) => {
onFocus={(event: React.FocusEvent<never, never>) => {
onFocusInternal();
onFocus?.(event);
}}
onMouseOver={(event: React.MouseEvent) => {
onMouseOver={(event: React.MouseEvent<never, never>) => {
if (focusOnMouseOver) onFocusInternal();
onMouseOver?.(event);
}}

View File

@@ -8,6 +8,6 @@ Please see LICENSE files in the repository root for full details.
import { RefObject } from "react";
export type Ref = RefObject<HTMLElement | null>;
export type Ref = RefObject<HTMLElement>;
export type FocusHandler = () => void;

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ReactNode, type JSX } from "react";
import React, { ReactNode } from "react";
import { Text, Heading, Button, Separator } from "@vector-im/compound-web";
import PopOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/pop-out";

View File

@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";

View File

@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef, type JSX } from "react";
import React, { createRef } from "react";
import FileSaver from "file-saver";
import { logger } from "matrix-js-sdk/src/logger";
import { AuthDict, CrossSigningKeys, MatrixError, UIAFlow, UIAResponse } from "matrix-js-sdk/src/matrix";

View File

@@ -178,9 +178,7 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
type="password"
disabled={disableForm}
autoComplete="new-password"
fieldRef={(field) => {
this.fieldPassword = field;
}}
fieldRef={(field) => (this.fieldPassword = field)}
/>
</div>
<div className="mx_E2eKeysDialog_inputRow">
@@ -197,9 +195,7 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
type="password"
disabled={disableForm}
autoComplete="new-password"
fieldRef={(field) => {
this.fieldPasswordConfirm = field;
}}
fieldRef={(field) => (this.fieldPasswordConfirm = field)}
/>
</div>
</div>

View File

@@ -30,7 +30,7 @@ export interface ICompletion {
type?: "at-room" | "command" | "community" | "room" | "user";
completion: string;
completionId?: string;
component: ReactElement<any>;
component: ReactElement;
range: ISelectionRange;
command?: string;
suffix?: string;

View File

@@ -8,11 +8,16 @@ Please see LICENSE files in the repository root for full details.
*/
import classNames from "classnames";
import React, { ReactNode, WheelEvent } from "react";
import React, { HTMLAttributes, ReactHTML, ReactNode, WheelEvent } from "react";
export type IProps<T extends React.ElementType> = React.ComponentPropsWithoutRef<T> & {
element?: T;
type DynamicHtmlElementProps<T extends keyof JSX.IntrinsicElements> =
JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps<T> : DynamicElementProps<"div">;
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<Omit<JSX.IntrinsicElements[T], "ref">>;
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<DynamicHtmlElementProps<T>, "onScroll"> & {
element: T;
className?: string;
onScroll?: (event: Event) => void;
onWheel?: (event: WheelEvent) => void;
style?: React.CSSProperties;
tabIndex?: number;
@@ -20,8 +25,12 @@ export type IProps<T extends React.ElementType> = React.ComponentPropsWithoutRef
children: ReactNode;
};
export default class AutoHideScrollbar<T extends React.ElementType> extends React.Component<IProps<T>> {
public readonly containerRef: React.RefObject<HTMLDivElement | null> = React.createRef();
export default class AutoHideScrollbar<T extends keyof JSX.IntrinsicElements> extends React.Component<IProps<T>> {
public static defaultProps = {
element: "div" as keyof ReactHTML,
};
public readonly containerRef: React.RefObject<HTMLDivElement> = React.createRef();
public componentDidMount(): void {
if (this.containerRef.current && this.props.onScroll) {
@@ -46,7 +55,7 @@ export default class AutoHideScrollbar<T extends React.ElementType> extends Reac
const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props;
return React.createElement(
element ?? "div",
element,
{
...otherProps,
ref: this.containerRef,

View File

@@ -22,8 +22,8 @@ interface AutocompleteInputProps {
selection: ICompletion[];
onSelectionChange: (selection: ICompletion[]) => void;
maxSuggestions?: number;
renderSuggestion?: (s: ICompletion) => ReactElement<any>;
renderSelection?: (m: ICompletion) => ReactElement<any>;
renderSuggestion?: (s: ICompletion) => ReactElement;
renderSelection?: (m: ICompletion) => ReactElement;
additionalFilter?: (suggestion: ICompletion) => boolean;
}
@@ -142,6 +142,7 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
{isFocused && suggestions.length ? (
<div
className="mx_AutocompleteInput_matches"
// eslint-disable-next-line react-compiler/react-compiler
style={{ top: editorContainerRef.current?.clientHeight }}
data-testid="autocomplete-matches"
>
@@ -163,11 +164,11 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
type SelectionItemProps = {
item: ICompletion;
onClick: (completion: ICompletion) => void;
render?: (completion: ICompletion) => ReactElement<any>;
render?: (completion: ICompletion) => ReactElement;
};
const SelectionItem: React.FC<SelectionItemProps> = ({ item, onClick, render }) => {
const withContainer = (children: ReactNode): ReactElement<any> => (
const withContainer = (children: ReactNode): ReactElement => (
<span
className="mx_AutocompleteInput_editor_selection"
data-testid={`autocomplete-selection-item-${item.completionId}`}
@@ -194,7 +195,7 @@ type SuggestionItemProps = {
item: ICompletion;
selection: ICompletion[];
onClick: (completion: ICompletion) => void;
render?: (completion: ICompletion) => ReactElement<any>;
render?: (completion: ICompletion) => ReactElement;
};
const SuggestionItem: React.FC<SuggestionItemProps> = ({ item, selection, onClick, render }) => {
@@ -204,7 +205,7 @@ const SuggestionItem: React.FC<SuggestionItemProps> = ({ item, selection, onClic
"mx_AutocompleteInput_suggestion--selected": isSelected,
});
const withContainer = (children: ReactNode): ReactElement<any> => (
const withContainer = (children: ReactNode): ReactElement => (
<div
className={classes}
// `onClick` cannot be used here as it would lead to focus loss and closing the suggestion list.

View File

@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { CSSProperties, RefObject, SyntheticEvent, useRef, useState, type JSX } from "react";
import React, { CSSProperties, RefObject, SyntheticEvent, useRef, useState } from "react";
import ReactDOM from "react-dom";
import classNames from "classnames";
import FocusLock from "react-focus-lock";
@@ -440,7 +440,7 @@ export default class ContextMenu extends React.PureComponent<React.PropsWithChil
);
}
public render(): JSX.Element {
public render(): React.ReactChild {
if (this.props.mountAsChild) {
// Render as a child of the current parent
return this.renderMenu();
@@ -582,13 +582,13 @@ export const alwaysAboveRightOf = (
type ContextMenuTuple<T> = [
boolean,
RefObject<T | null>,
RefObject<T>,
(ev?: SyntheticEvent) => void,
(ev?: SyntheticEvent) => void,
(val: boolean) => void,
];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject<T | null>): ContextMenuTuple<T> => {
export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject<T>): ContextMenuTuple<T> => {
let button = useRef<T>(null);
if (inputRef) {
// if we are given a ref, use it instead of ours
@@ -607,6 +607,7 @@ export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject
setIsOpen(false);
};
// eslint-disable-next-line react-compiler/react-compiler
return [button.current ? isOpen : false, button, open, close, setIsOpen];
};

View File

@@ -40,8 +40,8 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
private unmounted = false;
private dispatcherRef?: string;
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
this.state = {
page: "",

View File

@@ -286,9 +286,7 @@ class FilePanel extends React.Component<IProps, IState> {
ref={this.card}
header={_t("right_panel|files_button")}
>
{this.card.current && (
<Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />
)}
<Measured sensor={this.card} onMeasurement={this.onMeasurement} />
<SearchWarning isRoomEncrypted={isRoomEncrypted} kind={WarningKind.Files} />
<TimelinePanel
manageReadReceipts={false}

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import classNames from "classnames";
import React, { FunctionComponent, Key, PropsWithChildren, ReactNode, type JSX } from "react";
import React, { FunctionComponent, Key, PropsWithChildren, ReactNode } from "react";
import { MenuItemRadio } from "../../accessibility/context_menu/MenuItemRadio";
import { ButtonEvent } from "../views/elements/AccessibleButton";

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import * as React from "react";
import { useContext, useState, type JSX } from "react";
import { useContext, useState } from "react";
import AutoHideScrollbar from "./AutoHideScrollbar";
import { getHomePageUrl } from "../../utils/pages";

View File

@@ -5,12 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ComponentProps, createRef } from "react";
import React, { createRef } from "react";
import AutoHideScrollbar from "./AutoHideScrollbar";
import AutoHideScrollbar, { IProps as AutoHideScrollbarProps } from "./AutoHideScrollbar";
import UIStore, { UI_EVENTS } from "../../stores/UIStore";
export type IProps<T extends React.ElementType> = Omit<ComponentProps<typeof AutoHideScrollbar<T>>, "onWheel"> & {
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<AutoHideScrollbarProps<T>, "onWheel" | "element"> & {
element?: T;
// If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator
// and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning
// by the parent element.
@@ -29,7 +30,10 @@ interface IState {
rightIndicatorOffset: string;
}
export default class IndicatorScrollbar<T extends React.ElementType> extends React.Component<IProps<T>, IState> {
export default class IndicatorScrollbar<T extends keyof JSX.IntrinsicElements> extends React.Component<
IProps<T>,
IState
> {
private autoHideScrollbar = createRef<AutoHideScrollbar<any>>();
private scrollElement?: HTMLDivElement;
private likelyTrackpadUser: boolean | null = null;

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import * as React from "react";
import { createRef, type JSX } from "react";
import { createRef } from "react";
import classNames from "classnames";
import dis from "../../dispatcher/dispatcher";

View File

@@ -123,9 +123,9 @@ class LoggedInView extends React.Component<IProps, IState> {
public static displayName = "LoggedInView";
protected readonly _matrixClient: MatrixClient;
protected readonly _roomView: React.RefObject<RoomView | null>;
protected readonly _resizeContainer: React.RefObject<HTMLDivElement | null>;
protected readonly resizeHandler: React.RefObject<HTMLDivElement | null>;
protected readonly _roomView: React.RefObject<RoomView>;
protected readonly _resizeContainer: React.RefObject<HTMLDivElement>;
protected readonly resizeHandler: React.RefObject<HTMLDivElement>;
protected layoutWatcherRef?: string;
protected compactLayoutWatcherRef?: string;
protected backgroundImageWatcherRef?: string;

View File

@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ReactNode, type JSX } from "react";
import React, { ReactNode } from "react";
import { NumberSize, Resizable } from "re-resizable";
import { Direction } from "re-resizable/lib/resizer";
import { WebPanelResize } from "@matrix-org/analytics-events/types/typescript/WebPanelResize";

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef, lazy, type JSX } from "react";
import React, { createRef, lazy } from "react";
import {
ClientEvent,
createClient,
@@ -132,6 +132,7 @@ import { SessionLockStolenView } from "./auth/SessionLockStolenView";
import { ConfirmSessionLockTheftView } from "./auth/ConfirmSessionLockTheftView";
import { LoginSplashView } from "./auth/LoginSplashView";
import { cleanUpDraftsIfRequired } from "../../DraftCleaner";
import { InitialCryptoSetupStore } from "../../stores/InitialCryptoSetupStore";
// legacy export
export { default as Views } from "../../Views";
@@ -428,6 +429,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
!(await shouldSkipSetupEncryption(cli))
) {
// if cross-signing is not yet set up, do so now if possible.
InitialCryptoSetupStore.sharedInstance().startInitialCryptoSetup(
cli,
Boolean(this.tokenLogin),
this.stores,
this.onCompleteSecurityE2eSetupFinished,
);
this.setStateForNewView({ view: Views.E2E_SETUP });
} else {
this.onLoggedIn();
@@ -2073,14 +2080,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} else if (this.state.view === Views.COMPLETE_SECURITY) {
view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.E2E_SETUP) {
view = (
<E2eSetup
matrixClient={MatrixClientPeg.safeGet()}
onFinished={this.onCompleteSecurityE2eSetupFinished}
accountPassword={this.stores.accountPasswordStore.getPassword()}
tokenLogin={!!this.tokenLogin}
/>
);
view = <E2eSetup onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.LOGGED_IN) {
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
// latter is set via the dispatcher). If we don't yet have a `page_type`,

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef, ReactNode, TransitionEvent, type JSX } from "react";
import React, { createRef, ReactNode, TransitionEvent } from "react";
import classNames from "classnames";
import { Room, MatrixClient, RoomStateEvent, EventStatus, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
@@ -252,8 +252,8 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
public grouperKeyMap = new WeakMap<MatrixEvent, string>();
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof RoomContext>) {
super(props, context);
this.state = {
// previous positions the read marker has been in, so we can

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { logger } from "matrix-js-sdk/src/logger";
import NotificationsIcon from "@vector-im/compound-design-tokens/assets/web/icons/notifications";
@@ -38,8 +38,8 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
private card = React.createRef<HTMLDivElement>();
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof RoomContext>) {
super(props, context);
this.state = {
narrow: false,
@@ -95,7 +95,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
onClose={this.props.onClose}
withoutScrollContainer={true}
>
{this.card.current && <Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />}
<Measured sensor={this.card} onMeasurement={this.onMeasurement} />
{content}
</BaseCard>
</ScopedRoomContextProvider>

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef, type JSX } from "react";
import React, { createRef } from "react";
import UIStore, { UI_EVENTS } from "../../stores/UIStore";
import { lerp } from "../../utils/AnimationUtils";

View File

@@ -280,7 +280,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
}
export const PipContainer: React.FC = () => {
const movePersistedElement = useRef<() => void>(undefined);
const movePersistedElement = useRef<() => void>();
return <PipContainerInner movePersistedElement={movePersistedElement} />;
};

View File

@@ -65,8 +65,8 @@ export default class RightPanel extends React.Component<Props, IState> {
public static contextType = MatrixClientContext;
declare public context: React.ContextType<typeof MatrixClientContext>;
public constructor(props: Props) {
super(props);
public constructor(props: Props, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
this.state = {
searchQuery: "",

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { forwardRef, useCallback, useContext, useEffect, useRef, useState, type JSX } from "react";
import React, { forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import {
ISearchResults,
IThreadBundledRelationship,
@@ -58,8 +58,8 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
const [results, setResults] = useState<ISearchResults | null>(null);
const aborted = useRef(false);
// A map from room ID to permalink creator
const permalinkCreators = useRef(new Map<string, RoomPermalinkCreator>()).current;
const innerRef = useRef<ScrollPanel | null>(undefined);
const permalinkCreators = useMemo(() => new Map<string, RoomPermalinkCreator>(), []);
const innerRef = useRef<ScrollPanel | null>();
useEffect(() => {
return () => {

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ReactNode, type JSX } from "react";
import React, { ReactNode } from "react";
import {
ClientEvent,
EventStatus,
@@ -91,8 +91,8 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
public static contextType = MatrixClientContext;
declare public context: React.ContextType<typeof MatrixClientContext>;
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
this.state = {
syncState: this.context.getSyncState(),

View File

@@ -15,10 +15,10 @@ interface RoomStatusBarUnsentMessagesProps {
title: ReactNode;
description?: string;
notificationState: StaticNotificationState;
buttons: ReactElement<any>;
buttons: ReactElement;
}
export const RoomStatusBarUnsentMessages = (props: RoomStatusBarUnsentMessagesProps): ReactElement<any> => {
export const RoomStatusBarUnsentMessages = (props: RoomStatusBarUnsentMessagesProps): ReactElement => {
return (
<div className="mx_RoomStatusBar mx_RoomStatusBar_unsentMessages">
<div role="alert">

View File

@@ -251,7 +251,7 @@ interface LocalRoomViewProps {
localRoom: LocalRoom;
resizeNotifier: ResizeNotifier;
permalinkCreator: RoomPermalinkCreator;
roomView: RefObject<HTMLElement | null>;
roomView: RefObject<HTMLElement>;
onFileDrop: (dataTransfer: DataTransfer) => Promise<void>;
mainSplitContentType: MainSplitContentType;
}
@@ -262,7 +262,7 @@ interface LocalRoomViewProps {
* @param {LocalRoomViewProps} props Room view props
* @returns {ReactElement}
*/
function LocalRoomView(props: LocalRoomViewProps): ReactElement<any> {
function LocalRoomView(props: LocalRoomViewProps): ReactElement {
const context = useScopedRoomContext("room");
const room = context.room as LocalRoom;
const encryptionEvent = props.localRoom.currentState.getStateEvents(EventType.RoomEncryption)[0];
@@ -273,6 +273,7 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement<any> {
}
const onRetryClicked = (): void => {
// eslint-disable-next-line react-compiler/react-compiler
room.state = LocalRoomState.NEW;
defaultDispatcher.dispatch({
action: "local_room_event",
@@ -280,8 +281,8 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement<any> {
});
};
let statusBar: ReactElement<any> | null = null;
let composer: ReactElement<any> | null = null;
let statusBar: ReactElement | null = null;
let composer: ReactElement | null = null;
if (room.isError) {
const buttons = (
@@ -340,7 +341,7 @@ interface ILocalRoomCreateLoaderProps {
* @param {ILocalRoomCreateLoaderProps} props Room view props
* @return {ReactElement}
*/
function LocalRoomCreateLoader(props: ILocalRoomCreateLoaderProps): ReactElement<any> {
function LocalRoomCreateLoader(props: ILocalRoomCreateLoaderProps): ReactElement {
const text = _t("room|creating_room_text", { names: props.names });
return (
<div className="mx_RoomView mx_RoomView--local">
@@ -374,16 +375,16 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
public static contextType = SDKContext;
declare public context: React.ContextType<typeof SDKContext>;
public constructor(props: IRoomProps) {
super(props);
public constructor(props: IRoomProps, context: React.ContextType<typeof SDKContext>) {
super(props, context);
this.askToJoinEnabled = SettingsStore.getValue("feature_ask_to_join");
if (!this.context.client) {
if (!context.client) {
throw new Error("Unable to create RoomView without MatrixClient");
}
const llMembers = this.context.client.hasLazyLoadMembersEnabled();
const llMembers = context.client.hasLazyLoadMembersEnabled();
this.state = {
roomId: undefined,
roomLoading: true,
@@ -417,7 +418,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
showJoinLeaves: true,
showAvatarChanges: true,
showDisplaynameChanges: true,
matrixClientIsReady: this.context.client?.isInitialSyncComplete(),
matrixClientIsReady: context.client?.isInitialSyncComplete(),
mainSplitContentType: MainSplitContentType.Timeline,
timelineRenderingType: TimelineRenderingType.Room,
liveTimeline: undefined,
@@ -2514,9 +2515,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
mainSplitContentClassName = "mx_MainSplit_timeline";
mainSplitBody = (
<>
{this.roomViewBody.current && (
<Measured sensor={this.roomViewBody.current} onMeasurement={this.onMeasurement} />
)}
<Measured sensor={this.roomViewBody} onMeasurement={this.onMeasurement} />
{auxPanel}
{pinnedMessageBanner}
<main className={timelineClasses}>

View File

@@ -19,7 +19,6 @@ import React, {
useMemo,
useRef,
useState,
type JSX,
} from "react";
import {
Room,
@@ -135,7 +134,7 @@ const Tile: React.FC<ITileProps> = ({
}
};
let button: ReactElement<any>;
let button: ReactElement;
if (busy) {
button = (
<AccessibleButton
@@ -170,7 +169,7 @@ const Tile: React.FC<ITileProps> = ({
);
}
let checkbox: ReactElement<any> | undefined;
let checkbox: ReactElement | undefined;
if (onToggleClick) {
if (hasPermissions) {
checkbox = <StyledCheckbox checked={!!selected} onChange={onToggleClick} tabIndex={isActive ? 0 : -1} />;
@@ -188,7 +187,7 @@ const Tile: React.FC<ITileProps> = ({
}
}
let avatar: ReactElement<any>;
let avatar: ReactElement;
if (joinedRoom) {
avatar = <RoomAvatar room={joinedRoom} size="20px" />;
} else {
@@ -234,12 +233,12 @@ const Tile: React.FC<ITileProps> = ({
);
}
let joinedSection: ReactElement<any> | undefined;
let joinedSection: ReactElement | undefined;
if (joinedRoom) {
joinedSection = <div className="mx_SpaceHierarchy_roomTile_joined">{_t("common|joined")}</div>;
}
let suggestedSection: ReactElement<any> | undefined;
let suggestedSection: ReactElement | undefined;
if (suggested && (!joinedRoom || hasPermissions)) {
suggestedSection = <InfoTooltip tooltip={_t("space|suggested_tooltip")}>{_t("space|suggested")}</InfoTooltip>;
}
@@ -620,7 +619,7 @@ const useIntersectionObserver = (callback: () => void): ((element: HTMLDivElemen
}
};
const observerRef = useRef<IntersectionObserver>(undefined);
const observerRef = useRef<IntersectionObserver>();
return (element: HTMLDivElement) => {
if (observerRef.current) {
observerRef.current.disconnect();

View File

@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import { EventType, RoomType, JoinRule, Preset, Room, RoomEvent } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger";
import React, { useCallback, useContext, useRef, useState, type JSX } from "react";
import React, { useCallback, useContext, useRef, useState } from "react";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import createRoom, { IOpts } from "../../createRoom";
@@ -601,13 +601,13 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
private dispatcherRef?: string;
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
let phase = Phase.Landing;
const creator = this.props.space.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
const showSetup = this.props.justCreatedOpts && this.context.getSafeUserId() === creator;
const showSetup = this.props.justCreatedOpts && context.getSafeUserId() === creator;
if (showSetup) {
phase =

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import classNames from "classnames";
import React, { DetailedHTMLProps, HTMLAttributes, ReactNode, type JSX } from "react";
import React, { DetailedHTMLProps, HTMLAttributes, ReactNode } from "react";
interface Props extends DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> {
className?: string;

View File

@@ -18,8 +18,6 @@ import { NonEmptyArray } from "../../@types/common";
import { RovingAccessibleButton, RovingTabIndexProvider } from "../../accessibility/RovingTabIndex";
import { useWindowWidth } from "../../hooks/useWindowWidth";
import type { JSX } from "react";
/**
* Represents a tab for the TabbedView.
*/

View File

@@ -204,7 +204,7 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
ref={card}
closeButtonRef={closeButonRef}
>
{card.current && <Measured sensor={card.current} onMeasurement={setNarrow} />}
<Measured sensor={card} onMeasurement={setNarrow} />
{timelineSet ? (
<TimelinePanel
key={filterOption + ":" + (timelineSet.getFilter()?.filterId ?? roomId)}

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef, KeyboardEvent, type JSX } from "react";
import React, { createRef, KeyboardEvent } from "react";
import {
Thread,
THREAD_RELATION_TYPE,
@@ -86,8 +86,8 @@ export default class ThreadView extends React.Component<IProps, IState> {
// Set by setEventId in ctor.
private eventId!: string;
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof RoomContext>) {
super(props, context);
this.setEventId(this.props.mxEvent);
const thread = this.props.room.getThread(this.eventId) ?? undefined;
@@ -443,7 +443,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
PosthogTrackers.trackInteraction("WebThreadViewBackButton", ev);
}}
>
{this.card.current && <Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />}
<Measured sensor={this.card} onMeasurement={this.onMeasurement} />
<div className="mx_ThreadView_timelinePanelWrapper">{timeline}</div>
{ContentMessages.sharedInstance().getCurrentUploads(threadRelation).length > 0 && (

View File

@@ -259,8 +259,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
private callEventGroupers = new Map<string, LegacyCallEventGrouper>();
private initialReadMarkerId: string | null = null;
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof RoomContext>) {
super(props, context);
debuglog("mounting");

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { createRef, ReactNode, type JSX } from "react";
import React, { createRef, ReactNode } from "react";
import { Room } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../MatrixClientPeg";
@@ -81,10 +81,10 @@ export default class UserMenu extends React.Component<IProps, IState> {
private dispatcherRef?: string;
private themeWatcherRef?: string;
private readonly dndWatcherRef?: string;
private buttonRef: React.RefObject<HTMLButtonElement | null> = createRef();
private buttonRef: React.RefObject<HTMLButtonElement> = createRef();
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {
super(props, context);
this.state = {
contextMenuPosition: null,

View File

@@ -34,8 +34,8 @@ export default class UserView extends React.Component<IProps, IState> {
public static contextType = MatrixClientContext;
declare public context: React.ContextType<typeof MatrixClientContext>;
public constructor(props: IProps) {
super(props);
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
this.state = {
loading: true,
};

View File

@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import SyntaxHighlight from "../views/elements/SyntaxHighlight";

View File

@@ -21,7 +21,7 @@ import SdkConfig from "../../SdkConfig";
import { useScopedRoomContext } from "../../contexts/ScopedRoomContext.tsx";
interface Props {
roomView: RefObject<HTMLElement | null>;
roomView: RefObject<HTMLElement>;
resizeNotifier: ResizeNotifier;
inviteEvent: MatrixEvent;
}

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";

View File

@@ -7,17 +7,13 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import AuthPage from "../../views/auth/AuthPage";
import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody";
import CreateCrossSigningDialog from "../../views/dialogs/security/CreateCrossSigningDialog";
import { InitialCryptoSetupDialog } from "../../views/dialogs/security/InitialCryptoSetupDialog";
interface IProps {
matrixClient: MatrixClient;
onFinished: () => void;
accountPassword?: string;
tokenLogin: boolean;
}
export default class E2eSetup extends React.Component<IProps> {
@@ -25,12 +21,7 @@ export default class E2eSetup extends React.Component<IProps> {
return (
<AuthPage>
<CompleteSecurityBody>
<CreateCrossSigningDialog
matrixClient={this.props.matrixClient}
onFinished={this.props.onFinished}
accountPassword={this.props.accountPassword}
tokenLogin={this.props.tokenLogin}
/>
<InitialCryptoSetupDialog onFinished={this.props.onFinished} />
</CompleteSecurityBody>
</AuthPage>
);

View File

@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ReactNode, type JSX } from "react";
import React, { ReactNode } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { sleep } from "matrix-js-sdk/src/utils";
import { LockSolidIcon, CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
@@ -388,9 +388,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
label={_td("auth|change_password_new_label")}
value={this.state.password}
minScore={PASSWORD_MIN_SCORE}
fieldRef={(field) => {
this.fieldPassword = field;
}}
fieldRef={(field) => (this.fieldPassword = field)}
onChange={this.onInputChanged.bind(this, "password")}
autoComplete="new-password"
/>
@@ -401,9 +399,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
labelInvalid={_td("auth|reset_password|passwords_mismatch")}
value={this.state.password2}
password={this.state.password}
fieldRef={(field) => {
this.fieldPasswordConfirm = field;
}}
fieldRef={(field) => (this.fieldPasswordConfirm = field)}
onChange={this.onInputChanged.bind(this, "password2")}
autoComplete="new-password"
/>

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { ReactNode, type JSX } from "react";
import React, { ReactNode } from "react";
import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger";
import { SSOFlow, SSOAction } from "matrix-js-sdk/src/matrix";

View File

@@ -20,7 +20,7 @@ import {
SSOAction,
RegisterResponse,
} from "matrix-js-sdk/src/matrix";
import React, { Fragment, ReactNode, type JSX } from "react";
import React, { Fragment, ReactNode } from "react";
import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger";

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import SplashPage from "../SplashPage";
import { _t } from "../../../languageHandler";

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import React from "react";
import { KeyBackupInfo, VerificationRequest } from "matrix-js-sdk/src/crypto-api";
import { logger } from "matrix-js-sdk/src/logger";
import { SecretStorageKeyDescription } from "matrix-js-sdk/src/secret-storage";

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