Merge branch 'element-hq:develop' into poll-votes
|
@ -266,6 +266,9 @@ module.exports = {
|
|||
parserOptions: {
|
||||
project: ["./playwright/tsconfig.json"],
|
||||
},
|
||||
rules: {
|
||||
"react-hooks/rules-of-hooks": ["off"],
|
||||
},
|
||||
},
|
||||
],
|
||||
settings: {
|
||||
|
|
3
.github/workflows/release_prepare.yml
vendored
|
@ -20,6 +20,9 @@ on:
|
|||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
# The order is specified bottom-up to avoid any races for allchange
|
||||
REPOS: matrix-js-sdk element-web element-desktop
|
||||
steps:
|
||||
- name: Checkout Element Desktop
|
||||
uses: actions/checkout@v4
|
||||
|
|
|
@ -1 +1 @@
|
|||
20
|
||||
22
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:20-bullseye as builder
|
||||
FROM --platform=$BUILDPLATFORM node:22-bullseye as builder
|
||||
|
||||
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||
ARG USE_CUSTOM_SDKS=false
|
||||
|
|
|
@ -29,7 +29,7 @@ default theme, you would use `default_theme: "custom-Electric Blue"`.
|
|||
|
||||
e.g. in config.json:
|
||||
|
||||
```
|
||||
```json5
|
||||
"setting_defaults": {
|
||||
"custom_themes": [
|
||||
{
|
||||
|
@ -59,6 +59,10 @@ e.g. in config.json:
|
|||
"timeline-text-color": "#2e2f32",
|
||||
"timeline-text-secondary-color": "#61708b",
|
||||
"timeline-highlights-color": "#f3f8fd",
|
||||
|
||||
// These should both be 8 values long
|
||||
"username-colors": ["#ff0000", /*...*/],
|
||||
"avatar-background-colors": ["#cc0000", /*...*/]
|
||||
},
|
||||
"compound": {
|
||||
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",
|
||||
|
|
|
@ -46,5 +46,13 @@
|
|||
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx",
|
||||
"setting_defaults": {
|
||||
"RustCrypto.staged_rollout_percent": 60
|
||||
},
|
||||
"features": {
|
||||
"feature_video_rooms": true,
|
||||
"feature_group_calls": true,
|
||||
"feature_element_call_video_rooms": true
|
||||
},
|
||||
"element_call": {
|
||||
"url": "https://call.element.io"
|
||||
}
|
||||
}
|
||||
|
|
10
package.json
|
@ -84,7 +84,7 @@
|
|||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@formatjs/intl-segmenter": "^11.5.7",
|
||||
"@matrix-org/analytics-events": "^0.26.0",
|
||||
"@matrix-org/analytics-events": "^0.28.0",
|
||||
"@matrix-org/emojibase-bindings": "^1.3.3",
|
||||
"@vector-im/matrix-wysiwyg": "2.37.13",
|
||||
"@matrix-org/react-sdk-module-api": "^2.4.0",
|
||||
|
@ -148,7 +148,7 @@
|
|||
"tar-js": "^0.3.0",
|
||||
"temporal-polyfill": "^0.2.5",
|
||||
"ua-parser-js": "^1.0.2",
|
||||
"uuid": "^10.0.0",
|
||||
"uuid": "^11.0.0",
|
||||
"what-input": "^5.2.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -208,7 +208,7 @@
|
|||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "18.3.3",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/sdp-transform": "^2.4.6",
|
||||
|
@ -219,7 +219,7 @@
|
|||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"axe-core": "4.10.0",
|
||||
"axe-core": "4.10.2",
|
||||
"babel-jest": "^29.0.0",
|
||||
"babel-loader": "^9.0.0",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
|
@ -242,7 +242,7 @@
|
|||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-matrix-org": "^2.0.2",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-unicorn": "^56.0.0",
|
||||
"express": "^4.18.2",
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM mcr.microsoft.com/playwright:v1.48.0-jammy
|
||||
FROM mcr.microsoft.com/playwright:v1.48.2-jammy
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
|
|
@ -196,14 +196,7 @@ export class Helpers {
|
|||
*/
|
||||
async assertEmptyPinnedMessagesList() {
|
||||
const rightPanel = this.getRightPanel();
|
||||
await expect(rightPanel).toMatchScreenshot(`pinned-messages-list-empty.png`, {
|
||||
// hide the tooltip "Room information" to avoid flakiness
|
||||
css: `
|
||||
[data-floating-ui-portal] {
|
||||
display: none !important;
|
||||
}
|
||||
`,
|
||||
});
|
||||
await expect(rightPanel).toMatchScreenshot(`pinned-messages-list-empty.png`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,7 +42,7 @@ export class Helpers {
|
|||
*/
|
||||
async assertReleaseAnnouncementIsVisible(name: string) {
|
||||
await expect(this.getReleaseAnnouncement(name)).toBeVisible();
|
||||
await expect(this.page).toMatchScreenshot(`release-announcement-${name}.png`);
|
||||
await expect(this.page).toMatchScreenshot(`release-announcement-${name}.png`, { showTooltips: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -345,6 +345,7 @@ export const expect = baseExpect.extend({
|
|||
|
||||
if (!options?.showTooltips) {
|
||||
css += `
|
||||
[data-floating-ui-portal],
|
||||
[role="tooltip"] {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
|
|||
// Docker tag to use for synapse docker image.
|
||||
// We target a specific digest as every now and then a Synapse update will break our CI.
|
||||
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
||||
const DOCKER_TAG = "develop@sha256:85dc2cf25f45ee91fd87efa0ddf2220a5933d212ed656886d5f3832ae3a9ddaf";
|
||||
const DOCKER_TAG = "develop@sha256:47ed20bdf698523c1876aded101ffa5cd5c5b879c80762b7b18b87347349123b";
|
||||
|
||||
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
||||
const templateDir = path.join(__dirname, "templates", opts.template);
|
||||
|
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
@ -287,7 +287,6 @@
|
|||
@import "./views/rooms/_HistoryTile.pcss";
|
||||
@import "./views/rooms/_IRCLayout.pcss";
|
||||
@import "./views/rooms/_JumpToBottomButton.pcss";
|
||||
@import "./views/rooms/_LegacyRoomHeader.pcss";
|
||||
@import "./views/rooms/_LinkPreviewGroup.pcss";
|
||||
@import "./views/rooms/_LinkPreviewWidget.pcss";
|
||||
@import "./views/rooms/_LiveContentSummary.pcss";
|
||||
|
|
|
@ -181,11 +181,6 @@ Please see LICENSE files in the repository root for full details.
|
|||
}
|
||||
}
|
||||
|
||||
/* Rooms with immersive content */
|
||||
.mx_RoomView_immersive .mx_LegacyRoomHeader_wrapper {
|
||||
border: unset;
|
||||
}
|
||||
|
||||
.mx_RoomView_inCall {
|
||||
.mx_RoomView_statusAreaBox_line {
|
||||
margin-top: 2px;
|
||||
|
|
|
@ -18,10 +18,9 @@ Please see LICENSE files in the repository root for full details.
|
|||
.mx_EditableItem_delete {
|
||||
@mixin customisedCancelButton;
|
||||
order: 3;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: $alert;
|
||||
mask-size: 100%;
|
||||
}
|
||||
|
@ -42,7 +41,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
.mx_EditableItem_item {
|
||||
flex: auto 1 0;
|
||||
order: 1;
|
||||
width: calc(100% - 14px); /* leave space for the remove button */
|
||||
width: calc(100% - 28px); /* leave space for the remove button */
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
|
|
@ -1,281 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--RoomHeader-indicator-dot-size: 8px;
|
||||
--RoomHeader-indicator-dot-offset: -3px;
|
||||
--RoomHeader-indicator-pulseColor: $alert;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader {
|
||||
flex: 0 0 50px;
|
||||
border-bottom: 1px solid $primary-hairline-color;
|
||||
background-color: $background;
|
||||
|
||||
.mx_LegacyRoomHeader_icon {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
|
||||
&.mx_LegacyRoomHeader_icon_video {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
background-color: $secondary-content;
|
||||
mask-image: url("$(res)/img/element-icons/call/video-call.svg");
|
||||
mask-size: 100%;
|
||||
}
|
||||
|
||||
&.mx_E2EIcon {
|
||||
margin: 0;
|
||||
height: 100%; /* To give the tooltip room to breathe */
|
||||
}
|
||||
}
|
||||
|
||||
.mx_CallDuration {
|
||||
margin-top: calc(($font-15px - $font-13px) / 2); /* To align with the name */
|
||||
font-size: $font-13px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_wrapper {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
padding: 10px 20px 9px 16px;
|
||||
border-bottom: 1px solid $separator;
|
||||
|
||||
.mx_InviteOnlyIcon_large {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mx_BetaCard_betaPill {
|
||||
margin-right: $spacing-8;
|
||||
}
|
||||
|
||||
/* The container of E2EIcon in the legacy header needs to have its height set */
|
||||
& > span {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_name {
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
color: $primary-content;
|
||||
font: var(--cpd-font-heading-sm-semibold);
|
||||
font-weight: var(--cpd-font-weight-semibold);
|
||||
min-height: 24px;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
margin: 0 3px;
|
||||
padding: 1px 4px;
|
||||
display: flex;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $quinary-content;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_nametext {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_chevron {
|
||||
align-self: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
mask-position: center;
|
||||
mask-size: 20px;
|
||||
mask-repeat: no-repeat;
|
||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
||||
background-color: $tertiary-content;
|
||||
}
|
||||
|
||||
&.mx_LegacyRoomHeader_name--textonly {
|
||||
cursor: unset;
|
||||
|
||||
&:hover {
|
||||
background-color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
&[aria-expanded="true"] {
|
||||
background-color: $separator;
|
||||
|
||||
.mx_LegacyRoomHeader_chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_settingsHint {
|
||||
color: $settings-grey-fg-color !important;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_searchStatus {
|
||||
font-weight: normal;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.mx_RoomTopic {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_topic {
|
||||
$lines: 2;
|
||||
|
||||
flex: 1;
|
||||
color: $secondary-content;
|
||||
font: var(--cpd-font-body-sm-regular);
|
||||
line-height: 1rem;
|
||||
max-height: calc(1rem * $lines);
|
||||
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: $lines; /* See: https://drafts.csswg.org/css-overflow-3/#webkit-line-clamp */
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_topic .mx_Emoji {
|
||||
/* Undo font size increase to prevent vertical cropping and ensure the same size */
|
||||
/* as in plain text emojis */
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_avatar {
|
||||
flex: 0;
|
||||
margin: 0 7px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_button_unreadIndicator_bg {
|
||||
position: absolute;
|
||||
right: var(--RoomHeader-indicator-dot-offset);
|
||||
top: var(--RoomHeader-indicator-dot-offset);
|
||||
margin: 4px;
|
||||
width: var(--RoomHeader-indicator-dot-size);
|
||||
height: var(--RoomHeader-indicator-dot-size);
|
||||
border-radius: 50%;
|
||||
transform: scale(1.6);
|
||||
transform-origin: center center;
|
||||
background: $background;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_button_unreadIndicator {
|
||||
position: absolute;
|
||||
right: var(--RoomHeader-indicator-dot-offset);
|
||||
top: var(--RoomHeader-indicator-dot-offset);
|
||||
margin: 4px;
|
||||
|
||||
&.mx_Indicator_highlight {
|
||||
background: var(--cpd-color-icon-critical-primary);
|
||||
box-shadow: var(--cpd-color-icon-critical-primary);
|
||||
}
|
||||
|
||||
&.mx_Indicator_notification {
|
||||
background: var(--cpd-color-icon-success-primary);
|
||||
box-shadow: var(--cpd-color-icon-success-primary);
|
||||
}
|
||||
|
||||
&.mx_Indicator_activity {
|
||||
background: var(--cpd-color-icon-primary);
|
||||
box-shadow: var(--cpd-color-icon-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_forgetButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/leave.svg");
|
||||
width: 26px;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_appsButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/room/apps.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_appsButton_highlight::before {
|
||||
background-color: $accent;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_searchButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/room/search-inset.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_inviteButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/room/invite.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_voiceCallButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/call/voice-call.svg");
|
||||
|
||||
/* The call button SVG is padded slightly differently, so match it up to the size */
|
||||
/* of the other icons */
|
||||
mask-size: 20px;
|
||||
mask-position: center;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_videoCallButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/call/video-call.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_layoutButton--freedom::before,
|
||||
.mx_LegacyRoomHeader_freedomIcon::before {
|
||||
mask-image: url("$(res)/img/element-icons/call/freedom.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_layoutButton--spotlight::before,
|
||||
.mx_LegacyRoomHeader_spotlightIcon::before {
|
||||
mask-image: url("$(res)/img/element-icons/call/spotlight.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_closeButton {
|
||||
&::before {
|
||||
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
|
||||
mask-size: 20px;
|
||||
mask-position: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: unset; /* remove background color on hover */
|
||||
|
||||
&::before {
|
||||
background-color: $icon-button-color; /* set the default background color */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_minimiseButton::before {
|
||||
mask-image: url("$(res)/img/element-icons/reduce.svg");
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader_layoutMenu .mx_IconizedContextMenu_icon::before {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
mask-position: center;
|
||||
mask-size: 20px;
|
||||
mask-repeat: no-repeat;
|
||||
background: $primary-content;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 480px) {
|
||||
.mx_LegacyRoomHeader_wrapper {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mx_LegacyRoomHeader {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
|
@ -88,3 +88,8 @@ Please see LICENSE files in the repository root for full details.
|
|||
.mx_RoomHeader .mx_BaseAvatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_videoCallOption {
|
||||
/* Workaround for https://github.com/element-hq/compound/issues/331 */
|
||||
min-width: 240px;
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="17px" height="15px" viewBox="-1 -1 16 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>icon_camera</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="06a-Room-settings" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="06_4-Room-settings-admin" sketch:type="MSArtboardGroup" transform="translate(-248.000000, -71.000000)" fill="#454545">
|
||||
<path d="M255.5,76.25 C256.119795,76.25 256.649737,76.4700499 257.089844,76.9101562 C257.52995,77.3502626 257.75,77.8802052 257.75,78.5 C257.75,79.1197948 257.52995,79.6497374 257.089844,80.0898438 C256.649737,80.5299501 256.119795,80.75 255.5,80.75 C254.880205,80.75 254.350263,80.5299501 253.910156,80.0898438 C253.47005,79.6497374 253.25,79.1197948 253.25,78.5 C253.25,77.8802052 253.47005,77.3502626 253.910156,76.9101562 C254.350263,76.4700499 254.880205,76.25 255.5,76.25 L255.5,76.25 Z M261,73 C261.552086,73 262.023436,73.1953105 262.414062,73.5859375 C262.804689,73.9765645 263,74.4479139 263,75 L263,82 C263,82.5520861 262.804689,83.0234355 262.414062,83.4140625 C262.023436,83.8046895 261.552086,84 261,84 L250,84 C249.447914,84 248.976564,83.8046895 248.585938,83.4140625 C248.195311,83.0234355 248,82.5520861 248,82 L248,75 C248,74.4479139 248.195311,73.9765645 248.585938,73.5859375 C248.976564,73.1953105 249.447914,73 250,73 L251.75,73 L252.148438,71.9375 C252.247396,71.6822904 252.428384,71.4622405 252.691406,71.2773438 C252.954428,71.092447 253.223957,71 253.5,71 L257.5,71 C257.776043,71 258.045572,71.092447 258.308594,71.2773438 C258.571616,71.4622405 258.752604,71.6822904 258.851562,71.9375 L259.25,73 L261,73 Z M255.5,82 C256.463546,82 257.287757,81.6575555 257.972656,80.9726562 C258.657556,80.287757 259,79.4635465 259,78.5 C259,77.5364535 258.657556,76.712243 257.972656,76.0273438 C257.287757,75.3424445 256.463546,75 255.5,75 C254.536454,75 253.712243,75.3424445 253.027344,76.0273438 C252.342444,76.712243 252,77.5364535 252,78.5 C252,79.4635465 252.342444,80.287757 253.027344,80.9726562 C253.712243,81.6575555 254.536454,82 255.5,82 L255.5,82 Z" id="icon_camera" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,3 +0,0 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.28571 3.66667C6.28571 2.74619 7.03191 2 7.95238 2H11.7619C12.6824 2 13.4286 2.74619 13.4286 3.66667V7.47619C13.4286 8.39666 12.6824 9.14286 11.7619 9.14286H7.95238C7.03191 9.14286 6.28571 8.39667 6.28571 7.47619V3.66667ZM16.5238 2C15.6033 2 14.8571 2.74619 14.8571 3.66667V7.47619C14.8571 8.39667 15.6033 9.14286 16.5238 9.14286H20.3333C21.2538 9.14286 22 8.39666 22 7.47619V3.66667C22 2.74619 21.2538 2 20.3333 2H16.5238ZM16.5238 10.5714C15.6033 10.5714 14.8571 11.3176 14.8571 12.2381V16.0476C14.8571 16.9681 15.6033 17.7143 16.5238 17.7143H20.3333C21.2538 17.7143 22 16.9681 22 16.0476V12.2381C22 11.3176 21.2538 10.5714 20.3333 10.5714H16.5238ZM3.63265 10.5714C2.73096 10.5714 2 11.3024 2 12.2041V20.3673C2 21.269 2.73097 22 3.63266 22H11.7959C12.6976 22 13.4286 21.269 13.4286 20.3673V12.2041C13.4286 11.3024 12.6976 10.5714 11.7959 10.5714H3.63265Z" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1 KiB |
|
@ -1,3 +0,0 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 17.8689V2.51551C4 2.06669 4.53728 1.83452 4.86986 2.13591C8.18767 5.14263 10.9111 7.48209 13.102 9.36399L13.102 9.36403C18.3295 13.8544 20.5243 15.7398 20.5243 17.8689C20.5243 19.4181 19.6538 20.0153 18.1044 20.79C16.5549 21.5648 14.4534 22 12.2621 22C10.0709 22 7.96938 21.5648 6.41992 20.79C4.87047 20.0153 4 18.9646 4 17.8689ZM12.2621 20.9673C16.2548 20.9673 19.4915 19.5801 19.4915 17.869C19.4915 16.1578 16.2548 14.7707 12.2621 14.7707C8.26947 14.7707 5.03277 16.1578 5.03277 17.869C5.03277 19.5801 8.26947 20.9673 12.2621 20.9673ZM16.2618 8.67876C16.1718 8.64549 16.1718 8.51831 16.2618 8.48504L17.84 7.90103C17.8683 7.89057 17.8906 7.86828 17.901 7.84001L18.4851 6.26174C18.5183 6.17182 18.6455 6.17182 18.6788 6.26174L19.2628 7.84001C19.2733 7.86828 19.2955 7.89057 19.3238 7.90103L20.9021 8.48504C20.992 8.51831 20.992 8.64549 20.9021 8.67876L19.3238 9.26277C19.2955 9.27323 19.2733 9.29552 19.2628 9.32379L18.6788 10.9021C18.6455 10.992 18.5183 10.992 18.4851 10.9021L17.901 9.32379C17.8906 9.29552 17.8683 9.27323 17.84 9.26277L16.2618 8.67876ZM13.2618 5.45232C13.1718 5.48559 13.1718 5.61276 13.2618 5.64604L14.0862 5.95111C14.1145 5.96157 14.1368 5.98386 14.1472 6.01213L14.4523 6.83657C14.4856 6.92649 14.6127 6.92649 14.646 6.83657L14.9511 6.01213C14.9615 5.98386 14.9838 5.96157 15.0121 5.95111L15.8365 5.64603C15.9265 5.61276 15.9265 5.48559 15.8365 5.45232L15.0121 5.14725C14.9838 5.13679 14.9615 5.1145 14.9511 5.08623L14.646 4.26178C14.6127 4.17187 14.4856 4.17187 14.4523 4.26178L14.1472 5.08623C14.1368 5.1145 14.1145 5.13679 14.0862 5.14725L13.2618 5.45232Z" fill="white"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,4 +0,0 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0759 13.6172C13.0273 13.7343 13.0004 13.8625 13 13.997C13 13.998 13 13.999 13 14L13 14.0007L13 20C13 20.5523 13.4477 21 14 21C14.5523 21 15 20.5523 15 20L15 16.4142L18.7929 20.2071C19.1834 20.5976 19.8166 20.5976 20.2071 20.2071C20.5976 19.8166 20.5976 19.1834 20.2071 18.7929L16.4142 15L20 15C20.5523 15 21 14.5523 21 14C21 13.4477 20.5523 13 20 13L14.0007 13L14 13C13.999 13 13.998 13 13.997 13C13.743 13.0008 13.4892 13.0977 13.295 13.2908C13.2943 13.2915 13.2936 13.2922 13.2929 13.2929C13.2922 13.2936 13.2915 13.2943 13.2908 13.295C13.196 13.3904 13.1243 13.5001 13.0759 13.6172ZM13.0759 13.6172C13.1262 13.4959 13.1996 13.3867 13.2908 13.295L13.0759 13.6172Z" fill="#737D8C"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.9241 10.3828C10.9727 10.2657 10.9996 10.1375 11 10.003C11 10.002 11 10.001 11 10V9.9993L11 4C11 3.44772 10.5523 3 10 3C9.44772 3 9 3.44772 9 4L9 7.58579L5.20711 3.79289C4.81658 3.40237 4.18342 3.40237 3.79289 3.79289C3.40237 4.18342 3.40237 4.81658 3.79289 5.20711L7.58579 9L4 9C3.44771 9 3 9.44771 3 10C3 10.5523 3.44771 11 4 11L9.9993 11H10C10.001 11 10.002 11 10.003 11C10.257 10.9992 10.5108 10.9023 10.705 10.7092C10.7057 10.7085 10.7064 10.7078 10.7071 10.7071C10.7078 10.7064 10.7085 10.7057 10.7092 10.705C10.804 10.6096 10.8757 10.4999 10.9241 10.3828ZM10.9241 10.3828C10.8738 10.5041 10.8004 10.6133 10.7092 10.705L10.9241 10.3828Z" fill="#737D8C"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,4 +0,0 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 2L18 6L7 17H3V13L14 2V2Z" stroke="#2E2F32" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 22H21" stroke="#2E2F32" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 333 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="9" height="4" viewBox="0 0 9 4" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.5 4L0.602887 0.25L8.39711 0.250001L4.5 4Z" fill="#0DBD8B"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 171 B |
|
@ -1,3 +0,0 @@
|
|||
<svg height="45" viewBox="0 0 69 45" width="69" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m6 0h57c3.3137085 0 6 2.6862915 6 6v33c0 3.3137085-2.6862915 6-6 6h-57c-3.3137085 0-6-2.6862915-6-6v-33c0-3.3137085 2.6862915-6 6-6zm23.3703704 11c-.6518551 0-1.1841955.3860101-1.5970371 1.158042l-7.7244444 14.7006993-7.7896296-14.7006993c-.4128416-.7720319-.9560461-1.158042-1.6296297-1.158042-.4780271 0-.86913425.1554763-1.1733333.4664336-.30419906.3109572-.4562963.7130511-.4562963 1.2062937v19.7510489c0 .4717973.13580111.8524461.40740741 1.1419581.27160629.2895119.63555329.4342657 1.09185189.4342657.478027 0 .8474061-.1393925 1.1081481-.4181818.2607421-.2787893.3911111-.6647994.3911111-1.158042v-14.8293706l6.4207408 11.8699301c.4128415.7505865.9451819 1.1258741 1.597037 1.1258741s1.1841955-.3752876 1.597037-1.1258741l6.3881482-11.9986014v14.9580419c0 .4932426.130369.8792527.3911111 1.158042.260742.2787893.619257.4181818 1.0755555.4181818.4780271 0 .8528382-.1393925 1.1244445-.4181818s.4074074-.6647994.4074074-1.158042v-19.7510489c0-.4932426-.1520972-.8953365-.4562963-1.2062937-.304199-.3109573-.6953062-.4664336-1.1733333-.4664336zm18.1296296 17.8786797-7.3180195-7.3180195c-.5857864-.5857865-1.5355339-.5857865-2.1213203 0-.5857865.5857864-.5857865 1.5355339 0 2.1213203l9.8994949 9.899495c.3056756.3056756.7104567.4518432 1.1109127.438503.400456.0133402.8052371-.1328274 1.1109127-.438503l9.899495-9.899495c.5857864-.5857864.5857864-1.5355339 0-2.1213203-.5857865-.5857865-1.535534-.5857865-2.1213204 0l-7.4601551 7.4601551v-16.5208153c0-.8284271-.6715729-1.5-1.5-1.5s-1.5.6715729-1.5 1.5z" fill="#d8d8d8" fill-rule="evenodd"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.6 KiB |
|
@ -1,5 +0,0 @@
|
|||
<svg width="11" height="12" viewBox="0 0 11 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="0.5" width="2.2" height="11" fill="#0DBD8B"/>
|
||||
<rect x="4.40015" y="2.70001" width="2.2" height="8.8" fill="#0DBD8B"/>
|
||||
<rect x="8.79993" y="7.10004" width="2.2" height="4.4" fill="#0DBD8B"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 302 B |
2
res/jitsi_external_api.min.js
vendored
|
@ -82,6 +82,10 @@ export class DecryptionFailureTracker {
|
|||
return "HistoricalMessage";
|
||||
case DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED:
|
||||
return "ExpectedDueToMembership";
|
||||
case DecryptionFailureCode.SENDER_IDENTITY_PREVIOUSLY_VERIFIED:
|
||||
return "ExpectedVerificationViolation";
|
||||
case DecryptionFailureCode.UNSIGNED_SENDER_DEVICE:
|
||||
return "ExpectedSentByInsecureDevice";
|
||||
default:
|
||||
return "UnknownError";
|
||||
}
|
||||
|
|
|
@ -292,16 +292,11 @@ export default class DeviceListener {
|
|||
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
|
||||
|
||||
// cross signing isn't enabled - nag to enable it
|
||||
// There are 3 different toasts for:
|
||||
// There are 2 different toasts for:
|
||||
if (!(await crypto.getCrossSigningKeyId()) && (await crypto.userHasCrossSigningKeys())) {
|
||||
// Cross-signing on account but this device doesn't trust the master key (verify this session)
|
||||
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
|
||||
this.checkKeyBackupStatus();
|
||||
} else {
|
||||
const backupInfo = await this.getKeyBackupInfo();
|
||||
if (backupInfo) {
|
||||
// No cross-signing on account but key backup available (upgrade encryption)
|
||||
showSetupEncryptionToast(SetupKind.UPGRADE_ENCRYPTION);
|
||||
} else {
|
||||
// No cross-signing or key backup on account (set up encryption)
|
||||
await cli.waitForClientWellKnown();
|
||||
|
@ -315,7 +310,6 @@ export default class DeviceListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be done after awaiting on getUserDeviceInfo() above, so
|
||||
// we make sure we get the devices after the fetch is done.
|
||||
|
|
|
@ -11,7 +11,7 @@ import React, { createRef } from "react";
|
|||
import FileSaver from "file-saver";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { AuthDict, CrossSigningKeys, MatrixError, UIAFlow, UIAResponse } from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoEvent, BackupTrustInfo, GeneratedSecretStorageKey, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
import { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api";
|
||||
import classNames from "classnames";
|
||||
import CheckmarkIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
|
||||
|
||||
|
@ -25,7 +25,6 @@ import StyledRadioButton from "../../../../components/views/elements/StyledRadio
|
|||
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
|
||||
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
||||
import InlineSpinner from "../../../../components/views/elements/InlineSpinner";
|
||||
import RestoreKeyBackupDialog from "../../../../components/views/dialogs/security/RestoreKeyBackupDialog";
|
||||
import {
|
||||
getSecureBackupSetupMethods,
|
||||
isSecureBackupRequired,
|
||||
|
@ -45,7 +44,6 @@ enum Phase {
|
|||
Loading = "loading",
|
||||
LoadError = "load_error",
|
||||
ChooseKeyPassphrase = "choose_key_passphrase",
|
||||
Migrate = "migrate",
|
||||
Passphrase = "passphrase",
|
||||
PassphraseConfirm = "passphrase_confirm",
|
||||
ShowKey = "show_key",
|
||||
|
@ -72,24 +70,6 @@ interface IState {
|
|||
downloaded: boolean;
|
||||
setPassphrase: boolean;
|
||||
|
||||
/** Information on the current key backup version, as returned by the server.
|
||||
*
|
||||
* `null` could mean any of:
|
||||
* * we haven't yet requested the data from the server.
|
||||
* * we were unable to reach the server.
|
||||
* * the server returned key backup version data we didn't understand or was malformed.
|
||||
* * there is actually no backup on the server.
|
||||
*/
|
||||
backupInfo: KeyBackupInfo | null;
|
||||
|
||||
/**
|
||||
* Information on whether the backup in `backupInfo` is correctly signed, and whether we have the right key to
|
||||
* decrypt it.
|
||||
*
|
||||
* `undefined` if `backupInfo` is null, or if crypto is not enabled in the client.
|
||||
*/
|
||||
backupTrustInfo: BackupTrustInfo | undefined;
|
||||
|
||||
// does the server offer a UI auth flow with just m.login.password
|
||||
// for /keys/device_signing/upload?
|
||||
canUploadKeysWithPasswordOnly: boolean | null;
|
||||
|
@ -141,16 +121,17 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
this.queryKeyUploadAuth();
|
||||
}
|
||||
|
||||
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
|
||||
const phase = keyFromCustomisations ? Phase.Loading : Phase.ChooseKeyPassphrase;
|
||||
|
||||
this.state = {
|
||||
phase: Phase.Loading,
|
||||
phase,
|
||||
passPhrase: "",
|
||||
passPhraseValid: false,
|
||||
passPhraseConfirm: "",
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
setPassphrase: false,
|
||||
backupInfo: null,
|
||||
backupTrustInfo: undefined,
|
||||
// does the server offer a UI auth flow with just m.login.password
|
||||
// for /keys/device_signing/upload?
|
||||
accountPasswordCorrect: null,
|
||||
|
@ -160,60 +141,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
accountPassword,
|
||||
};
|
||||
|
||||
cli.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatusChange);
|
||||
|
||||
this.getInitialPhase();
|
||||
if (keyFromCustomisations) this.initExtension(keyFromCustomisations);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
MatrixClientPeg.get()?.removeListener(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatusChange);
|
||||
}
|
||||
|
||||
private getInitialPhase(): void {
|
||||
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup.createSecretStorageKey();
|
||||
if (keyFromCustomisations) {
|
||||
private initExtension(keyFromCustomisations: Uint8Array): void {
|
||||
logger.log("CryptoSetupExtension: Created key via extension, jumping to bootstrap step");
|
||||
this.recoveryKey = {
|
||||
privateKey: keyFromCustomisations,
|
||||
};
|
||||
this.bootstrapSecretStorage();
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchBackupInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get information on the current backup from the server, and update the state.
|
||||
*
|
||||
* Updates {@link IState.backupInfo} and {@link IState.backupTrustInfo}, and picks an appropriate phase for
|
||||
* {@link IState.phase}.
|
||||
*
|
||||
* @returns If the backup data was retrieved successfully, the trust info for the backup. Otherwise, undefined.
|
||||
*/
|
||||
private async fetchBackupInfo(): Promise<BackupTrustInfo | undefined> {
|
||||
try {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const backupInfo = await cli.getKeyBackupVersion();
|
||||
const backupTrustInfo =
|
||||
// we may not have started crypto yet, in which case we definitely don't trust the backup
|
||||
backupInfo ? await cli.getCrypto()?.isKeyBackupTrusted(backupInfo) : undefined;
|
||||
|
||||
const { forceReset } = this.props;
|
||||
const phase = backupInfo && !forceReset ? Phase.Migrate : Phase.ChooseKeyPassphrase;
|
||||
|
||||
this.setState({
|
||||
phase,
|
||||
backupInfo,
|
||||
backupTrustInfo,
|
||||
});
|
||||
|
||||
return backupTrustInfo;
|
||||
} catch (e) {
|
||||
console.error("Error fetching backup data from server", e);
|
||||
this.setState({ phase: Phase.LoadError });
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async queryKeyUploadAuth(): Promise<void> {
|
||||
|
@ -237,10 +173,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
}
|
||||
}
|
||||
|
||||
private onKeyBackupStatusChange = (): void => {
|
||||
if (this.state.phase === Phase.Migrate) this.fetchBackupInfo();
|
||||
};
|
||||
|
||||
private onKeyPassphraseChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({
|
||||
passPhraseKeySelected: e.target.value,
|
||||
|
@ -265,15 +197,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
}
|
||||
};
|
||||
|
||||
private onMigrateFormSubmit = (e: React.FormEvent): void => {
|
||||
e.preventDefault();
|
||||
if (this.state.backupTrustInfo?.trusted) {
|
||||
this.bootstrapSecretStorage();
|
||||
} else {
|
||||
this.restoreBackup();
|
||||
}
|
||||
};
|
||||
|
||||
private onCopyClick = (): void => {
|
||||
const successful = copyNode(this.recoveryKeyNode.current);
|
||||
if (successful) {
|
||||
|
@ -340,16 +263,28 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
};
|
||||
|
||||
private bootstrapSecretStorage = async (): Promise<void> => {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const crypto = cli.getCrypto()!;
|
||||
const { forceReset } = this.props;
|
||||
|
||||
let backupInfo;
|
||||
// First, unless we know we want to do a reset, we see if there is an existing key backup
|
||||
if (!forceReset) {
|
||||
try {
|
||||
this.setState({ phase: Phase.Loading });
|
||||
backupInfo = await cli.getKeyBackupVersion();
|
||||
} catch (e) {
|
||||
logger.error("Error fetching backup data from server", e);
|
||||
this.setState({ phase: Phase.LoadError });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
phase: Phase.Storing,
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const crypto = cli.getCrypto()!;
|
||||
|
||||
const { forceReset } = this.props;
|
||||
|
||||
try {
|
||||
if (forceReset) {
|
||||
logger.log("Forcing secret storage reset");
|
||||
|
@ -371,8 +306,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
});
|
||||
await crypto.bootstrapSecretStorage({
|
||||
createSecretStorageKey: async () => this.recoveryKey!,
|
||||
keyBackupInfo: this.state.backupInfo!,
|
||||
setupNewKeyBackup: !this.state.backupInfo,
|
||||
setupNewKeyBackup: !backupInfo,
|
||||
});
|
||||
}
|
||||
await initialiseDehydration(true);
|
||||
|
@ -381,20 +315,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
phase: Phase.Stored,
|
||||
});
|
||||
} catch (e) {
|
||||
if (
|
||||
this.state.canUploadKeysWithPasswordOnly &&
|
||||
e instanceof MatrixError &&
|
||||
e.httpStatus === 401 &&
|
||||
e.data.flows
|
||||
) {
|
||||
this.setState({
|
||||
accountPassword: "",
|
||||
accountPasswordCorrect: false,
|
||||
phase: Phase.Migrate,
|
||||
});
|
||||
} else {
|
||||
this.setState({ error: true });
|
||||
}
|
||||
logger.error("Error bootstrapping secret storage", e);
|
||||
}
|
||||
};
|
||||
|
@ -403,27 +324,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
private restoreBackup = async (): Promise<void> => {
|
||||
const { finished } = Modal.createDialog(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
showSummary: false,
|
||||
},
|
||||
undefined,
|
||||
/* priority = */ false,
|
||||
/* static = */ false,
|
||||
);
|
||||
|
||||
await finished;
|
||||
const backupTrustInfo = await this.fetchBackupInfo();
|
||||
if (backupTrustInfo?.trusted && this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
|
||||
this.bootstrapSecretStorage();
|
||||
}
|
||||
};
|
||||
|
||||
private onLoadRetryClick = (): void => {
|
||||
this.setState({ phase: Phase.Loading });
|
||||
this.fetchBackupInfo();
|
||||
this.bootstrapSecretStorage();
|
||||
};
|
||||
|
||||
private onShowKeyContinueClick = (): void => {
|
||||
|
@ -495,12 +397,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
});
|
||||
};
|
||||
|
||||
private onAccountPasswordChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({
|
||||
accountPassword: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
private renderOptionKey(): JSX.Element {
|
||||
return (
|
||||
<StyledRadioButton
|
||||
|
@ -565,55 +461,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
);
|
||||
}
|
||||
|
||||
private renderPhaseMigrate(): JSX.Element {
|
||||
let authPrompt;
|
||||
let nextCaption = _t("action|next");
|
||||
if (this.state.canUploadKeysWithPasswordOnly) {
|
||||
authPrompt = (
|
||||
<div>
|
||||
<div>{_t("settings|key_backup|setup_secure_backup|requires_password_confirmation")}</div>
|
||||
<div>
|
||||
<Field
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
type="password"
|
||||
label={_t("common|password")}
|
||||
value={this.state.accountPassword}
|
||||
onChange={this.onAccountPasswordChange}
|
||||
forceValidity={this.state.accountPasswordCorrect === false ? false : undefined}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (!this.state.backupTrustInfo?.trusted) {
|
||||
authPrompt = (
|
||||
<div>
|
||||
<div>{_t("settings|key_backup|setup_secure_backup|requires_key_restore")}</div>
|
||||
</div>
|
||||
);
|
||||
nextCaption = _t("action|restore");
|
||||
} else {
|
||||
authPrompt = <p>{_t("settings|key_backup|setup_secure_backup|requires_server_authentication")}</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={this.onMigrateFormSubmit}>
|
||||
<p>{_t("settings|key_backup|setup_secure_backup|session_upgrade_description")}</p>
|
||||
<div>{authPrompt}</div>
|
||||
<DialogButtons
|
||||
primaryButton={nextCaption}
|
||||
onPrimaryButtonClick={this.onMigrateFormSubmit}
|
||||
hasCancel={false}
|
||||
primaryDisabled={!!this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
|
||||
>
|
||||
<button type="button" className="danger" onClick={this.onCancelClick}>
|
||||
{_t("action|skip")}
|
||||
</button>
|
||||
</DialogButtons>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
private renderPhasePassPhrase(): JSX.Element {
|
||||
return (
|
||||
<form onSubmit={this.onPassPhraseNextClick}>
|
||||
|
@ -829,8 +676,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
switch (phase) {
|
||||
case Phase.ChooseKeyPassphrase:
|
||||
return _t("encryption|set_up_toast_title");
|
||||
case Phase.Migrate:
|
||||
return _t("settings|key_backup|setup_secure_backup|title_upgrade_encryption");
|
||||
case Phase.Passphrase:
|
||||
return _t("settings|key_backup|setup_secure_backup|title_set_phrase");
|
||||
case Phase.PassphraseConfirm:
|
||||
|
@ -889,9 +734,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
case Phase.ChooseKeyPassphrase:
|
||||
content = this.renderPhaseChooseKeyPassphrase();
|
||||
break;
|
||||
case Phase.Migrate:
|
||||
content = this.renderPhaseMigrate();
|
||||
break;
|
||||
case Phase.Passphrase:
|
||||
content = this.renderPhasePassPhrase();
|
||||
break;
|
||||
|
|
|
@ -61,6 +61,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
|
||||
private unmounted = true;
|
||||
private image = createRef<HTMLImageElement>();
|
||||
private placeholder = createRef<HTMLDivElement>();
|
||||
private timeout?: number;
|
||||
private sizeWatcher?: string;
|
||||
|
||||
|
@ -453,7 +454,11 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
"mx_MImageBody_placeholder--blurhash": this.props.mxEvent.getContent().info?.[BLURHASH_FIELD],
|
||||
});
|
||||
|
||||
placeholder = <div className={classes}>{this.getPlaceholder(maxWidth, maxHeight)}</div>;
|
||||
placeholder = (
|
||||
<div className={classes} ref={this.placeholder}>
|
||||
{this.getPlaceholder(maxWidth, maxHeight)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let showPlaceholder = Boolean(placeholder);
|
||||
|
@ -499,8 +504,19 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
if (!this.props.forExport) {
|
||||
placeholder = (
|
||||
<SwitchTransition mode="out-in">
|
||||
<CSSTransition classNames="mx_rtg--fade" key={`img-${showPlaceholder}`} timeout={300}>
|
||||
{showPlaceholder ? placeholder : <></> /* Transition always expects a child */}
|
||||
<CSSTransition
|
||||
classNames="mx_rtg--fade"
|
||||
key={`img-${showPlaceholder}`}
|
||||
timeout={300}
|
||||
nodeRef={this.placeholder}
|
||||
>
|
||||
{
|
||||
showPlaceholder ? (
|
||||
placeholder
|
||||
) : (
|
||||
<div ref={this.placeholder} />
|
||||
) /* Transition always expects a child */
|
||||
}
|
||||
</CSSTransition>
|
||||
</SwitchTransition>
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import React, { createRef } from "react";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import { CSSTransition } from "react-transition-group";
|
||||
|
||||
|
@ -61,6 +61,7 @@ const RoomBreadcrumbTile: React.FC<{ room: Room; onClick: (ev: ButtonEvent) => v
|
|||
|
||||
export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState> {
|
||||
private isMounted = true;
|
||||
private toolbar = createRef<HTMLDivElement>();
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -113,8 +114,18 @@ export default class RoomBreadcrumbs extends React.PureComponent<IProps, IState>
|
|||
if (tiles.length > 0) {
|
||||
// NOTE: The CSSTransition timeout MUST match the timeout in our CSS!
|
||||
return (
|
||||
<CSSTransition appear={true} in={this.state.doAnimation} timeout={640} classNames="mx_RoomBreadcrumbs">
|
||||
<Toolbar className="mx_RoomBreadcrumbs" aria-label={_t("room_list|breadcrumbs_label")}>
|
||||
<CSSTransition
|
||||
appear={true}
|
||||
in={this.state.doAnimation}
|
||||
timeout={640}
|
||||
classNames="mx_RoomBreadcrumbs"
|
||||
nodeRef={this.toolbar}
|
||||
>
|
||||
<Toolbar
|
||||
className="mx_RoomBreadcrumbs"
|
||||
aria-label={_t("room_list|breadcrumbs_label")}
|
||||
ref={this.toolbar}
|
||||
>
|
||||
{tiles.slice(this.state.skipFirst ? 1 : 0)}
|
||||
</Toolbar>
|
||||
</CSSTransition>
|
||||
|
|
|
@ -27,7 +27,7 @@ import { useRoomMemberCount, useRoomMembers } from "../../../hooks/useRoomMember
|
|||
import { _t } from "../../../languageHandler";
|
||||
import { Flex } from "../../utils/Flex";
|
||||
import { Box } from "../../utils/Box";
|
||||
import { getPlatformCallTypeLabel, useRoomCall } from "../../../hooks/room/useRoomCall";
|
||||
import { getPlatformCallTypeChildren, getPlatformCallTypeLabel, useRoomCall } from "../../../hooks/room/useRoomCall";
|
||||
import { useRoomThreadNotifications } from "../../../hooks/room/useRoomThreadNotifications";
|
||||
import { useGlobalNotificationState } from "../../../hooks/useGlobalNotificationState";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
|
@ -172,6 +172,8 @@ export default function RoomHeader({
|
|||
key={option}
|
||||
label={getPlatformCallTypeLabel(option)}
|
||||
aria-label={getPlatformCallTypeLabel(option)}
|
||||
children={getPlatformCallTypeChildren(option)}
|
||||
className="mx_RoomHeader_videoCallOption"
|
||||
onClick={(ev) => videoCallClick(ev, option)}
|
||||
Icon={VideoCallIcon}
|
||||
onSelect={() => {} /* Dummy handler since we want the click event.*/}
|
||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import React, { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { CallType } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import { useFeatureEnabled } from "../useSettings";
|
||||
|
@ -35,6 +35,7 @@ import { isVideoRoom } from "../../utils/video-rooms";
|
|||
import { useGuestAccessInformation } from "./useGuestAccessInformation";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { UIFeature } from "../../settings/UIFeature";
|
||||
import { BetaPill } from "../../components/views/beta/BetaCard";
|
||||
|
||||
export enum PlatformCallType {
|
||||
ElementCall,
|
||||
|
@ -51,6 +52,14 @@ export const getPlatformCallTypeLabel = (platformCallType: PlatformCallType): st
|
|||
return _t("voip|legacy_call");
|
||||
}
|
||||
};
|
||||
export const getPlatformCallTypeChildren = (platformCallType: PlatformCallType): ReactNode => {
|
||||
switch (platformCallType) {
|
||||
case PlatformCallType.ElementCall:
|
||||
return <BetaPill />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const enum State {
|
||||
NoCall,
|
||||
NoOneHere,
|
|
@ -103,7 +103,6 @@
|
|||
"report_content": "Report Content",
|
||||
"resend": "Resend",
|
||||
"reset": "Reset",
|
||||
"restore": "Restore",
|
||||
"resume": "Resume",
|
||||
"retry": "Retry",
|
||||
"review": "Review",
|
||||
|
@ -930,7 +929,6 @@
|
|||
},
|
||||
"unable_to_setup_keys_error": "Unable to set up keys",
|
||||
"unsupported": "This client does not support end-to-end encryption.",
|
||||
"upgrade_toast_title": "Encryption upgrade available",
|
||||
"verification": {
|
||||
"accepting": "Accepting…",
|
||||
"after_new_login": {
|
||||
|
@ -2594,18 +2592,13 @@
|
|||
"pass_phrase_match_failed": "That doesn't match.",
|
||||
"pass_phrase_match_success": "That matches!",
|
||||
"phrase_strong_enough": "Great! This Security Phrase looks strong enough.",
|
||||
"requires_key_restore": "Restore your key backup to upgrade your encryption",
|
||||
"requires_password_confirmation": "Enter your account password to confirm the upgrade:",
|
||||
"requires_server_authentication": "You'll need to authenticate with the server to confirm the upgrade.",
|
||||
"secret_storage_query_failure": "Unable to query secret storage status",
|
||||
"security_key_safety_reminder": "Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.",
|
||||
"session_upgrade_description": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
|
||||
"set_phrase_again": "Go back to set it again.",
|
||||
"settings_reminder": "You can also set up Secure Backup & manage your keys in Settings.",
|
||||
"title_confirm_phrase": "Confirm Security Phrase",
|
||||
"title_save_key": "Save your Security Key",
|
||||
"title_set_phrase": "Set a Security Phrase",
|
||||
"title_upgrade_encryption": "Upgrade your encryption",
|
||||
"unable_to_setup": "Unable to set up secret storage",
|
||||
"use_different_passphrase": "Use a different passphrase?",
|
||||
"use_phrase_only_you_know": "Use a secret phrase only you know, and optionally save a Security Key to use for backup."
|
||||
|
|
|
@ -416,16 +416,43 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
|
||||
/**
|
||||
* Implements {@link WidgetDriver#sendToDevice}
|
||||
* Encrypted to-device events are not supported.
|
||||
*/
|
||||
public async sendToDevice(
|
||||
eventType: string,
|
||||
encrypted: boolean,
|
||||
contentMap: { [userId: string]: { [deviceId: string]: object } },
|
||||
): Promise<void> {
|
||||
if (encrypted) throw new Error("Encrypted to-device events are not supported");
|
||||
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
|
||||
if (encrypted) {
|
||||
const crypto = client.getCrypto();
|
||||
if (!crypto) throw new Error("E2EE not enabled");
|
||||
|
||||
// attempt to re-batch these up into a single request
|
||||
const invertedContentMap: { [content: string]: { userId: string; deviceId: string }[] } = {};
|
||||
|
||||
for (const userId of Object.keys(contentMap)) {
|
||||
const userContentMap = contentMap[userId];
|
||||
for (const deviceId of Object.keys(userContentMap)) {
|
||||
const content = userContentMap[deviceId];
|
||||
const stringifiedContent = JSON.stringify(content);
|
||||
invertedContentMap[stringifiedContent] = invertedContentMap[stringifiedContent] || [];
|
||||
invertedContentMap[stringifiedContent].push({ userId, deviceId });
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(invertedContentMap).map(async ([stringifiedContent, recipients]) => {
|
||||
const batch = await crypto.encryptToDeviceMessages(
|
||||
eventType,
|
||||
recipients,
|
||||
JSON.parse(stringifiedContent),
|
||||
);
|
||||
|
||||
await client.queueToDevice(batch);
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
await client.queueToDevice({
|
||||
eventType,
|
||||
batch: Object.entries(contentMap).flatMap(([userId, userContentMap]) =>
|
||||
|
@ -437,6 +464,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private pickRooms(roomIds?: (string | Symbols.AnyRoom)[]): Room[] {
|
||||
const client = MatrixClientPeg.get();
|
||||
|
|
|
@ -23,8 +23,6 @@ const getTitle = (kind: Kind): string => {
|
|||
switch (kind) {
|
||||
case Kind.SET_UP_ENCRYPTION:
|
||||
return _t("encryption|set_up_toast_title");
|
||||
case Kind.UPGRADE_ENCRYPTION:
|
||||
return _t("encryption|upgrade_toast_title");
|
||||
case Kind.VERIFY_THIS_SESSION:
|
||||
return _t("encryption|verify_toast_title");
|
||||
}
|
||||
|
@ -33,7 +31,6 @@ const getTitle = (kind: Kind): string => {
|
|||
const getIcon = (kind: Kind): string => {
|
||||
switch (kind) {
|
||||
case Kind.SET_UP_ENCRYPTION:
|
||||
case Kind.UPGRADE_ENCRYPTION:
|
||||
return "secure_backup";
|
||||
case Kind.VERIFY_THIS_SESSION:
|
||||
return "verification_warning";
|
||||
|
@ -44,8 +41,6 @@ const getSetupCaption = (kind: Kind): string => {
|
|||
switch (kind) {
|
||||
case Kind.SET_UP_ENCRYPTION:
|
||||
return _t("action|continue");
|
||||
case Kind.UPGRADE_ENCRYPTION:
|
||||
return _t("action|upgrade");
|
||||
case Kind.VERIFY_THIS_SESSION:
|
||||
return _t("action|verify");
|
||||
}
|
||||
|
@ -54,7 +49,6 @@ const getSetupCaption = (kind: Kind): string => {
|
|||
const getDescription = (kind: Kind): string => {
|
||||
switch (kind) {
|
||||
case Kind.SET_UP_ENCRYPTION:
|
||||
case Kind.UPGRADE_ENCRYPTION:
|
||||
return _t("encryption|set_up_toast_description");
|
||||
case Kind.VERIFY_THIS_SESSION:
|
||||
return _t("encryption|verify_toast_description");
|
||||
|
@ -63,7 +57,6 @@ const getDescription = (kind: Kind): string => {
|
|||
|
||||
export enum Kind {
|
||||
SET_UP_ENCRYPTION = "set_up_encryption",
|
||||
UPGRADE_ENCRYPTION = "upgrade_encryption",
|
||||
VERIFY_THIS_SESSION = "verify_this_session",
|
||||
}
|
||||
|
||||
|
|
|
@ -161,24 +161,19 @@ export default class HTMLExporter extends Exporter {
|
|||
<div class="mx_MatrixChat_wrapper" aria-hidden="false">
|
||||
<div class="mx_MatrixChat">
|
||||
<main class="mx_RoomView">
|
||||
<div class="mx_LegacyRoomHeader light-panel">
|
||||
<div class="mx_LegacyRoomHeader_wrapper" aria-owns="mx_RightPanel">
|
||||
<div class="mx_LegacyRoomHeader_avatar">
|
||||
<div class="mx_DecoratedRoomAvatar">
|
||||
<div class="mx_Flex mx_RoomHeader light-panel">
|
||||
${roomAvatar}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx_LegacyRoomHeader_name">
|
||||
<div class="mx_RoomHeader_infoWrapper">
|
||||
<div
|
||||
dir="auto"
|
||||
class="mx_LegacyRoomHeader_nametext"
|
||||
class="mx_RoomHeader_info"
|
||||
title="${safeRoomName}"
|
||||
>
|
||||
<span class="mx_RoomHeader_truncated mx_lineClamp">
|
||||
${safeRoomName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx_LegacyRoomHeader_topic" dir="auto"> ${safeTopic} </div>
|
||||
</div>
|
||||
</div>
|
||||
${previousMessagesLink}
|
||||
<div class="mx_MainSplit">
|
||||
|
|
|
@ -130,6 +130,14 @@ a.mx_reply_anchor:hover {
|
|||
}
|
||||
}
|
||||
|
||||
.mx_RoomHeader {
|
||||
--mx-flex-display: flex;
|
||||
--mx-flex-direction: row;
|
||||
--mx-flex-align: center;
|
||||
--mx-flex-justify: start;
|
||||
--mx-flex-gap: var(--cpd-space-3x);
|
||||
}
|
||||
|
||||
.mx_ReplyChain_Export {
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
|
|
|
@ -127,6 +127,10 @@ export function createTestClient(): MatrixClient {
|
|||
prepareToEncrypt: jest.fn(),
|
||||
bootstrapCrossSigning: jest.fn(),
|
||||
getActiveSessionBackupVersion: jest.fn().mockResolvedValue(null),
|
||||
isKeyBackupTrusted: jest.fn().mockResolvedValue({}),
|
||||
createRecoveryKeyFromPassphrase: jest.fn().mockResolvedValue({}),
|
||||
bootstrapSecretStorage: jest.fn(),
|
||||
isDehydrationSupported: jest.fn().mockResolvedValue(false),
|
||||
}),
|
||||
|
||||
getPushActionsForEvent: jest.fn(),
|
||||
|
@ -270,6 +274,7 @@ export function createTestClient(): MatrixClient {
|
|||
getOrCreateFilter: jest.fn(),
|
||||
sendStickerMessage: jest.fn(),
|
||||
getLocalAliases: jest.fn().mockReturnValue([]),
|
||||
uploadDeviceSigningKeys: jest.fn(),
|
||||
} as unknown as MatrixClient;
|
||||
|
||||
client.reEmitter = new ReEmitter(client);
|
||||
|
|
|
@ -496,6 +496,8 @@ describe("DecryptionFailureTracker", function () {
|
|||
await createAndTrackEventWithError(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED);
|
||||
await createAndTrackEventWithError(DecryptionFailureCode.MEGOLM_KEY_WITHHELD);
|
||||
await createAndTrackEventWithError(DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE);
|
||||
await createAndTrackEventWithError(DecryptionFailureCode.SENDER_IDENTITY_PREVIOUSLY_VERIFIED);
|
||||
await createAndTrackEventWithError(DecryptionFailureCode.UNSIGNED_SENDER_DEVICE);
|
||||
await createAndTrackEventWithError(DecryptionFailureCode.UNKNOWN_ERROR);
|
||||
|
||||
// Pretend "now" is Infinity
|
||||
|
@ -510,6 +512,8 @@ describe("DecryptionFailureTracker", function () {
|
|||
"ExpectedDueToMembership",
|
||||
"OlmKeysNotSentError",
|
||||
"RoomKeysWithheldForUnverifiedDevice",
|
||||
"ExpectedVerificationViolation",
|
||||
"ExpectedSentByInsecureDevice",
|
||||
"UnknownError",
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -351,13 +351,13 @@ describe("DeviceListener", () => {
|
|||
mockCrypto!.getCrossSigningKeyId.mockResolvedValue("abc");
|
||||
});
|
||||
|
||||
it("shows upgrade encryption toast when user has a key backup available", async () => {
|
||||
it("shows set up encryption toast when user has a key backup available", async () => {
|
||||
// non falsy response
|
||||
mockClient!.getKeyBackupVersion.mockResolvedValue({} as unknown as KeyBackupInfo);
|
||||
await createAndStart();
|
||||
|
||||
expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
|
||||
SetupEncryptionToast.Kind.UPGRADE_ENCRYPTION,
|
||||
SetupEncryptionToast.Kind.SET_UP_ENCRYPTION,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,42 +10,23 @@ import { render, RenderResult, screen } from "jest-matrix-react";
|
|||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { Crypto, MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
||||
import { defer, IDeferred, sleep } from "matrix-js-sdk/src/utils";
|
||||
import { BackupTrustInfo, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
import { MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import {
|
||||
filterConsole,
|
||||
flushPromises,
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsCrypto,
|
||||
mockClientMethodsServer,
|
||||
} from "../../../../../test-utils";
|
||||
import { filterConsole, stubClient } from "../../../../../test-utils";
|
||||
import CreateSecretStorageDialog from "../../../../../../src/async-components/views/dialogs/security/CreateSecretStorageDialog";
|
||||
import Modal from "../../../../../../src/Modal";
|
||||
import RestoreKeyBackupDialog from "../../../../../../src/components/views/dialogs/security/RestoreKeyBackupDialog";
|
||||
|
||||
describe("CreateSecretStorageDialog", () => {
|
||||
let mockClient: MockedObject<MatrixClient>;
|
||||
let mockCrypto: MockedObject<Crypto.CryptoApi>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
...mockClientMethodsCrypto(),
|
||||
uploadDeviceSigningKeys: jest.fn().mockImplementation(async () => {
|
||||
mockClient = mocked(stubClient());
|
||||
mockClient.uploadDeviceSigningKeys.mockImplementation(async () => {
|
||||
await sleep(0); // CreateSecretStorageDialog doesn't expect this to resolve immediately
|
||||
throw new MatrixError({ flows: [] });
|
||||
}),
|
||||
});
|
||||
|
||||
mockCrypto = mocked(mockClient.getCrypto()!);
|
||||
Object.assign(mockCrypto, {
|
||||
isKeyBackupTrusted: jest.fn(),
|
||||
isDehydrationSupported: jest.fn(() => false),
|
||||
bootstrapCrossSigning: jest.fn(),
|
||||
bootstrapSecretStorage: jest.fn(),
|
||||
});
|
||||
// Mock the clipboard API
|
||||
document.execCommand = jest.fn().mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -59,11 +40,37 @@ describe("CreateSecretStorageDialog", () => {
|
|||
return render(<CreateSecretStorageDialog onFinished={onFinished} {...props} />);
|
||||
}
|
||||
|
||||
it("shows a loading spinner initially", async () => {
|
||||
const { container } = renderComponent();
|
||||
expect(screen.getByTestId("spinner")).toBeDefined();
|
||||
expect(container).toMatchSnapshot();
|
||||
await flushPromises();
|
||||
it("handles the happy path", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText(
|
||||
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.",
|
||||
);
|
||||
expect(result.container).toMatchSnapshot();
|
||||
await userEvent.click(result.getByRole("button", { name: "Continue" }));
|
||||
|
||||
await screen.findByText("Save your Security Key");
|
||||
expect(result.container).toMatchSnapshot();
|
||||
// Copy the key to enable the continue button
|
||||
await userEvent.click(screen.getByRole("button", { name: "Copy" }));
|
||||
expect(result.queryByText("Copied!")).not.toBeNull();
|
||||
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
||||
|
||||
await screen.findByText("Your keys are now being backed up from this device.");
|
||||
});
|
||||
|
||||
it("when there is an error when bootstraping the secret storage, it shows an error", async () => {
|
||||
jest.spyOn(mockClient.getCrypto()!, "bootstrapSecretStorage").mockRejectedValue(new Error("error"));
|
||||
|
||||
renderComponent();
|
||||
await screen.findByText(
|
||||
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.",
|
||||
);
|
||||
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
||||
await screen.findByText("Save your Security Key");
|
||||
await userEvent.click(screen.getByRole("button", { name: "Copy" }));
|
||||
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
||||
|
||||
await screen.findByText("Unable to set up secret storage");
|
||||
});
|
||||
|
||||
describe("when there is an error fetching the backup version", () => {
|
||||
|
@ -75,139 +82,19 @@ describe("CreateSecretStorageDialog", () => {
|
|||
});
|
||||
|
||||
const result = renderComponent();
|
||||
// We go though the dialog until we have to get the key backup
|
||||
await userEvent.click(result.getByRole("button", { name: "Continue" }));
|
||||
await userEvent.click(screen.getByRole("button", { name: "Copy" }));
|
||||
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
||||
|
||||
// XXX the error message is... misleading.
|
||||
await result.findByText("Unable to query secret storage status");
|
||||
expect(result.container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows 'Generate a Security Key' text if no key backup is present", async () => {
|
||||
const result = renderComponent();
|
||||
await flushPromises();
|
||||
expect(result.container).toMatchSnapshot();
|
||||
result.getByText("Generate a Security Key");
|
||||
});
|
||||
|
||||
describe("when canUploadKeysWithPasswordOnly", () => {
|
||||
// spy on Modal.createDialog
|
||||
let modalSpy: jest.SpyInstance;
|
||||
|
||||
// deferred which should be resolved to indicate that the created dialog has completed
|
||||
let restoreDialogFinishedDefer: IDeferred<[done?: boolean]>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient.getKeyBackupVersion.mockResolvedValue({} as KeyBackupInfo);
|
||||
mockClient.uploadDeviceSigningKeys.mockImplementation(async () => {
|
||||
await sleep(0);
|
||||
throw new MatrixError({
|
||||
flows: [{ stages: ["m.login.password"] }],
|
||||
});
|
||||
});
|
||||
|
||||
restoreDialogFinishedDefer = defer<[done?: boolean]>();
|
||||
modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue({
|
||||
finished: restoreDialogFinishedDefer.promise,
|
||||
close: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it("prompts for a password and then shows RestoreKeyBackupDialog", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText(/Enter your account password to confirm the upgrade/);
|
||||
await screen.findByText("Unable to query secret storage status");
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
await userEvent.type(result.getByPlaceholderText("Password"), "my pass");
|
||||
result.getByRole("button", { name: "Next" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
showSummary: false,
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
});
|
||||
|
||||
it("calls bootstrapSecretStorage once keys are restored if the backup is now trusted", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText(/Enter your account password to confirm the upgrade/);
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
await userEvent.type(result.getByPlaceholderText("Password"), "my pass");
|
||||
result.getByRole("button", { name: "Next" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
|
||||
// While we restore the key backup, its signature becomes accepted
|
||||
mockCrypto.isKeyBackupTrusted.mockResolvedValue({ trusted: true } as BackupTrustInfo);
|
||||
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
await flushPromises();
|
||||
|
||||
// XXX no idea why this is a sensible thing to do. I just work here.
|
||||
expect(mockCrypto.bootstrapCrossSigning).toHaveBeenCalled();
|
||||
expect(mockCrypto.bootstrapSecretStorage).toHaveBeenCalled();
|
||||
|
||||
await result.findByText("Your keys are now being backed up from this device.");
|
||||
});
|
||||
|
||||
describe("when there is an error fetching the backup version after RestoreKeyBackupDialog", () => {
|
||||
filterConsole("Error fetching backup data from server");
|
||||
|
||||
it("handles the error sensibly", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText(/Enter your account password to confirm the upgrade/);
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
await userEvent.type(result.getByPlaceholderText("Password"), "my pass");
|
||||
result.getByRole("button", { name: "Next" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
|
||||
mockClient.getKeyBackupVersion.mockImplementation(async () => {
|
||||
throw new Error("bleh bleh");
|
||||
});
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
await result.findByText("Unable to query secret storage status");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when backup is present but not trusted", () => {
|
||||
beforeEach(() => {
|
||||
mockClient.getKeyBackupVersion.mockResolvedValue({} as KeyBackupInfo);
|
||||
});
|
||||
|
||||
it("shows migrate text, then 'RestoreKeyBackupDialog' if 'Restore' is clicked", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText("Restore your key backup to upgrade your encryption");
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
// before we click "Restore", set up a spy on createDialog
|
||||
const restoreDialogFinishedDefer = defer<[done?: boolean]>();
|
||||
const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue({
|
||||
finished: restoreDialogFinishedDefer.promise,
|
||||
close: jest.fn(),
|
||||
});
|
||||
|
||||
result.getByRole("button", { name: "Restore" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
showSummary: false,
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
// simulate RestoreKeyBackupDialog completing, to run that code path
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
// Now we can get the backup and we retry
|
||||
mockClient.getKeyBackupVersion.mockRestore();
|
||||
await userEvent.click(screen.getByRole("button", { name: "Retry" }));
|
||||
await screen.findByText("Your keys are now being backed up from this device.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CreateSecretStorageDialog shows 'Generate a Security Key' text if no key backup is present 1`] = `
|
||||
exports[`CreateSecretStorageDialog handles the happy path 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
|
@ -128,47 +128,7 @@ exports[`CreateSecretStorageDialog shows 'Generate a Security Key' text if no ke
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog shows a loading spinner initially 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
/>
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when backup is present but not trusted shows migrate text, then 'RestoreKeyBackupDialog' if 'Restore' is clicked 1`] = `
|
||||
exports[`CreateSecretStorageDialog handles the happy path 2`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
|
@ -185,105 +145,47 @@ exports[`CreateSecretStorageDialog when backup is present but not trusted shows
|
|||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
class="mx_Heading_h3 mx_Dialog_title mx_CreateSecretStorageDialog_titleWithIcon mx_CreateSecretStorageDialog_secureBackupTitle"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
Save your Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<div>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Restore your key backup to upgrade your encryption
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer"
|
||||
>
|
||||
<div
|
||||
class="mx_CreateSecretStorageDialog_recoveryKeyContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_CreateSecretStorageDialog_recoveryKey"
|
||||
>
|
||||
<code />
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
class="mx_CreateSecretStorageDialog_recoveryKeyButtons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
<div
|
||||
class="mx_AccessibleButton mx_Dialog_primary mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Restore
|
||||
</button>
|
||||
Download
|
||||
</div>
|
||||
<span>
|
||||
or
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
class="mx_AccessibleButton mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly calls bootstrapSecretStorage once keys are restored if the backup is now trusted 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Enter your account password to confirm the upgrade:
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_CreateSecretStorageDialog_password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
Copy
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -294,196 +196,18 @@ exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly calls boot
|
|||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
Continue
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly prompts for a password and then shows RestoreKeyBackupDialog 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Enter your account password to confirm the upgrade:
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_CreateSecretStorageDialog_password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly when there is an error fetching the backup version after RestoreKeyBackupDialog handles the error sensibly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Enter your account password to confirm the upgrade:
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_CreateSecretStorageDialog_password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
|
|
|
@ -201,10 +201,72 @@ describe("StopGapWidgetDriver", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("raises an error if encrypted", async () => {
|
||||
await expect(driver.sendToDevice("org.example.foo", true, contentMap)).rejects.toThrow(
|
||||
"Encrypted to-device events are not supported",
|
||||
it("sends encrypted messages", async () => {
|
||||
const encryptToDeviceMessages = jest
|
||||
.fn()
|
||||
.mockImplementation(
|
||||
(eventType, recipients: { userId: string; deviceId: string }[], content: object) => ({
|
||||
eventType: "m.room.encrypted",
|
||||
batch: recipients.map(({ userId, deviceId }) => ({
|
||||
userId,
|
||||
deviceId,
|
||||
payload: {
|
||||
eventType,
|
||||
content,
|
||||
},
|
||||
})),
|
||||
}),
|
||||
);
|
||||
|
||||
MatrixClientPeg.safeGet().getCrypto()!.encryptToDeviceMessages = encryptToDeviceMessages;
|
||||
|
||||
await driver.sendToDevice("org.example.foo", true, {
|
||||
"@alice:example.org": {
|
||||
aliceMobile: {
|
||||
hello: "alice",
|
||||
},
|
||||
},
|
||||
"@bob:example.org": {
|
||||
bobDesktop: {
|
||||
hello: "bob",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(encryptToDeviceMessages).toHaveBeenCalledWith(
|
||||
"org.example.foo",
|
||||
[{ deviceId: "aliceMobile", userId: "@alice:example.org" }],
|
||||
{
|
||||
hello: "alice",
|
||||
},
|
||||
);
|
||||
expect(encryptToDeviceMessages).toHaveBeenCalledWith(
|
||||
"org.example.foo",
|
||||
[{ deviceId: "bobDesktop", userId: "@bob:example.org" }],
|
||||
{
|
||||
hello: "bob",
|
||||
},
|
||||
);
|
||||
expect(client.queueToDevice).toHaveBeenCalledWith({
|
||||
eventType: "m.room.encrypted",
|
||||
batch: expect.arrayContaining([
|
||||
{
|
||||
deviceId: "aliceMobile",
|
||||
payload: { content: { hello: "alice" }, eventType: "org.example.foo" },
|
||||
userId: "@alice:example.org",
|
||||
},
|
||||
]),
|
||||
});
|
||||
expect(client.queueToDevice).toHaveBeenCalledWith({
|
||||
eventType: "m.room.encrypted",
|
||||
batch: expect.arrayContaining([
|
||||
{
|
||||
deviceId: "bobDesktop",
|
||||
payload: { content: { hello: "bob" }, eventType: "org.example.foo" },
|
||||
userId: "@bob:example.org",
|
||||
},
|
||||
]),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -17,24 +17,19 @@ exports[`HTMLExport should export 1`] = `
|
|||
<div class="mx_MatrixChat_wrapper" aria-hidden="false">
|
||||
<div class="mx_MatrixChat">
|
||||
<main class="mx_RoomView">
|
||||
<div class="mx_LegacyRoomHeader light-panel">
|
||||
<div class="mx_LegacyRoomHeader_wrapper" aria-owns="mx_RightPanel">
|
||||
<div class="mx_LegacyRoomHeader_avatar">
|
||||
<div class="mx_DecoratedRoomAvatar">
|
||||
<div class="mx_Flex mx_RoomHeader light-panel">
|
||||
<span role="presentation" title="!myroom:example.org" data-testid="avatar-img" data-type="round" data-color="1" class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61" style="--cpd-avatar-size:32px">!</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx_LegacyRoomHeader_name">
|
||||
<div class="mx_RoomHeader_infoWrapper">
|
||||
<div
|
||||
dir="auto"
|
||||
class="mx_LegacyRoomHeader_nametext"
|
||||
class="mx_RoomHeader_info"
|
||||
title="!myroom:example.org"
|
||||
>
|
||||
<span class="mx_RoomHeader_truncated mx_lineClamp">
|
||||
!myroom:example.org
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx_LegacyRoomHeader_topic" dir="auto"> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx_MainSplit">
|
||||
|
|
368
yarn.lock
|
@ -1421,14 +1421,26 @@
|
|||
resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b"
|
||||
integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||
"@eslint-community/eslint-utils@^4.2.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
||||
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
|
||||
"@eslint-community/eslint-utils@^4.4.0":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56"
|
||||
integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@eslint-community/regexpp@^4.10.0":
|
||||
version "4.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
|
||||
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
|
||||
|
||||
"@eslint-community/regexpp@^4.6.1":
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f"
|
||||
integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==
|
||||
|
@ -1504,37 +1516,37 @@
|
|||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
|
||||
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
|
||||
|
||||
"@formatjs/ecma402-abstract@2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.0.tgz#36f5bc0dac4ca77ca429fe44bd95b32d5ccd98dd"
|
||||
integrity sha512-IpM+ev1E4QLtstniOE29W1rqH9eTdx5hQdNL8pzrflMj/gogfaoONZqL83LUeQScHAvyMbpqP5C9MzNf+fFwhQ==
|
||||
dependencies:
|
||||
"@formatjs/fast-memoize" "2.2.1"
|
||||
"@formatjs/intl-localematcher" "0.5.5"
|
||||
tslib "^2.7.0"
|
||||
|
||||
"@formatjs/fast-memoize@2.2.1":
|
||||
"@formatjs/ecma402-abstract@2.2.1":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.1.tgz#74575f18c6a789472517995ca9686e7a3f7c0b60"
|
||||
integrity sha512-XS2RcOSyWxmUB7BUjj3mlPH0exsUzlf6QfhhijgI941WaJhVxXQ6mEWkdUFIdnKi3TuTYxRdelsgv3mjieIGIA==
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.1.tgz#2e62bc5c22b0e6a5e13bfec6aac15d3d403e1065"
|
||||
integrity sha512-O4ywpkdJybrjFc9zyL8qK5aklleIAi5O4nYhBVJaOFtCkNrnU+lKFeJOFC48zpsZQmR8Aok2V79hGpHnzbmFpg==
|
||||
dependencies:
|
||||
tslib "^2.7.0"
|
||||
"@formatjs/fast-memoize" "2.2.2"
|
||||
"@formatjs/intl-localematcher" "0.5.6"
|
||||
tslib "2"
|
||||
|
||||
"@formatjs/intl-localematcher@0.5.5":
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.5.tgz#b24f100f30658104d5f6db35b0b8d97235298681"
|
||||
integrity sha512-t5tOGMgZ/i5+ALl2/offNqAQq/lfUnKLEw0mXQI4N4bqpedhrSE+fyKLpwnd22sK0dif6AV+ufQcTsKShB9J1g==
|
||||
"@formatjs/fast-memoize@2.2.2":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.2.tgz#2409ec10f5f7d6c65f4c04e6c2d6cc56fa1e4cef"
|
||||
integrity sha512-mzxZcS0g1pOzwZTslJOBTmLzDXseMLLvnh25ymRilCm8QLMObsQ7x/rj9GNrH0iUhZMlFisVOD6J1n6WQqpKPQ==
|
||||
dependencies:
|
||||
tslib "^2.7.0"
|
||||
tslib "2"
|
||||
|
||||
"@formatjs/intl-localematcher@0.5.6":
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.6.tgz#cd0cd99483673d3196a15b4e2c924cfda7f002f8"
|
||||
integrity sha512-roz1+Ba5e23AHX6KUAWmLEyTRZegM5YDuxuvkHCyK3RJddf/UXB2f+s7pOMm9ktfPGla0g+mQXOn5vsuYirnaA==
|
||||
dependencies:
|
||||
tslib "2"
|
||||
|
||||
"@formatjs/intl-segmenter@^11.5.7":
|
||||
version "11.5.9"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.5.9.tgz#197aae6991d456cc28827928ed10db0f284c5e2a"
|
||||
integrity sha512-/vRTJsue3GWk+jvf0VJao9xL+/sIykoMwTBzbq1UWGiyIcUdI4P+02KRbXCURgHovy2Ew7zkV7p8MYjTw8lrMw==
|
||||
version "11.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.1.tgz#61f0654adb7eb48c4893ac3f882ed93a34580491"
|
||||
integrity sha512-rlJ0C2wq+NSQEFW6Lp3jf0qIhGX6smfz14hMRR/DYCJwQHOR2idW4zuwqcn/CrEQxXTOxAV0f2+N9AdCPeH5QA==
|
||||
dependencies:
|
||||
"@formatjs/ecma402-abstract" "2.2.0"
|
||||
"@formatjs/intl-localematcher" "0.5.5"
|
||||
tslib "^2.7.0"
|
||||
"@formatjs/ecma402-abstract" "2.2.1"
|
||||
"@formatjs/intl-localematcher" "0.5.6"
|
||||
tslib "2"
|
||||
|
||||
"@humanwhocodes/config-array@^0.13.0":
|
||||
version "0.13.0"
|
||||
|
@ -1919,10 +1931,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
|
||||
integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
|
||||
|
||||
"@matrix-org/analytics-events@^0.26.0":
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.26.0.tgz#7c8f8f924d8313c87951a0e941640ef8ff78f3d6"
|
||||
integrity sha512-cjKZBejajUG8wPhVygMkBTwTLdEn74luUP6g6RjCUqPR3RYIl3NVi58Zil8CWfRTILb4wVLCPpAvehgXJn1HnQ==
|
||||
"@matrix-org/analytics-events@^0.28.0":
|
||||
version "0.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.28.0.tgz#91f501bb25435b9418f785ca850ca858aa6efc76"
|
||||
integrity sha512-RvvGBYzgJrk2wTRVGk2fWhGM1f69f6nBraRqTiuqlqE2eQd2hZ2onHyRhvhxJeKVC/oNBsvrupObqrrWowXsnQ==
|
||||
|
||||
"@matrix-org/emojibase-bindings@^1.3.3":
|
||||
version "1.3.3"
|
||||
|
@ -2015,11 +2027,11 @@
|
|||
webcrypto-core "^1.8.0"
|
||||
|
||||
"@playwright/test@^1.40.1":
|
||||
version "1.48.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.48.1.tgz#343e710fcf2e559529e3ec8d7782e09f325b9396"
|
||||
integrity sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==
|
||||
version "1.48.2"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.48.2.tgz#87dd40633f980872283404c8142a65744d3f13d6"
|
||||
integrity sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==
|
||||
dependencies:
|
||||
playwright "1.48.1"
|
||||
playwright "1.48.2"
|
||||
|
||||
"@polka/url@^1.0.0-next.24":
|
||||
version "1.0.0-next.28"
|
||||
|
@ -2317,43 +2329,43 @@
|
|||
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
|
||||
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
|
||||
|
||||
"@sentry-internal/browser-utils@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.34.0.tgz#36a50d503ad4ad51fce22e80670f8fd6fd195a27"
|
||||
integrity sha512-4AcYOzPzD1tL5eSRQ/GpKv5enquZf4dMVUez99/Bh3va8qiJrNP55AcM7UzZ7WZLTqKygIYruJTU5Zu2SpEAPQ==
|
||||
"@sentry-internal/browser-utils@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.35.0.tgz#92602f8dd2bb777af2994eb446cb3cf71bf0cfad"
|
||||
integrity sha512-uj9nwERm7HIS13f/Q52hF/NUS5Al8Ma6jkgpfYGeppYvU0uSjPkwMogtqoJQNbOoZg973tV8qUScbcWY616wNA==
|
||||
dependencies:
|
||||
"@sentry/core" "8.34.0"
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/utils" "8.34.0"
|
||||
"@sentry/core" "8.35.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
"@sentry/utils" "8.35.0"
|
||||
|
||||
"@sentry-internal/feedback@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.34.0.tgz#ff0db65c36f13665db99e3e22f2032bfdda98731"
|
||||
integrity sha512-aYSM2KPUs0FLPxxbJCFSwCYG70VMzlT04xepD1Y/tTlPPOja/02tSv2tyOdZbv8Uw7xslZs3/8Lhj74oYcTBxw==
|
||||
"@sentry-internal/feedback@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.35.0.tgz#b31fb7fbec8ecd9cc683948a0d1af2b87731b0a1"
|
||||
integrity sha512-7bjSaUhL0bDArozre6EiIhhdWdT/1AWNWBC1Wc5w1IxEi5xF7nvF/FfvjQYrONQzZAI3HRxc45J2qhLUzHBmoQ==
|
||||
dependencies:
|
||||
"@sentry/core" "8.34.0"
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/utils" "8.34.0"
|
||||
"@sentry/core" "8.35.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
"@sentry/utils" "8.35.0"
|
||||
|
||||
"@sentry-internal/replay-canvas@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.34.0.tgz#10acadaef74e982dee2b9842a3eb6fec73f032ed"
|
||||
integrity sha512-x8KhZcCDpbKHqFOykYXiamX6x0LRxv6N1OJHoH+XCrMtiDBZr4Yo30d/MaS6rjmKGMtSRij30v+Uq+YWIgxUrg==
|
||||
"@sentry-internal/replay-canvas@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.35.0.tgz#de7849e0d4212ee37a9225b1fc346188d9b05072"
|
||||
integrity sha512-TUrH6Piv19kvHIiRyIuapLdnuwxk/Un/l1WDCQfq7mK9p1Pac0FkQ7Uufjp6zY3lyhDDZQ8qvCS4ioCMibCwQg==
|
||||
dependencies:
|
||||
"@sentry-internal/replay" "8.34.0"
|
||||
"@sentry/core" "8.34.0"
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/utils" "8.34.0"
|
||||
"@sentry-internal/replay" "8.35.0"
|
||||
"@sentry/core" "8.35.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
"@sentry/utils" "8.35.0"
|
||||
|
||||
"@sentry-internal/replay@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.34.0.tgz#b730919a174cc5ae8a77f79fb24a5ffb18e44db5"
|
||||
integrity sha512-EoMh9NYljNewZK1quY23YILgtNdGgrkzJ9TPsj6jXUG0LZ0Q7N7eFWd0xOEDBvFxrmI3cSXF1i4d1sBb+eyKRw==
|
||||
"@sentry-internal/replay@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.35.0.tgz#f71abae95cb492a54b43885386adbc5c639486c7"
|
||||
integrity sha512-3wkW03vXYMyWtTLxl9yrtkV+qxbnKFgfASdoGWhXzfLjycgT6o4/04eb3Gn71q9aXqRwH17ISVQbVswnRqMcmA==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.34.0"
|
||||
"@sentry/core" "8.34.0"
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/utils" "8.34.0"
|
||||
"@sentry-internal/browser-utils" "8.35.0"
|
||||
"@sentry/core" "8.35.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
"@sentry/utils" "8.35.0"
|
||||
|
||||
"@sentry/babel-plugin-component-annotate@2.22.5":
|
||||
version "2.22.5"
|
||||
|
@ -2361,17 +2373,17 @@
|
|||
integrity sha512-+93qwB9vTX1nj4hD8AMWowXZsZVkvmP9OwTqSh5d4kOeiJ+dZftUk4+FKeKkAX9lvY2reyHV8Gms5mo67c27RQ==
|
||||
|
||||
"@sentry/browser@^8.0.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.34.0.tgz#d2dfc2dbbfa9132d5c3e951f0a4b467805bc4c75"
|
||||
integrity sha512-3HHG2NXxzHq1lVmDy2uRjYjGNf9NsJsTPlOC70vbQdOb+S49EdH/XMPy+J3ruIoyv6Cu0LwvA6bMOM6rHZOgNQ==
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.35.0.tgz#67820951fd092ef72ee1a4897464bc7c8d317d77"
|
||||
integrity sha512-WHfI+NoZzpCsmIvtr6ChOe7yWPLQyMchPnVhY3Z4UeC70bkYNdKcoj/4XZbX3m0D8+71JAsm0mJ9s9OC3Ue6MQ==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.34.0"
|
||||
"@sentry-internal/feedback" "8.34.0"
|
||||
"@sentry-internal/replay" "8.34.0"
|
||||
"@sentry-internal/replay-canvas" "8.34.0"
|
||||
"@sentry/core" "8.34.0"
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/utils" "8.34.0"
|
||||
"@sentry-internal/browser-utils" "8.35.0"
|
||||
"@sentry-internal/feedback" "8.35.0"
|
||||
"@sentry-internal/replay" "8.35.0"
|
||||
"@sentry-internal/replay-canvas" "8.35.0"
|
||||
"@sentry/core" "8.35.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
"@sentry/utils" "8.35.0"
|
||||
|
||||
"@sentry/bundler-plugin-core@2.22.5":
|
||||
version "2.22.5"
|
||||
|
@ -2441,25 +2453,25 @@
|
|||
"@sentry/cli-win32-i686" "2.37.0"
|
||||
"@sentry/cli-win32-x64" "2.37.0"
|
||||
|
||||
"@sentry/core@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.34.0.tgz#92efe1cc8ced843beee636c344e66086d8915563"
|
||||
integrity sha512-adrXCTK/zsg5pJ67lgtZqdqHvyx6etMjQW3P82NgWdj83c8fb+zH+K79Z47pD4zQjX0ou2Ws5nwwi4wJbz4bfA==
|
||||
"@sentry/core@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.35.0.tgz#17090f4d2d3bb983d9d99ecd2d27f4e9e107e0b0"
|
||||
integrity sha512-Ci0Nmtw5ETWLqQJGY4dyF+iWh7PWKy6k303fCEoEmqj2czDrKJCp7yHBNV0XYbo00prj2ZTbCr6I7albYiyONA==
|
||||
dependencies:
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/utils" "8.34.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
"@sentry/utils" "8.35.0"
|
||||
|
||||
"@sentry/types@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.34.0.tgz#b02da72d1be67df5246aa9a97ca661ee71569372"
|
||||
integrity sha512-zLRc60CzohGCo6zNsNeQ9JF3SiEeRE4aDCP9fDDdIVCOKovS+mn1rtSip0qd0Vp2fidOu0+2yY0ALCz1A3PJSQ==
|
||||
"@sentry/types@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.35.0.tgz#535c807800f7e378f61416f30177c0ef81b95012"
|
||||
integrity sha512-AVEZjb16MlYPifiDDvJ19dPQyDn0jlrtC1PHs6ZKO+Rzyz+2EX2BRdszvanqArldexPoU1p5Bn2w81XZNXThBA==
|
||||
|
||||
"@sentry/utils@8.34.0":
|
||||
version "8.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.34.0.tgz#5ba543381a9de0ada1196df1fc5cde3b891de41e"
|
||||
integrity sha512-W1KoRlFUjprlh3t86DZPFxLfM6mzjRzshVfMY7vRlJFymBelJsnJ3A1lPeBZM9nCraOSiw6GtOWu6k5BAkiGIg==
|
||||
"@sentry/utils@8.35.0":
|
||||
version "8.35.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.35.0.tgz#1e099fcbc60040091c79f028a83226c145d588ee"
|
||||
integrity sha512-MdMb6+uXjqND7qIPWhulubpSeHzia6HtxeJa8jYI09OCvIcmNGPydv/Gx/LZBwosfMHrLdTWcFH7Y7aCxrq7cg==
|
||||
dependencies:
|
||||
"@sentry/types" "8.34.0"
|
||||
"@sentry/types" "8.35.0"
|
||||
|
||||
"@sentry/webpack-plugin@^2.7.1":
|
||||
version "2.22.5"
|
||||
|
@ -2631,9 +2643,9 @@
|
|||
pretty-format "^27.0.2"
|
||||
|
||||
"@testing-library/jest-dom@^6.4.8":
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.1.tgz#835612c9d8c529c835b15bbc1d1a924310c6c73c"
|
||||
integrity sha512-mNYIiAuP4yJwV2zBRQCV7PHoQwbb6/8TfMpPcwSUzcSVDJHWOXt6hjNtIN1v5knDmimYnjJxKhsoVd4LVGIO+w==
|
||||
version "6.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz#8186aa9a07263adef9cc5a59a4772db8c31f4a5b"
|
||||
integrity sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==
|
||||
dependencies:
|
||||
"@adobe/css-tools" "^4.4.0"
|
||||
aria-query "^5.0.0"
|
||||
|
@ -3078,10 +3090,10 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-dom@18.3.0":
|
||||
version "18.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0"
|
||||
integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==
|
||||
"@types/react-dom@18.3.1":
|
||||
version "18.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07"
|
||||
integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
|
@ -3218,29 +3230,29 @@
|
|||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^8.0.0":
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.10.0.tgz#9c8218ed62f9a322df10ded7c34990f014df44f2"
|
||||
integrity sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz#c2ef660bb83fd1432368319312a2581fc92ccac1"
|
||||
integrity sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.10.0"
|
||||
"@typescript-eslint/type-utils" "8.10.0"
|
||||
"@typescript-eslint/utils" "8.10.0"
|
||||
"@typescript-eslint/visitor-keys" "8.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.12.2"
|
||||
"@typescript-eslint/type-utils" "8.12.2"
|
||||
"@typescript-eslint/utils" "8.12.2"
|
||||
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.3.1"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/parser@^8.0.0":
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.10.0.tgz#3cbe7206f5e42835878a74a76da533549f977662"
|
||||
integrity sha512-E24l90SxuJhytWJ0pTQydFT46Nk0Z+bsLKo/L8rtQSL93rQ6byd1V/QbDpHUTdLPOMsBCcYXZweADNCfOCmOAg==
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.12.2.tgz#2e8173b34e1685e918b2d571c16c906d3747bad2"
|
||||
integrity sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.10.0"
|
||||
"@typescript-eslint/types" "8.10.0"
|
||||
"@typescript-eslint/typescript-estree" "8.10.0"
|
||||
"@typescript-eslint/visitor-keys" "8.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.12.2"
|
||||
"@typescript-eslint/types" "8.12.2"
|
||||
"@typescript-eslint/typescript-estree" "8.12.2"
|
||||
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.10.0":
|
||||
|
@ -3251,6 +3263,14 @@
|
|||
"@typescript-eslint/types" "8.10.0"
|
||||
"@typescript-eslint/visitor-keys" "8.10.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.12.2":
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz#6db0213745e6392c8e90fe9af5915e6da32eb94a"
|
||||
integrity sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.12.2"
|
||||
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.9.0":
|
||||
version "8.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz#c98fef0c4a82a484e6a1eb610a55b154d14d46f3"
|
||||
|
@ -3259,13 +3279,13 @@
|
|||
"@typescript-eslint/types" "8.9.0"
|
||||
"@typescript-eslint/visitor-keys" "8.9.0"
|
||||
|
||||
"@typescript-eslint/type-utils@8.10.0":
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.10.0.tgz#99f1d2e21f8c74703e7d9c4a67a87271eaf57597"
|
||||
integrity sha512-PCpUOpyQSpxBn230yIcK+LeCQaXuxrgCm2Zk1S+PTIRJsEfU6nJ0TtwyH8pIwPK/vJoA+7TZtzyAJSGBz+s/dg==
|
||||
"@typescript-eslint/type-utils@8.12.2":
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz#132b0c52d45f6814e6f2e32416c7951ed480b016"
|
||||
integrity sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "8.10.0"
|
||||
"@typescript-eslint/utils" "8.10.0"
|
||||
"@typescript-eslint/typescript-estree" "8.12.2"
|
||||
"@typescript-eslint/utils" "8.12.2"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
|
@ -3274,6 +3294,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.10.0.tgz#eb29c4bc2ed23489348c297469c76d28c38fb618"
|
||||
integrity sha512-k/E48uzsfJCRRbGLapdZgrX52csmWJ2rcowwPvOZ8lwPUv3xW6CcFeJAXgx4uJm+Ge4+a4tFOkdYvSpxhRhg1w==
|
||||
|
||||
"@typescript-eslint/types@8.12.2":
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.12.2.tgz#8d70098c0e90442495b53d0296acdca6d0f3f73c"
|
||||
integrity sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==
|
||||
|
||||
"@typescript-eslint/types@8.9.0":
|
||||
version "8.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.9.0.tgz#b733af07fb340b32e962c6c63b1062aec2dc0fe6"
|
||||
|
@ -3293,6 +3318,20 @@
|
|||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.12.2":
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz#206df9b1cbff212aaa9401985ef99f04daa84da5"
|
||||
integrity sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.12.2"
|
||||
"@typescript-eslint/visitor-keys" "8.12.2"
|
||||
debug "^4.3.4"
|
||||
fast-glob "^3.3.2"
|
||||
is-glob "^4.0.3"
|
||||
minimatch "^9.0.4"
|
||||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.9.0":
|
||||
version "8.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz#1714f167e9063062dc0df49c1d25afcbc7a96199"
|
||||
|
@ -3307,15 +3346,15 @@
|
|||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/utils@8.10.0", "@typescript-eslint/utils@^8.8.0":
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.10.0.tgz#d78d1ce3ea3d2a88a2593ebfb1c98490131d00bf"
|
||||
integrity sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==
|
||||
"@typescript-eslint/utils@8.12.2":
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.12.2.tgz#726cc9f49f5866605bd15bbc1768ffc15637930e"
|
||||
integrity sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "8.10.0"
|
||||
"@typescript-eslint/types" "8.10.0"
|
||||
"@typescript-eslint/typescript-estree" "8.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.12.2"
|
||||
"@typescript-eslint/types" "8.12.2"
|
||||
"@typescript-eslint/typescript-estree" "8.12.2"
|
||||
|
||||
"@typescript-eslint/utils@^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||
version "8.9.0"
|
||||
|
@ -3327,6 +3366,16 @@
|
|||
"@typescript-eslint/types" "8.9.0"
|
||||
"@typescript-eslint/typescript-estree" "8.9.0"
|
||||
|
||||
"@typescript-eslint/utils@^8.8.0":
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.10.0.tgz#d78d1ce3ea3d2a88a2593ebfb1c98490131d00bf"
|
||||
integrity sha512-Oq4uZ7JFr9d1ZunE/QKy5egcDRXT/FrS2z/nlxzPua2VHFtmMvFNDvpq1m/hq0ra+T52aUezfcjGRIB7vNJF9w==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "8.10.0"
|
||||
"@typescript-eslint/types" "8.10.0"
|
||||
"@typescript-eslint/typescript-estree" "8.10.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.10.0":
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.10.0.tgz#7ce4c0c3b82140415c9cd9babe09e0000b4e9979"
|
||||
|
@ -3335,6 +3384,14 @@
|
|||
"@typescript-eslint/types" "8.10.0"
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.12.2":
|
||||
version "8.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz#94d7410f78eb6d134b9fcabaf1eeedb910ba8c38"
|
||||
integrity sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.12.2"
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.9.0":
|
||||
version "8.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz#5f11f4d9db913f37da42776893ffe0dd1ae78f78"
|
||||
|
@ -3349,9 +3406,9 @@
|
|||
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
|
||||
|
||||
"@vector-im/compound-design-tokens@^1.8.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-1.8.0.tgz#bc844cb6b9842c1eb8e5c42f5cedcaf51a49b86f"
|
||||
integrity sha512-PtQMG7kDzwtjw/fLKD63uWP5rJ8cgWc/aXarfEzxYUf9ceWxBajnYOBI2jDqtE3WIUe9uTVBzNEvmOBG/VIgTA==
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-1.9.0.tgz#a3788845110fdcafb1720f633cb060b86f9a1592"
|
||||
integrity sha512-09eIRJSiWtAqK605eIu+PfT1ugu7u13gkvfxvfN7kjJMHQOzHSvDxmwADmfIzlV7oBQ8M+5D4KSKHNskvMxWsA==
|
||||
|
||||
"@vector-im/compound-web@^7.1.0":
|
||||
version "7.1.0"
|
||||
|
@ -3884,10 +3941,10 @@ await-lock@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/await-lock/-/await-lock-2.2.2.tgz#a95a9b269bfd2f69d22b17a321686f551152bcef"
|
||||
integrity sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==
|
||||
|
||||
axe-core@4.10.0:
|
||||
version "4.10.0"
|
||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
|
||||
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==
|
||||
axe-core@4.10.2:
|
||||
version "4.10.2"
|
||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df"
|
||||
integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==
|
||||
|
||||
axe-core@^4.10.0, axe-core@~4.10.0:
|
||||
version "4.10.1"
|
||||
|
@ -5626,10 +5683,10 @@ eslint-plugin-matrix-org@^2.0.2:
|
|||
resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-2.0.2.tgz#95b86b0f16704ab19740f7c3c62eae69e20365e6"
|
||||
integrity sha512-cQy5Rjeq6uyu1mLXlPZwEJdyM0NmclrnEz68y792FSuuxzMyJNNYLGDQ5CkYW8H+PrD825HUFZ34pNXnjMOzOw==
|
||||
|
||||
eslint-plugin-react-hooks@^4.3.0:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596"
|
||||
integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==
|
||||
eslint-plugin-react-hooks@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101"
|
||||
integrity sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==
|
||||
|
||||
eslint-plugin-react@^7.28.0:
|
||||
version "7.37.1"
|
||||
|
@ -8320,10 +8377,10 @@ mdn-data@2.10.0:
|
|||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.10.0.tgz#701da407f8fbc7a42aa0ba0c149ec897daef8986"
|
||||
integrity sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==
|
||||
|
||||
mdn-data@^2.0.30:
|
||||
version "2.11.1"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.11.1.tgz#bb973c4272a446005444259fd8227d7f727dc047"
|
||||
integrity sha512-Hdx3wmyqPFrhd6YHVuSkUK2eIGAcxR0xlndcgZqjA68yMJTbfXrjJwbgsBOsNjI7LnBIVUQnmyMVSdi/ob0GpQ==
|
||||
mdn-data@^2.11.1:
|
||||
version "2.12.1"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.1.tgz#10cb462215c13d95c92ff60d0fb3becac1bbb924"
|
||||
integrity sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==
|
||||
|
||||
mdurl@^1.0.1, mdurl@~1.0.1:
|
||||
version "1.0.1"
|
||||
|
@ -9088,17 +9145,17 @@ pkg-dir@^7.0.0:
|
|||
dependencies:
|
||||
find-up "^6.3.0"
|
||||
|
||||
playwright-core@1.48.1, playwright-core@^1.45.1:
|
||||
version "1.48.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.48.1.tgz#5fe28fb9a9326dae88d4608c35e819163cceeb23"
|
||||
integrity sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==
|
||||
playwright-core@1.48.2, playwright-core@^1.45.1:
|
||||
version "1.48.2"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.48.2.tgz#cd76ed8af61690edef5c05c64721c26a8db2f3d7"
|
||||
integrity sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==
|
||||
|
||||
playwright@1.48.1:
|
||||
version "1.48.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.48.1.tgz#2a920cfbec4572c84789e757d8b044baaed49435"
|
||||
integrity sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==
|
||||
playwright@1.48.2:
|
||||
version "1.48.2"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.48.2.tgz#fca45ae8abdc34835c715718072aaff7e305167e"
|
||||
integrity sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==
|
||||
dependencies:
|
||||
playwright-core "1.48.1"
|
||||
playwright-core "1.48.2"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
|
@ -11019,14 +11076,14 @@ stylelint-config-standard@^36.0.0:
|
|||
stylelint-config-recommended "^14.0.1"
|
||||
|
||||
stylelint-scss@^6.0.0:
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-6.8.0.tgz#bf3992bdb708b78b115a039f5be97dd7482133fa"
|
||||
integrity sha512-6gjsCZ30UUF6ivjZB2Z+1lb6k0+JFa1uR2MgGbYu76xRjEfvNTpSS1nQim1Gom1ijFF9GzauOiq1Kr7zKptQOw==
|
||||
version "6.8.1"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-6.8.1.tgz#b6554d93f2ea0bf37ffdcae571bbfaa35d79ba8a"
|
||||
integrity sha512-al+5eRb72bKrFyVAY+CLWKUMX+k+wsDCgyooSfhISJA2exqnJq1PX1iIIpdrvhu3GtJgNJZl9/BIW6EVSMCxdg==
|
||||
dependencies:
|
||||
css-tree "^3.0.0"
|
||||
is-plain-object "^5.0.0"
|
||||
known-css-properties "^0.34.0"
|
||||
mdn-data "^2.0.30"
|
||||
mdn-data "^2.11.1"
|
||||
postcss-media-query-parser "^0.2.3"
|
||||
postcss-resolve-nested-selector "^0.1.6"
|
||||
postcss-selector-parser "^6.1.2"
|
||||
|
@ -11388,7 +11445,7 @@ tsconfig-paths@^3.15.0:
|
|||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2, tslib@^2.7.0:
|
||||
tslib@2, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2, tslib@^2.7.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b"
|
||||
integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==
|
||||
|
@ -11644,7 +11701,7 @@ utils-merge@1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||
|
||||
uuid@10, uuid@^10.0.0:
|
||||
uuid@10:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
|
||||
integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
|
||||
|
@ -11654,6 +11711,11 @@ uuid@8.3.2, uuid@^8.3.2:
|
|||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
uuid@^11.0.0:
|
||||
version "11.0.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.2.tgz#a8d68ba7347d051e7ea716cc8dcbbab634d66875"
|
||||
integrity sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==
|
||||
|
||||
uuid@^9.0.0:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||
|
|