Compare commits

...

87 Commits

Author SHA1 Message Date
RiotRobot 2ef02fa39e v1.12.9
Docker / Docker Buildx (push) Failing after 54s
2026-01-27 12:50:07 +00:00
RiotRobot 68cc500705 Upgrade dependency to matrix-js-sdk@40.1.0 2026-01-27 12:41:33 +00:00
RiotRobot 1ec05298cf v1.12.9-rc.1
Docker / Docker Buildx (push) Failing after 54s
2026-01-20 14:18:57 +00:00
ElementRobot eaa34e6dfa Update secret paths for OCI credentials in Docker workflow (#31802) (#31803)
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-20 14:16:04 +00:00
RiotRobot fefe093ce0 v1.12.9-rc.0
Docker / Docker Buildx (push) Failing after 52s
2026-01-20 14:02:47 +00:00
RiotRobot ee4277fd95 Upgrade dependency to matrix-js-sdk@40.1.0-rc.0 2026-01-20 13:59:36 +00:00
Will Hunt a15efcc6d0 Allow local log downloads when a rageshake URL is not configured. (#31716)
* Add support for storing debug logs locally and allowing local downloads.

* static

* Comprehensive testing for bug report flow.

* Driveby cleanup of typography

* fix i18n

* Improvements to UX

* More testing

* update snaps

* linting

* lint

* Fix feedback

* Fix boldnewss

* fix bold

* fix heading

* Increase test coverage

* remove focus

* Don't show the FAQ depending on whether you can submit feedback.

* move reset

* fix err

* Remove unused

* update snap

* Remove text

* Bumping up that coverage

* tidy

* lint

* update snap

* Use a const

* fix imports

* Remove import in e2e test

* whoops
2026-01-20 12:29:18 +00:00
ElementRobot b7a2e8c64e [create-pull-request] automated change (#31795)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-20 08:59:40 +00:00
Aditya Cherukuru 93a8b67ed0 Fix flaky Jest test: RoomView search results (#31785)
Signed-off-by: aditya-cherukuru <cherukuru.aditya01@gmail.com>
2026-01-19 13:31:48 +00:00
Michael Telatynski 3b08b5c582 Improve icon rendering accessibility (#31776)
* Switch to rendered svg for Field select decoration instead of SVG mask

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

* Replace warning.svg with Compound icon

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

* Replace device kind icons with Compound

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

* Draw notification badge decoration using SVG rather than CSS masks

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

* Replace {collapse,expand}-message icons with Compound

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

* Remove stale icon prefetch

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

* Render icons in AddExistingToSpaceDialog using SVGs rather than CSS masks

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

* Render icons in Jitsi landing page using SVGs rather than CSS masks

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

* Delint

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

* Update snapshots

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

* Update screenshot

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

* Fix field label

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

* Update snapshots

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

* Add tests

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

* Revert icon colour

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-19 13:27:09 +00:00
Michael Telatynski f236c26356 Fix avatar decorations in thread activity centre not being atop avatar (#31789)
* Fix avatar decorations in thread activity centre not being atop avatar

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

* Delint

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>
2026-01-19 12:10:24 +00:00
Michael Telatynski b8ad0b93db Fix room settings roles tab getting confused if power level change fails (#31768)
* Fix room settings roles tab getting confused if power level change fails

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

* Iterate

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-19 12:08:42 +00:00
Florian Duros 3f021472c2 doc: fix typo in deprecated-modules.md file (#31788) 2026-01-19 10:28:59 +00:00
ElementRobot 1c42173e5b [create-pull-request] automated change (#31786)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-19 09:23:52 +00:00
Michael Telatynski 0b91be5a78 Update CODEOWNERS for i18n strings
Added CODEOWNERS entries for shared components translations.
2026-01-19 09:23:32 +00:00
ElementRobot c91a24f17b [create-pull-request] automated change (#31782)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-18 06:22:29 +00:00
ElementRobot 20de09e80f [create-pull-request] automated change (#31781)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-17 06:21:44 +00:00
Richard van der Hoff 52748d6d35 Minor corrections on shared-components/README.md (#31741)
* Minor corrections shared-components-README.md

a few minor edits and clarifications

* Wrap lines at 80 characters

Mostly, this means that diff comments on PRs work better.

* Update packages/shared-components/README.md

* Apply suggestion from @richvdh
2026-01-16 18:13:57 +00:00
Richard van der Hoff 92a6db5912 Show "Bob shared this message" on messages shared via MSC4268 (#31684)
* Factor out E2ePadlock to its own file

* Show "Bob shared this message" on messages shared via MSC4268

If we received the keys for a given message from another user, indicate that in
the timeline, rather than just saying "authenticity not guaranteed"

* Apply suggestions from code review

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

* Address review comments

* Move E2ePadlock to shared-components

* update snapshots

* Revert "update snapshots"

This reverts commit 751e31f9db901fda085143c98e5dffa3d2b4c099.

* Revert "Move E2ePadlock to shared-components"

This reverts commit 172ef9f70ab26fd36b0ac854379cfd3371d22c18.

---------

Co-authored-by: Florian Duros <florianduros@element.io>
2026-01-16 17:22:42 +00:00
Florian Duros d9a4858b1d doc: update module doc (#31775) 2026-01-16 16:06:24 +00:00
Florian Duros fbac316991 doc: add shared component mention in CONTRIBUTING.md (#31772) 2026-01-16 15:13:26 +00:00
Florian Duros b9638695b7 Update compound to 8.3.5 (#31736)
* chore: update compound to `8.3.5`

* refactor: remove incorrect color in shared components

* test: update shared components snapshots

* test: update shared component screenshots

* test: update EW snapshots

* test: update EW screenshots

* test: update snapshot
2026-01-16 14:30:08 +00:00
ElementRobot e2dad68169 [create-pull-request] automated change (#31753)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-16 11:45:30 +00:00
Michael Telatynski 466f60ead5 Update the way we render icons for accessibility (#31731)
* Switch to Compound icons to replace old icons

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

* Apply same treatment to missed icons

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

* Iterate

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

* Update snapshots

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

* Remove duplicated icon

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

* Update icon

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

* Update snapshots

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

* Switch from css masks to rendering svg in ImageView

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

* Switch from css masks to rendering svg in ExtensionsCard

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

* Switch from css masks to rendering svg in LegacyRoomListHeader

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

* Switch from css masks to rendering svg in ImageSizePanel

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

* Switch from css masks to rendering svg in LegacyRoomList

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

* Remove icon from CreateSecretStorageDialog title

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

* Switch from css masks to rendering svg in LiveContentSummary

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

* Switch from css masks to rendering svg in RoomCallBanner

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

* Switch from css masks to rendering svg in NonUrgentEchoFailureToast

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

* Switch from css masks to rendering svg in LegacyCallViewHeader

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

* Switch from css masks to rendering svg in CallEvent

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

* Delint

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

* Iterate

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

* Update screenshots

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

* Replace dark-light-mode.svg with Compound

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

* Draw stop icon using svg rather than square mask

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

* Replace masks in RoomSublist with SVG icons

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

* Replace masks with SVG icons in LegacyCall views

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

* Replace masks with SVG icons in EventTile

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

* Replace masks with SVG icons in ForwardDialog

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

* Remove redundant css style

The `::before` has no content so is never rendered

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

* delint

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

* Update tests

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

* Update playwright tests & 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>

* Remove snapshot as it causes issues

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

* Delint

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

* More tests

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-16 11:21:57 +00:00
Michael Telatynski 28464b4d12 Call shared-components i18n scripts from web's i18n scripts (#31760)
So that localazy_download.yaml sorts & lints both

This will be cleaner in a proper monorepo where the root package.json isn't its own project

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-16 10:19:15 +00:00
Florian Duros 420576f4ae doc: add missing entries to doc/SUMMARY.md (#31750) 2026-01-16 10:09:14 +00:00
Philip Örnfeldt e811cf0f84 Custom themes now import highlights in css (#31758)
Added import for codeblock highlights in css for custom themes

Signed-off-by: Örnfeldt Philip (66140321) <philip.ornfeldt@forsakringskassan.se>
2026-01-16 10:00:18 +00:00
Michael Telatynski 7e5a3a530d Switch from css masks to rendering svg (#31681)
* Switch to Compound icons to replace old icons

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

* Apply same treatment to missed icons

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

* Iterate

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

* Update snapshots

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

* Remove duplicated icon

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

* Update icon

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

* Update snapshots

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

* Switch from css masks to rendering svg in ImageView

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

* Switch from css masks to rendering svg in ExtensionsCard

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

* Switch from css masks to rendering svg in LegacyRoomListHeader

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

* Switch from css masks to rendering svg in ImageSizePanel

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

* Switch from css masks to rendering svg in LegacyRoomList

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

* Remove icon from CreateSecretStorageDialog title

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

* Switch from css masks to rendering svg in LiveContentSummary

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

* Switch from css masks to rendering svg in RoomCallBanner

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

* Switch from css masks to rendering svg in NonUrgentEchoFailureToast

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

* Switch from css masks to rendering svg in LegacyCallViewHeader

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

* Switch from css masks to rendering svg in CallEvent

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

* Delint

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

* Iterate

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>
2026-01-16 09:52:31 +00:00
ElementRobot f9778fcbd3 [create-pull-request] automated change (#31752)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-16 06:23:14 +00:00
ElementRobot 575b3b5400 Localazy Download (#31595)
* [create-pull-request] automated change

* chore: format i18n files

---------

Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
2026-01-15 10:52:53 +00:00
Florian Duros cac682247c Use correct translation for url preview settings (#31740)
* fix: use correct translation for url preview settings

* test: update snapshot
2026-01-15 10:36:57 +00:00
David Baker 82b270616f Fix error shown if accepting a 3pid invite (#31735)
* Fix error shown if accepting a 3pid invite

If no matrix ID was bound to the email address, the code would
just throw an exception and display a very generic error.

* Unused import

* I hate you too, yarn.

* i18n

* Add test
2026-01-15 09:43:19 +00:00
Michael Telatynski 6f0cd7621b Update fetch-mock-jest to @fetch-mock/jest (#31720)
* Remove tests which assert feature_oidc_native_flow=false behaviour, that setting is long gone

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

* Upgrade fetch-mock-jest to @fetch-mock/jest

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

* Update yarn.lock

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

* Make knip happy

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

* Disable broken tests

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

* Fix shared-components tests

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

* Snapshots

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-15 09:21:25 +00:00
ElementRobot f5c6477ef7 [create-pull-request] automated change (#31739)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-15 06:23:05 +00:00
Michael Telatynski 1eb07ba750 Push docker images to oci.element.io (#31734)
For https://github.com/element-hq/serverproduct-internal/issues/1153
2026-01-14 15:59:48 +00:00
Michael Telatynski 45ed3c500b Update reference desktop icon (#31730)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-14 15:59:32 +00:00
Hugh Nimmo-Smith 7f408bd6cf Support for stable MSC4191 account management action parameter (#31701)
* Support for stable MSC4191 account management action parameter

* Pass accountManagementActionsSupported around and refactor name

* Iterate

* Iterate

* Attempt to improve clarity of action fallback

* Use name "actions supported" consistently

* Update test cases for revised default behaviour
2026-01-14 14:53:44 +00:00
Florian Duros 9cc80c5f36 doc: add shared-components readme (#31725) 2026-01-14 14:13:09 +00:00
Florian Duros bdee36ca7c doc: add recommendation to use compound typography component (#31715)
* doc: add recommendation to use compound typograpghy component

* Update code_style.md

Co-authored-by: R Midhun Suresh <hi@midhun.dev>

---------

Co-authored-by: R Midhun Suresh <hi@midhun.dev>
2026-01-14 11:07:37 +00:00
Andy Balaam 9640c330e5 Remove the count indicator from toasts (#31718) 2026-01-14 09:57:00 +00:00
ElementRobot 33764d39ba [create-pull-request] automated change (#31722)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-14 06:23:34 +00:00
RiotRobot 775332b179 Reset matrix-js-sdk back to develop branch 2026-01-13 14:52:40 +00:00
RiotRobot b2a5f4d58b Merge branch 'master' into develop 2026-01-13 14:52:11 +00:00
Aditya Cherukuru 04800c15af Fix flaky MemberListView tests (#31707)
Replace unreliable setTimeout(1000) with proper waitFor() patterns for async assertions.

Fixes element-hq/element-web#31251

Fixes element-hq/element-web#31582

Signed-off-by: aditya-cherukuru <cherukuru.aditya01@gmail.com>
2026-01-13 12:43:20 +00:00
Hugh Nimmo-Smith fb060721dc Support for stable m.oauth UIA stage from MSC4312 (#31704)
* Support for stable m.oauth UIA stage from MSC4312

* Unit tests

* readonly props
2026-01-13 12:18:03 +00:00
Michael Telatynski f079224dd6 Remove old pro pipeline trigger (#31677)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-13 11:41:03 +00:00
ElementRobot 5e8024009c [create-pull-request] automated change (#31706)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-13 06:23:01 +00:00
Will Hunt 2e6cf8734b Refactor RoomStatusBar into MVVM (#31523)
* Refactor RoomStatusBar into MVVM

* cleanup

* updated snaps

* More cleanup

* fix loop

* fixup

* drop comment

* lint

* cleanup console statements

* Starting to move to a MVVM v2 component.

* extra

* Refactor as a shared-componend / MVVM v2

* some cleanup

* i18n for banner

* remove removed css

* Update playwright tests to have a two stage on the consent bar.

* Update snaps

* Update snapshots

* cleanup

* update snaps

* refactor to use enum

* fix slight differences in pw snaps

* Add unit tests

* fix snaps

* snaps updated

* more test cleanups

* fix snaps

* fixed now?

* Disable animationsq

* lint lint lint

* remove console

* lint

* fix snap

* Refactor based on review comments.

* update view model test

* oops!

* fix snap

* Update snaps

* snap snap snap

* switch to a const map of strings

* Use this.disposables

* Update translations to be inside shared-components

* fix the tac

* Also retry

* Cleanup

* update snaps

* update other snaps

* snap updates
2026-01-12 21:13:15 +00:00
Michael Telatynski d9be851965 Fix space settings visibility tab crashing (#31703)
* Fix space settings visibility tab crashing

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

* Add test

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

* Remove spurious semi-colon

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-12 17:08:20 +00:00
Hugh Nimmo-Smith 2db10037f2 Fix links to contributing and review guidelines in PR template (#31702)
* Fix links to contributing and review guidelines in PR

* Make links absolute
2026-01-12 14:08:04 +00:00
ElementRobot 133951b663 [create-pull-request] automated change (#31695)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-10 06:22:26 +00:00
Will Hunt 06f70d1d7c Ensure we react to RTC transport changes in useRoomCall (#31691) 2026-01-09 13:33:47 +00:00
Will Hunt 7ad6b4b411 Ensure correct focus configuration for Element Call before allowing users to call. (#31490)
* fixup type

* Validate Element Call foci config

* revert changes

* Split out logic to CallStore so we don't repeat checks.

* Refactor to use CallStore so we only fetch once.

* Add test for useRoomCall

* lint

* Ensure we enable MatrixRTC when configuring element call.

* fix test

* Update @element-hq/element-web-playwright-common to 2.2.2 and enable matrix rtc

* lint

* Ensure call is configured for header test

* type

* Improve coverage

* Update based on feedback

* fix type
2026-01-09 12:04:31 +00:00
ElementRobot 239527996a [create-pull-request] automated change (#31688)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-09 10:41:14 +00:00
renovate[bot] 795780da66 Update dependency typescript to v5.9.3 (#30492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-08 19:23:40 +00:00
Michael Telatynski ce741f055c Switch to Compound icons to replace old icons (#31667)
* Switch to Compound icons to replace old icons

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

* Apply same treatment to missed icons

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

* Iterate

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

* Update snapshots

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

* Remove duplicated icon

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

* Update icon

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>
2026-01-08 15:50:02 +00:00
R Midhun Suresh 7d72775af9 Add more MVVM documentation (#31680)
* Move benefits section to the top

* Improve existing doc

* Add more documentation
2026-01-08 12:59:40 +00:00
Michael Telatynski 540d71f49c Fix emoji font in emoji picker header buttons (#31679)
* Fix emoji font in emoji picker header buttons

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

* Update screnshots

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-08 12:58:02 +00:00
Michael Telatynski 220e93596a Switch from svg masks to svg rendering in more places (#31652)
* Replace icons with Compound alternatives

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

* Remove unused icon

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

* Replace more icons with Compound alternatives

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

* Swap for outline icons in spotlight & update screenshots

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

* Switch emoji picker to use emoji for header icons

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

* Update screenshot

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

* Update football emoji

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

* Switch from svg masks to svg rendering in ExtensionsCard

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

* Switch from svg masks to svg rendering in BaseCard

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

* Switch from svg masks to svg rendering in EmojiPicker

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

* Switch from svg masks to svg rendering in Spotlight

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>

* Tweak emoji and fix disabled state

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

* Switch from svg masks to svg rendering in HomePage

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

* Switch from svg masks to svg rendering in DecoratedRoomAvatar

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

* Switch from svg masks to svg rendering in SpaceRoomView

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

* Switch from svg masks to svg rendering in DialPadBackspaceButton

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

* Switch from svg masks to svg rendering in NewRoomIntro

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

* Switch from svg masks to svg rendering in SpaceCreateMenu

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

* Switch from svg masks to svg rendering in InlineTermsAgreement

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

* Switch from svg masks to svg rendering in PollCreateDialog

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

* Switch from svg masks to svg rendering in ServerPicker

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

* Revert size change

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

* Delint

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>
2026-01-08 11:20:55 +00:00
Aditya Cherukuru 2c90eee2dd chore: use matrix-sort-i18n instead of jq for i18n:sort (#31669)
* chore: use matrix-sort-i18n instead of jq for i18n:sort

Signed-off-by: aditya-cherukuru <cherukuru.aditya01@gmail.com>

* chore: fix prettier formatting in package.json

Signed-off-by: aditya-cherukuru <cherukuru.aditya01@gmail.com>

---------

Signed-off-by: aditya-cherukuru <cherukuru.aditya01@gmail.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-08 11:12:00 +00:00
Michael Telatynski edd4eab195 Switch from svg masks to svg rendering in more places (#31650)
* Replace icons with Compound alternatives

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

* Remove unused icon

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

* Replace more icons with Compound alternatives

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

* Swap for outline icons in spotlight & update screenshots

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

* Switch emoji picker to use emoji for header icons

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

* Update screenshot

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

* Update football emoji

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

* Switch from svg masks to svg rendering in ExtensionsCard

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

* Switch from svg masks to svg rendering in BaseCard

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

* Switch from svg masks to svg rendering in EmojiPicker

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

* Switch from svg masks to svg rendering in Spotlight

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>

* Tweak emoji and fix disabled state

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

* Revert size change

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

* Delint

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

* Add test

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-08 10:44:54 +00:00
ElementRobot 15c409491d [create-pull-request] automated change (#31676)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-08 06:22:45 +00:00
Michael Telatynski d060b77e8f Update notification icons using Compound icons (#31671)
* Update notification icons using Compound icons

For https://element-io.atlassian.net/browse/PSB-968

This removes icons in places where we only have on/off variants rather than the previous 4

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

* Update tests

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

* Update screenshot

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-07 15:58:28 +00:00
Michael Telatynski 6e9d168dd2 Exclude Upload* workflows from blocking develop CD (#31674) 2026-01-07 15:28:03 +00:00
Michael Telatynski cb7e8f4910 Memoise ListView context (#31668)
* Update npm non-major dependencies

* Make types happier

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

* Memoise ListView context

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

* Discard changes to yarn.lock

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-07 15:05:14 +00:00
renovate[bot] a5af6cf23f Update dependency @element-hq/element-web-playwright-common to v2.2.0 (#31659)
* Update dependency @element-hq/element-web-playwright-common to v2.2.0

* Update playwright-common

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

* Update playwright-common s'more

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>
2026-01-07 14:45:03 +00:00
renovate[bot] 9db08a4574 Update dependency matrix-web-i18n to v3.5.0 (#31660)
* Update dependency matrix-web-i18n to v3.5.0

* Update matrix-web-i18n

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

* Update matrix-web-i18n in shared-components too

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

* matrix-web-i18n is now ESM

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>
2026-01-07 14:15:03 +00:00
David Baker 2f817f32ce Fix shared components i18n check (#31672)
* Remove extra encryption strings

Found their way in during the i18n changes

* Remove log level silent

to try & work out why it only fails on CI

* Try giving it its own prettierrc

so the import is correct

* Comment why it needs its own prettierrc & put log level silent back

* Comment the right .prettierrc
2026-01-07 12:42:30 +00:00
David Baker 13696af194 Split translations between EW and shared components (#31441)
* Split translations between EW and shared components

Uses update module API with global TranslationKey type that can be
overridden.

WIP.

* Removed the wrong script (for now)

* Add the type files

* Add shared components i18n file

* More i18n strings

* Add i18n check for shared conmponents

* Needs a different name

* rerun i18n for merge from develop, fix test

* Move translated strings to shared components file

NB. there are lots of removed strings for a few languages where we
seem to have hit a localazy bug or something where the key/value
for plurals got switched, making the translations invalid. They've
been missing for a while so I'm removing them rather than trying to
restore them,

* Add shared components files to localazy

* Merge element web & shared component translations

for the built app

* Use right translations for shared component tests

and fix missign en_EN strings

* Pull shared components translations too

* Fix/disable warnings

* We can now remove the build:res call

...right? (right?)

* Remove webpack import for languages index

..and just load it using a relative path which we do for the individual
language files and also did anyway for the index because even in non-test
it was an object, not a string, so we always usesd the 'test' code path.

* Make the storybook language selector work

...without referring to the parent app's files

* Revert unnecessary yarn lock change

* Typo

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>

* Add comment on why we use merge

* Fix localazy download config

to actually put the translations in the right place

* Better typescript syntax

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>

* Watch both translations files

---------

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-07 11:49:01 +00:00
renovate[bot] 4ee04d0661 Update npm non-major dependencies (#31662)
* Update npm non-major dependencies

* Make types happier

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>
2026-01-07 10:52:05 +00:00
Michael Telatynski 7f057faaad Switch emoji picker to use emoji for header icons (#31645)
* Replace icons with Compound alternatives

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

* Remove unused icon

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

* Replace more icons with Compound alternatives

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

* Swap for outline icons in spotlight & update screenshots

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

* Switch emoji picker to use emoji for header icons

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

* Update screenshot

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

* Update football emoji

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

* Tweak emoji and fix disabled state

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-07 10:22:26 +00:00
ElementRobot d7a58216ae [create-pull-request] automated change (#31670)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-07 07:27:41 +00:00
ElementRobot 4dd128fd18 [create-pull-request] automated change (#31580)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2026-01-06 16:11:27 +00:00
Valere Fedronic 126b216d44 fix flaky test by waiting for chat panel before counting messages (#31633)
* fix flaky test by waiting for chat panel before counting messages

* improve test stability by waiting for visibility before elements count()
2026-01-06 15:49:48 +00:00
renovate[bot] b8ecc0e07e Update GitHub Artifact Actions (#31665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:41:37 +00:00
renovate[bot] 4668c15ea1 Update dependency @formatjs/intl-segmenter to v12 (#31664)
* Update dependency @formatjs/intl-segmenter to v12

* Update imports

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>
2026-01-06 15:22:01 +00:00
renovate[bot] 867a6850e4 Update octokit/graphql-action action to v3.0.2 (#31657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:19:25 +00:00
renovate[bot] adc5ee22cc Update peter-evans/create-pull-request action to v8 (#31666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:09:55 +00:00
renovate[bot] aa6509e01c Update storybook (#31658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:08:29 +00:00
renovate[bot] 770ff42496 Update eslint-plugins (#31661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:07:03 +00:00
renovate[bot] db352ef876 Update actions/cache action to v5 (#31663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:04:37 +00:00
renovate[bot] acca876697 Update dependency caniuse-lite to v1.0.30001762 (#31656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 15:02:57 +00:00
renovate[bot] d9e7948920 Update docker/setup-buildx-action digest to 8d2750c (#31653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 14:57:30 +00:00
renovate[bot] d7feaa0b2a Update nginxinc/nginx-unprivileged:alpine-slim Docker digest to 2c49851 (#31654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 14:55:34 +00:00
renovate[bot] ebd7cdb09d Update Node.js to 32bde4f (#31655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 14:55:33 +00:00
Valere Fedronic df091a7d3e rtc: use new API in tests (instead of deprecated calls) (#31649) 2026-01-06 14:13:48 +00:00
Michael Telatynski fea10b3c19 Replace icons with Compound alternatives (#31642)
* Replace icons with Compound alternatives

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

* Remove unused icon

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

* Replace more icons with Compound alternatives

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

* Swap for outline icons in spotlight & update screenshots

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

* Update screenshot

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2026-01-06 13:55:04 +00:00
563 changed files with 13812 additions and 6132 deletions
+4 -1
View File
@@ -25,7 +25,10 @@
# 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
/packages/shared-components/src/i18n/strings
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
/packages/shared-components/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
# Ignore the synapse & mas plugins as this is updated by GHA for docker image updating
/playwright/testcontainers/synapse.ts
/playwright/testcontainers/mas.ts
+1 -1
View File
@@ -2,7 +2,7 @@
## Checklist
- [ ] I have read through [review guidelines](../docs/review.md) and [CONTRIBUTING.md](../CONTRIBUTING.md).
- [ ] I have read through [review guidelines](https://github.com/element-hq/element-web/blob/develop/docs/review.md) and [CONTRIBUTING.md](https://github.com/element-hq/element-web/blob/develop/CONTRIBUTING.md).
- [ ] Tests written for new code (and old code if feasible).
- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
- [ ] Linter and other CI checks pass.
+1 -1
View File
@@ -66,7 +66,7 @@ jobs:
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
- name: Upload Artifact
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: webapp-${{ matrix.image }}
path: webapp
+1 -1
View File
@@ -62,7 +62,7 @@ jobs:
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: element-web.deb
path: element-web.deb
+2 -2
View File
@@ -53,7 +53,7 @@ jobs:
- run: mv dist/element-*.tar.gz dist/develop.tar.gz
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: webapp
path: dist/develop.tar.gz
@@ -104,7 +104,7 @@ jobs:
running-workflow-name: "Build & Deploy develop.element.io"
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages).)*$
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages|Upload).)*$
# We keep the latest develop.tar.gz on R2 instead of relying on the github artifact uploaded earlier
# as the expires after 24h and requires auth to download.
+53 -29
View File
@@ -32,25 +32,10 @@ jobs:
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
with:
install: true
- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and load
id: test-build
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
@@ -102,12 +87,64 @@ jobs:
images: |
vectorim/element-web
ghcr.io/element-hq/element-web
oci-push.vpn.infra.element.io/element-web
tags: |
type=ref,event=branch
type=ref,event=tag
flavor: |
latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Connect to Tailscale
uses: tailscale/github-action@53acf823325fe9ca47f4cdaa951f90b4b0de5bb9 # v4
if: github.event_name != 'pull_request'
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
audience: ${{ secrets.TS_AUDIENCE }}
tags: tag:github-actions
- name: Compute vault jwt role name
id: vault-jwt-role
if: github.event_name != 'pull_request'
run: |
echo "role_name=github_service_management_$( echo "${{ github.repository }}" | sed -r 's|[/-]|_|g')" | tee -a "$GITHUB_OUTPUT"
- name: Get team registry token
id: import-secrets
uses: hashicorp/vault-action@4c06c5ccf5c0761b6029f56cfb1dcf5565918a3b # v3
if: github.event_name != 'pull_request'
with:
url: https://vault.infra.ci.i.element.dev
role: ${{ steps.vault-jwt-role.outputs.role_name }}
path: service-management/github-actions
jwtGithubAudience: https://vault.infra.ci.i.element.dev
method: jwt
secrets: |
services/web-repositories/secret/data/oci.element.io username | OCI_USERNAME ;
services/web-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
- name: Login to oci.element.io Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
if: github.event_name != 'pull_request'
with:
registry: oci-push.vpn.infra.element.io
username: ${{ steps.import-secrets.outputs.OCI_USERNAME }}
password: ${{ steps.import-secrets.outputs.OCI_PASSWORD }}
- name: Build and push
id: build-and-push
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
@@ -139,16 +176,3 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: vectorim/element-web
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4
if: github.event_name != 'pull_request'
with:
repository: element-hq/element-web-pro
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
event-type: image-built
# Stable way to determine the :version
client-payload: |-
{
"base-ref": "${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}"
}
@@ -25,7 +25,7 @@ jobs:
actions: read
steps:
- name: Download HTML report
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
+6 -6
View File
@@ -74,7 +74,7 @@ jobs:
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
- name: Upload Artifact
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: webapp
path: webapp
@@ -128,7 +128,7 @@ jobs:
repository: element-hq/element-web
- name: 📥 Download artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: webapp
path: webapp
@@ -147,7 +147,7 @@ jobs:
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
- name: Cache playwright binaries
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
id: playwright-cache
with:
path: ~/.cache/ms-playwright
@@ -172,7 +172,7 @@ jobs:
- name: Upload blob report to GitHub Actions Artifacts
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
path: blob-report
@@ -212,7 +212,7 @@ jobs:
- name: Download blob reports from GitHub Actions Artifacts
if: inputs.skip != true
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: all-blob-reports-*
path: all-blob-reports
@@ -228,7 +228,7 @@ jobs:
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
- name: Upload HTML report
if: always() && inputs.skip != true
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: html-report
path: playwright-report
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
Exercise caution. Use test accounts.
- name: 📥 Download artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
@@ -32,7 +32,7 @@ jobs:
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/playwright-image-updates
@@ -27,7 +27,7 @@ jobs:
run: "sudo apt-get install -y tree"
- name: Download Diffs
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
@@ -44,7 +44,7 @@ jobs:
run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
- name: Cache playwright binaries
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
id: playwright-cache
with:
path: ~/.cache/ms-playwright
@@ -60,7 +60,7 @@ jobs:
- name: Upload received images & diffs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: received-images
path: packages/shared-components/playwright/shared-component-received
+11 -2
View File
@@ -41,8 +41,8 @@ jobs:
- name: Typecheck Shared Components
run: "yarn --cwd packages/shared-components run lint:types"
i18n_lint:
name: "i18n Check"
i18n_lint_ew:
name: "i18n Check (Element Web)"
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
permissions:
pull-requests: read
@@ -59,6 +59,15 @@ jobs:
devtools|settings|elementCallUrl
labs|sliding_sync_description
i18n_lint_shared_components:
name: "i18n Check (Shared Components)"
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
permissions:
pull-requests: read
with:
path: "packages/shared-components"
hardcoded-words: "Element"
rethemendex_lint:
name: "Rethemendex Check"
runs-on: ubuntu-24.04
+4 -4
View File
@@ -55,7 +55,7 @@ jobs:
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
- name: Jest Cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
with:
path: /tmp/jest_cache
key: ${{ hashFiles('**/yarn.lock') }}
@@ -84,7 +84,7 @@ jobs:
- name: Upload Artifact
if: env.ENABLE_COVERAGE == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage-${{ matrix.runner }}
path: |
@@ -136,7 +136,7 @@ jobs:
run: "yarn install"
- name: Jest Cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5
with:
path: /tmp/jest_cache
key: ${{ hashFiles('**/yarn.lock') }}
@@ -159,7 +159,7 @@ jobs:
- name: Upload Artifact
if: env.ENABLE_COVERAGE == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage-sharedcomponents
path: |
@@ -9,7 +9,7 @@ jobs:
name: Move PRs asking for design review to the design board
runs-on: ubuntu-24.04
steps:
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: find_team_members
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
@@ -52,7 +52,7 @@ jobs:
fi
env:
TEAM: "design"
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: add_to_project
if: steps.any_matching_reviewers.outputs.match == 'true'
with:
@@ -76,7 +76,7 @@ jobs:
name: Move PRs asking for design review to the design board
runs-on: ubuntu-24.04
steps:
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: find_team_members
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
@@ -119,7 +119,7 @@ jobs:
fi
env:
TEAM: "product"
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: add_to_project
if: steps.any_matching_reviewers.outputs.match == 'true'
with:
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
run: "yarn update:jitsi"
- name: Create Pull Request
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/jitsi-update
+31
View File
@@ -1,3 +1,34 @@
Changes in [1.12.9](https://github.com/element-hq/element-web/releases/tag/v1.12.9) (2026-01-27)
================================================================================================
## ✨ Features
* Allow local log downloads when a rageshake URL is not configured. ([#31716](https://github.com/element-hq/element-web/pull/31716)). Contributed by @Half-Shot.
* Improve icon rendering accessibility ([#31776](https://github.com/element-hq/element-web/pull/31776)). Contributed by @t3chguy.
* Show "Bob shared this message" on messages shared via MSC4268 ([#31684](https://github.com/element-hq/element-web/pull/31684)). Contributed by @richvdh.
* Update the way we render icons for accessibility ([#31731](https://github.com/element-hq/element-web/pull/31731)). Contributed by @t3chguy.
* Switch from css masks to rendering svg ([#31681](https://github.com/element-hq/element-web/pull/31681)). Contributed by @t3chguy.
* Support for stable MSC4191 account management action parameter ([#31701](https://github.com/element-hq/element-web/pull/31701)). Contributed by @hughns.
* Support for stable m.oauth UIA stage from MSC4312 ([#31704](https://github.com/element-hq/element-web/pull/31704)). Contributed by @hughns.
* Switch to Compound icons to replace old icons ([#31667](https://github.com/element-hq/element-web/pull/31667)). Contributed by @t3chguy.
* Switch from svg masks to svg rendering in more places ([#31652](https://github.com/element-hq/element-web/pull/31652)). Contributed by @t3chguy.
* Switch from svg masks to svg rendering in more places ([#31650](https://github.com/element-hq/element-web/pull/31650)). Contributed by @t3chguy.
* Update notification icons using Compound icons ([#31671](https://github.com/element-hq/element-web/pull/31671)). Contributed by @t3chguy.
* Memoise ListView context ([#31668](https://github.com/element-hq/element-web/pull/31668)). Contributed by @t3chguy.
* Switch emoji picker to use emoji for header icons ([#31645](https://github.com/element-hq/element-web/pull/31645)). Contributed by @t3chguy.
* Replace icons with Compound alternatives ([#31642](https://github.com/element-hq/element-web/pull/31642)). Contributed by @t3chguy.
## 🐛 Bug Fixes
* Fix avatar decorations in thread activity centre not being atop avatar ([#31789](https://github.com/element-hq/element-web/pull/31789)). Contributed by @t3chguy.
* Fix room settings roles tab getting confused if power level change fails ([#31768](https://github.com/element-hq/element-web/pull/31768)). Contributed by @t3chguy.
* Custom themes now import highlights in css ([#31758](https://github.com/element-hq/element-web/pull/31758)). Contributed by @Philldomd.
* Use correct translation for url preview settings ([#31740](https://github.com/element-hq/element-web/pull/31740)). Contributed by @florianduros.
* Fix error shown if accepting a 3pid invite ([#31735](https://github.com/element-hq/element-web/pull/31735)). Contributed by @dbkr.
* Ensure correct focus configuration for Element Call before allowing users to call. ([#31490](https://github.com/element-hq/element-web/pull/31490)). Contributed by @Half-Shot.
* Fix emoji font in emoji picker header buttons ([#31679](https://github.com/element-hq/element-web/pull/31679)). Contributed by @t3chguy.
* fix flaky test by waiting for chat panel before counting messages ([#31633](https://github.com/element-hq/element-web/pull/31633)). Contributed by @BillCarsonFr.
Changes in [1.12.8](https://github.com/element-hq/element-web/releases/tag/v1.12.8) (2026-01-13)
================================================================================================
## 🦖 Deprecations
+10
View File
@@ -184,6 +184,16 @@ 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.
## Shared Components
When creating new UI components, consider whether they should be added to the shared components package (`packages/shared-components`) rather than directly in the main `src/` directory. Components should be placed in shared components if they:
- Are reusable across different parts of the application
- Could potentially be used by other Element projects (Element Desktop, Aurora, Element modules...)
- Follow established patterns and don't have tight coupling to specific application logic
For more details, see the [shared components README](./packages/shared-components/README.md).
## Attribution
Everyone who contributes anything to Matrix is welcome to be listed in the
+2 -2
View File
@@ -1,7 +1,7 @@
# syntax=docker.io/docker/dockerfile:1.20-labs@sha256:dbcde2ebc4abc8bb5c3c499b9c9a6876842bf5da243951cd2697f921a7aeb6a9
# Builder
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:5583cbe5d3347db372d9a9100eba272b548ca1f53246b9b769334bcd0eef2c26 AS builder
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:32bde4fc7635942cafb9681e5479a0ba4b2d53b279e44a67ba9303a71fecd706 AS builder
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
ARG USE_CUSTOM_SDKS=false
@@ -19,7 +19,7 @@ RUN /src/scripts/docker-package.sh
RUN cp /src/config.sample.json /src/webapp/config.json
# App
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:a6bec37058b9047ece799c01d98dc6d5aa0542b6583cc69f187652f91331a752
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:2c49851f9b34ef35567dc3cbbb56d06d1f56dbb764e75eeb4a599223ee64819c
# Need root user to install packages & manipulate the usr directory
USER root
+11
View File
@@ -194,6 +194,17 @@ To add a new translation, head to the [translating doc](docs/translating.md).
For a developer guide, see the [translating dev doc](docs/translating-dev.md).
# Extending Element Web with Modules
Element Web supports a module system that allows you to extend or modify functionality at runtime. Modules are loaded dynamically and provide a safe, predictable API for customization.
## What are modules?
Modules are extensions that can add or modify Element Web's functionality. They are:
- Built using the [`@element-hq/element-web-module-api`](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api)
- Loaded in EW via [config.json](docs/config.md#modules)
# Triaging issues
Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/element-hq/element-meta/wiki/Triage-process).
+2
View File
@@ -272,6 +272,8 @@ Inheriting all the rules of TypeScript, the following additionally apply:
18. Components should serve a single, or near-single, purpose.
19. Prefer to derive information from component properties rather than establish state.
20. Do not use `React.Component::forceUpdate`.
21. Prefer to use [compound typography components](https://compound.element.io/?path=/docs/compound-web_typography--docs) instead of raw HTML elements for text. This ensures consistent font usage and letter spacing across the app.
22. If you can't use 21, don't forget to apply the correct CSS classes for font and letter spacing.
## Stylesheets (\*.pcss = PostCSS + Plugins)
+139 -10
View File
@@ -8,7 +8,13 @@ General description of the pattern can be found [here](https://en.wikipedia.org/
If you do MVVM right, your view should be dumb i.e it gets data from the view model and merely displays it.
### Practical guidelines for MVVM in element-web
## Why are we using MVVM?
1. MVVM forces a separation of concern i.e we will no longer have large react components that have a lot of state and rendering code mixed together. This improves code readability and makes it easier to introduce changes.
2. Introduces the possibility of code reuse. You can reuse an old view model with a new view or vice versa.
3. Adding to the point above, in future you could import element-web view models to your project and supply your own views thus creating something similar to the [hydrogen sdk](https://github.com/element-hq/hydrogen-web/blob/master/doc/SDK.md).
## Practical guidelines for MVVM in element-web
A first documentation and implementation of MVVM was done in [MVVM-v1.md](MVVM-v1.md). This v1 version is now deprecated and this document describes the current implementation.
@@ -19,12 +25,12 @@ This is anywhere your data or business logic comes from. If your view model is a
#### View
1. Located in [`shared-components`](https://github.com/element-hq/element-web/tree/develop/packages/shared-components). Develop it in storybook!
2. Views are simple react components (eg: `FooView`).
3. Views use [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore) internally where the view model is the external store.
2. Views are simple react components (eg: `FooView`) with very little state and logic.
3. Views must call `useViewModel` hook with the corresponding view model passed in as argument. This allows the view to re-render when something has changed in the view model. This entire mechanism is powered by [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore).
4. Views should define the interface of the view model they expect:
```tsx
// Snapshot is the return type of your view model
// Snapshot is the data that your view-model provides which is rendered by the view.
interface FooViewSnapshot {
value: string;
}
@@ -34,16 +40,16 @@ This is anywhere your data or business logic comes from. If your view model is a
doSomething: () => void;
}
// ViewModel is a type defining the methods needed for `useSyncExternalStore`
// ViewModel is an object (usually a class) that implements both the interfaces listed above.
// https://github.com/element-hq/element-web/blob/develop/packages/shared-components/src/ViewModel.ts
type FooViewModel = ViewModel<FooViewSnapshot> & FooViewActions;
interface FooViewProps {
// Ideally the view only depends on the view model i.e you don't expect any other props here.
vm: FooViewModel;
}
function FooView({ vm }: FooViewProps) {
// useViewModel is a helper function that uses useSyncExternalStore under the hood
const { value } = useViewModel(vm);
return (
<button type="button" onClick={() => vm.doSomething()}>
@@ -82,8 +88,131 @@ This is anywhere your data or business logic comes from. If your view model is a
4. A full example is available [here](https://github.com/element-hq/element-web/blob/develop/src/viewmodels/audio/AudioPlayerViewModel.ts)
### Benefits
### `useViewModel` hook
1. MVVM forces a separation of concern i.e we will no longer have large react components that have a lot of state and rendering code mixed together. This improves code readability and makes it easier to introduce changes.
2. Introduces the possibility of code reuse. You can reuse an old view model with a new view or vice versa.
3. Adding to the point above, in future you could import element-web view models to your project and supply your own views thus creating something similar to the [hydrogen sdk](https://github.com/element-hq/hydrogen-web/blob/master/doc/SDK.md).
Your view must call this hook with the view-model as argument. Think of this as your view subscribing to the view model.<br>
This hook returns the snapshot from your view-model.
## Disposables and helper hooks
Disposables provide a mechanism for tracking and releases resources. This is necessary for avoiding memory leaks.
### Lifecycle of a view model
The lifecycle of a given view model is from its creation (usually through the constructor i.e `new FooViewModel(prop1, prop2)`) to the time the `dispose` method on it is called (eg: `fooViewModel.dispose()`). It is the responsibility of whoever creates the view model to call the dispose method when the view model is no longer necessary.
Disposable work by tracking anything that needs to be disposed of and then sequentially disposing them when `viewModel.dispose()` is called.
### How to use disposables
Consider the following scenarios:
#### Scenario 1: Your view model listens to some event on an event emitter <br>
In the example given below, how do you ensure that the listener on `props.emitter` is removed when the view model is disposed?
```ts
class FooViewModel ... {
constructor(props: Props) {
...
props.emitter.on("my-event", this.doSomething());
}
}
```
You can use disposables to remove the listener when the view-model is disposed:
```ts
class FooViewModel ... {
constructor(props: Props) {
...
this.disposables.trackListener(props.emitter, "my-event", this.doSomething());
}
}
```
#### Scenario 2: Your view model creates sub view models <br>
```ts
class FooViewModel ... {
constructor(props: Props) {
...
this.barViewModel = new BarViewModel(...);
}
}
```
Here, we want to ensure that when `FooViewModel.dispose()` is called, it also disposes any sub view models (in this case `BarViewModel`):
```ts
class FooViewModel ... {
constructor(props: Props) {
...
this.barViewModel = this.disposables.track(new BarViewModel(...));
}
}
```
#### Scenario 3: Tracking and disposing arbitrary resources <br>
A disposable is:
- a function
- an object with `dispose` method (like a view model)
You can therefore use disposables to track any resource that must be eventually relinquished, eg:
```ts
class Call {
....
public endCall();
public stopConnections();
}
class CallViewModel {
...
constructor(props: Props) {
const call = new Call();
// When the view model is disposed, the following call methods will be called
this.disposables.track(() => {
call.endCall();
call.stopConnections();
});
}
}
```
### Disposing view models from non-MVVMed react components
While we eventually want all our UI code to use MVVM, the current reality is that most of the existing code is just normal react components. We follow a bottoms up approach when it comes to moving code over to MVVM i.e we deal with child components before dealing with parent components.
This means that you need to dispose child view models from the non-MVVMed parent component.
#### Class component:
Create the view model in `componentDidMount()` and dispose the view model in `componentWillUnmount()`:
```ts
class FooComponent extends Component {
componentDidMount() {
this.barViewModel = new BarViewModel(...);
}
componentWillUnmount() {
this.barViewModel.dispose();
}
}
```
#### Functional Component:
Use the `useCreateAutoDisposedViewModel` hook:
```ts
export function FooComponent(props) {
const vm = useCreateAutoDisposedViewModel(() => new BarViewModel(...));
return <BarView vm={vm}>;
}
```
This hook will call the `dispose` method on the view model when `FooComponent` is unmounted.
+3 -1
View File
@@ -19,7 +19,7 @@
# Build
- [Customisations](customisations.md)
- [Modules](modules.md)
- [Deprecated Modules](deprecated-modules.md)
- [Native Node modules](native-node-modules.md)
# Contribution
@@ -40,6 +40,8 @@
- [Feature flags](feature-flags.md)
- [OIDC and delegated authentication](oidc.md)
- [Release Process](release.md)
- [MVVM](MVVM.md)
- [Settings](settings.md)
# Deep dive
+1
View File
@@ -407,6 +407,7 @@ If you run your own rageshake server to collect bug reports, the following optio
1. `bug_report_endpoint_url`: URL for where to submit rageshake logs to. Rageshakes include feedback submissions and bug reports. When
not present in the config, the app will disable all rageshake functionality. Set to `https://rageshakes.element.io/api/submit` to submit
rageshakes to us, or use your own rageshake server.
You may also set the value to `"local"` if you wish to only store logs locally, in order to download them for debugging.
2. `uisi_autorageshake_app`: If a user has enabled the "automatically send debug logs on decryption errors" flag, this option will be sent
alongside the rageshake so the rageshake server can filter them by app name. By default, this will be `element-auto-uisi`
(in contrast to other rageshakes submitted by the app, which use `element-web`).
@@ -1,4 +1,7 @@
# Module system
# Deprecated Module system
> [!CAUTION]
> DEPRECATED. Use [Element web module api](https://github.com/element-hq/element-modules/tree/main/packages/element-web-module-api) instead.
The module system in Element Web is a way to add or modify functionality of Element Web itself, bundled at compile time
for the app. This means that modules are loaded as part of the `yarn build` process but have an effect on user experience
+2 -3
View File
@@ -11,7 +11,7 @@ import { env } from "process";
import type { Config } from "jest";
const config: Config = {
testEnvironment: "jsdom",
testEnvironment: "jest-fixed-jsdom",
testEnvironmentOptions: {
url: "http://localhost/",
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
@@ -39,11 +39,10 @@ const config: Config = {
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
"^!!raw-loader!.*": "jest-raw-loader",
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
"counterpart": "<rootDir>/node_modules/counterpart",
},
transformIgnorePatterns: [
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|is-ip|ip-regex|super-regex|function-timeout|time-span|convert-hrtime|clone-regexp|is-regexp)).+$",
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|is-ip|ip-regex|super-regex|function-timeout|time-span|convert-hrtime|clone-regexp|is-regexp|matrix-web-i18n)).+$",
],
collectCoverageFrom: [
"<rootDir>/src/**/*.{js,ts,tsx}",
+2 -1
View File
@@ -43,7 +43,8 @@ export default {
// Embedded into webapp
"@element-hq/element-call-embedded",
// Transitive dep of jest
"jsdom",
"@jest/globals",
"vitest-environment-jest-fixed-jsdom",
// Used by matrix-js-sdk, which means we have to include them as a
// dependency so that // we can run `tsc` (since we import the typescript
+16
View File
@@ -18,6 +18,18 @@
"file": "element-web.json",
"excludes": ["src/i18n/strings/en_EN.json"],
"lang": "${autodetectLang}"
},
{
"pattern": "packages/shared-components/src/i18n/strings/en_EN.json",
"file": "shared-components.json",
"lang": "inherited"
},
{
"group": "existing",
"pattern": "packages/shared-components/src/i18n/strings/*.json",
"file": "shared-components.json",
"excludes": ["packages/shared-components/src/i18n/strings/en_EN.json"],
"lang": "${autodetectLang}"
}
]
},
@@ -27,6 +39,10 @@
{
"conditions": "equals: ${file}, element-web.json",
"output": "src/i18n/strings/${langLsrUnderscore}.json"
},
{
"conditions": "equals: ${file}, shared-components.json",
"output": "packages/shared-components/src/i18n/strings/${langLsrUnderscore}.json"
}
],
"includeSourceLang": "${includeSourceLang|false}",
+17 -20
View File
@@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.12.8",
"version": "1.12.9",
"description": "Element: the future of secure communication",
"author": "New Vector Ltd.",
"repository": {
@@ -29,9 +29,9 @@
"UserFriendlyError"
],
"scripts": {
"i18n": "matrix-gen-i18n src res packages/shared-components/src && yarn i18n:sort && yarn i18n:lint",
"i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json",
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
"i18n": "matrix-gen-i18n src res && yarn i18n:sort && yarn i18n:lint",
"i18n:sort": "matrix-sort-i18n src/i18n/strings/en_EN.json && yarn --cwd packages/shared-components i18n:sort",
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null && yarn --cwd packages/shared-components i18n:lint",
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
"make-component": "node scripts/make-react-component.js",
"rethemendex": "./res/css/rethemendex.sh",
@@ -69,12 +69,12 @@
"postinstall": "patch-package"
},
"resolutions": {
"**/pretty-format/react-is": "19.2.1",
"**/pretty-format/react-is": "19.2.3",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"oidc-client-ts": "3.4.1",
"jwt-decode": "4.0.0",
"caniuse-lite": "1.0.30001759",
"caniuse-lite": "1.0.30001762",
"testcontainers": "^11.0.0",
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
@@ -85,15 +85,15 @@
"@element-hq/web-shared-components": "link:packages/shared-components",
"@fontsource/fira-code": "^5",
"@fontsource/inter": "^5",
"@formatjs/intl-segmenter": "^11.5.7",
"@formatjs/intl-segmenter": "^12.0.0",
"@matrix-org/analytics-events": "^0.30.0",
"@matrix-org/emojibase-bindings": "^1.5.0",
"@matrix-org/react-sdk-module-api": "^2.4.0",
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^10.0.0",
"@types/png-chunks-extract": "^1.0.2",
"@vector-im/compound-design-tokens": "6.4.3",
"@vector-im/compound-web": "^8.3.4",
"@vector-im/compound-design-tokens": "6.8.0",
"@vector-im/compound-web": "^8.3.5",
"@vector-im/matrix-wysiwyg": "2.40.0",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
@@ -129,7 +129,7 @@
"lodash": "^4.17.21",
"maplibre-gl": "^5.0.0",
"matrix-encrypt-attachment": "^1.0.3",
"matrix-js-sdk": "40.0.0",
"matrix-js-sdk": "40.1.0",
"matrix-widget-api": "^1.15.0",
"memoize-one": "^6.0.0",
"mime": "^4.0.4",
@@ -137,7 +137,7 @@
"opus-recorder": "^8.0.3",
"pako": "^2.0.3",
"png-chunks-extract": "^1.0.0",
"posthog-js": "1.302.2",
"posthog-js": "1.313.0",
"qrcode": "1.5.4",
"re-resizable": "6.11.2",
"react": "^19.0.0",
@@ -181,7 +181,8 @@
"@babel/runtime": "^7.12.5",
"@casualbot/jest-sonar-reporter": "2.5.0",
"@element-hq/element-call-embedded": "0.16.3",
"@element-hq/element-web-playwright-common": "^2.0.0",
"@element-hq/element-web-playwright-common": "2.2.3",
"@fetch-mock/jest": "^0.2.20",
"@peculiar/webcrypto": "^1.4.3",
"@playwright/test": "1.57.0",
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
@@ -210,7 +211,6 @@
"@types/minimist": "^1.2.5",
"@types/modernizr": "^3.5.3",
"@types/node": "18",
"@types/node-fetch": "^2.6.2",
"@types/pako": "^2.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "19.2.7",
@@ -231,7 +231,6 @@
"chokidar": "^5.0.0",
"concurrently": "^9.0.0",
"copy-webpack-plugin": "^13.0.0",
"core-js": "^3.38.1",
"cronstrue": "^3.0.0",
"css-loader": "^7.0.0",
"css-minimizer-webpack-plugin": "^7.0.0",
@@ -250,25 +249,23 @@
"eslint-plugin-unicorn": "^56.0.0",
"express": "^5.0.0",
"fake-indexeddb": "^6.0.0",
"fetch-mock": "9.11.0",
"fetch-mock-jest": "^1.5.1",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^5.5.3",
"husky": "^9.0.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^30.0.0",
"jest-canvas-mock": "^2.5.2",
"jest-environment-jsdom": "^30.0.0",
"jest-environment-jsdom": "^30.2.0",
"jest-fixed-jsdom": "^0.0.11",
"jest-mock": "^30.0.0",
"jest-raw-loader": "^1.0.1",
"jsqr": "^1.4.0",
"knip": "^5.36.2",
"lint-staged": "^16.0.0",
"matrix-web-i18n": "^3.2.1",
"matrix-web-i18n": "3.5.2",
"mini-css-extract-plugin": "2.9.2",
"minimist": "^1.2.6",
"modernizr": "^3.12.0",
"node-fetch": "^2.6.7",
"patch-package": "^8.0.0",
"playwright-core": "^1.51.0",
"postcss": "8.4.46",
@@ -294,7 +291,7 @@
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
"terser-webpack-plugin": "^5.3.9",
"testcontainers": "^11.0.0",
"typescript": "5.8.3",
"typescript": "5.9.3",
"util": "^0.12.5",
"web-streams-polyfill": "^4.0.0",
"webpack": "^5.89.0",
@@ -0,0 +1,5 @@
// Even though this (at time of writing) is identical Element Web's
// .prettierrc.js, shared components needs its own because otherwise
// this refers to element web's copy of eslint-plugin-matrix-org which
// would require element-web's modules to be installed.
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");
@@ -10,16 +10,14 @@ import { WithTooltip, IconButton, TooltipLinkList } from "storybook/internal/com
import React from "react";
import { GlobeIcon } from "@storybook/icons";
// We can't import `shared/i18n.tsx` directly here.
// The storybook addon doesn't seem to benefit the vite config of storybook and we can't resolve the alias in i18n.tsx.
import json from "../../../webapp/i18n/languages.json";
const languages = Object.keys(json).filter((lang) => lang !== "default");
const languages = JSON.parse(process.env.STORYBOOK_LANGUAGES);
/**
* Returns the title of a language in the user's locale.
*/
function languageTitle(language: string): string {
return new Intl.DisplayNames([language], { type: "language", style: "short" }).of(language) || language;
const normalisedLang = language.toLowerCase().replace("_", "-");
return new Intl.DisplayNames([normalisedLang], { type: "language", style: "short" }).of(normalisedLang) || language;
}
export const languageAddon: Addon = {
+44 -3
View File
@@ -7,12 +7,15 @@ Please see LICENSE files in the repository root for full details.
import type { StorybookConfig } from "@storybook/react-vite";
import path from "node:path";
import fs from "node:fs";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import { mergeConfig } from "vite";
// Get a list of available languages so the language selector can display them at runtime
const languages = fs.readdirSync("src/i18n/strings").map((f) => f.slice(0, -5));
const config: StorybookConfig = {
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
staticDirs: ["../../../webapp"],
addons: ["@storybook/addon-docs", "@storybook/addon-designs", "@storybook/addon-a11y"],
framework: "@storybook/react-vite",
core: {
@@ -29,8 +32,42 @@ const config: StorybookConfig = {
$webapp: path.resolve("../../webapp"),
},
},
// Needed for counterpart to work
plugins: [nodePolyfills({ include: ["process", "util"] })],
plugins: [
// Needed for counterpart to work
nodePolyfills({ include: ["process", "util"] }),
{
name: "language-middleware",
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url === "/i18n/languages.json") {
// Dynamically generate a languages.json file based on what files are available
const langJson: Record<string, string> = {};
for (const lang of languages) {
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
const languageParts = normalizedLanguage.split("-");
if (languageParts.length === 2 && languageParts[0] === languageParts[1]) {
langJson[languageParts[0]] = `${lang}.json`;
} else {
langJson[normalizedLanguage] = `${lang}.json`;
}
}
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(langJson));
} else if (req.url?.startsWith("/i18n/")) {
// Serve the individual language files, which annoyingly can't be a simple
// static dir because the directory structure in src doesn't match what
// the app requests.
const langFile = req.url.split("/").pop();
res.setHeader("Content-Type", "application/json");
fs.createReadStream(`src/i18n/strings/${langFile}`).pipe(res);
} else {
next();
}
});
},
},
],
server: {
allowedHosts: ["localhost", ".docker.internal"],
},
@@ -42,5 +79,9 @@ const config: StorybookConfig = {
url: "https://element-hq.github.io/compound-web/",
},
},
env: (config) => ({
...config,
STORYBOOK_LANGUAGES: JSON.stringify(languages),
}),
};
export default config;
@@ -20,7 +20,7 @@ const config: TestRunnerConfig = {
// If you want to take screenshot of multiple browsers, use
// page.context().browser().browserType().name() to get the browser name to prefix the file name
const image = await page.screenshot();
const image = await page.screenshot({ animations: "disabled" });
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: `${context.id}-${process.platform}`,
+165
View File
@@ -0,0 +1,165 @@
# @element-hq/web-shared-components
Shared React components library for Element Web, Aurora, Element
modules... This package provides opinionated UI components built on top of the
[Compound Design System](https://compound.element.io) and [Compound
Web](https://github.com/element-hq/compound-web). This is not a design system
by itself, but rather a set of larger components.
## Installation in a new project
When adding this library to a new project, as well as installing
`@element-hq/web-shared-components` as normal, you will also need to add
[compound-web](https://github.com/element-hq/compound-web) as a peer
dependency:
```bash
yarn add @element-hq/web-shared-components
yarn add @vector-im/compound-web
```
(This avoids problems where we end up with different versions of compound-web in the
top-level project and web-shared-components).
## Usage
### Basic Import
Both JavaScript and CSS can be imported as follows:
```javascript
import { RoomListHeaderView, useViewModel } from "@element-hq/web-shared-components";
import "@element-hq/web-shared-components/dist/element-web-shared-components.css";
```
or in CSS file:
```css
@import url("@element-hq/web-shared-components");
```
### Using Components
There are two kinds of components in this library:
- _regular_ react component which doesn't follow specific pattern.
- _view_ component(MVVM pattern).
> [!TIP]
> These components are available in the project storybook.
#### Regular Components
These components can be used directly by passing props. Example:
```tsx
import { Flex } from "@element-hq/web-shared-components";
function MyApp() {
return <Flex align="center" />;
}
```
#### View (MVVM) Components
These components follow the [MVVM pattern](../../docs/MVVM.md). A ViewModel
instance should be provided as a prop.
Here's a basic example:
```jsx
import { ViewExample } from "@element-hq/web-shared-components";
function MyApp() {
const viewModel = new ViewModelExample();
return <ViewExample vm={viewModel} />;
}
```
### Utilities
#### Internationalization
- `useI18n()` - Hook for translations
- `I18nApi` - Internationalization API utilities
#### Date & Time
- `DateUtils` - Date formatting and manipulation
- `humanize` - Human-readable time formatting
#### Formatting
- `FormattingUtils` - Text and data formatting utilities
- `numbers` - Number formatting utilities
## Development
### Prerequisites
- Node.js >= 20.0.0
- Yarn 1.22.22+
### Setup
```bash
# Install dependencies
yarn install
# Build the library
yarn prepare
```
### Running Storybook
```bash
yarn storybook
```
### Write components
Most components should be written as [MVVM pattern](../../docs/MVVM.md) view
components. See existing components for examples. The exceptions are low level
components that don't need a view model.
### Tests
Two types of tests are available: unit tests and visual regression tests.
### Unit Tests
These tests cover the logic of the components and utilities. Built with Jest
and React Testing Library.
```bash
yarn test
```
### Visual Regression Tests
These tests ensure the UI components render correctly. They need Storybook to
be running and they will run in docker using [Playwright](../../playwright.md).
First run storybook:
```bash
yarn storybook
```
Then, in another terminal, run:
```bash
yarn test:storybook:update
```
Each story will be rendered and a screenshot will be taken and compared to the
existing baseline. If there are visual changes or AXE violation, the test will
fail.
### Translations
First see our [translation guide](../../docs/translation.md) and [translation dev guide](../../docs/translation-dev.md).
To generate translation strings for this package, run:
```bash
yarn i18n
```
+2 -2
View File
@@ -10,7 +10,7 @@ import { env } from "process";
import type { Config } from "jest";
const config: Config = {
testEnvironment: "jsdom",
testEnvironment: "jest-fixed-jsdom",
testEnvironmentOptions: {
url: "http://localhost/",
},
@@ -30,7 +30,7 @@ const config: Config = {
"workers/(.+)Factory": "<rootDir>/__mocks__/workerFactoryMock.js",
},
transformIgnorePatterns: [
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|@storybook|storybook)).+$",
"/node_modules/(?!(mime|matrix-js-sdk|uuid|p-retry|is-network-error|react-merge-refs|@storybook|storybook|matrix-web-i18n)).+$",
],
collectCoverageFrom: [
"<rootDir>/src/**/*.{js,ts,tsx}",
+7 -4
View File
@@ -34,8 +34,11 @@
"package.json"
],
"scripts": {
"i18n": "matrix-gen-i18n src && yarn i18n:sort && yarn i18n:lint",
"i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json",
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
"test": "jest",
"prepare": "patch-package && yarn --cwd ../.. build:res && node scripts/gatherTranslationKeys.ts && vite build",
"prepare": "patch-package && vite build",
"storybook": "storybook dev -p 6007",
"build-storybook": "storybook build",
"lint": "yarn lint:types && yarn lint:js",
@@ -54,13 +57,13 @@
"classnames": "^2.5.1",
"counterpart": "^0.18.6",
"lodash": "^4.17.21",
"matrix-web-i18n": "^3.4.0",
"matrix-web-i18n": "3.5.2",
"patch-package": "^8.0.1",
"react-merge-refs": "^3.0.2",
"temporal-polyfill": "^0.3.0"
},
"devDependencies": {
"@element-hq/element-web-playwright-common": "^2.0.0",
"@element-hq/element-web-playwright-common": "2.2.3",
"@playwright/test": "1.57.0",
"@storybook/addon-a11y": "^10.0.7",
"@storybook/addon-designs": "^11.0.1",
@@ -93,6 +96,6 @@
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
"peerDependencies": {
"@vector-im/compound-web": "^8.3.4"
"@vector-im/compound-web": "^8.3.5"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -1,67 +0,0 @@
/*
Copyright 2025 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
// Gathers all the translation keys from element-web's en_EN.json into a TypeScript type definition file
// that exports a type `TranslationKey` which is a union of all supported translation keys.
// This prevents having to import the json file and make typescript do the work as this results in vite-dts
// generating an import to the json file in the .d.ts which doesn't work at runtime: this way, the type
// gets put into the bundle.
// XXX: It should *not* be in the 'src' directory, being a generated file, but if it isn't then the type
// bundler won't bundle the types and will leave the file as a relative import, which will break.
import * as fs from "fs";
import * as path from "path";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const i18nStringsPath = path.resolve(__dirname, "../../../src/i18n/strings/en_EN.json");
const outPath = path.resolve(__dirname, "../src/i18nKeys.d.ts");
function gatherKeys(obj: any, prefix: string[] = []): string[] {
if (typeof obj !== "object" || obj === null) return [];
let keys: string[] = [];
for (const key of Object.keys(obj)) {
const value = obj[key];
// add the path (for both leaves and intermediates as then we include plurals)
keys.push([...prefix, key].join("|"));
if (typeof value === "object" && value !== null) {
// If the value is an object, recurse
keys = keys.concat(gatherKeys(value, [...prefix, key]));
}
}
return keys;
}
function main() {
const json = JSON.parse(fs.readFileSync(i18nStringsPath, "utf8"));
const keys = gatherKeys(json);
const typeDef =
"/*\n" +
" * Copyright 2025 Element Creations Ltd.\n" +
" *\n" +
" * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial\n" +
" * Please see LICENSE files in the repository root for full details.\n" +
" */\n" +
"\n" +
"// This file is auto-generated by gatherTranslationKeys.ts\n" +
"// Do not edit manually.\n\n" +
"export type TranslationKey =\n" +
keys.map((k) => ` | \"${k}\"`).join("\n") +
";\n";
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, typeDef, "utf8");
console.log(`Wrote ${keys.length} keys to ${outPath}`);
}
if (import.meta.url.startsWith("file:")) {
const modulePath = fileURLToPath(import.meta.url);
if (process.argv[1] === modulePath) {
main();
}
}
+14
View File
@@ -0,0 +1,14 @@
/*
Copyright 2025 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type TranslationKey as _TranslationKey } from "matrix-web-i18n";
import type Translations from "../i18n/strings/en_EN.json";
declare global {
type TranslationKey = _TranslationKey<typeof Translations>;
}
@@ -16,7 +16,7 @@ exports[`AudioPlayerView renders the audio player in default state 1`] = `
aria-disabled="false"
aria-label="Play"
aria-labelledby="_r_0_"
class="_icon-button_1pz9o_8 button"
class="_icon-button_1215g_8 button"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
@@ -107,7 +107,7 @@ exports[`AudioPlayerView renders the audio player in error state 1`] = `
aria-disabled="false"
aria-label="Play"
aria-labelledby="_r_i_"
class="_icon-button_1pz9o_8 button"
class="_icon-button_1215g_8 button"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
@@ -203,7 +203,7 @@ exports[`AudioPlayerView renders the audio player without media name 1`] = `
aria-disabled="false"
aria-label="Play"
aria-labelledby="_r_6_"
class="_icon-button_1pz9o_8 button"
class="_icon-button_1215g_8 button"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
@@ -294,7 +294,7 @@ exports[`AudioPlayerView renders the audio player without size 1`] = `
aria-disabled="false"
aria-label="Play"
aria-labelledby="_r_c_"
class="_icon-button_1pz9o_8 button"
class="_icon-button_1215g_8 button"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
@@ -6,7 +6,7 @@ exports[`PlayPauseButton renders the button in default state 1`] = `
aria-disabled="false"
aria-label="Play"
aria-labelledby="_r_0_"
class="_icon-button_1pz9o_8 button"
class="_icon-button_1215g_8 button"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
@@ -38,7 +38,7 @@ exports[`PlayPauseButton renders the button in playing state 1`] = `
aria-disabled="false"
aria-label="Pause"
aria-labelledby="_r_6_"
class="_icon-button_1pz9o_8 button"
class="_icon-button_1215g_8 button"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 32px;"
@@ -12,6 +12,7 @@ import React, {
type ReactNode,
type PropsWithChildren,
useMemo,
type HTMLAttributes,
} from "react";
import { Button } from "@vector-im/compound-web";
import CheckCircleIcon from "@vector-im/compound-design-tokens/assets/web/icons/check-circle";
@@ -32,8 +33,6 @@ interface BannerProps {
*/
avatar?: React.ReactNode;
className?: string;
/**
* Actions presented to the user in the right-hand side of the banner alongside the dismiss button.
*/
@@ -60,21 +59,21 @@ export function Banner({
actions,
onClose,
...props
}: PropsWithChildren<BannerProps>): ReactElement {
}: PropsWithChildren<BannerProps & HTMLAttributes<HTMLDivElement>>): ReactElement {
const classes = classNames(styles.banner, className);
const icon = useMemo(() => {
const icon = useMemo((): ReactElement => {
switch (type) {
case "critical":
return <ErrorIcon fontSize={24} {...props} />;
return <ErrorIcon fontSize={24} />;
case "info":
return <InfoIcon fontSize={24} {...props} />;
return <InfoIcon fontSize={24} />;
case "success":
return <CheckCircleIcon fontSize={24} {...props} />;
return <CheckCircleIcon fontSize={24} />;
default:
return <InfoIcon fontSize={24} {...props} />;
return <InfoIcon fontSize={24} />;
}
}, [type, props]);
}, [type]);
return (
<div {...props} className={classes} data-type={type}>
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Panel posunu zvuku"
},
"action": {
"delete": "Smazat",
"dismiss": "Zavřít",
"explore_rooms": "Procházet místnosti",
"pause": "Pozastavit",
"play": "Přehrát",
"search": "Hledání"
},
"left_panel": {
"open_dial_pad": "Otevřít číselník"
},
"time": {
"about_day_ago": "před jedním dnem",
"about_hour_ago": "asi před hodinou",
"about_minute_ago": "před minutou",
"few_seconds_ago": "před pár vteřinami",
"in_about_day": "asi za den",
"in_about_hour": "asi za hodinu",
"in_about_minute": "asi za minutu",
"in_few_seconds": "za pár vteřin",
"in_n_days": "za %(num)s dní",
"in_n_hours": "za %(num)s hodin",
"in_n_minutes": "za %(num)s minut",
"n_days_ago": "před %(num)s dny",
"n_hours_ago": "před %(num)s hodinami",
"n_minutes_ago": "před %(num)s minutami"
},
"timeline": {
"m.audio": {
"audio_player": "Audio přehrávač",
"error_downloading_audio": "Chyba při stahování audia",
"unnamed_audio": "Nepojmenovaný audio soubor"
}
}
}
@@ -0,0 +1,60 @@
{
"a11y": {
"seek_bar_label": "Bar chwilio sain"
},
"action": {
"delete": "Dileu",
"dismiss": "Gwrthod",
"explore_rooms": "Archwilio Ystafelloedd",
"pause": "Oedi",
"play": "Chwarae",
"retry": "Ceisio eto",
"search": "Chwilio"
},
"left_panel": {
"open_dial_pad": "Agor y pad deialu"
},
"room": {
"status_bar": {
"delete_all": "Dileu'r cyfan",
"exceeded_resource_limit_description": "Cysylltwch â gweinyddwr eich gwasanaeth i barhau i ddefnyddio'r gwasanaeth.",
"exceeded_resource_limit_title": "Dyw eich neges heb ei anfon oherwydd bod y gweinydd cartref hwn wedi mynd y tu hwnt i derfyn ei adnoddau.",
"failed_to_create_room_title": "Methwyd dechrau sgwrs gyda'r defnyddiwr hwn",
"history_visible": "Mae'r ystafell hon wedi'i ffurfweddu fel y gall aelodau newydd ddarllen hanes. <a>Dysgu Mwy</a>",
"homeserver_blocked_title": "Dyw eich neges heb ei anfon oherwydd bod y gweinydd cartref hwn wedi'i rwystro gan ei weinyddwr.",
"monthly_user_limit_reached_title": "Dyw eich neges heb ei anfon oherwydd bod y gweinydd cartref hwn wedi cyrraedd ei Derfyn Defnyddiwr Gweithredol Misol.",
"requires_consent_agreement_title": "Does dim modd i chi anfon unrhyw negeseuon nes i chi adolygu a chytuno â'n telerau ac amodau.",
"retry_all": "Ail-geisio popeth",
"select_messages_to_retry": "Gallwch ddewis pob neges neu negeseuon unigol i geisio eto neu eu dileu",
"server_connectivity_lost_description": "Bydd negeseuon sy'n cael eu hanfon yn cael eu cadw nes bod eich cysylltiad wedi dychwelyd.",
"server_connectivity_lost_title": "Mae'r cysylltiad â'r gweinydd wedi'i golli.",
"some_messages_not_sent": "Dyw rhai o'ch negeseuon heb eu hanfon"
}
},
"terms": {
"tac_button": "Adolygwch y telerau a'r amodau"
},
"time": {
"about_day_ago": "tua diwrnod yn ôl",
"about_hour_ago": "tua awr yn ol",
"about_minute_ago": "tua munud yn ôl",
"few_seconds_ago": "ychydig eiliadau yn ôl",
"in_about_day": "tua diwrnod o nawr",
"in_about_hour": "tuag awr o hyn",
"in_about_minute": "tua munud o nawr",
"in_few_seconds": "ychydig eiliadau o nawr",
"in_n_days": "%(num)s diwrnod o nawr",
"in_n_hours": "%(num)s awr o nawr",
"in_n_minutes": "%(num)s munud o nawr",
"n_days_ago": "%(num)s diwrnod yn ôl",
"n_hours_ago": "%(num)s awr yn ôl",
"n_minutes_ago": "%(num)s munud yn ôl"
},
"timeline": {
"m.audio": {
"audio_player": "Chwaraewr sain",
"error_downloading_audio": "Gwall wrth llwytho i lawrsain",
"unnamed_audio": "Sain dienw"
}
}
}
@@ -0,0 +1,35 @@
{
"a11y": {
"seek_bar_label": "Progressionsmarkør for lydafspiller"
},
"action": {
"delete": "Slet",
"dismiss": "Afvis",
"explore_rooms": "Udforsk rum",
"pause": "Pausér",
"play": "Afspil",
"search": "Søg"
},
"time": {
"about_day_ago": "omkring en dag siden",
"about_hour_ago": "for omkring en time siden",
"about_minute_ago": "for omkring et minut siden",
"few_seconds_ago": "for et par sekunder siden",
"in_about_day": "om cirka en dag fra nu",
"in_about_hour": "omkring en time fra nu",
"in_about_minute": "omkring et minut fra nu",
"in_few_seconds": "et par sekunder fra nu",
"in_n_days": "%(num)s dage fra nu",
"in_n_hours": "%(num)s timer fra nu",
"in_n_minutes": "%(num)s minutter fra nu",
"n_days_ago": "%(num)s dage siden",
"n_hours_ago": "%(num)s timer siden",
"n_minutes_ago": "%(num)s minutter siden"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Fejl ved download af lyd",
"unnamed_audio": "Unavngiven lyd"
}
}
}
@@ -0,0 +1,58 @@
{
"a11y": {
"seek_bar_label": "Audio-Suchleiste"
},
"action": {
"delete": "Löschen",
"dismiss": "Ausblenden",
"explore_rooms": "Chats erkunden",
"pause": "Pausieren",
"play": "Abspielen",
"retry": "Erneut versuchen",
"search": "Suchen"
},
"left_panel": {
"open_dial_pad": "Wähltastatur öffnen"
},
"room": {
"status_bar": {
"delete_all": "Alle löschen",
"exceeded_resource_limit_title": "Deine Nachricht konnte nicht versendet werden, da dein Homeserver ein Ressourcenlimit überschritten hat.",
"failed_to_create_room_title": "Es konnte kein Chat mit diesem Nutzer gestartet werden",
"history_visible": "Diese Gruppe wurde konfiguriert, neuen Mitgliedern Zugriff auf den vergangenen Nachrichtenverlauf zu gestatten. <a>Mehr erfahren</a>",
"homeserver_blocked_title": "Deine Nachricht konnte nicht versendet werden, da der Admin deinen Homeserver gesperrt hat.",
"monthly_user_limit_reached_title": "Deine Nachricht konnte nicht versendet werden, da dein Homeserver das monatliche Nutzerlimit erreicht hat.",
"requires_consent_agreement_title": "Du kannst erst dann Nachrichten verschicken, wenn du unsere Geschäftsbedingungen gelesen und akzeptiert hast.",
"select_messages_to_retry": "Du kannst einzelne oder alle Nachrichten erneut senden oder löschen",
"server_connectivity_lost_description": "Nachrichten werden gespeichert und gesendet, wenn die Internetverbindung wiederhergestellt ist.",
"server_connectivity_lost_title": "Verbindung zum Server wurde unterbrochen.",
"some_messages_not_sent": "Einige Nachrichten konnten nicht gesendet werden"
}
},
"terms": {
"tac_button": "Geschäftsbedingungen anzeigen"
},
"time": {
"about_day_ago": "vor etwa einem Tag",
"about_hour_ago": "vor etwa einer Stunde",
"about_minute_ago": "vor etwa einer Minute",
"few_seconds_ago": "vor ein paar Sekunden",
"in_about_day": "in etwa einem Tag",
"in_about_hour": "in etwa einer Stunde",
"in_about_minute": "in etwa einer Minute",
"in_few_seconds": "in ein paar Sekunden",
"in_n_days": "in %(num)s Tagen",
"in_n_hours": "in %(num)s Stunden",
"in_n_minutes": "In etwa %(num)s Minuten",
"n_days_ago": "vor %(num)s Tagen",
"n_hours_ago": "vor %(num)s Stunden",
"n_minutes_ago": "vor %(num)s Minuten"
},
"timeline": {
"m.audio": {
"audio_player": "Audio-Player",
"error_downloading_audio": "Fehler beim Herunterladen der Audiodatei",
"unnamed_audio": "Unbenannte Audiodatei"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "Διαγραφή",
"dismiss": "Απόρριψη",
"explore_rooms": "Εξερευνήστε αίθουσες",
"pause": "Παύση",
"play": "Αναπαραγωγή",
"search": "Αναζήτηση"
},
"left_panel": {
"open_dial_pad": "Άνοιγμα πληκτρολογίου κλήσης"
},
"time": {
"about_day_ago": "σχεδόν μία μέρα πριν",
"about_hour_ago": "σχεδόν μία ώρα πριν",
"about_minute_ago": "σχεδόν ένα λεπτό πριν",
"few_seconds_ago": "λίγα δευτερόλεπτα πριν",
"in_about_day": "περίπου μια μέρα από τώρα",
"in_about_hour": "περίπου μία ώρα από τώρα",
"in_about_minute": "περίπου ένα λεπτό από τώρα",
"in_few_seconds": "λίγα δευτερόλεπτα από τώρα",
"in_n_days": "%(num)s μέρες από τώρα",
"in_n_hours": "%(num)s ώρες από τώρα",
"in_n_minutes": "%(num)s λεπτά από τώρα",
"n_days_ago": "%(num)s μέρες πριν",
"n_hours_ago": "%(num)s ώρες πριν",
"n_minutes_ago": "%(num)s λεπτά πριν"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Σφάλμα λήψης ήχου",
"unnamed_audio": "Ήχος χωρίς όνομα"
}
}
}
@@ -0,0 +1,60 @@
{
"a11y": {
"seek_bar_label": "Audio seek bar"
},
"action": {
"delete": "Delete",
"dismiss": "Dismiss",
"explore_rooms": "Explore rooms",
"pause": "Pause",
"play": "Play",
"retry": "Retry",
"search": "Search"
},
"left_panel": {
"open_dial_pad": "Open dial pad"
},
"room": {
"status_bar": {
"delete_all": "Delete all",
"exceeded_resource_limit_description": "Please contact your service administrator to continue using the service.",
"exceeded_resource_limit_title": "Your message wasn't sent because this homeserver has exceeded a resource limit.",
"failed_to_create_room_title": "Could not start a chat with this user",
"history_visible": "This room has been configured so that new members can read history. <a>Learn More</a>",
"homeserver_blocked_title": "Your message wasn't sent because this homeserver has been blocked by its administrator.",
"monthly_user_limit_reached_title": "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit.",
"requires_consent_agreement_title": "You can't send any messages until you review and agree to our terms and conditions.",
"retry_all": "Retry all",
"select_messages_to_retry": "You can select all or individual messages to retry or delete",
"server_connectivity_lost_description": "Sent messages will be stored until your connection has returned.",
"server_connectivity_lost_title": "Connectivity to the server has been lost.",
"some_messages_not_sent": "Some of your messages have not been sent"
}
},
"terms": {
"tac_button": "Review terms and conditions"
},
"time": {
"about_day_ago": "about a day ago",
"about_hour_ago": "about an hour ago",
"about_minute_ago": "about a minute ago",
"few_seconds_ago": "a few seconds ago",
"in_about_day": "about a day from now",
"in_about_hour": "about an hour from now",
"in_about_minute": "about a minute from now",
"in_few_seconds": "a few seconds from now",
"in_n_days": "%(num)s days from now",
"in_n_hours": "%(num)s hours from now",
"in_n_minutes": "%(num)s minutes from now",
"n_days_ago": "%(num)s days ago",
"n_hours_ago": "%(num)s hours ago",
"n_minutes_ago": "%(num)s minutes ago"
},
"timeline": {
"m.audio": {
"audio_player": "Audio player",
"error_downloading_audio": "Error downloading audio",
"unnamed_audio": "Unnamed audio"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "Forigi",
"dismiss": "Rezigni",
"explore_rooms": "Esplori ĉambrojn",
"pause": "Paŭzigi",
"play": "Ludi",
"search": "Serĉi"
},
"left_panel": {
"open_dial_pad": "Malfermi ciferplaton"
},
"time": {
"about_day_ago": "antaŭ ĉirkaŭ tago",
"about_hour_ago": "antaŭ ĉirkaŭ horo",
"about_minute_ago": "antaŭ ĉirkaŭ minuto",
"few_seconds_ago": "antaŭ kelkaj sekundoj",
"in_about_day": "ĉirkaŭ tagon de nun",
"in_about_hour": "ĉirkaŭ horon de nun",
"in_about_minute": "ĉirkaŭ minuton de nun",
"in_few_seconds": "kelkajn sekundojn de nun",
"in_n_days": "%(num)s tagojn de nun",
"in_n_hours": "%(num)s horojn de nun",
"in_n_minutes": "%(num)s minutojn de nun",
"n_days_ago": "antaŭ %(num)s tagoj",
"n_hours_ago": "antaŭ %(num)s horoj",
"n_minutes_ago": "antaŭ %(num)s minutoj"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Eraris elŝuto de sondosiero",
"unnamed_audio": "Sennoma sondosiero"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Barra de búsqueda de audio"
},
"action": {
"delete": "Borrar",
"dismiss": "Omitir",
"explore_rooms": "Explorar salas",
"pause": "Pausar",
"play": "Reproducir",
"search": "Buscar"
},
"left_panel": {
"open_dial_pad": "Abrir teclado numérico"
},
"time": {
"about_day_ago": "hace aprox. un día",
"about_hour_ago": "hace aprox. una hora",
"about_minute_ago": "hace aproximadamente un minuto",
"few_seconds_ago": "hace unos segundos",
"in_about_day": "dentro de un día",
"in_about_hour": "dentro de una hora",
"in_about_minute": "dentro de un minuto",
"in_few_seconds": "dentro de unos segundos",
"in_n_days": "dentro de %(num)s días",
"in_n_hours": "dentro de %(num)s horas",
"in_n_minutes": "dentro de %(num)s minutos",
"n_days_ago": "hace %(num)s días",
"n_hours_ago": "hace %(num)s horas",
"n_minutes_ago": "hace %(num)s minutos"
},
"timeline": {
"m.audio": {
"audio_player": "Reproductor de audio",
"error_downloading_audio": "Error al descargar el audio",
"unnamed_audio": "Audio sin título"
}
}
}
@@ -0,0 +1,44 @@
{
"a11y": {
"seek_bar_label": "Heli kerimisriba"
},
"action": {
"delete": "Kustuta",
"dismiss": "Loobu",
"explore_rooms": "Tutvu jututubadega",
"pause": "Peata",
"play": "Esita",
"search": "Otsing"
},
"left_panel": {
"open_dial_pad": "Ava numbriklahvistik"
},
"room": {
"status_bar": {
"history_visible": "See jututuba on seadistatud sel viisil, et uued liikmed saavad lugeda varasemat ajalugu.<a> Lisateave</a>"
}
},
"time": {
"about_day_ago": "umbes päev tagasi",
"about_hour_ago": "umbes tund aega tagasi",
"about_minute_ago": "umbes minut tagasi",
"few_seconds_ago": "mõni sekund tagasi",
"in_about_day": "umbes päeva pärast",
"in_about_hour": "umbes tunni pärast",
"in_about_minute": "umbes minuti pärast",
"in_few_seconds": "mõne sekundi pärast",
"in_n_days": "%(num)s päeva pärast",
"in_n_hours": "%(num)s tunni pärast",
"in_n_minutes": "%(num)s minuti pärast",
"n_days_ago": "%(num)s päeva tagasi",
"n_hours_ago": "%(num)s tundi tagasi",
"n_minutes_ago": "%(num)s minutit tagasi"
},
"timeline": {
"m.audio": {
"audio_player": "Meediaesitaja",
"error_downloading_audio": "Helifaili allalaadimine ei õnnestunud",
"unnamed_audio": "Nimetu helifail"
}
}
}
@@ -0,0 +1,29 @@
{
"action": {
"delete": "پاک‌کردن",
"dismiss": "نادیده بگیر",
"explore_rooms": "جستجو در اتاق ها",
"pause": "متوقف‌کردن",
"play": "اجرا کردن",
"search": "جستجو"
},
"left_panel": {
"open_dial_pad": "باز کردن صفحه شماره‌گیری"
},
"time": {
"about_day_ago": "حدود یک روز قبل",
"about_hour_ago": "حدود یک ساعت قبل",
"about_minute_ago": "حدود یک دقیقه قبل",
"few_seconds_ago": "چند ثانیه قبل",
"in_about_day": "حدود یک روز دیگر",
"in_about_hour": "حدود یک ساعت دیگر",
"in_about_minute": "حدود یک دقیقه دیگر",
"in_few_seconds": "چند ثانیه دیگر",
"in_n_days": "%(num)s روز دیگر",
"in_n_hours": "%(num)s ساعت دیگر",
"in_n_minutes": "%(num)s دقیقه دیگر",
"n_days_ago": "%(num)s روز قبل",
"n_hours_ago": "%(num)s ساعت قبل",
"n_minutes_ago": "%(num)s دقیقه قبل"
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Äänen siirtymispalkki"
},
"action": {
"delete": "Poista",
"dismiss": "Hylkää",
"explore_rooms": "Selaa huoneita",
"pause": "Keskeytä",
"play": "Toista",
"search": "Haku"
},
"left_panel": {
"open_dial_pad": "Avaa näppäimistö"
},
"time": {
"about_day_ago": "noin päivä sitten",
"about_hour_ago": "noin tunti sitten",
"about_minute_ago": "noin minuutti sitten",
"few_seconds_ago": "muutama sekunti sitten",
"in_about_day": "noin päivä sitten",
"in_about_hour": "noin tunti sitten",
"in_about_minute": "noin minuutti sitten",
"in_few_seconds": "muutama sekunti sitten",
"in_n_days": "%(num)s päivää sitten",
"in_n_hours": "%(num)s tuntia sitten",
"in_n_minutes": "%(num)s minuuttia sitten",
"n_days_ago": "%(num)s päivää sitten",
"n_hours_ago": "%(num)s tuntia sitten",
"n_minutes_ago": "%(num)s minuuttia sitten"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Virhe ääntä ladattaessa",
"unnamed_audio": "Nimetön ääni"
}
}
}
@@ -0,0 +1,60 @@
{
"a11y": {
"seek_bar_label": "Barre de recherche audio"
},
"action": {
"delete": "Supprimer",
"dismiss": "Ignorer",
"explore_rooms": "Parcourir les salons",
"pause": "Pause",
"play": "Lecture",
"retry": "Réessayer",
"search": "Rechercher"
},
"left_panel": {
"open_dial_pad": "Ouvrir le pavé de numérotation"
},
"room": {
"status_bar": {
"delete_all": "Tout supprimer",
"exceeded_resource_limit_description": "Veuillez contacter votre administrateur pour continuer à utiliser le service.",
"exceeded_resource_limit_title": "Votre message n'a pas pu être envoyé car ce serveur d'accueil a dépassé sa limite de ressources.",
"failed_to_create_room_title": "Impossible de démarrer une discussion avec cet utilisateur",
"history_visible": "Ce salon a été configuré afin que les nouveaux membres puissent lire l'historique.<a> En savori plus</a>",
"homeserver_blocked_title": "Votre message n'a pas pu être envoyé car ce serveur d'accueil a été bloqué par son administrateur.",
"monthly_user_limit_reached_title": "Votre message n'a pas pu être envoyé car ce serveur d'accueil a atteint sa limite mensuelle d'utilisateurs actifs.",
"requires_consent_agreement_title": "Vous ne pouvez envoyer aucun message tant que vous n'aurez pas consulté et accepté nos conditions générales.",
"retry_all": "Tout réessayer",
"select_messages_to_retry": "Vous pouvez choisir de renvoyer ou supprimer tous les messages ou seulement certains",
"server_connectivity_lost_description": "Les messages envoyés seront stockés jusqu’à ce que votre connexion revienne.",
"server_connectivity_lost_title": "La connexion avec le serveur a été perdue.",
"some_messages_not_sent": "Certains de vos messages nont pas été envoyés"
}
},
"terms": {
"tac_button": "Voir les conditions générales"
},
"time": {
"about_day_ago": "il y a environ un jour",
"about_hour_ago": "il y a environ une heure",
"about_minute_ago": "il y a environ une minute",
"few_seconds_ago": "il y a quelques secondes",
"in_about_day": "dans un jour environ",
"in_about_hour": "dans une heure environ",
"in_about_minute": "dans une minute environ",
"in_few_seconds": "dans quelques secondes",
"in_n_days": "dans %(num)s jours",
"in_n_hours": "dans %(num)s heures",
"in_n_minutes": "dans %(num)s minutes",
"n_days_ago": "il y a %(num)s jours",
"n_hours_ago": "il y a %(num)s heures",
"n_minutes_ago": "il y a %(num)s minutes"
},
"timeline": {
"m.audio": {
"audio_player": "Lecteur audio",
"error_downloading_audio": "Erreur lors du téléchargement de laudio",
"unnamed_audio": "Audio sans nom"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "Eliminar",
"dismiss": "Rexeitar",
"explore_rooms": "Explorar salas",
"pause": "Deter",
"play": "Reproducir",
"search": "Busca"
},
"left_panel": {
"open_dial_pad": "Abrir marcador"
},
"time": {
"about_day_ago": "onte",
"about_hour_ago": "fai unha hora",
"about_minute_ago": "fai un minuto",
"few_seconds_ago": "fai uns segundos",
"in_about_day": "foi onte",
"in_about_hour": "fará unha hora",
"in_about_minute": "haberá un minuto",
"in_few_seconds": "hai só uns segundos",
"in_n_days": "fará %(num)s días",
"in_n_hours": "fará %(num)s horas",
"in_n_minutes": "fará %(num)s minutos",
"n_days_ago": "fai %(num)s días",
"n_hours_ago": "fai %(num)s horas",
"n_minutes_ago": "fai %(num)s minutos"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Erro ao descargar o audio",
"unnamed_audio": "Audio sen nome"
}
}
}
@@ -0,0 +1,27 @@
{
"action": {
"delete": "מחק",
"dismiss": "התעלם",
"explore_rooms": "גלה חדרים",
"search": "חפש"
},
"left_panel": {
"open_dial_pad": "פתח לוח חיוג"
},
"time": {
"about_day_ago": "בערך לפני יום",
"about_hour_ago": "בערך לפני כשעה",
"about_minute_ago": "לפני בערך דקה",
"few_seconds_ago": "לפני מספר שניות",
"in_about_day": "בערך בעוד יום מעכשיו",
"in_about_hour": "בערך בעוד כשעה",
"in_about_minute": "בערך עוד דקה אחת",
"in_few_seconds": "בעוד מספר שניות מעכשיו",
"in_n_days": "בעוד %(num)s ימים מעכשיו",
"in_n_hours": "בעוד %(num)s שעות",
"in_n_minutes": "בעוד %(num)s דקות",
"n_days_ago": "לפני %(num)s ימים",
"n_hours_ago": "לפני %(num)s שעות",
"n_minutes_ago": "לפני %(num)s דקות"
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Hang keresősávja"
},
"action": {
"delete": "Törlés",
"dismiss": "Eltüntetés",
"explore_rooms": "Szobák felderítése",
"pause": "Szünet",
"play": "Lejátszás",
"search": "Keresés"
},
"left_panel": {
"open_dial_pad": "Számlap megnyitása"
},
"time": {
"about_day_ago": "egy napja",
"about_hour_ago": "egy órája",
"about_minute_ago": "egy perce",
"few_seconds_ago": "néhány másodperce",
"in_about_day": "egy nap múlva",
"in_about_hour": "egy óra múlva",
"in_about_minute": "egy perc múlva",
"in_few_seconds": "másodpercek múlva",
"in_n_days": "%(num)s nap múlva",
"in_n_hours": "%(num)s óra múlva",
"in_n_minutes": "%(num)s perc múlva",
"n_days_ago": "%(num)s nappal ezelőtt",
"n_hours_ago": "%(num)s órával ezelőtt",
"n_minutes_ago": "%(num)s perccel ezelőtt"
},
"timeline": {
"m.audio": {
"audio_player": "Hanglejátszó",
"error_downloading_audio": "Hiba a hang letöltésekor",
"unnamed_audio": "Névtelen hang"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Աուդիո որոնման գոտի"
},
"action": {
"delete": "Ջնջել",
"dismiss": "Հեռացնել",
"explore_rooms": "Փնտրել սենյակներ",
"pause": "Դադար",
"play": "Միացնել",
"search": "Որոնել"
},
"left_panel": {
"open_dial_pad": "Բացեք թվերի հավաքման վահանակը"
},
"time": {
"about_day_ago": "մոտ մեկ օր առաջ",
"about_hour_ago": "մոտ մեկ ժամ առաջ",
"about_minute_ago": "մոտ մեկ րոպե առաջ",
"few_seconds_ago": "մի քանի վայրկյան առաջ",
"in_about_day": "մոտ մեկ օր անց",
"in_about_hour": "մոտ մեկ ժամ անց",
"in_about_minute": "մոտ մեկ րոպե անց",
"in_few_seconds": "մի քանի վայրկյան անց",
"in_n_days": "%(num)s օր անց",
"in_n_hours": "%(num)s ժամ անց",
"in_n_minutes": "%(num)s րոպեներ անց",
"n_days_ago": "%(num)s օր առաջ",
"n_hours_ago": "%(num)s ժամ առաջ",
"n_minutes_ago": "%(num)s րոպե առաջ"
},
"timeline": {
"m.audio": {
"audio_player": "Աուդիո նվագարկիչ",
"error_downloading_audio": "Աուդիո ներբեռնման սխալ",
"unnamed_audio": "Անանուն աուդիո"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Bilah pencarian audio"
},
"action": {
"delete": "Hapus",
"dismiss": "Abaikan",
"explore_rooms": "Jelajahi ruangan",
"pause": "Jeda",
"play": "Mainkan",
"search": "Cari"
},
"left_panel": {
"open_dial_pad": "Buka tombol penyetel"
},
"time": {
"about_day_ago": "1 hari yang lalu",
"about_hour_ago": "1 jam yang lalu",
"about_minute_ago": "1 menit yang lalu",
"few_seconds_ago": "beberapa detik yang lalu",
"in_about_day": "1 hari dari sekarang",
"in_about_hour": "1 jam dari sekarang",
"in_about_minute": "1 menit dari sekarang",
"in_few_seconds": "beberapa detik dari sekarang",
"in_n_days": "%(num)s hari dari sekarang",
"in_n_hours": "%(num)s jam dari sekarang",
"in_n_minutes": "%(num)s dari sekarang",
"n_days_ago": "%(num)s hari yang lalu",
"n_hours_ago": "%(num)s jam yang lalu",
"n_minutes_ago": "%(num)s menit yang lalu"
},
"timeline": {
"m.audio": {
"audio_player": "Pemutar audio",
"error_downloading_audio": "Terjadi kesalahan mengunduh audio",
"unnamed_audio": "Audio tidak dinamai"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "Eyða",
"dismiss": "Hunsa",
"explore_rooms": "Kanna spjallrásir",
"pause": "Bið",
"play": "Spila",
"search": "Leita"
},
"left_panel": {
"open_dial_pad": "Opna talnaborð"
},
"time": {
"about_day_ago": "fyrir um degi síðan",
"about_hour_ago": "fyrir um klukkustund síðan",
"about_minute_ago": "fyrir um það bil mínútu síðan",
"few_seconds_ago": "fyrir örfáum sekúndum síðan",
"in_about_day": "eftir um það bil einn dag",
"in_about_hour": "eftir um það bil klukkustund",
"in_about_minute": "eftir um það bil mínútu",
"in_few_seconds": "eftir nokkrar sekúndur",
"in_n_days": "eftir %(num)s daga",
"in_n_hours": "eftir %(num)s klukkustundir",
"in_n_minutes": "eftir %(num)s mínútur",
"n_days_ago": "fyrir %(num)s dögum síðan",
"n_hours_ago": "fyrir %(num)s klukkustundum síðan",
"n_minutes_ago": "fyrir %(num)s mínútum síðan"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Villa við að sækja hljóð",
"unnamed_audio": "Nafnlaust hljóð"
}
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Barra di ricerca audio"
},
"action": {
"delete": "Elimina",
"dismiss": "Chiudi",
"explore_rooms": "Esplora stanze",
"pause": "Pausa",
"play": "Riproduci",
"search": "Cerca"
},
"left_panel": {
"open_dial_pad": "Apri tastierino"
},
"time": {
"about_day_ago": "circa un giorno fa",
"about_hour_ago": "circa un'ora fa",
"about_minute_ago": "circa un minuto fa",
"few_seconds_ago": "pochi secondi fa",
"in_about_day": "circa un giorno da adesso",
"in_about_hour": "circa un'ora da adesso",
"in_about_minute": "circa un minuto da adesso",
"in_few_seconds": "pochi secondi da adesso",
"in_n_days": "%(num)s giorni da adesso",
"in_n_hours": "%(num)s ore da adesso",
"in_n_minutes": "%(num)s minuti da adesso",
"n_days_ago": "%(num)s giorni fa",
"n_hours_ago": "%(num)s ore fa",
"n_minutes_ago": "%(num)s minuti fa"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Errore di scaricamento dell'audio",
"unnamed_audio": "Audio senza nome"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "削除",
"dismiss": "閉じる",
"explore_rooms": "ルームを探す",
"pause": "一時停止",
"play": "再生",
"search": "検索"
},
"left_panel": {
"open_dial_pad": "ダイヤルパッドを開く"
},
"time": {
"about_day_ago": "約1日前",
"about_hour_ago": "約1時間前",
"about_minute_ago": "約1分前",
"few_seconds_ago": "数秒前",
"in_about_day": "今から約1日前",
"in_about_hour": "今から約1時間前",
"in_about_minute": "今から約1分前",
"in_few_seconds": "今から数秒前",
"in_n_days": "今から%(num)s日前",
"in_n_hours": "今から%(num)s時間前",
"in_n_minutes": "今から%(num)s分前",
"n_days_ago": "%(num)s日前",
"n_hours_ago": "%(num)s時間前",
"n_minutes_ago": "%(num)s分前"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "音声をダウンロードする際にエラーが発生しました",
"unnamed_audio": "名前のない音声"
}
}
}
@@ -0,0 +1,32 @@
{
"action": {
"delete": "წაშლა",
"dismiss": "დახურვა",
"explore_rooms": "ოთახების დათავლიერება",
"pause": "პაუზა",
"play": "დაკვრა",
"search": "ძიება"
},
"time": {
"about_day_ago": "დაახლოებით ერთი დღის წინ",
"about_hour_ago": "დაახლოებით ერთი საათის წინ",
"about_minute_ago": "დაახლოებით ერთი წუთის წინ",
"few_seconds_ago": "რამდენიმე წამის წინ",
"in_about_day": "დაახლოებით ერთი დღის შემდეგ",
"in_about_hour": "დაახლოებით ერთი საათის შემდეგ",
"in_about_minute": "დაახლოებით ერთი წუთის შემდეგ",
"in_few_seconds": "რამდენიმე წამის შემდეგ",
"in_n_days": "%(num)sდღეებიდან",
"in_n_hours": "%(num)sსაათის შემდეგ",
"in_n_minutes": "%(num)sწუთის შემდეგ",
"n_days_ago": "%(num)sდღის წინ",
"n_hours_ago": "%(num)sსაათის წინ",
"n_minutes_ago": "%(num)sწუთის წინ"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "შეცდომა აუდიოს ჩამოტვირთვისას",
"unnamed_audio": "უსახელო აუდიო"
}
}
}
@@ -0,0 +1,60 @@
{
"a11y": {
"seek_bar_label": "오디오 탐색 바"
},
"action": {
"delete": "삭제",
"dismiss": "버리기",
"explore_rooms": "방 검색",
"pause": "일시중지",
"play": "재생",
"retry": "재시도",
"search": "찾기"
},
"left_panel": {
"open_dial_pad": "다이얼 패드 열기"
},
"room": {
"status_bar": {
"delete_all": "전체 삭제",
"exceeded_resource_limit_description": "서비스를 계속 이용하시려면 서비스 관리자에게 문의하십시오.",
"exceeded_resource_limit_title": "이 홈 서버가 리소스 한도를 초과하여 귀하의 메시지가 전송되지 않았습니다.",
"failed_to_create_room_title": "이 사용자와 채팅을 시작할 수 없습니다",
"history_visible": "이 방은 새 멤버가 대화 기록을 볼 수 있도록 설정되었습니다. <a>자세히 알아보기</a>",
"homeserver_blocked_title": "귀하의 메시지는 이 홈서버 관리자에 의해 차단되었기 때문에 전송되지 않았습니다.",
"monthly_user_limit_reached_title": "해당 홈서버의 월간 활성 사용자 수 제한에 도달하여 메시지가 전송되지 않았습니다.",
"requires_consent_agreement_title": "이용약관을 검토하고 동의하기 전까지는 메시지를 보낼 수 없습니다.",
"retry_all": "전체 재시도",
"select_messages_to_retry": "모든 메시지 또는 개별 메시지를 선택하여 재시도하거나 삭제할 수 있습니다.",
"server_connectivity_lost_description": "보낸 메시지는 연결이 복구될 때까지 저장됩니다.",
"server_connectivity_lost_title": "서버와의 연결이 끊어졌습니다.",
"some_messages_not_sent": "일부 메시지가 전송되지 않았습니다."
}
},
"terms": {
"tac_button": "이용 약관을 검토하세요."
},
"time": {
"about_day_ago": "약 1일 전",
"about_hour_ago": "약 1 시간 전",
"about_minute_ago": "약 1분 전",
"few_seconds_ago": "몇 초 전",
"in_about_day": "하루 정도 후",
"in_about_hour": "지금부터 한 시간 정도 후에",
"in_about_minute": "지금부터 약 1분 후",
"in_few_seconds": "몇 초 후",
"in_n_days": "지금부터 %(num)s 일 후에",
"in_n_hours": "지금부터 %(num)s 시간 후",
"in_n_minutes": "지금부터 %(num)s분 후",
"n_days_ago": "%(num)s일 전",
"n_hours_ago": "%(num)s 시간 전",
"n_minutes_ago": "%(num)s분 전"
},
"timeline": {
"m.audio": {
"audio_player": "오디오 플레이어",
"error_downloading_audio": "오디오 다운로드 중 오류 발생",
"unnamed_audio": "이름 없는 오디오"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "ລຶບ",
"dismiss": "ຍົກເລີກ",
"explore_rooms": "ການສຳຫຼວດຫ້ອງ",
"pause": "ຢຸດຊົ່ວຄາວ",
"play": "ຫຼິ້ນ",
"search": "ຊອກຫາ"
},
"left_panel": {
"open_dial_pad": "ເປີດແຜ່ນປັດ"
},
"time": {
"about_day_ago": "ປະມານຫນຶ່ງມື້ກ່ອນຫນ້ານີ້",
"about_hour_ago": "ປະມານຫນຶ່ງຊົ່ວໂມງກ່ອນຫນ້ານີ້",
"about_minute_ago": "ປະມານໜຶ່ງວິນາທີກ່ອນຫນ້ານີ້",
"few_seconds_ago": "ສອງສາມວິນາທີກ່ອນຫນ້ານີ້",
"in_about_day": "ປະມານນຶ່ງມື້ຈາກນີ້",
"in_about_hour": "ປະມານຫນຶ່ງຊົ່ວໂມງຈາກປະຈຸບັນນີ້",
"in_about_minute": "ປະມານໜຶ່ງນາທີຕໍ່ຈາກນີ້",
"in_few_seconds": "ສອງສາມວິນາທີຕໍ່ຈາກນີ້ໄປ",
"in_n_days": "%(num)s ມື້ຕໍ່ຈາກນີ້",
"in_n_hours": "%(num)s ຊົ່ວໂມງຈາກປະຈຸບັນນີ້",
"in_n_minutes": "%(num)s ນາທີຕໍ່ຈາກນີ້",
"n_days_ago": "%(num)sມື້ກ່ອນຫນ້ານີ້",
"n_hours_ago": "%(num)s ຊົ່ວໂມງກ່ອນ",
"n_minutes_ago": "%(num)s ນາທີກ່ອນ"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "ເກີດຄວາມຜິດພາດໃນການດາວໂຫຼດສຽງ",
"unnamed_audio": "ສຽງບໍ່ມີຊື່"
}
}
}
@@ -0,0 +1,24 @@
{
"action": {
"delete": "Ištrinti",
"dismiss": "Atmesti",
"explore_rooms": "Žvalgyti kambarius",
"search": "Ieškoti"
},
"time": {
"about_day_ago": "maždaug prieš dieną",
"about_hour_ago": "maždaug prieš valandą",
"about_minute_ago": "maždaug prieš minutę",
"few_seconds_ago": "prieš kelias sekundes",
"in_about_day": "apie dieną nuo dabar",
"in_about_hour": "apie valandą nuo dabar",
"in_about_minute": "apie minutę nuo dabar",
"in_few_seconds": "keletą sekundžių nuo dabar",
"in_n_days": "%(num)s dienas(-ų) nuo dabar",
"in_n_hours": "%(num)s valandas(-ų) nuo dabar",
"in_n_minutes": "%(num)s minutes(-ų) nuo dabar",
"n_days_ago": "prieš %(num)s dienas(-ų)",
"n_hours_ago": "prieš %(num)s valandas(-ų)",
"n_minutes_ago": "prieš %(num)s minutes(-ų)"
}
}
@@ -0,0 +1,35 @@
{
"a11y": {
"seek_bar_label": "Audio meklēšanas josla"
},
"action": {
"delete": "Izdzēst",
"dismiss": "Atmest",
"explore_rooms": "Pārlūkot istabas",
"pause": "Pauzēt",
"play": "Atskaņot",
"search": "Meklēt"
},
"time": {
"about_day_ago": "aptuveni dienu iepriekš",
"about_hour_ago": "aptuveni stundu iepriekš",
"about_minute_ago": "aptuveni minūti iepriekš",
"few_seconds_ago": "pirms dažām sekundēm",
"in_about_day": "aptuveni dienu kopš šī brīža",
"in_about_hour": "aptuveni stundu kopš šī brīža",
"in_about_minute": "aptuveni minūti kopš šī brīža",
"in_few_seconds": "dažas sekundes kopš šī brīža",
"in_n_days": "%(num)s dienas kopš šī brīža",
"in_n_hours": "%(num)s stundas kopš šī brīža",
"in_n_minutes": "%(num)s minūtes kopš šī brīža",
"n_days_ago": "%(num)s dienas iepriekš",
"n_hours_ago": "%(num)s stundas iepriekš",
"n_minutes_ago": "%(num)s minūtes iepriekš"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Kļūda skaņas lejupielādēšanā",
"unnamed_audio": "Nenosaukts audio"
}
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Audio mitady bar"
},
"action": {
"delete": "Esorina",
"dismiss": "Hanario",
"explore_rooms": "Tsidiho ny efitrano",
"pause": "Mihato",
"play": "Milalao",
"search": "Karohina"
},
"left_panel": {
"open_dial_pad": "Sokafy ny dial pad"
},
"time": {
"about_day_ago": "Tokony ho iray andro izay",
"about_hour_ago": "Manakaiky adin'iray Teo ho eo",
"about_minute_ago": "Misy iray minitra Teo izay",
"few_seconds_ago": "Segondra vitsy lasa",
"in_about_day": "Anatiny iray andro eo ho eo",
"in_about_hour": "Adiny iray eo ho eo",
"in_about_minute": "Afaka iray minitra eo ho eo",
"in_few_seconds": "Afaka segondra vitsy",
"in_n_days": "%(num) s andro manomboka izao",
"in_n_hours": "% (num) sAnatiny ora vitsivitsy",
"in_n_minutes": "% (Num) sAfaka minitra vitsy",
"n_days_ago": "%(num)s Andro vitsivitsy izay",
"n_hours_ago": "%(num)sOra maromaro",
"n_minutes_ago": "%(Num)s Minitra vitsivitsy izay"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Hadisoana tamin'ny fampidinana feo",
"unnamed_audio": "Audio tsy voatonona anarana"
}
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Søkelinje for lyd"
},
"action": {
"delete": "Slett",
"dismiss": "Avvis",
"explore_rooms": "Se alle rom",
"play": "Spill av",
"search": "Søk"
},
"left_panel": {
"open_dial_pad": "Åpne nummerpanelet"
},
"time": {
"about_day_ago": "cirka 1 dag siden",
"about_hour_ago": "cirka 1 time siden",
"about_minute_ago": "cirka 1 minutt siden",
"few_seconds_ago": "noen sekunder siden",
"in_about_day": "rundt en dag fra nå",
"in_about_hour": "rundt en time fra nå",
"in_about_minute": "rundt et minutt fra nå",
"in_few_seconds": "om noen sekunder fra nå",
"in_n_days": "%(num)s dager fra nå",
"in_n_hours": "%(num)s timer fra nå",
"in_n_minutes": "%(num)s minutter fra nå",
"n_days_ago": "%(num)s dager siden",
"n_hours_ago": "%(num)s timer siden",
"n_minutes_ago": "%(num)s minutter siden"
},
"timeline": {
"m.audio": {
"audio_player": "Lydavspiller",
"error_downloading_audio": "Feil ved nedlasting av lyd",
"unnamed_audio": "Ikke navngitt lyd"
}
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Audio zoekbalk"
},
"action": {
"delete": "Verwijderen",
"dismiss": "Sluiten",
"explore_rooms": "Kamers ontdekken",
"pause": "Pauze",
"play": "Afspelen",
"search": "Zoeken"
},
"left_panel": {
"open_dial_pad": "Kiestoetsen openen"
},
"time": {
"about_day_ago": "ongeveer een dag geleden",
"about_hour_ago": "ongeveer een uur geleden",
"about_minute_ago": "ongeveer een minuut geleden",
"few_seconds_ago": "enige tellen geleden",
"in_about_day": "over een dag of zo",
"in_about_hour": "over ongeveer een uur",
"in_about_minute": "over ongeveer een minuut",
"in_few_seconds": "over een paar tellen",
"in_n_days": "over %(num)s dagen",
"in_n_hours": "over %(num)s uur",
"in_n_minutes": "over %(num)s minuten",
"n_days_ago": "%(num)s dagen geleden",
"n_hours_ago": "%(num)s uur geleden",
"n_minutes_ago": "%(num)s minuten geleden"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Fout bij downloaden van audio",
"unnamed_audio": "Naamloze audio"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Pasek wyszukiwania audio"
},
"action": {
"delete": "Usuń",
"dismiss": "Pomiń",
"explore_rooms": "Przeglądaj pokoje",
"pause": "Wstrzymaj",
"play": "Odtwórz",
"search": "Szukaj"
},
"left_panel": {
"open_dial_pad": "Otwórz klawiaturę numeryczną"
},
"time": {
"about_day_ago": "około dzień temu",
"about_hour_ago": "około godziny temu",
"about_minute_ago": "około minuty temu",
"few_seconds_ago": "kilka sekund temu",
"in_about_day": "około dnia od teraz",
"in_about_hour": "około godziny od teraz",
"in_about_minute": "około minuty od teraz",
"in_few_seconds": "za kilka sekund",
"in_n_days": "za %(num)s dni",
"in_n_hours": "za %(num)s godzin",
"in_n_minutes": "za %(num)s minut",
"n_days_ago": "%(num)s dni temu",
"n_hours_ago": "%(num)s godzin temu",
"n_minutes_ago": "%(num)s minut temu"
},
"timeline": {
"m.audio": {
"audio_player": "Odtwarzacz audio",
"error_downloading_audio": "Wystąpił błąd w trakcie pobierania audio",
"unnamed_audio": "Audio bez nazwy"
}
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Barra de procura de áudio"
},
"action": {
"delete": "Apagar",
"dismiss": "Descartar",
"explore_rooms": "Explorar rooms",
"pause": "Pausar",
"play": "Reproduzir",
"search": "Pesquisar"
},
"left_panel": {
"open_dial_pad": "Abre o teclado de marcação"
},
"time": {
"about_day_ago": "há cerca de um dia",
"about_hour_ago": "há cerca de uma hora",
"about_minute_ago": "há cerca de um minuto",
"few_seconds_ago": "há alguns segundos atrás",
"in_about_day": "daqui a um dia",
"in_about_hour": "daqui a uma hora",
"in_about_minute": "daqui a um minuto",
"in_few_seconds": "daqui a alguns segundos",
"in_n_days": "daqui a %(num)s dias",
"in_n_hours": "daqui a %(num)s horas",
"in_n_minutes": "daqui a %(num)s minutos",
"n_days_ago": "%(num)s dias atrás",
"n_hours_ago": "%(num)s horas atrás",
"n_minutes_ago": "%(num)s minutos atrás"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Erro ao descarregar áudio",
"unnamed_audio": "Áudio sem nome"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Barra de busca de áudio"
},
"action": {
"delete": "Excluir",
"dismiss": "Dispensar",
"explore_rooms": "Explorar salas",
"pause": "Pausar",
"play": "Reproduzir",
"search": "Buscar"
},
"left_panel": {
"open_dial_pad": "Abrir o teclado de discagem"
},
"time": {
"about_day_ago": "há aproximadamente um dia",
"about_hour_ago": "há aproximadamente uma hora",
"about_minute_ago": "há aproximadamente um minuto",
"few_seconds_ago": "há alguns segundos",
"in_about_day": "dentro de aproximadamente um dia",
"in_about_hour": "dentro de aproximadamente uma hora",
"in_about_minute": "dentro de aproximadamente um minuto",
"in_few_seconds": "dentro de alguns segundos",
"in_n_days": "dentro de %(num)s dias",
"in_n_hours": "dentro de %(num)s horas",
"in_n_minutes": "dentro de %(num)s minutos",
"n_days_ago": "há %(num)s dias",
"n_hours_ago": "há %(num)s horas",
"n_minutes_ago": "há %(num)s minutos"
},
"timeline": {
"m.audio": {
"audio_player": "Reprodutor de Áudio",
"error_downloading_audio": "Erro ao baixar o áudio",
"unnamed_audio": "Áudio sem nome"
}
}
}
@@ -0,0 +1,59 @@
{
"a11y": {
"seek_bar_label": "Панель поиска аудио"
},
"action": {
"delete": "Удалить",
"dismiss": "Закрыть",
"explore_rooms": "Обзор комнат",
"pause": "Пауза",
"play": "Воспроизведение",
"search": "Поиск"
},
"left_panel": {
"open_dial_pad": "Открыть панель набора номера"
},
"room": {
"status_bar": {
"delete_all": "Удалить всё",
"exceeded_resource_limit_description": "Свяжись с администратором сервиса, чтобы продолжить пользоваться услугой.",
"exceeded_resource_limit_title": "Ваше сообщение не было отправлено, поскольку на этом домашнем сервере превышен лимит ресурсов.",
"failed_to_create_room_title": "Не удалось начать чат с этим пользователем.",
"history_visible": "Эта комната оборудована таким образом, чтобы новые участники могли ознакомиться с историей. <a>Подробнее</a>",
"homeserver_blocked_title": "Ваше сообщение не было отправлено, потому что этот домашний сервер заблокирован его администратором.",
"monthly_user_limit_reached_title": "Ваше сообщение не было отправлено, потому что на этом домашнем сервере достигнут лимит ежемесячных активных пользователей.",
"requires_consent_agreement_title": "Вы не сможете отправлять сообщения, пока не ознакомитесь с условиями и положениями и не согласитесь с ними.",
"retry_all": "Повторить попытку для всех",
"select_messages_to_retry": "Вы можете выбрать все или отдельные сообщения для повторной попытки или удаления",
"server_connectivity_lost_description": "Отправленные сообщения будут сохранены, пока соединение не восстановится.",
"server_connectivity_lost_title": "Соединение с сервером потеряно",
"some_messages_not_sent": "Некоторые сообщения не были отправлены"
}
},
"terms": {
"tac_button": "Просмотр условий и положений"
},
"time": {
"about_day_ago": "около суток назад",
"about_hour_ago": "около часа назад",
"about_minute_ago": "около минуты назад",
"few_seconds_ago": "несколько секунд назад",
"in_about_day": "примерно через день",
"in_about_hour": "примерно через час",
"in_about_minute": "примерно через минуту",
"in_few_seconds": "несколько секунд назад",
"in_n_days": "%(num)s дней спустя",
"in_n_hours": "%(num)s часов спустя",
"in_n_minutes": "%(num)s минут спустя",
"n_days_ago": "%(num)s дней назад",
"n_hours_ago": "%(num)s часов назад",
"n_minutes_ago": "%(num)s минут назад"
},
"timeline": {
"m.audio": {
"audio_player": "Аудиоплеер",
"error_downloading_audio": "Ошибка загрузки аудио",
"unnamed_audio": "Безымянное аудио"
}
}
}
@@ -0,0 +1,44 @@
{
"a11y": {
"seek_bar_label": "Panel vyhľadávania zvuku"
},
"action": {
"delete": "Vymazať",
"dismiss": "Zamietnuť",
"explore_rooms": "Preskúmať miestnosti",
"pause": "Pozastaviť",
"play": "Prehrať",
"search": "Hľadať"
},
"left_panel": {
"open_dial_pad": "Otvoriť číselník"
},
"room": {
"status_bar": {
"history_visible": "Správy, ktoré odošlete, budú zdieľané s novými členmi pozvanými do tejto miestnosti. <a>Zistiť viac</a>"
}
},
"time": {
"about_day_ago": "asi pred jedným dňom",
"about_hour_ago": "približne pred hodinou",
"about_minute_ago": "približne pred minútou",
"few_seconds_ago": "pred pár sekundami",
"in_about_day": "približne o deň",
"in_about_hour": "približne o hodinu",
"in_about_minute": "približne o minútu",
"in_few_seconds": "o pár sekúnd",
"in_n_days": "o %(num)s dní",
"in_n_hours": "o %(num)s hodín",
"in_n_minutes": "o %(num)s minút",
"n_days_ago": "pred %(num)s dňami",
"n_hours_ago": "pred %(num)s hodinami",
"n_minutes_ago": "pred %(num)s minútami"
},
"timeline": {
"m.audio": {
"audio_player": "Prehrávač zvuku",
"error_downloading_audio": "Chyba pri sťahovaní zvuku",
"unnamed_audio": "Nepomenovaný zvukový záznam"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "Fshije",
"dismiss": "Mos e merr parasysh",
"explore_rooms": "Eksploroni dhoma",
"pause": "Ndalesë",
"play": "Luaje",
"search": "Kërkoni"
},
"left_panel": {
"open_dial_pad": "Hap butona numrash"
},
"time": {
"about_day_ago": "rreth një ditë më parë",
"about_hour_ago": "rreth një orë më parë",
"about_minute_ago": "rreth një minutë më parë",
"few_seconds_ago": "pak sekonda më parë",
"in_about_day": "rreth një ditë nga tani",
"in_about_hour": "rreth një orë nga tani",
"in_about_minute": "rreth një minutë nga tani",
"in_few_seconds": "pak sekonda nga tani",
"in_n_days": "%(num)s ditë nga tani",
"in_n_hours": "%(num)s orë nga tani",
"in_n_minutes": "%(num)s minuta nga tani",
"n_days_ago": "%(num)s ditë më parë",
"n_hours_ago": "%(num)s orë më parë",
"n_minutes_ago": "%(num)s minuta më parë"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Gabim në shkarkim audioje",
"unnamed_audio": "Audio pa emër"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Förloppsfält för ljud"
},
"action": {
"delete": "Radera",
"dismiss": "Avvisa",
"explore_rooms": "Utforska rum",
"pause": "Pausa",
"play": "Spela",
"search": "Sök"
},
"left_panel": {
"open_dial_pad": "Öppna knappsats"
},
"time": {
"about_day_ago": "cirka en dag sedan",
"about_hour_ago": "cirka en timme sedan",
"about_minute_ago": "cirka en minut sedan",
"few_seconds_ago": "några sekunder sedan",
"in_about_day": "om cirka en dag",
"in_about_hour": "om cirka en timme",
"in_about_minute": "om cirka en minut",
"in_few_seconds": "om några sekunder",
"in_n_days": "om %(num)s dagar",
"in_n_hours": "om %(num)s timmar",
"in_n_minutes": "om %(num)s minuter",
"n_days_ago": "%(num)s dagar sedan",
"n_hours_ago": "%(num)s timmar sedan",
"n_minutes_ago": "%(num)s minuter sedan"
},
"timeline": {
"m.audio": {
"audio_player": "Ljudspelare",
"error_downloading_audio": "Fel vid nedladdning av ljud",
"unnamed_audio": "Namnlöst ljud"
}
}
}
@@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Ses arama çubuğu"
},
"action": {
"delete": "Sil",
"dismiss": "Kapat",
"explore_rooms": "Odaları keşfet",
"pause": "Durdur",
"play": "Oynat",
"search": "Ara"
},
"left_panel": {
"open_dial_pad": "Arama tuşlarını aç"
},
"time": {
"about_day_ago": "yaklaşık bir gün önce",
"about_hour_ago": "yaklaşık bir saat önce",
"about_minute_ago": "yaklaşık bir dakika önce",
"few_seconds_ago": "bir kaç saniye önce",
"in_about_day": "şu andan itibaren yaklaşık bir gün",
"in_about_hour": "şu andan itibaren yaklaşık bir saat",
"in_about_minute": "şu andan itibaren yaklaşık bir dakika",
"in_few_seconds": "şu andan itibaren bir kaç saniye",
"in_n_days": "şu andan itibaren %(num)s gün",
"in_n_hours": "şu andan itibaren %(num)s saat",
"in_n_minutes": "şu andan itibaren %(num)s dakika",
"n_days_ago": "%(num)s gün önce",
"n_hours_ago": "%(num)s saat önce",
"n_minutes_ago": "%(num)s dakika önce"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Ses dosyası indirilirken hata oluştu",
"unnamed_audio": "İsimsiz ses"
}
}
}
@@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Панель гортання аудіо"
},
"action": {
"delete": "Видалити",
"dismiss": "Відхилити",
"explore_rooms": "Каталог кімнат",
"pause": "Призупинити",
"play": "Відтворити",
"search": "Пошук"
},
"left_panel": {
"open_dial_pad": "Відкрити номеронабирач"
},
"time": {
"about_day_ago": "близько доби тому",
"about_hour_ago": "близько години тому",
"about_minute_ago": "близько хвилини тому",
"few_seconds_ago": "Декілька секунд тому",
"in_about_day": "приблизно через день",
"in_about_hour": "приблизно через годину",
"in_about_minute": "приблизно через хвилинку",
"in_few_seconds": "декілька секунд тому",
"in_n_days": "%(num)s днів по тому",
"in_n_hours": "%(num)s годин по тому",
"in_n_minutes": "%(num)s хвилин по тому",
"n_days_ago": "%(num)s днів тому",
"n_hours_ago": "%(num)s годин тому",
"n_minutes_ago": "%(num)s хвилин тому"
},
"timeline": {
"m.audio": {
"audio_player": "Звуковий програвач",
"error_downloading_audio": "Помилка завантаження аудіо",
"unnamed_audio": "Аудіо без назви"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "Xoá",
"dismiss": "Bỏ qua",
"explore_rooms": "Khám phá các phòng",
"pause": "Tạm dừng",
"play": "Chạy",
"search": "Tìm kiếm"
},
"left_panel": {
"open_dial_pad": "Mở bàn phím quay số"
},
"time": {
"about_day_ago": "khoảng một ngày trước",
"about_hour_ago": "khoảng một giờ trước",
"about_minute_ago": "khoảng một phút trước",
"few_seconds_ago": "vài giây trước",
"in_about_day": "khoảng một ngày kể từ bây giờ",
"in_about_hour": "khoảng một giờ kể từ bây giờ",
"in_about_minute": "khoảng một phút kể từ bây giờ",
"in_few_seconds": "một vài giây kể từ bây giờ",
"in_n_days": "%(num)s ngày kể từ bây giờ",
"in_n_hours": "%(num)s giờ kể từ bây giờ",
"in_n_minutes": "%(num)s phút kể từ bây giờ",
"n_days_ago": "%(num)s ngày trước",
"n_hours_ago": "%(num)s giờ trước",
"n_minutes_ago": "%(num)s phút trước"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Lỗi khi tải xuống âm thanh",
"unnamed_audio": "Âm thanh không tên"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "删除",
"dismiss": "忽略",
"explore_rooms": "查找房间",
"pause": "暂停",
"play": "播放",
"search": "搜索"
},
"left_panel": {
"open_dial_pad": "打开拨号键盘"
},
"time": {
"about_day_ago": "约一天前",
"about_hour_ago": "约一小时前",
"about_minute_ago": "约一分钟前",
"few_seconds_ago": "数秒前",
"in_about_day": "从现在开始约一天",
"in_about_hour": "从现在开始约一小时",
"in_about_minute": "从现在开始约一分钟",
"in_few_seconds": "从现在开始数秒",
"in_n_days": "从现在开始%(num)s天",
"in_n_hours": "从现在开始%(num)s小时",
"in_n_minutes": "从现在开始%(num)s分钟",
"n_days_ago": "%(num)s天前",
"n_hours_ago": "%(num)s小时前",
"n_minutes_ago": "%(num)s分钟前"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "下载音频时出错",
"unnamed_audio": "未命名的音频"
}
}
}
@@ -0,0 +1,35 @@
{
"action": {
"delete": "刪除",
"dismiss": "關閉",
"explore_rooms": "探索聊天室",
"pause": "暫停",
"play": "播放",
"search": "搜尋"
},
"left_panel": {
"open_dial_pad": "開啟撥號鍵盤"
},
"time": {
"about_day_ago": "大約一天前",
"about_hour_ago": "大約一小時前",
"about_minute_ago": "大約一分鐘前",
"few_seconds_ago": "數秒前",
"in_about_day": "從現在開始大約一天",
"in_about_hour": "從現在開始大約一小時",
"in_about_minute": "從現在開始大約一分鐘",
"in_few_seconds": "從現在開始數秒鐘",
"in_n_days": "從現在開始 %(num)s 天",
"in_n_hours": "從現在開始 %(num)s 小時",
"in_n_minutes": "從現在開始 %(num)s 分鐘",
"n_days_ago": "%(num)s 天前",
"n_hours_ago": "%(num)s 小時前",
"n_minutes_ago": "%(num)s 分鐘前"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "下載音訊時發生錯誤",
"unnamed_audio": "未命名的音訊"
}
}
}
+1 -4
View File
@@ -17,6 +17,7 @@ export * from "./event-tiles/TextualEventView";
export * from "./message-body/MediaBody";
export * from "./pill-input/Pill";
export * from "./pill-input/PillInput";
export * from "./room/RoomStatusBar";
export * from "./rich-list/RichItem";
export * from "./rich-list/RichList";
export * from "./room-list/RoomListSearchView";
@@ -36,7 +37,3 @@ export * from "./utils/I18nApi";
export * from "./viewmodel";
export * from "./useMockedViewModel";
export * from "./useViewModel";
// i18n (we must export this directly in order to not confuse the type bundler, it seems,
// otherwise it will leave it as a relative import rather than bundling it)
export type * from "./i18nKeys.d.ts";
@@ -61,7 +61,7 @@ export function Pill({ className, children, label, onClick, ...props }: PropsWit
aria-label={_t("action|delete")}
className="mx_Dialog_nonDialogButton"
>
<CloseIcon color="var(--cpd-color-icon-tertiary)" />
<CloseIcon />
</IconButton>
)}
</Flex>
@@ -18,7 +18,7 @@ exports[`Pill renders the pill 1`] = `
<button
aria-describedby="_r_0_"
aria-label="Delete"
class="_icon-button_1pz9o_8 mx_Dialog_nonDialogButton"
class="_icon-button_1215g_8 mx_Dialog_nonDialogButton"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 16px;"
@@ -29,7 +29,6 @@ exports[`Pill renders the pill 1`] = `
style="--cpd-icon-button-size: 100%;"
>
<svg
color="var(--cpd-color-icon-tertiary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
@@ -0,0 +1,11 @@
.container {
color: var(--cpd-color-text-primary);
svg {
/* Ensure button icons are primary too */
color: var(--cpd-color-text-primary) !important;
}
}
.description {
color: var(--cpd-color-text-secondary);
}
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import { type Meta, type StoryFn } from "@storybook/react-vite";
import React, { type JSX } from "react";
import { fn } from "storybook/test";
import { useMockedViewModel } from "../../useMockedViewModel";
import {
RoomStatusBarState,
RoomStatusBarView,
type RoomStatusBarViewActions,
type RoomStatusBarViewSnapshot,
} from "./RoomStatusBarView";
type RoomStatusBarProps = RoomStatusBarViewSnapshot & RoomStatusBarViewActions;
const RoomStatusBarViewWrapper = ({
onResendAllClick,
onDeleteAllClick,
onRetryRoomCreationClick,
onTermsAndConditionsClicked,
...rest
}: RoomStatusBarProps): JSX.Element => {
const vm = useMockedViewModel(rest, {
onResendAllClick,
onDeleteAllClick,
onRetryRoomCreationClick,
onTermsAndConditionsClicked,
});
return <RoomStatusBarView vm={vm} />;
};
export default {
title: "room/RoomStatusBarView",
component: RoomStatusBarViewWrapper,
tags: ["autodocs"],
argTypes: {},
args: {
onResendAllClick: fn(),
onDeleteAllClick: fn(),
onRetryRoomCreationClick: fn(),
onTermsAndConditionsClicked: fn(),
},
} as Meta<typeof RoomStatusBarViewWrapper>;
const Template: StoryFn<typeof RoomStatusBarViewWrapper> = (args) => <RoomStatusBarViewWrapper {...args} />;
/**
* Rendered when the client has lost connection with the server.
*/
export const WithConnectionLost = Template.bind({});
WithConnectionLost.args = {
state: RoomStatusBarState.ConnectionLost,
};
/**
* Rendered when the client needs the user to consent to some terms and conditions before
* they can perform any room actions.
*/
export const WithConsentLink = Template.bind({});
WithConsentLink.args = {
state: RoomStatusBarState.NeedsConsent,
consentUri: "#example",
};
/**
* Rendered when the server has hit a usage limit and is forbidding the user from performing
* any actions in the room. There is an optional parameter to link to an admin to contact.
*/
export const WithResourceLimit = Template.bind({});
WithResourceLimit.args = {
state: RoomStatusBarState.ResourceLimited,
resourceLimit: "hs_disabled",
adminContactHref: "#example",
};
/**
* Rendered when the client has some unsent messages in the room, stored locally.
*/
export const WithUnsentMessages = Template.bind({});
WithUnsentMessages.args = {
state: RoomStatusBarState.UnsentMessages,
isResending: false,
};
/**
* Rendered when the client has some unsent messages in the room, stored locally and is
* trying to send them.
*/
export const WithUnsentMessagesSending = Template.bind({});
WithUnsentMessagesSending.args = {
state: RoomStatusBarState.UnsentMessages,
isResending: true,
};
/**
* Rendered when a local room has failed to be created.
*/
export const WithLocalRoomRetry = Template.bind({});
WithLocalRoomRetry.args = {
state: RoomStatusBarState.LocalRoomFailed,
};
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render } from "jest-matrix-react";
import { composeStories } from "@storybook/react-vite";
import userEvent from "@testing-library/user-event";
import * as stories from "./RoomStatusBarView.stories.tsx";
const { WithConnectionLost, WithConsentLink, WithResourceLimit, WithUnsentMessages, WithLocalRoomRetry } =
composeStories(stories);
describe("RoomStatusBarView", () => {
it("renders connection lost", () => {
const { container } = render(<WithConnectionLost />);
expect(container).toMatchSnapshot();
});
it("renders resource limit error", () => {
const { container } = render(<WithResourceLimit />);
expect(container).toMatchSnapshot();
});
it("renders consent link", () => {
const { container, getByRole } = render(<WithConsentLink />);
expect(container).toMatchSnapshot();
const button = getByRole("link");
expect(button.getAttribute("href")).toEqual("#example");
});
it("renders unsent messages", async () => {
const { container } = render(
<WithUnsentMessages onDeleteAllClick={jest.fn()} onRetryRoomCreationClick={jest.fn()} />,
);
expect(container).toMatchSnapshot();
});
it("renders unsent messages and deletes all", async () => {
const onDeleteAllClick = jest.fn();
const { container, getByRole } = render(<WithUnsentMessages onDeleteAllClick={onDeleteAllClick} />);
expect(container).toMatchSnapshot();
const button = getByRole("button", { name: "Delete all" });
await userEvent.click(button);
expect(onDeleteAllClick).toHaveBeenCalled();
});
it("renders unsent messages and resends all", async () => {
const onResendAllClick = jest.fn();
const { container, getByRole } = render(<WithUnsentMessages onResendAllClick={onResendAllClick} />);
expect(container).toMatchSnapshot();
const button = getByRole("button", { name: "Retry all" });
await userEvent.click(button);
expect(onResendAllClick).toHaveBeenCalled();
});
it("renders local room error", async () => {
const onRetryRoomCreationClick = jest.fn();
const { container, getByRole } = render(
<WithLocalRoomRetry onRetryRoomCreationClick={onRetryRoomCreationClick} />,
);
expect(container).toMatchSnapshot();
const button = getByRole("button", { name: "Retry" });
await userEvent.click(button);
expect(onRetryRoomCreationClick).toHaveBeenCalled();
});
});

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