diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000000..21ebc70c27c --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,30 @@ +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + branches: + - develop + +jobs: + backport: + name: Backport + runs-on: ubuntu-latest + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport') + ) + ) + steps: + - uses: tibdex/backport@v2 + with: + labels_template: "<%= JSON.stringify(labels) %>" + # We can't use GITHUB_TOKEN here or CI won't run on the new PR + github_token: ${{ secrets.ELEMENT_BOT_TOKEN }} diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index 98cf39a1568..b111cdcfb5f 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -79,3 +79,38 @@ jobs: - name: Dead Code Analysis run: "yarn run analyse:unused-exports" + + tsc-strict: + name: Typescript Strict Error Checker + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + pull-requests: read + checks: write + steps: + - uses: actions/checkout@v3 + + - name: Get diff lines + id: diff + uses: Equip-Collaboration/diff-line-numbers@v1.0.0 + with: + include: '["\\.tsx?$"]' + + - name: Detecting files changed + id: files + uses: futuratrepadeira/changed-files@v3.2.1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + pattern: '^.*\.tsx?$' + + - uses: t3chguy/typescript-check-action@main + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + use-check: false + check-fail-mode: added + output-behaviour: annotate + ts-extra-args: '--strict' + files-changed: ${{ steps.files.outputs.files_updated }} + files-added: ${{ steps.files.outputs.files_created }} + files-deleted: ${{ steps.files.outputs.files_deleted }} + line-numbers: ${{ steps.diff.outputs.lineNumbers }} diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml index dddc5140ad4..b768ab44b34 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -115,12 +115,12 @@ jobs: env: PROJECT_ID: "PN_kwDOAM0swc1HvQ" GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} - - move_voice-message_issues: - name: A-Voice Messages to voice message board + + Search_issues_to_board: + name: Search issues to project board runs-on: ubuntu-latest if: > - contains(github.event.issue.labels.*.name, 'A-Voice Messages') + contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') steps: - uses: octokit/graphql-action@v2.x with: @@ -136,14 +136,14 @@ jobs: projectid: ${{ env.PROJECT_ID }} contentid: ${{ github.event.issue.node_id }} env: - PROJECT_ID: "PN_kwDOAM0swc2KCw" + PROJECT_ID: "PN_kwDOAM0swc4ADtaO" GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} - move_threads_issues: - name: A-Threads to Thread board + move_voice-message_issues: + name: A-Voice Messages to voice message board runs-on: ubuntu-latest if: > - contains(github.event.issue.labels.*.name, 'A-Threads') + contains(github.event.issue.labels.*.name, 'A-Voice Messages') steps: - uses: octokit/graphql-action@v2.x with: @@ -159,9 +159,8 @@ jobs: projectid: ${{ env.PROJECT_ID }} contentid: ${{ github.event.issue.node_id }} env: - PROJECT_ID: "PN_kwDOAM0swc0rRA" + PROJECT_ID: "PN_kwDOAM0swc2KCw" GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} - move_message_bubbles_issues: name: A-Message-Bubbles to Message bubbles board runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index ec95487a6d6..c0a8aa9172a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,32 @@ +Changes in [1.11.5](https://github.com/vector-im/element-web/releases/tag/v1.11.5) (2022-09-13) +=============================================================================================== + +## ✨ Features + * Device manager - hide unverified security recommendation when only current session is unverified ([\#9228](https://github.com/matrix-org/matrix-react-sdk/pull/9228)). Contributed by @kerryarchibald. + * Device manager - scroll to filtered list from security recommendations ([\#9227](https://github.com/matrix-org/matrix-react-sdk/pull/9227)). Contributed by @kerryarchibald. + * Device manager - updated dropdown style in filtered device list ([\#9226](https://github.com/matrix-org/matrix-react-sdk/pull/9226)). Contributed by @kerryarchibald. + * Device manager - device type and verification icons on device tile ([\#9197](https://github.com/matrix-org/matrix-react-sdk/pull/9197)). Contributed by @kerryarchibald. + +## 🐛 Bug Fixes + * Description of DM room with more than two other people is now being displayed correctly ([\#9231](https://github.com/matrix-org/matrix-react-sdk/pull/9231)). Fixes #23094. + * Fix voice messages with multiple composers ([\#9208](https://github.com/matrix-org/matrix-react-sdk/pull/9208)). Fixes #23023. Contributed by @grimhilt. + * Fix suggested rooms going missing ([\#9236](https://github.com/matrix-org/matrix-react-sdk/pull/9236)). Fixes #23190. + * Fix tooltip infinitely recursing ([\#9235](https://github.com/matrix-org/matrix-react-sdk/pull/9235)). Fixes matrix-org/element-web-rageshakes#15107, matrix-org/element-web-rageshakes#15093 matrix-org/element-web-rageshakes#15092 and matrix-org/element-web-rageshakes#15077. + * Fix plain text export saving ([\#9230](https://github.com/matrix-org/matrix-react-sdk/pull/9230)). Contributed by @jryans. + * Add missing space in SecurityRoomSettingsTab ([\#9222](https://github.com/matrix-org/matrix-react-sdk/pull/9222)). Contributed by @gefgu. + * Make use of js-sdk roomNameGenerator to handle i18n for generated room names ([\#9209](https://github.com/matrix-org/matrix-react-sdk/pull/9209)). Fixes #21369. + * Fix progress bar regression throughout the app ([\#9219](https://github.com/matrix-org/matrix-react-sdk/pull/9219)). Fixes #23121. + * Reuse empty string & space string logic for event types in devtools ([\#9218](https://github.com/matrix-org/matrix-react-sdk/pull/9218)). Fixes #23115. + Changes in [1.11.4](https://github.com/vector-im/element-web/releases/tag/v1.11.4) (2022-08-31) =============================================================================================== +## 🔒 Security +* Fixes for [CVE-2022-36059](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D36059) and [CVE-2022-36060](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D36060) + +Learn more about what we've been up to at https://element.io/blog/element-web-desktop-1-11-4-a-security-update-deferred-dms-and-more/ +Find more details of the vulnerabilities at https://matrix.org/blog/2022/08/31/security-releases-matrix-js-sdk-19-4-0-and-matrix-react-sdk-3-53-0 + ## ✨ Features * Device manager - scroll to filtered list from security recommendations ([\#9227](https://github.com/matrix-org/matrix-react-sdk/pull/9227)). Contributed by @kerryarchibald. * Device manager - updated dropdown style in filtered device list ([\#9226](https://github.com/matrix-org/matrix-react-sdk/pull/9226)). Contributed by @kerryarchibald. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e14c75df973..37391f54f5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,283 @@ -Contributing code to Element -============================ +Contributing code to Element Web +================================ -Element follows the same pattern as the [matrix-js-sdk](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md). +Everyone is welcome to contribute code to Element Web, provided that they are +willing to license their contributions under the same license as the project +itself. We follow a simple 'inbound=outbound' model for contributions: the act +of submitting an 'inbound' contribution means that the contributor agrees to +license the code under the same terms as the project's overall 'outbound' +license - in this case, Apache Software License v2 (see +[LICENSE](LICENSE)). + +How to contribute +----------------- + +The preferred and easiest way to contribute changes to the project is to fork +it on github, and then create a pull request to ask us to pull your changes +into our repo (https://help.github.com/articles/using-pull-requests/) + +We use GitHub's pull request workflow to review the contribution, and either +ask you to make any refinements needed or merge it and make them ourselves. + +Things that should go into your PR description: + * A changelog entry in the `Notes` section (see below) + * References to any bugs fixed by the change (in GitHub's `Fixes` notation) + * Describe the why and what is changing in the PR description so it's easy for + onlookers and reviewers to onboard and context switch. This information is + also helpful when we come back to look at this in 6 months and ask "why did + we do it like that?" we have a chance of finding out. + * Why didn't it work before? Why does it work now? What use cases does it + unlock? + * If you find yourself adding information on how the code works or why you + chose to do it the way you did, make sure this information is instead + written as comments in the code itself. + * Sometimes a PR can change considerably as it is developed. In this case, + the description should be updated to reflect the most recent state of + the PR. (It can be helpful to retain the old content under a suitable + heading, for additional context.) + * Include both **before** and **after** screenshots to easily compare and discuss + what's changing. + * Include a step-by-step testing strategy so that a reviewer can check out the + code locally and easily get to the point of testing your change. + * Add comments to the diff for the reviewer that might help them to understand + why the change is necessary or how they might better understand and review it. + +We rely on information in pull request to populate the information that goes into +the changelogs our users see, both for Element Web itself and other projects on +which it is based. This is picked up from both labels on the pull request and +the `Notes:` annotation in the description. By default, the PR title will be +used for the changelog entry, but you can specify more options, as follows. + +To add a longer, more detailed description of the change for the changelog: + + +*Fix llama herding bug* + +``` +Notes: Fix a bug (https://github.com/matrix-org/notaproject/issues/123) where the 'Herd' button would not herd more than 8 Llamas if the moon was in the waxing gibbous phase +``` + +For some PRs, it's not useful to have an entry in the user-facing changelog (this is +the default for PRs labelled with `T-Task`): + +*Remove outdated comment from `Ungulates.ts`* +``` +Notes: none +``` + +Sometimes, you're fixing a bug in a downstream project, in which case you want +an entry in that project's changelog. You can do that too: + +*Fix another herding bug* +``` +Notes: Fix a bug where the `herd()` function would only work on Tuesdays +element-web notes: Fix a bug where the 'Herd' button only worked on Tuesdays +``` + +This example is for Element Web. You can specify: + * matrix-react-sdk + * element-web + * element-desktop + +If your PR introduces a breaking change, use the `Notes` section in the same +way, additionally adding the `X-Breaking-Change` label (see below). There's no need +to specify in the notes that it's a breaking change - this will be added +automatically based on the label - but remember to tell the developer how to +migrate: + +*Remove legacy class* + +``` +Notes: Remove legacy `Camelopard` class. `Giraffe` should be used instead. +``` + +Other metadata can be added using labels. + * `X-Breaking-Change`: A breaking change - adding this label will mean the change causes a *major* version bump. + * `T-Enhancement`: A new feature - adding this label will mean the change causes a *minor* version bump. + * `T-Defect`: A bug fix (in either code or docs). + * `T-Task`: No user-facing changes, eg. code comments, CI fixes, refactors or tests. Won't have a changelog entry unless you specify one. + +If you don't have permission to add labels, your PR reviewer(s) can work with you +to add them: ask in the PR description or comments. + +We use continuous integration, and all pull requests get automatically tested: +if your change breaks the build, then the PR will show that there are failed +checks, so please check back after a few minutes. + +Tests +----- +Your PR should include tests. + +For new user facing features in `matrix-js-sdk`, `matrix-react-sdk` or `element-web`, you +must include: + +1. Comprehensive unit tests written in Jest. These are located in `/test`. +2. "happy path" end-to-end tests. + These are located in `/cypress/e2e` in `matrix-react-sdk`, and + are run using `element-web`. Ideally, you would also include tests for edge + and error cases. + +Unit tests are expected even when the feature is in labs. It's good practice +to write tests alongside the code as it ensures the code is testable from +the start, and gives you a fast feedback loop while you're developing the +functionality. End-to-end tests should be added prior to the feature +leaving labs, but don't have to be present from the start (although it might +be beneficial to have some running early, so you can test things faster). + +For bugs in those repos, your change must include at least one unit test or +end-to-end test; which is best depends on what sort of test most concisely +exercises the area. + +Changes to must be accompanied by unit tests written in Jest. +These are located in `/spec/` in `matrix-js-sdk` or `/test/` in `element-web` +and `matrix-react-sdk`. + +When writing unit tests, please aim for a high level of test coverage +for new code - 80% or greater. If you cannot achieve that, please document +why it's not possible in your PR. + +Some sections of code are not sensible to add coverage for, such as those +which explicitly inhibit noisy logging for tests. Which can be hidden using +an istanbul magic comment as [documented here][1]. See example: +```javascript +/* istanbul ignore if */ +if (process.env.NODE_ENV !== "test") { + logger.error("Log line that is noisy enough in tests to want to skip"); +} +``` + +Tests validate that your change works as intended and also document +concisely what is being changed. Ideally, your new tests fail +prior to your change, and succeed once it has been applied. You may +find this simpler to achieve if you write the tests first. + +If you're spiking some code that's experimental and not being used to support +production features, exceptions can be made to requirements for tests. +Note that tests will still be required in order to ship the feature, and it's +strongly encouraged to think about tests early in the process, as adding +tests later will become progressively more difficult. + +If you're not sure how to approach writing tests for your change, ask for help +in [#element-dev](https://matrix.to/#/#element-dev:matrix.org). + +Code style +---------- +Element Web aims to target TypeScript/ES6. All new files should be written in +TypeScript and existing files should use ES6 principles where possible. + +Members should not be exported as a default export in general - it causes problems +with the architecture of the SDK (index file becomes less clear) and could +introduce naming problems (as default exports get aliased upon import). In +general, avoid using `export default`. + +The remaining code style is documented in [code_style.md](./code_style.md). +Contributors are encouraged to it and follow the principles set out there. + +Please ensure your changes match the cosmetic style of the existing project, +and ***never*** mix cosmetic and functional changes in the same commit, as it +makes it horribly hard to review otherwise. + +Attribution +----------- +Everyone who contributes anything to Matrix is welcome to be listed in the +AUTHORS.rst file for the project in question. Please feel free to include a +change to AUTHORS.rst in your pull request to list yourself and a short +description of the area(s) you've worked on. Also, we sometimes have swag to +give away to contributors - if you feel that Matrix-branded apparel is missing +from your life, please mail us your shipping address to matrix at matrix.org +and we'll try to fix it :) + +Sign off +-------- +In order to have a concrete record that your contribution is intentional +and you agree to license it under the same terms as the project's license, we've +adopted the same lightweight approach that the Linux Kernel +(https://www.kernel.org/doc/Documentation/SubmittingPatches), Docker +(https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other +projects use: the DCO (Developer Certificate of Origin: +http://developercertificate.org/). This is a simple declaration that you wrote +the contribution or otherwise have the right to contribute it to Matrix: + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +If you agree to this for your contribution, then all that's needed is to +include the line in your commit or pull request comment: + +``` +Signed-off-by: Your Name +``` + +We accept contributions under a legally identifiable name, such as your name on +government documentation or common-law names (names claimed by legitimate usage +or repute). Unfortunately, we cannot accept anonymous contributions at this +time. + +Git allows you to add this signoff automatically when using the `-s` flag to +`git commit`, which uses the name and email set in your `user.name` and +`user.email` git configs. + +If you forgot to sign off your commits before making your pull request and are +on Git 2.17+ you can mass signoff using rebase: + +``` +git rebase --signoff origin/develop +``` + +Review expectations +=================== + +See https://github.com/vector-im/element-meta/wiki/Review-process + + +Merge Strategy +============== + +The preferred method for merging pull requests is squash merging to keep the +commit history trim, but it is up to the discretion of the team member merging +the change. We do not support rebase merges due to `allchange` being unable to +handle them. When merging make sure to leave the default commit title, or +at least leave the PR number at the end in brackets like by default. +When stacking pull requests, you may wish to do the following: + +1. Branch from develop to your branch (branch1), push commits onto it and open a pull request +2. Branch from your base branch (branch1) to your work branch (branch2), push commits and open a pull request configuring the base to be branch1, saying in the description that it is based on your other PR. +3. Merge the first PR using a merge commit otherwise your stacked PR will need a rebase. Github will automatically adjust the base branch of your other PR to be develop. + + +[1]: https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md diff --git a/code_style.md b/code_style.md new file mode 100644 index 00000000000..79840d4eeb1 --- /dev/null +++ b/code_style.md @@ -0,0 +1,455 @@ +# Element Web/Desktop code style guide + +This code style applies to projects which the element-web team directly maintains or is reasonably +adjacent to. As of writing, these are: + +* element-desktop +* element-web +* matrix-react-sdk +* matrix-js-sdk + +Other projects might extend this code style for increased strictness. For example, matrix-events-sdk +has stricter code organization to reduce the maintenance burden. These projects will declare their code +style within their own repos. + +Note that some requirements will be layer-specific. Where the requirements don't make sense for the +project, they are used to the best of their ability, used in spirit, or ignored if not applicable, +in that order. + +## Guiding principles + +1. We want the lint rules to feel natural for most team members. No one should have to think too much + about the linter. +2. We want to stay relatively close to [industry standards](https://google.github.io/styleguide/tsguide.html) + to make onboarding easier. +3. We describe what good code looks like rather than point out bad examples. We do this to avoid + excessively punishing people for writing code which fails the linter. +4. When something isn't covered by the style guide, we come up with a reasonable rule rather than + claim that it "passes the linter". We update the style guide and linter accordingly. +5. While we aim to improve readability, understanding, and other aspects of the code, we deliberately + do not let solely our personal preferences drive decisions. +6. We aim to have an understandable guide. + +## Coding practices + +1. Lint rules enforce decisions made by this guide. The lint rules and this guide are kept in + perfect sync. +2. Commit messages are descriptive for the changes. When the project supports squash merging, + only the squashed commit needs to have a descriptive message. +3. When there is disagreement with a code style approved by the linter, a PR is opened against + the lint rules rather than making exceptions on the responsible code PR. +4. Rules which are intentionally broken (via eslint-ignore, @ts-ignore, etc) have a comment + included in the immediate vicinity for why. Determination of whether this is valid applies at + code review time. +5. When editing a file, nearby code is updated to meet the modern standards. "Nearby" is subjective, + but should be whatever is reasonable at review time. Such an example might be to update the + class's code style, but not the file's. + 1. These changes should be minor enough to include in the same commit without affecting a code + reviewer's job. + +## All code + +Unless otherwise specified, the following applies to all code: + +1. 120 character limit per line. Match existing code in the file if it is using a lower guide. +2. A tab/indentation is 4 spaces. +3. Newlines are Unix. +4. A file has a single empty line at the end. +5. Lines are trimmed of all excess whitespace, including blank lines. +6. Long lines are broken up for readability. + +## TypeScript / JavaScript {#typescript-javascript} + +1. Write TypeScript. Turn JavaScript into TypeScript when working in the area. +2. Use named exports. +3. Break long lines to appear as follows: + + ```typescript + // Function arguments + function doThing( + arg1: string, + arg2: string, + arg3: string, + ): boolean { + return !!arg1 + && !!arg2 + && !!arg3; + } + + // Calling a function + doThing( + "String 1", + "String 2", + "String 3", + ); + + // Reduce line verbosity when possible/reasonable + doThing( + "String1", "String 2", + "A much longer string 3", + ); + + // Chaining function calls + something.doThing() + .doOtherThing() + .doMore() + .somethingElse(it => + useIt(it) + ); + ``` +4. Use semicolons for block/line termination. + 1. Except when defining interfaces, classes, and non-arrow functions specifically. +5. When a statement's body is a single line, it may be written without curly braces, so long as the body is placed on + the same line as the statement. + + ```typescript + if (x) doThing(); + ``` +6. Blocks for `if`, `for`, `switch` and so on must have a space surrounding the condition, but not + within the condition. + + ```typescript + if (x) { + doThing(); + } + ``` +7. Mixing of logical operands requires brackets to explicitly define boolean logic. + + ```typescript + if ((a > b && b > c) || (d < e)) return true; + ``` +8. Ternaries use the same rules as `if` statements, plus the following: + + ```typescript + // Single line is acceptable + const val = a > b ? doThing() : doOtherThing(); + + // Multiline is also okay + const val = a > b + ? doThing() + : doOtherThing(); + + // Use brackets when using multiple conditions. + // Maximum 3 conditions, prefer 2 or less. + const val = (a > b && b > c) ? doThing() : doOtherThing(); + ``` +9. lowerCamelCase is used for function and variable naming. +10. UpperCamelCase is used for general naming. +11. Interface names should not be marked with an uppercase `I`. +12. One variable declaration per line. +13. If a variable is not receiving a value on declaration, its type must be defined. + + ```typescript + let errorMessage: Optional; + ``` +14. Objects, arrays, enums and so on must have each line terminated with a comma: + + ```typescript + const obj = { + prop: 1, + else: 2, + }; + + const arr = [ + "one", + "two", + ]; + + enum Thing { + Foo, + Bar, + } + + doThing( + "arg1", + "arg2", + ); + ``` +15. Objects can use shorthand declarations, including mixing of types. + + ```typescript + { + room, + prop: this.prop, + } + // ... or ... + { room, prop: this.prop } + ``` +16. Object keys should always be non-strings when possible. + + ```typescript + { + property: "value", + "m.unavoidable": true, + [EventType.RoomMessage]: true, + } + ``` +17. Explicitly cast to a boolean. + + ```typescript + !!stringVar || Boolean(stringVar) + ``` +18. Use `switch` statements when checking against more than a few enum-like values. +19. Use `const` for constants, `let` for mutability. +20. Describe types exhaustively (ensure noImplictAny would pass). + 1. Notable exceptions are arrow functions used as parameters, when a void return type is + obvious, and when declaring and assigning a variable in the same line. +21. Declare member visibility (public/private/protected). +22. Private members are private and not prefixed unless required for naming conflicts. + 1. Convention is to use an underscore or the word "internal" to denote conflicted member names. + 2. "Conflicted" typically refers to a getter which wants the same name as the underlying variable. +23. Prefer readonly members over getters backed by a variable, unless an internal setter is required. +24. Prefer Interfaces for object definitions, and types for parameter-value-only declarations. + 1. Note that an explicit type is optional if not expected to be used outside of the function call, + unlike in this example: + + ```typescript + interface MyObject { + hasString: boolean; + } + + type Options = MyObject | string; + + function doThing(arg: Options) { + // ... + } + ``` +25. Variables/properties which are `public static` should also be `readonly` when possible. +26. Interface and type properties are terminated with semicolons, not commas. +27. Prefer arrow formatting when declaring functions for interfaces/types: + + ```typescript + interface Test { + myCallback: (arg: string) => Promise; + } + ``` +28. Prefer a type definition over an inline type. For example, define an interface. +29. Always prefer to add types or declare a type over the use of `any`. Prefer inferred types + when they are not `any`. + 1. When using `any`, a comment explaining why must be present. +30. `import` should be used instead of `require`, as `require` does not have types. +31. Export only what can be reused. +32. Prefer a type like `Optional` (`type Optional = T | null | undefined`) instead + of truly optional parameters. + 1. A notable exception is when the likelihood of a bug is minimal, such as when a function + takes an argument that is more often not required than required. An example where the + `?` operator is inappropriate is when taking a room ID: typically the caller should + supply the room ID if it knows it, otherwise deliberately acknowledge that it doesn't + have one with `null`. + + ```typescript + function doThingWithRoom( + thing: string, + room: Optional, // require the caller to specify + ) { + // ... + } + ``` +33. There should be approximately one interface, class, or enum per file unless the file is named + "types.ts", "global.d.ts", or ends with "-types.ts". + 1. The file name should match the interface, class, or enum name. +34. Bulk functions can be declared in a single file, though named as "foo-utils.ts" or "utils/foo.ts". +35. Imports are grouped by external module imports first, then by internal imports. +36. File ordering is not strict, but should generally follow this sequence: + 1. Licence header + 2. Imports + 3. Constants + 4. Enums + 5. Interfaces + 6. Functions + 7. Classes + 1. Public/protected/private static properties + 2. Public/protected/private properties + 3. Constructors + 4. Public/protected/private getters & setters + 5. Protected and abstract functions + 6. Public/private functions + 7. Public/protected/private static functions +37. Variable names should be noticeably unique from their types. For example, "str: string" instead + of "string: string". +38. Use double quotes to enclose strings. You may use single quotes if the string contains double quotes. + + ```typescript + const example1 = "simple string"; + const example2 = 'string containing "double quotes"'; + ``` +39. Prefer async-await to promise-chaining + + ```typescript + async function () { + const result = await anotherAsyncFunction(); + // ... + } + ``` + +## React + +Inheriting all the rules of TypeScript, the following additionally apply: + +1. Types for lifecycle functions are not required (render, componentDidMount, and so on). +2. Class components must always have a `Props` interface declared immediately above them. It can be + empty if the component accepts no props. +3. Class components should have an `State` interface declared immediately above them, but after `Props`. +4. Props and State should not be exported. Use `React.ComponentProps` + instead. +5. One component per file, except when a component is a utility component specifically for the "primary" + component. The utility component should not be exported. +6. Exported constants, enums, interfaces, functions, etc must be separate from files containing components + or stores. +7. Stores should use a singleton pattern with a static instance property: + + ```typescript + class FooStore { + public static readonly instance = new FooStore(); + + // or if the instance can't be created eagerly: + private static _instance: FooStore; + public static get instance(): FooStore { + if (!FooStore._instance) { + FooStore._instance = new FooStore(); + } + return FooStore._instance; + } + } + ``` +8. Stores must support using an alternative MatrixClient and dispatcher instance. +9. Utilities which require JSX must be split out from utilities which do not. This is to prevent import + cycles during runtime where components accidentally include more of the app than they intended. +10. Interdependence between stores should be kept to a minimum. Break functions and constants out to utilities + if at all possible. +11. A component should only use CSS class names in line with the component name. + 1. When knowingly using a class name from another component, document it. +12. Break components over multiple lines like so: + + ```typescript + function render() { + return ; + + // or + + return ( + + ); + + // or if children are needed (infer parens usage) + + return { _t("Short string here") }; + + + + return + { _t("Longer string here") } + ; + } + ``` +13. Curly braces within JSX should be padded with a space, however properties on those components should not. + See above code example. +14. Functions used as properties should either be defined on the class or stored in a variable. They should not + be inline unless mocking/short-circuiting the value. +15. Prefer hooks (functional components) over class components. Be consistent with the existing area if unsure + which should be used. + 1. Unless the component is considered a "structure", in which case use classes. +16. Write more views than structures. Structures are chunks of functionality like MatrixChat while views are + isolated components. +17. Components should serve a single, or near-single, purpose. +18. Prefer to derive information from component properties rather than establish state. +19. Do not use `React.Component::forceUpdate`. + +## Stylesheets (\*.pcss = PostCSS + Plugins) + +Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, but actually it is not. + +1. Class names must be prefixed with "mx_". +2. Class names should denote the component which defines them, followed by any context: + 1. mx_MyFoo + 2. mx_MyFoo_avatar + 3. mx_MyFoo_avatar--user +3. Use the `$font` and `$spacing` variables instead of manual values. +4. Keep indentation/nesting to a minimum. Maximum suggested nesting is 5 layers. +5. Use the whole class name instead of shortcuts: + + ```scss + .mx_MyFoo { + & .mx_MyFoo_avatar { // instead of &_avatar + // ... + } + } + ``` +6. Break multiple selectors over multiple lines this way: + + ```scss + .mx_MyFoo, + .mx_MyBar, + .mx_MyFooBar { + // ... + } + ``` +7. Non-shared variables should use $lowerCamelCase. Shared variables use $dashed-naming. +8. Overrides to Z indexes, adjustments of dimensions/padding with pixels, and so on should all be + documented for what the values mean: + + ```scss + .mx_MyFoo { + width: calc(100% - 12px); // 12px for read receipts + top: -2px; // visually centred vertically + z-index: 10; // above user avatar, but below dialogs + } + ``` +9. Avoid the use of `!important`. If necessary, add a comment. + +## Tests + +1. Tests must be written in TypeScript. +2. Jest mocks are declared below imports, but above everything else. +3. Use the following convention template: + + ```typescript + // Describe the class, component, or file name. + describe("FooComponent", () => { + // all test inspecific variables go here + + beforeEach(() => { + // exclude if not used. + }); + + afterEach(() => { + // exclude if not used. + }); + + // Use "it should..." terminology + it("should call the correct API", async () => { + // test-specific variables go here + + // function calls/state changes go here + + // expectations go here + }); + }); + + // If the file being tested is a utility class: + describe("foo-utils", () => { + describe("firstUtilFunction", () => { + it("should...", async () => { + // ... + }); + }); + + describe("secondUtilFunction", () => { + it("should...", async () => { + // ... + }); + }); + }); + ``` diff --git a/package.json b/package.json index c0ec4ee58b4..07af95a6bfe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "element-web", - "version": "1.11.4", + "version": "1.11.5", "description": "A feature-rich client for Matrix.org", "author": "New Vector Ltd.", "repository": { @@ -61,8 +61,8 @@ "gfm.css": "^1.1.2", "jsrsasign": "^10.5.25", "katex": "^0.12.0", - "matrix-js-sdk": "19.4.0", - "matrix-react-sdk": "3.53.0", + "matrix-js-sdk": "19.5.0", + "matrix-react-sdk": "3.54.0", "matrix-widget-api": "^1.0.0", "prop-types": "^15.7.2", "react": "17.0.2", diff --git a/release.sh b/release.sh index 77876972f41..0969ad7ce96 100755 --- a/release.sh +++ b/release.sh @@ -1,68 +1,9 @@ #!/bin/bash # # Script to perform a release of element-web. -# -# Requires github-changelog-generator; to install, do -# pip install git+https://github.com/matrix-org/github-changelog-generator.git set -e -orig_args=$@ - -# chomp any args starting with '-' as these need to go -# through to the release script and otherwise we'll get -# confused about what the version arg is. -while [[ "$1" == -* ]]; do - shift -done - -cd `dirname $0` - -for i in matrix-js-sdk matrix-react-sdk -do - echo "Checking version of $i..." - depver=`cat package.json | jq -r .dependencies[\"$i\"]` - latestver=`yarn info -s $i dist-tags.next` - if [ "$depver" != "$latestver" ] - then - echo "The latest version of $i is $latestver but package.json depends on $depver." - echo -n "Type 'u' to auto-upgrade, 'c' to continue anyway, or 'a' to abort:" - read resp - if [ "$resp" != "u" ] && [ "$resp" != "c" ] - then - echo "Aborting." - exit 1 - fi - if [ "$resp" == "u" ] - then - echo "Upgrading $i to $latestver..." - yarn add -E $i@$latestver - git add -u - git commit -m "Upgrade $i to $latestver" - fi - fi -done - -./node_modules/matrix-js-sdk/release.sh -n "$orig_args" - -release="${1#v}" -tag="v${release}" -prerelease=0 -# We check if this build is a prerelease by looking to -# see if the version has a hyphen in it. Crude, -# but semver doesn't support postreleases so anything -# with a hyphen is a prerelease. -echo $release | grep -q '-' && prerelease=1 +cd "$(dirname "$0")" -if [ $prerelease -eq 0 ] -then - # For a release, reset SDK deps back to the `develop` branch. - for i in matrix-js-sdk matrix-react-sdk - do - echo "Resetting $i to develop branch..." - yarn add github:matrix-org/$i#develop - git add -u - git commit -m "Reset $i back to develop branch" - done - git push origin develop -fi +./node_modules/matrix-js-sdk/release.sh "$@" diff --git a/release_config.yaml b/release_config.yaml index 4d207a32d89..514dc319191 100644 --- a/release_config.yaml +++ b/release_config.yaml @@ -2,3 +2,5 @@ signing_id: releases@riot.im subprojects: matrix-react-sdk: includeByDefault: true + matrix-js-sdk: + includeByDefault: false diff --git a/scripts/docker-package.sh b/scripts/docker-package.sh index 3beb01151c8..90875366c24 100755 --- a/scripts/docker-package.sh +++ b/scripts/docker-package.sh @@ -7,8 +7,10 @@ DIST_VERSION=$(git describe --abbrev=0 --tags) DIR=$(dirname "$0") -# If we're not using custom SDKs and on a branch other than master, generate a version akin go develop.element.io -if [[ $USE_CUSTOM_SDKS == false ]] && [[ $BRANCH != 'master' ]] +# If the branch comes out as HEAD then we're probably checked out to a tag, so if the thing is *not* +# coming out as HEAD then we're on a branch. When we're on a branch, we want to resolve ourselves to +# a few SHAs rather than a version. +if [[ $BRANCH != HEAD && ! $BRANCH =~ heads/v.+ ]] then DIST_VERSION=$("$DIR"/get-version-from-git.sh) fi diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 6ec82ab1d53..7ff9e9b5e18 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -5,7 +5,7 @@ "Sign In": "Anmelden", "Create Account": "Konto erstellen", "Explore rooms": "Räume erkunden", - "Unexpected error preparing the app. See console for details.": "Unerwarteter Fehler bei der Vorbereitung der App. Siehe in die Konsole für mehr Details.", + "Unexpected error preparing the app. See console for details.": "Unerwarteter Fehler bei der Vorbereitung der App; mehr Details in der Konsole.", "Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Ungültige Konfiguration: Es kann nur eine der Optionen default_server_config, default_server_name oder default_hs_url angegeben werden.", "Invalid configuration: no default server specified.": "Ungültige Konfiguration: Es wurde kein Standardserver angegeben.", "The message from the parser is: %(message)s": "Die Nachricht des Parsers ist: %(message)s", @@ -21,7 +21,7 @@ "You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Du kannst deinen aktuellen Browser weiterhin verwenden. Es ist aber möglich, dass nicht alles richtig funktioniert oder das Aussehen der App inkorrekt ist.", "I understand the risks and wish to continue": "Ich verstehe die Risiken und möchte fortfahren", "Your Element is misconfigured": "Dein Element ist falsch konfiguriert", - "Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Deine Elementkonfiguration enthält ungültiges JSON. Bitte korrigiere das Problem und lade die Seite neu.", + "Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Deine Element-Konfiguration enthält ungültiges JSON. Bitte korrigiere das Problem und lade die Seite neu.", "Download Completed": "Herunterladen fertiggestellt", "Open": "Öffnen", "%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s verwendet erweiterte Browserfunktionen, die von deinem Browser nicht unterstützt werden.", diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index dc5a792a7b0..b140df45c37 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -1,5 +1,5 @@ /* -Copyright 2020 New Vector Ltd. +Copyright 2020-2022 New Vector Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ import { KJUR } from 'jsrsasign'; import { IOpenIDCredentials, IWidgetApiRequest, + IWidgetApiRequestData, + IWidgetApiResponseData, VideoConferenceCapabilities, WidgetApi, + WidgetApiAction, } from "matrix-widget-api"; import { ElementWidgetActions } from "matrix-react-sdk/src/stores/widgets/ElementWidgetActions"; import { logger } from "matrix-js-sdk/src/logger"; @@ -58,9 +61,7 @@ let widgetApi: WidgetApi; let meetApi: any; // JitsiMeetExternalAPI let skipOurWelcomeScreen = false; -const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev.detail, {}); - -(async function() { +const setupCompleted = (async () => { try { // Queue a config.json lookup asap, so we can use it later on. We want this to be concurrent with // other setup work and therefore do not block. @@ -90,24 +91,88 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev } // Set this up as early as possible because Element will be hitting it almost immediately. - let readyPromise: Promise<[void, void]>; + let widgetApiReady: Promise; if (parentUrl && widgetId) { const parentOrigin = new URL(qsParam('parentUrl')).origin; widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin); + + widgetApiReady = new Promise(resolve => widgetApi.once("ready", resolve)); widgetApi.requestCapabilities(VideoConferenceCapabilities); - readyPromise = Promise.all([ - new Promise(resolve => { - widgetApi.once(`action:${ElementWidgetActions.ClientReady}`, ev => { - ev.preventDefault(); - resolve(); - widgetApi.transport.reply(ev.detail, {}); - }); - }), - new Promise(resolve => { - widgetApi.once("ready", () => resolve()); - }), - ]); widgetApi.start(); + + const handleAction = ( + action: WidgetApiAction, + handler: (request: IWidgetApiRequestData) => void, + ): void => { + widgetApi.on(`action:${action}`, async (ev: CustomEvent) => { + ev.preventDefault(); + await setupCompleted; + + let response: IWidgetApiResponseData; + try { + await handler(ev.detail.data); + response = {}; + } catch (e) { + if (e instanceof Error) { + response = { error: { message: e.message } }; + } else { + throw e; + } + } + + await widgetApi.transport.reply(ev.detail, response); + }); + }; + + handleAction(ElementWidgetActions.JoinCall, async ({ audioInput, videoInput }) => { + joinConference(audioInput as string | null, videoInput as string | null); + }); + handleAction(ElementWidgetActions.HangupCall, async ({ force }) => { + if (force === true) { + meetApi?.dispose(); + notifyHangup(); + meetApi = null; + closeConference(); + } else { + meetApi?.executeCommand('hangup'); + } + }); + handleAction(ElementWidgetActions.MuteAudio, async () => { + if (meetApi && !await meetApi.isAudioMuted()) { + meetApi.executeCommand('toggleAudio'); + } + }); + handleAction(ElementWidgetActions.UnmuteAudio, async () => { + if (meetApi && await meetApi.isAudioMuted()) { + meetApi.executeCommand('toggleAudio'); + } + }); + handleAction(ElementWidgetActions.MuteVideo, async () => { + if (meetApi && !await meetApi.isVideoMuted()) { + meetApi.executeCommand('toggleVideo'); + } + }); + handleAction(ElementWidgetActions.UnmuteVideo, async () => { + if (meetApi && await meetApi.isVideoMuted()) { + meetApi.executeCommand('toggleVideo'); + } + }); + handleAction(ElementWidgetActions.TileLayout, async () => { + meetApi?.executeCommand('setTileView', true); + }); + handleAction(ElementWidgetActions.SpotlightLayout, async () => { + meetApi?.executeCommand('setTileView', false); + }); + handleAction(ElementWidgetActions.StartLiveStream, async ({ rtmpStreamKey }) => { + if (!meetApi) throw new Error("Conference not joined"); + meetApi.executeCommand('startRecording', { + mode: 'stream', + // this looks like it should be rtmpStreamKey but we may be on too old + // a version of jitsi meet + //rtmpStreamKey, + youtubeStreamKey: rtmpStreamKey, + }); + }); } else { logger.warn("No parent URL or no widget ID - assuming no widget API is available"); } @@ -136,7 +201,7 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev toggleConferenceVisibility(skipOurWelcomeScreen); if (widgetApi) { - await readyPromise; + await widgetApiReady; // See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { @@ -144,99 +209,6 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev openIdToken = await widgetApi.requestOpenIDConnectToken(); logger.log("Got OpenID Connect token"); } - - widgetApi.on(`action:${ElementWidgetActions.JoinCall}`, - (ev: CustomEvent) => { - ev.preventDefault(); - const { audioDevice, videoDevice } = ev.detail.data; - joinConference(audioDevice as string | null, videoDevice as string | null); - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.HangupCall}`, - (ev: CustomEvent) => { - ev.preventDefault(); - meetApi?.executeCommand('hangup'); - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.ForceHangupCall}`, - (ev: CustomEvent) => { - ev.preventDefault(); - meetApi?.dispose(); - notifyHangup(); - meetApi = null; - closeConference(); - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.MuteAudio}`, - async (ev: CustomEvent) => { - ev.preventDefault(); - if (meetApi && !await meetApi.isAudioMuted()) { - meetApi.executeCommand('toggleAudio'); - } - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.UnmuteAudio}`, - async (ev: CustomEvent) => { - ev.preventDefault(); - if (meetApi && await meetApi.isAudioMuted()) { - meetApi.executeCommand('toggleAudio'); - } - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.MuteVideo}`, - async (ev: CustomEvent) => { - ev.preventDefault(); - if (meetApi && !await meetApi.isVideoMuted()) { - meetApi.executeCommand('toggleVideo'); - } - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.UnmuteVideo}`, - async (ev: CustomEvent) => { - ev.preventDefault(); - if (meetApi && await meetApi.isVideoMuted()) { - meetApi.executeCommand('toggleVideo'); - } - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.TileLayout}`, - (ev: CustomEvent) => { - ev.preventDefault(); - meetApi?.executeCommand('setTileView', true); - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.SpotlightLayout}`, - (ev: CustomEvent) => { - ev.preventDefault(); - meetApi?.executeCommand('setTileView', false); - ack(ev); - }, - ); - widgetApi.on(`action:${ElementWidgetActions.StartLiveStream}`, - (ev: CustomEvent) => { - ev.preventDefault(); - if (meetApi) { - meetApi.executeCommand('startRecording', { - mode: 'stream', - // this looks like it should be rtmpStreamKey but we may be on too old - // a version of jitsi meet - //rtmpStreamKey: ev.detail.data.rtmpStreamKey, - youtubeStreamKey: ev.detail.data.rtmpStreamKey, - }); - ack(ev); - } else { - widgetApi.transport.reply(ev.detail, { error: { message: "Conference not joined" } }); - } - }, - ); } // Now that everything should be set up, skip to the Jitsi splash screen if needed @@ -245,13 +217,6 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev } enableJoinButton(); // always enable the button - - // Inform the client that we're ready to receive events - try { - await widgetApi?.transport.send(ElementWidgetActions.WidgetReady, {}); - } catch (e) { - logger.error(e); - } } catch (e) { logger.error("Error setting up Jitsi widget", e); document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget"; @@ -346,11 +311,11 @@ function closeConference() { } // event handler bound in HTML -// An audio device of undefined instructs Jitsi to start unmuted with whatever -// audio device it can find, while a device of null instructs it to start muted, -// and a non-nullish device specifies the label of a specific device to use. -// Same for video devices. -function joinConference(audioDevice?: string | null, videoDevice?: string | null) { +// An audio input of undefined instructs Jitsi to start unmuted with whatever +// audio input it can find, while an input of null instructs it to start muted, +// and a non-nullish input specifies the label of a specific device to use. +// Same for video inputs. +function joinConference(audioInput?: string | null, videoInput?: string | null) { let jwt; if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { if (!openIdToken?.access_token) { // eslint-disable-line camelcase @@ -376,8 +341,8 @@ function joinConference(audioDevice?: string | null, videoDevice?: string | null parentNode: document.querySelector("#jitsiContainer"), roomName: conferenceId, devices: { - audioInput: audioDevice, - videoInput: videoDevice, + audioInput, + videoInput, }, userInfo: { displayName, @@ -392,8 +357,8 @@ function joinConference(audioDevice?: string | null, videoDevice?: string | null configOverwrite: { subject: roomName, startAudioOnly, - startWithAudioMuted: audioDevice === null, - startWithVideoMuted: videoDevice === null, + startWithAudioMuted: audioInput === null, + startWithVideoMuted: videoInput === null, // Request some log levels for inclusion in rageshakes // Ideally we would capture all possible log levels, but this can // cause Jitsi Meet to try to post various circular data structures diff --git a/yarn.lock b/yarn.lock index 6bcf7e7a44c..a7e6b2cf827 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7961,10 +7961,10 @@ matrix-events-sdk@^0.0.1-beta.7: resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934" integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA== -matrix-js-sdk@19.4.0: - version "19.4.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-19.4.0.tgz#ea333ec3b7893ea953bd1ac00c0ce1108fffc0d0" - integrity sha512-B8Mm4jCsCHaMaChcdM3VhZDVKrn0nMSDtYvHmS15Iu8Pe0G4qmIpk2AoADBAL9U9yN3pCqvs3TDXaQhM8UxRRA== +matrix-js-sdk@19.5.0: + version "19.5.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-19.5.0.tgz#debc8e92b2e73204c8d5bc9a128a2d0ec0a92ade" + integrity sha512-WTmXMwyhGjUVv3eR71P9wdZj4qqNPgzg9Ud7V6kB3avhZJTZlIpNdHuldXXUdPJ8WTDKY+/yDtEFLIg8pj2Q8A== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" @@ -7985,10 +7985,10 @@ matrix-mock-request@^2.0.0: dependencies: expect "^28.1.0" -matrix-react-sdk@3.53.0: - version "3.53.0" - resolved "https://registry.yarnpkg.com/matrix-react-sdk/-/matrix-react-sdk-3.53.0.tgz#eb102d0c9f740735a9385bf8dddc9a27f3ec6bab" - integrity sha512-852TdcXKEQsBu9jTCDvXRdkk8czhOzpPcUvqlJbJiFy5jFTRMCsUqYqBYuFip09/e61q8KHMfKfOWslyT8DKFw== +matrix-react-sdk@3.54.0: + version "3.54.0" + resolved "https://registry.yarnpkg.com/matrix-react-sdk/-/matrix-react-sdk-3.54.0.tgz#fadff4178a3cac87542b15a760c12bec2b41a00d" + integrity sha512-RpbXNUA7nRov67FP/bXZkuJR2z59vmkJ10HPKoOl2aj0ry1nvrjWItIVcr+JVe51tGzV/quFm1NVFd9Z3x7lsQ== dependencies: "@babel/runtime" "^7.12.5" "@matrix-org/analytics-events" "^0.2.0" @@ -8028,7 +8028,7 @@ matrix-react-sdk@3.53.0: maplibre-gl "^1.15.2" matrix-encrypt-attachment "^1.0.3" matrix-events-sdk "^0.0.1-beta.7" - matrix-js-sdk "19.4.0" + matrix-js-sdk "19.5.0" matrix-widget-api "^1.0.0" minimist "^1.2.5" opus-recorder "^8.0.3"