From 94f3168ab8089024ef34e99230763d44a0370e2f Mon Sep 17 00:00:00 2001 From: Kerry Date: Mon, 8 Aug 2022 08:59:22 +0200 Subject: [PATCH 001/107] Device manager - New device tile info design (#9122)(PSG-637) * redesign device tile info * test DeviceTile except for broken date mocking * mock dates the nice way, test lastactivity in device tile * tweak spacing style * update comment style in rethemendex * i18n --- res/css/_components.pcss | 1 + .../views/settings/devices/_DeviceTile.pcss | 41 +++++++ res/css/rethemendex.sh | 2 +- .../views/settings/DevicesPanelEntry.tsx | 36 +----- .../views/settings/devices/DeviceTile.tsx | 87 ++++++++++++++ src/i18n/strings/en_EN.json | 2 +- .../settings/devices/DeviceTile-test.tsx | 107 ++++++++++++++++++ .../__snapshots__/DeviceTile-test.tsx.snap | 93 +++++++++++++++ 8 files changed, 334 insertions(+), 35 deletions(-) create mode 100644 res/css/components/views/settings/devices/_DeviceTile.pcss create mode 100644 src/components/views/settings/devices/DeviceTile.tsx create mode 100644 test/components/views/settings/devices/DeviceTile-test.tsx create mode 100644 test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap diff --git a/res/css/_components.pcss b/res/css/_components.pcss index e1303dcd601..fe23a1c3882 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -27,6 +27,7 @@ @import "./components/views/location/_ZoomButtons.pcss"; @import "./components/views/messages/_MBeaconBody.pcss"; @import "./components/views/messages/shared/_MediaProcessingError.pcss"; +@import "./components/views/settings/devices/_DeviceTile.pcss"; @import "./components/views/spaces/_QuickThemeSwitcher.pcss"; @import "./structures/_AutoHideScrollbar.pcss"; @import "./structures/_BackdropPanel.pcss"; diff --git a/res/css/components/views/settings/devices/_DeviceTile.pcss b/res/css/components/views/settings/devices/_DeviceTile.pcss new file mode 100644 index 00000000000..159cace6ac0 --- /dev/null +++ b/res/css/components/views/settings/devices/_DeviceTile.pcss @@ -0,0 +1,41 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_DeviceTile { + display: flex; + flex-direction: row; + align-items: center; + + width: 100%; +} + +.mx_DeviceTile_info { + flex: 1 1 0; +} + +.mx_DeviceTile_metadata { + margin-top: 2px; + font-size: $font-12px; + color: $secondary-content; +} + +.mx_DeviceTile_actions { + display: grid; + grid-gap: $spacing-8; + grid-auto-flow: column; + + margin-left: $spacing-8; +} diff --git a/res/css/rethemendex.sh b/res/css/rethemendex.sh index 1fc1bb84ccb..37090b96d8f 100755 --- a/res/css/rethemendex.sh +++ b/res/css/rethemendex.sh @@ -3,7 +3,7 @@ cd `dirname $0` { - echo "// autogenerated by rethemendex.sh" + echo "/* autogenerated by rethemendex.sh */" # we used to have exclude /themes from the find at this point. # as themes are no longer a spurious subdirectory of css/, we don't diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index 2e7094c7b35..5a5330fd3ee 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -20,15 +20,14 @@ import { logger } from "matrix-js-sdk/src/logger"; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import { formatDate } from '../../../DateUtils'; import StyledCheckbox, { CheckboxStyle } from '../elements/StyledCheckbox'; import AccessibleButton from "../elements/AccessibleButton"; import Field from "../elements/Field"; -import TextWithTooltip from "../elements/TextWithTooltip"; import Modal from "../../../Modal"; import SetupEncryptionDialog from '../dialogs/security/SetupEncryptionDialog'; import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDialog'; import LogoutDialog from '../dialogs/LogoutDialog'; +import DeviceTile from './devices/DeviceTile'; interface IProps { device: IMyDevice; @@ -114,17 +113,6 @@ export default class DevicesPanelEntry extends React.Component { }; public render(): JSX.Element { - const device = this.props.device; - - let lastSeen = ""; - if (device.last_seen_ts) { - const lastSeenDate = new Date(device.last_seen_ts); - lastSeen = _t("Last seen %(date)s at %(ip)s", { - date: formatDate(lastSeenDate), - ip: device.last_seen_ip, - }); - } - const myDeviceClass = this.props.isOwnDevice ? " mx_DevicesPanel_myDevice" : ''; let iconClass = ''; @@ -153,16 +141,6 @@ export default class DevicesPanelEntry extends React.Component { ; - const deviceName = device.display_name ? - - - { device.display_name } - - : - - { device.device_id } - ; - const buttons = this.state.renaming ?
{ return (
{ left } -
-
- { deviceName } -
-
- { lastSeen } -
-
-
+ { buttons } -
+
); } diff --git a/src/components/views/settings/devices/DeviceTile.tsx b/src/components/views/settings/devices/DeviceTile.tsx new file mode 100644 index 00000000000..03d952fbb1e --- /dev/null +++ b/src/components/views/settings/devices/DeviceTile.tsx @@ -0,0 +1,87 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { Fragment } from "react"; +import { IMyDevice } from "matrix-js-sdk/src/matrix"; + +import { _t } from "../../../../languageHandler"; +import { formatDate, formatRelativeTime } from "../../../../DateUtils"; +import TooltipTarget from "../../elements/TooltipTarget"; +import { Alignment } from "../../elements/Tooltip"; +import Heading from "../../typography/Heading"; + +interface Props { + device: IMyDevice; + children?: React.ReactNode; +} + +const DeviceTileName: React.FC<{ device: IMyDevice }> = ({ device }) => { + if (device.display_name) { + return + + { device.display_name } + + ; + } + return + { device.device_id } + ; +}; + +const MS_6_DAYS = 6 * 24 * 60 * 60 * 1000; +const formatLastActivity = (timestamp: number, now = new Date().getTime()): string => { + // less than a week ago + if (timestamp + MS_6_DAYS >= now) { + const date = new Date(timestamp); + // Tue 20:15 + return formatDate(date); + } + return formatRelativeTime(new Date(timestamp)); +}; + +const DeviceMetadata: React.FC<{ value: string, id: string }> = ({ value, id }) => ( + value ? { value } : null +); + +const DeviceTile: React.FC = ({ device, children }) => { + const lastActivity = device.last_seen_ts && `${_t('Last activity')} ${formatLastActivity(device.last_seen_ts)}`; + const metadata = [ + { id: 'lastActivity', value: lastActivity }, + { id: 'lastSeenIp', value: device.last_seen_ip }, + ]; + + return
+
+ +
+ { metadata.map(({ id, value }, index) => + + { !!index && ' · ' } + + , + ) } +
+
+
+ { children } +
+
; +}; + +export default DeviceTile; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 07b563820da..7ce4908afe8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1303,7 +1303,6 @@ "You aren't signed into any other devices.": "You aren't signed into any other devices.", "This device": "This device", "Failed to set display name": "Failed to set display name", - "Last seen %(date)s at %(ip)s": "Last seen %(date)s at %(ip)s", "Sign Out": "Sign Out", "Display Name": "Display Name", "Rename": "Rename", @@ -1691,6 +1690,7 @@ "Please enter verification code sent via text.": "Please enter verification code sent via text.", "Verification code": "Verification code", "Discovery options will appear once you have added a phone number above.": "Discovery options will appear once you have added a phone number above.", + "Last activity": "Last activity", "Unable to remove contact information": "Unable to remove contact information", "Remove %(email)s?": "Remove %(email)s?", "Invalid Email Address": "Invalid Email Address", diff --git a/test/components/views/settings/devices/DeviceTile-test.tsx b/test/components/views/settings/devices/DeviceTile-test.tsx new file mode 100644 index 00000000000..d688eca9135 --- /dev/null +++ b/test/components/views/settings/devices/DeviceTile-test.tsx @@ -0,0 +1,107 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { IMyDevice } from 'matrix-js-sdk/src/matrix'; + +import DeviceTile from '../../../../../src/components/views/settings/devices/DeviceTile'; + +describe('', () => { + const defaultProps = { + device: { + device_id: '123', + }, + }; + const getComponent = (props = {}) => ( + + ); + // 14.03.2022 16:15 + const now = 1647270879403; + + jest.useFakeTimers(); + + beforeEach(() => { + jest.setSystemTime(now); + }); + + it('renders a device with no metadata', () => { + const { container } = render(getComponent()); + expect(container).toMatchSnapshot(); + }); + + it('renders display name with a tooltip', () => { + const device: IMyDevice = { + device_id: '123', + display_name: 'My device', + }; + const { container } = render(getComponent({ device })); + expect(container).toMatchSnapshot(); + }); + + it('renders last seen ip metadata', () => { + const device: IMyDevice = { + device_id: '123', + display_name: 'My device', + last_seen_ip: '1.2.3.4', + }; + const { getByTestId } = render(getComponent({ device })); + expect(getByTestId('device-metadata-lastSeenIp').textContent).toEqual(device.last_seen_ip); + }); + + it('separates metadata with a dot', () => { + const device: IMyDevice = { + device_id: '123', + last_seen_ip: '1.2.3.4', + last_seen_ts: now - 60000, + }; + const { container } = render(getComponent({ device })); + expect(container).toMatchSnapshot(); + }); + + describe('Last activity', () => { + const MS_DAY = 24 * 60 * 60 * 1000; + it('renders with day of week and time when last activity is less than 6 days ago', () => { + const device: IMyDevice = { + device_id: '123', + last_seen_ip: '1.2.3.4', + last_seen_ts: now - (MS_DAY * 3), + }; + const { getByTestId } = render(getComponent({ device })); + expect(getByTestId('device-metadata-lastActivity').textContent).toEqual('Last activity Fri 15:14'); + }); + + it('renders with month and date when last activity is more than 6 days ago', () => { + const device: IMyDevice = { + device_id: '123', + last_seen_ip: '1.2.3.4', + last_seen_ts: now - (MS_DAY * 8), + }; + const { getByTestId } = render(getComponent({ device })); + expect(getByTestId('device-metadata-lastActivity').textContent).toEqual('Last activity Mar 6'); + }); + + it('renders with month, date, year when activity is in a different calendar year', () => { + const device: IMyDevice = { + device_id: '123', + last_seen_ip: '1.2.3.4', + last_seen_ts: new Date('2021-12-29').getTime(), + }; + const { getByTestId } = render(getComponent({ device })); + expect(getByTestId('device-metadata-lastActivity').textContent).toEqual('Last activity Dec 29, 2021'); + }); + }); +}); diff --git a/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap new file mode 100644 index 00000000000..299d72348c8 --- /dev/null +++ b/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap @@ -0,0 +1,93 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders a device with no metadata 1`] = ` +
+
+
+

+ 123 +

+ +
+
+
+
+`; + +exports[` renders display name with a tooltip 1`] = ` +
+
+
+
+

+ My device +

+
+ +
+
+
+
+`; + +exports[` separates metadata with a dot 1`] = ` +
+
+
+

+ 123 +

+ +
+
+
+
+`; From 4a1488293850a8ba2be47515ce3639574edbc91b Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 8 Aug 2022 10:57:38 +0200 Subject: [PATCH 002/107] Move pin drop out of labs (PSG-664) (#9135) * Move pin drop out of labs (PSG-664) Signed-off-by: Johannes Marbach * Regenerate strings Signed-off-by: Johannes Marbach --- cypress/e2e/location/location.spec.ts | 4 ---- src/components/views/location/LocationShareMenu.tsx | 4 +--- src/i18n/strings/en_EN.json | 1 - src/settings/Settings.tsx | 7 ------- test/components/views/location/LocationShareMenu-test.tsx | 2 -- 5 files changed, 1 insertion(+), 17 deletions(-) diff --git a/cypress/e2e/location/location.spec.ts b/cypress/e2e/location/location.spec.ts index 65b1e8bcc8a..b0cc5e555c1 100644 --- a/cypress/e2e/location/location.spec.ts +++ b/cypress/e2e/location/location.spec.ts @@ -18,7 +18,6 @@ limitations under the License. import { SynapseInstance } from "../../plugins/synapsedocker"; import Chainable = Cypress.Chainable; -import { SettingLevel } from "../../../src/settings/SettingLevel"; describe("Location sharing", () => { let synapse: SynapseInstance; @@ -40,9 +39,6 @@ describe("Location sharing", () => { cy.initTestUser(synapse, "Tom"); }); - - // enable pin drop location sharing feature - cy.setSettingValue("feature_location_share_pin_drop", null, SettingLevel.DEVICE, true); }); afterEach(() => { diff --git a/src/components/views/location/LocationShareMenu.tsx b/src/components/views/location/LocationShareMenu.tsx index 7b31b45b04f..0d9ec242c66 100644 --- a/src/components/views/location/LocationShareMenu.tsx +++ b/src/components/views/location/LocationShareMenu.tsx @@ -49,9 +49,7 @@ const getEnabledShareTypes = (relation): LocationShareType[] => { enabledShareTypes.push(LocationShareType.Live); } - if (SettingsStore.getValue("feature_location_share_pin_drop")) { - enabledShareTypes.push(LocationShareType.Pin); - } + enabledShareTypes.push(LocationShareType.Pin); return enabledShareTypes; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7ce4908afe8..d509ec64942 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -900,7 +900,6 @@ "Jump to date (adds /jumptodate and jump to date headers)": "Jump to date (adds /jumptodate and jump to date headers)", "Send read receipts": "Send read receipts", "Right-click message context menu": "Right-click message context menu", - "Location sharing - pin drop": "Location sharing - pin drop", "Live Location Sharing (temporary implementation: locations persist in room history)": "Live Location Sharing (temporary implementation: locations persist in room history)", "Favourite Messages (under active development)": "Favourite Messages (under active development)", "Font size": "Font size", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 8414b25394b..05e4308a1c8 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -413,13 +413,6 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Right-click message context menu"), default: false, }, - "feature_location_share_pin_drop": { - isFeature: true, - labsGroup: LabGroup.Messaging, - supportedLevels: LEVELS_FEATURE, - displayName: _td("Location sharing - pin drop"), - default: false, - }, "feature_location_share_live": { isFeature: true, labsGroup: LabGroup.Messaging, diff --git a/test/components/views/location/LocationShareMenu-test.tsx b/test/components/views/location/LocationShareMenu-test.tsx index e25fab3ade0..94e2cd62130 100644 --- a/test/components/views/location/LocationShareMenu-test.tsx +++ b/test/components/views/location/LocationShareMenu-test.tsx @@ -213,8 +213,6 @@ describe('', () => { }); describe('with pin drop share type enabled', () => { - beforeEach(() => enableSettings(["feature_location_share_pin_drop"])); - it('renders share type switch with own and pin drop options', () => { const component = getComponent(); expect(component.find('LocationPicker').length).toBe(0); From cb15222594c2d8a7fd494d0df2e7e986a1bebc1f Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Mon, 8 Aug 2022 13:17:04 +0200 Subject: [PATCH 003/107] Make percy snapshots for user onboarding more reliable (#9132) * Make percy snapshots for user onboarding more reliable * Increase stability of lazy loading test Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- cypress/e2e/lazy-loading/lazy-loading.spec.ts | 12 ++++++------ cypress/e2e/user-onboarding/user-onboarding-new.ts | 14 ++++++++++++-- res/css/views/dialogs/_AppDownloadDialog.pcss | 4 ++++ src/components/views/dialogs/AppDownloadDialog.tsx | 6 +++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/cypress/e2e/lazy-loading/lazy-loading.spec.ts b/cypress/e2e/lazy-loading/lazy-loading.spec.ts index af4d7ef6aec..7d26b48676d 100644 --- a/cypress/e2e/lazy-loading/lazy-loading.spec.ts +++ b/cypress/e2e/lazy-loading/lazy-loading.spec.ts @@ -115,21 +115,21 @@ describe("Lazy Loading", () => { }); } - function getMembersInMemberlist(): Chainable { - return cy.get(".mx_MemberList .mx_EntityTile_name"); + function getMemberInMemberlist(name: string): Chainable { + return cy.contains(".mx_MemberList .mx_EntityTile_name", name); } function checkMemberList(charlies: Charly[]) { - getMembersInMemberlist().contains("Alice").should("exist"); - getMembersInMemberlist().contains("Bob").should("exist"); + getMemberInMemberlist("Alice").should("exist"); + getMemberInMemberlist("Bob").should("exist"); charlies.forEach(charly => { - getMembersInMemberlist().contains(charly.displayName).should("exist"); + getMemberInMemberlist(charly.displayName).should("exist"); }); } function checkMemberListLacksCharlies(charlies: Charly[]) { charlies.forEach(charly => { - getMembersInMemberlist().contains(charly.displayName).should("not.exist"); + getMemberInMemberlist(charly.displayName).should("not.exist"); }); } diff --git a/cypress/e2e/user-onboarding/user-onboarding-new.ts b/cypress/e2e/user-onboarding/user-onboarding-new.ts index 9c00370558f..44787ee61e8 100644 --- a/cypress/e2e/user-onboarding/user-onboarding-new.ts +++ b/cypress/e2e/user-onboarding/user-onboarding-new.ts @@ -49,7 +49,14 @@ describe("User Onboarding (new user)", () => { it("page is shown", () => { cy.get('.mx_UserOnboardingPage').should('exist'); - cy.percySnapshot("User onboarding page"); + cy.get('.mx_UserOnboardingList') + .should('exist') + .should(($list) => { + const list = $list.get(0); + expect(getComputedStyle(list).opacity).to.be.eq("1"); + }); + cy.get('.mx_UserOnboardingPage') + .percySnapshotElement("User onboarding page"); }); it("app download dialog", () => { @@ -58,7 +65,10 @@ describe("User Onboarding (new user)", () => { cy.get('[role=dialog]') .contains("#mx_BaseDialog_title", "Download Element") .should("exist"); - cy.percySnapshot("App download dialog"); + cy.get('[role=dialog]') + .percySnapshotElement("App download dialog", { + widths: [640], + }); }); it("using find friends action should increase progress", () => { diff --git a/res/css/views/dialogs/_AppDownloadDialog.pcss b/res/css/views/dialogs/_AppDownloadDialog.pcss index 7e154976176..776171ad09a 100644 --- a/res/css/views/dialogs/_AppDownloadDialog.pcss +++ b/res/css/views/dialogs/_AppDownloadDialog.pcss @@ -4,6 +4,10 @@ gap: $spacing-32; color: $primary-content; + &.mx_Dialog_fixedWidth { + width: 640px; + } + .mx_AppDownloadDialog_desktop { display: flex; flex-direction: column; diff --git a/src/components/views/dialogs/AppDownloadDialog.tsx b/src/components/views/dialogs/AppDownloadDialog.tsx index ab5ebfd22cb..21bf50ee1b0 100644 --- a/src/components/views/dialogs/AppDownloadDialog.tsx +++ b/src/components/views/dialogs/AppDownloadDialog.tsx @@ -43,7 +43,11 @@ export const AppDownloadDialog: FC = ({ onFinished }: IDialogProps const urlFDroid = mobileBuilds?.get("fdroid") ?? fallbackFDroid; return ( - + { desktopBuilds?.get("available") && (
From 2c4ee7eb15257ab7020953b1c6709cd51fe270df Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 8 Aug 2022 14:06:04 +0200 Subject: [PATCH 004/107] Increase max length of voice messages to 15m (PSG-661) (#9133) * Increase max length of voice messages to 15m (PSG-661) Relates to: vector-im/element-ios#5415 Signed-off-by: Johannes Marbach * Fix comment Signed-off-by: Johannes Marbach * Update src/audio/VoiceRecording.ts Co-authored-by: Robin Co-authored-by: Robin --- src/audio/VoiceRecording.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/audio/VoiceRecording.ts b/src/audio/VoiceRecording.ts index 1c6c8b40d24..d0b34493d86 100644 --- a/src/audio/VoiceRecording.ts +++ b/src/audio/VoiceRecording.ts @@ -37,7 +37,7 @@ import mxRecorderWorkletPath from "./RecorderWorklet"; const CHANNELS = 1; // stereo isn't important export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality. const BITRATE = 24000; // 24kbps is pretty high quality for our use case in opus. -const TARGET_MAX_LENGTH = 120; // 2 minutes in seconds. Somewhat arbitrary, though longer == larger files. +const TARGET_MAX_LENGTH = 900; // 15 minutes in seconds. Somewhat arbitrary, though longer == larger files. const TARGET_WARN_TIME_LEFT = 10; // 10 seconds, also somewhat arbitrary. export const RECORDING_PLAYBACK_SAMPLES = 44; @@ -229,8 +229,8 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { // go horribly over the limit. We also emit a warning state if needed. // // We use the recorder's perspective of time to make sure we don't cut off the last - // frame of audio, otherwise we end up with a 1:59 clip (119.68 seconds). This extra - // safety can allow us to overshoot the target a bit, but at least when we say 2min + // frame of audio, otherwise we end up with a 14:59 clip (899.68 seconds). This extra + // safety can allow us to overshoot the target a bit, but at least when we say 15min // maximum we actually mean it. // // In testing, recorder time and worker time lag by about 400ms, which is roughly the From d89a46289d0d98e74519f7d15c6e91e2aef12bc4 Mon Sep 17 00:00:00 2001 From: Kerry Date: Mon, 8 Aug 2022 14:28:02 +0200 Subject: [PATCH 005/107] use stable reference for active tab in tabbedView (#9145) --- src/components/structures/TabbedView.tsx | 29 ++-- .../components/structures/TabbedView-test.tsx | 133 ++++++++++++++++++ .../__snapshots__/TabbedView-test.tsx.snap | 83 +++++++++++ 3 files changed, 227 insertions(+), 18 deletions(-) create mode 100644 test/components/structures/TabbedView-test.tsx create mode 100644 test/components/structures/__snapshots__/TabbedView-test.tsx.snap diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index a55e5140736..9a5f36ea0be 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -60,21 +60,16 @@ interface IProps { } interface IState { - activeTabIndex: number; + activeTabId: string; } export default class TabbedView extends React.Component { constructor(props: IProps) { super(props); - let activeTabIndex = 0; - if (props.initialTabId) { - const tabIndex = props.tabs.findIndex(t => t.id === props.initialTabId); - if (tabIndex >= 0) activeTabIndex = tabIndex; - } - + const initialTabIdIsValid = props.tabs.find(tab => tab.id === props.initialTabId); this.state = { - activeTabIndex, + activeTabId: initialTabIdIsValid ? props.initialTabId : props.tabs[0]?.id, }; } @@ -82,9 +77,8 @@ export default class TabbedView extends React.Component { tabLocation: TabLocation.LEFT, }; - private getActiveTabIndex() { - if (!this.state || !this.state.activeTabIndex) return 0; - return this.state.activeTabIndex; + private getTabById(id: string): Tab | undefined { + return this.props.tabs.find(tab => tab.id === id); } /** @@ -93,10 +87,10 @@ export default class TabbedView extends React.Component { * @private */ private setActiveTab(tab: Tab) { - const idx = this.props.tabs.indexOf(tab); - if (idx !== -1) { + // make sure this tab is still in available tabs + if (!!this.getTabById(tab.id)) { if (this.props.onChange) this.props.onChange(tab.id); - this.setState({ activeTabIndex: idx }); + this.setState({ activeTabId: tab.id }); } else { logger.error("Could not find tab " + tab.label + " in tabs"); } @@ -105,8 +99,7 @@ export default class TabbedView extends React.Component { private renderTabLabel(tab: Tab) { let classes = "mx_TabbedView_tabLabel "; - const idx = this.props.tabs.indexOf(tab); - if (idx === this.getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active"; + if (this.state.activeTabId === tab.id) classes += "mx_TabbedView_tabLabel_active"; let tabIcon = null; if (tab.icon) { @@ -143,8 +136,8 @@ export default class TabbedView extends React.Component { public render(): React.ReactNode { const labels = this.props.tabs.map(tab => this.renderTabLabel(tab)); - const tab = this.props.tabs[this.getActiveTabIndex()]; - const panel = this.renderTabPanel(tab); + const tab = this.getTabById(this.state.activeTabId); + const panel = tab ? this.renderTabPanel(tab) : null; const tabbedViewClasses = classNames({ 'mx_TabbedView': true, diff --git a/test/components/structures/TabbedView-test.tsx b/test/components/structures/TabbedView-test.tsx new file mode 100644 index 00000000000..e690a10ac34 --- /dev/null +++ b/test/components/structures/TabbedView-test.tsx @@ -0,0 +1,133 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { fireEvent, render } from "@testing-library/react"; +import { act } from 'react-dom/test-utils'; + +import TabbedView, { Tab, TabLocation } from "../../../src/components/structures/TabbedView"; + +describe('', () => { + const generalTab = new Tab( + 'GENERAL', + 'General', + 'general', +
general
, + ); + const labsTab = new Tab( + 'LABS', + 'Labs', + 'labs', +
labs
, + ); + const securityTab = new Tab( + 'SECURITY', + 'Security', + 'security', +
security
, + ); + const defaultProps = { + tabLocation: TabLocation.LEFT, + tabs: [generalTab, labsTab, securityTab], + }; + const getComponent = (props = {}): React.ReactElement => ; + + const getTabTestId = (tab: Tab): string => `settings-tab-${tab.id}`; + const getActiveTab = (container: HTMLElement): Element | undefined => + container.getElementsByClassName('mx_TabbedView_tabLabel_active')[0]; + const getActiveTabBody = (container: HTMLElement): Element | undefined => + container.getElementsByClassName('mx_TabbedView_tabPanel')[0]; + + it('renders tabs', () => { + const { container } = render(getComponent()); + expect(container).toMatchSnapshot(); + }); + + it('renders first tab as active tab when no initialTabId', () => { + const { container } = render(getComponent()); + expect(getActiveTab(container).textContent).toEqual(generalTab.label); + expect(getActiveTabBody(container).textContent).toEqual('general'); + }); + + it('renders first tab as active tab when initialTabId is not valid', () => { + const { container } = render(getComponent({ initialTabId: 'bad-tab-id' })); + expect(getActiveTab(container).textContent).toEqual(generalTab.label); + expect(getActiveTabBody(container).textContent).toEqual('general'); + }); + + it('renders initialTabId tab as active when valid', () => { + const { container } = render(getComponent({ initialTabId: securityTab.id })); + expect(getActiveTab(container).textContent).toEqual(securityTab.label); + expect(getActiveTabBody(container).textContent).toEqual('security'); + }); + + it('renders without error when there are no tabs', () => { + const { container } = render(getComponent({ tabs: [] })); + expect(container).toMatchSnapshot(); + }); + + it('sets active tab on tab click', () => { + const { container, getByTestId } = render(getComponent()); + + act(() => { + fireEvent.click(getByTestId(getTabTestId(securityTab))); + }); + + expect(getActiveTab(container).textContent).toEqual(securityTab.label); + expect(getActiveTabBody(container).textContent).toEqual('security'); + }); + + it('calls onchange on on tab click', () => { + const onChange = jest.fn(); + const { getByTestId } = render(getComponent({ onChange })); + + act(() => { + fireEvent.click(getByTestId(getTabTestId(securityTab))); + }); + + expect(onChange).toHaveBeenCalledWith(securityTab.id); + }); + + it('keeps same tab active when order of tabs changes', () => { + // start with middle tab active + const { container, rerender } = render(getComponent({ initialTabId: labsTab.id })); + + expect(getActiveTab(container).textContent).toEqual(labsTab.label); + + rerender(getComponent({ tabs: [labsTab, generalTab, securityTab] })); + + // labs tab still active + expect(getActiveTab(container).textContent).toEqual(labsTab.label); + }); + + it('does not reactivate inititalTabId on rerender', () => { + const { container, getByTestId, rerender } = render(getComponent()); + + expect(getActiveTab(container).textContent).toEqual(generalTab.label); + + // make security tab active + act(() => { + fireEvent.click(getByTestId(getTabTestId(securityTab))); + }); + expect(getActiveTab(container).textContent).toEqual(securityTab.label); + + // rerender with new tab location + rerender(getComponent({ tabLocation: TabLocation.TOP })); + + // still security tab + expect(getActiveTab(container).textContent).toEqual(securityTab.label); + }); +}); diff --git a/test/components/structures/__snapshots__/TabbedView-test.tsx.snap b/test/components/structures/__snapshots__/TabbedView-test.tsx.snap new file mode 100644 index 00000000000..bee195c1a08 --- /dev/null +++ b/test/components/structures/__snapshots__/TabbedView-test.tsx.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders tabs 1`] = ` +
+
+
+
+ + + General + +
+
+ + + Labs + +
+
+ + + Security + +
+
+
+
+
+ general +
+
+
+
+
+`; + +exports[` renders without error when there are no tabs 1`] = ` +
+
+
+
+
+`; From ed67aec33455dc1a5bcb2832c207f3e8da0315fb Mon Sep 17 00:00:00 2001 From: Kerry Date: Mon, 8 Aug 2022 15:51:00 +0200 Subject: [PATCH 006/107] Device Manager - add new labsed session manager screen (PSG-636) (#9119) * add feature_new_device_manager labs flag * add generic settings tab container * settingstab section styles * add session manager tab to user settings * add sessions tab case to UserSettingDialog test * fussy import ordering * remove posthog tracking * i18n --- res/css/views/settings/tabs/_SettingsTab.pcss | 8 +++++ .../views/dialogs/UserSettingsDialog.tsx | 27 +++++++++++++-- src/components/views/dialogs/UserTab.ts | 1 + .../views/settings/tabs/SettingsTab.tsx | 34 +++++++++++++++++++ .../settings/tabs/user/SessionManagerTab.tsx | 26 ++++++++++++++ src/i18n/strings/en_EN.json | 2 ++ src/settings/Settings.tsx | 7 ++++ .../views/dialogs/UserSettingsDialog-test.tsx | 26 ++++++++++---- .../views/settings/tabs/SettingsTab-test.tsx | 30 ++++++++++++++++ .../__snapshots__/SettingsTab-test.tsx.snap | 22 ++++++++++++ 10 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 src/components/views/settings/tabs/SettingsTab.tsx create mode 100644 src/components/views/settings/tabs/user/SessionManagerTab.tsx create mode 100644 test/components/views/settings/tabs/SettingsTab-test.tsx create mode 100644 test/components/views/settings/tabs/__snapshots__/SettingsTab-test.tsx.snap diff --git a/res/css/views/settings/tabs/_SettingsTab.pcss b/res/css/views/settings/tabs/_SettingsTab.pcss index 868ea4fec50..8b4d17d8e97 100644 --- a/res/css/views/settings/tabs/_SettingsTab.pcss +++ b/res/css/views/settings/tabs/_SettingsTab.pcss @@ -97,3 +97,11 @@ limitations under the License. .mx_SettingsTab_toggleWithDescription { margin-top: $spacing-24; } + +.mx_SettingsTab_sections { + display: grid; + grid-template-columns: 1fr; + grid-gap: $spacing-32; + + padding: 0 $spacing-16; +} diff --git a/src/components/views/dialogs/UserSettingsDialog.tsx b/src/components/views/dialogs/UserSettingsDialog.tsx index 6d4fe15fcc4..e15144969d1 100644 --- a/src/components/views/dialogs/UserSettingsDialog.tsx +++ b/src/components/views/dialogs/UserSettingsDialog.tsx @@ -35,6 +35,7 @@ import BaseDialog from "./BaseDialog"; import { IDialogProps } from "./IDialogProps"; import SidebarUserSettingsTab from "../settings/tabs/user/SidebarUserSettingsTab"; import KeyboardUserSettingsTab from "../settings/tabs/user/KeyboardUserSettingsTab"; +import SessionManagerTab from '../settings/tabs/user/SessionManagerTab'; import { UserTab } from "./UserTab"; interface IProps extends IDialogProps { @@ -43,25 +44,30 @@ interface IProps extends IDialogProps { interface IState { mjolnirEnabled: boolean; + newSessionManagerEnabled: boolean; } export default class UserSettingsDialog extends React.Component { - private mjolnirWatcher: string | undefined; + private settingsWatchers: string[] = []; constructor(props) { super(props); this.state = { mjolnirEnabled: SettingsStore.getValue("feature_mjolnir"), + newSessionManagerEnabled: SettingsStore.getValue("feature_new_device_manager"), }; } public componentDidMount(): void { - this.mjolnirWatcher = SettingsStore.watchSetting("feature_mjolnir", null, this.mjolnirChanged); + this.settingsWatchers = [ + SettingsStore.watchSetting("feature_mjolnir", null, this.mjolnirChanged), + SettingsStore.watchSetting("feature_new_device_manager", null, this.sessionManagerChanged), + ]; } public componentWillUnmount(): void { - this.mjolnirWatcher && SettingsStore.unwatchSetting(this.mjolnirWatcher); + this.settingsWatchers.forEach(watcherRef => SettingsStore.unwatchSetting(watcherRef)); } private mjolnirChanged: CallbackFn = (settingName, roomId, atLevel, newValue) => { @@ -69,6 +75,11 @@ export default class UserSettingsDialog extends React.Component this.setState({ mjolnirEnabled: newValue }); }; + private sessionManagerChanged: CallbackFn = (settingName, roomId, atLevel, newValue) => { + // We can cheat because we know what levels a feature is tracked at, and how it is tracked + this.setState({ newSessionManagerEnabled: newValue }); + }; + private getTabs() { const tabs: Tab[] = []; @@ -132,6 +143,16 @@ export default class UserSettingsDialog extends React.Component , "UserSettingsSecurityPrivacy", )); + if (this.state.newSessionManagerEnabled) { + tabs.push(new Tab( + UserTab.SessionManager, + _td("Sessions"), + "mx_UserSettingsDialog_securityIcon", + , + // don't track with posthog while under construction + undefined, + )); + } // Show the Labs tab if enabled or if there are any active betas if (SdkConfig.get("show_labs_settings") || SettingsStore.getFeatureSettingNames().some(k => SettingsStore.getBetaInfo(k)) diff --git a/src/components/views/dialogs/UserTab.ts b/src/components/views/dialogs/UserTab.ts index b5b2782c0d0..ab6a2132117 100644 --- a/src/components/views/dialogs/UserTab.ts +++ b/src/components/views/dialogs/UserTab.ts @@ -26,4 +26,5 @@ export enum UserTab { Labs = "USER_LABS_TAB", Mjolnir = "USER_MJOLNIR_TAB", Help = "USER_HELP_TAB", + SessionManager = "USER_SESSION_MANAGER_TAB", } diff --git a/src/components/views/settings/tabs/SettingsTab.tsx b/src/components/views/settings/tabs/SettingsTab.tsx new file mode 100644 index 00000000000..01a366846d3 --- /dev/null +++ b/src/components/views/settings/tabs/SettingsTab.tsx @@ -0,0 +1,34 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import React from "react"; + +import Heading from "../../typography/Heading"; + +export interface SettingsTabProps { + heading: string; + children?: React.ReactNode; +} + +const SettingsTab: React.FC = ({ heading, children }) => ( +
+ { heading } +
+ { children } +
+
+); + +export default SettingsTab; diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx new file mode 100644 index 00000000000..afa663392ba --- /dev/null +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -0,0 +1,26 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; + +import { _t } from "../../../../../languageHandler"; +import SettingsTab from '../SettingsTab'; + +const SessionManagerTab: React.FC = () => { + return ; +}; + +export default SessionManagerTab; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d509ec64942..9716fc98139 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -902,6 +902,7 @@ "Right-click message context menu": "Right-click message context menu", "Live Location Sharing (temporary implementation: locations persist in room history)": "Live Location Sharing (temporary implementation: locations persist in room history)", "Favourite Messages (under active development)": "Favourite Messages (under active development)", + "Use new session manager (under active development)": "Use new session manager (under active development)", "Font size": "Font size", "Use custom size": "Use custom size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", @@ -1561,6 +1562,7 @@ "Share anonymous data to help us identify issues. Nothing personal. No third parties.": "Share anonymous data to help us identify issues. Nothing personal. No third parties.", "Where you're signed in": "Where you're signed in", "Manage your signed-in devices below. A device's name is visible to people you communicate with.": "Manage your signed-in devices below. A device's name is visible to people you communicate with.", + "Sessions": "Sessions", "Sidebar": "Sidebar", "Spaces to show": "Spaces to show", "Spaces are ways to group rooms and people. Alongside the spaces you're in, you can use some pre-built ones too.": "Spaces are ways to group rooms and people. Alongside the spaces you're in, you can use some pre-built ones too.", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 05e4308a1c8..1a3f361c1a8 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -429,6 +429,13 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Favourite Messages (under active development)"), default: false, }, + "feature_new_device_manager": { + isFeature: true, + labsGroup: LabGroup.Experimental, + supportedLevels: LEVELS_FEATURE, + displayName: _td("Use new session manager (under active development)"), + default: false, + }, "baseFontSize": { displayName: _td("Font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, diff --git a/test/components/views/dialogs/UserSettingsDialog-test.tsx b/test/components/views/dialogs/UserSettingsDialog-test.tsx index 50dc3f97aa1..68385b62048 100644 --- a/test/components/views/dialogs/UserSettingsDialog-test.tsx +++ b/test/components/views/dialogs/UserSettingsDialog-test.tsx @@ -115,6 +115,12 @@ describe('', () => { expect(getByTestId(`settings-tab-${UserTab.Voice}`)).toBeTruthy(); }); + it('renders session manager tab when enabled', () => { + mockSettingsStore.getValue.mockImplementation((settingName) => settingName === "feature_new_device_manager"); + const { getByTestId } = render(getComponent()); + expect(getByTestId(`settings-tab-${UserTab.SessionManager}`)).toBeTruthy(); + }); + it('renders labs tab when show_labs_settings is enabled in config', () => { mockSdkConfig.get.mockImplementation((configName) => configName === "show_labs_settings"); const { getByTestId } = render(getComponent()); @@ -130,11 +136,11 @@ describe('', () => { expect(getByTestId(`settings-tab-${UserTab.Labs}`)).toBeTruthy(); }); - it('watches feature_mjolnir setting', () => { - let watchSettingCallback: CallbackFn = jest.fn(); + it('watches settings', () => { + const watchSettingCallbacks: Record = {}; mockSettingsStore.watchSetting.mockImplementation((settingName, roomId, callback) => { - watchSettingCallback = callback; + watchSettingCallbacks[settingName] = callback; return `mock-watcher-id-${settingName}`; }); @@ -142,16 +148,24 @@ describe('', () => { expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeFalsy(); expect(mockSettingsStore.watchSetting.mock.calls[0][0]).toEqual('feature_mjolnir'); + expect(mockSettingsStore.watchSetting.mock.calls[1][0]).toEqual('feature_new_device_manager'); // call the watch setting callback - watchSettingCallback("feature_mjolnir", '', SettingLevel.ACCOUNT, true, true); - + watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", '', SettingLevel.ACCOUNT, true, true); // tab is rendered now expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy(); + // call the watch setting callback + watchSettingCallbacks["feature_new_device_manager"]( + "feature_new_device_manager", '', SettingLevel.ACCOUNT, true, true, + ); + // tab is rendered now + expect(queryByTestId(`settings-tab-${UserTab.SessionManager}`)).toBeTruthy(); + unmount(); - // unwatches mjolnir after unmount + // unwatches settings on unmount expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith('mock-watcher-id-feature_mjolnir'); + expect(mockSettingsStore.unwatchSetting).toHaveBeenCalledWith('mock-watcher-id-feature_new_device_manager'); }); }); diff --git a/test/components/views/settings/tabs/SettingsTab-test.tsx b/test/components/views/settings/tabs/SettingsTab-test.tsx new file mode 100644 index 00000000000..001162635a5 --- /dev/null +++ b/test/components/views/settings/tabs/SettingsTab-test.tsx @@ -0,0 +1,30 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { ReactElement } from 'react'; +import { render } from '@testing-library/react'; + +import SettingsTab, { SettingsTabProps } from '../../../../../src/components/views/settings/tabs/SettingsTab'; + +describe('', () => { + const getComponent = (props: SettingsTabProps): ReactElement => (); + it('renders tab', () => { + const { container } = render(getComponent({ heading: 'Test Tab', children:
test
})); + + expect(container).toMatchSnapshot(); + }); +}); + diff --git a/test/components/views/settings/tabs/__snapshots__/SettingsTab-test.tsx.snap b/test/components/views/settings/tabs/__snapshots__/SettingsTab-test.tsx.snap new file mode 100644 index 00000000000..704189fb1f2 --- /dev/null +++ b/test/components/views/settings/tabs/__snapshots__/SettingsTab-test.tsx.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders tab 1`] = ` +
+
+

+ Test Tab +

+
+
+ test +
+
+
+
+`; From e08b11b9c9285b85749afcf53523bacfd4ad7647 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Aug 2022 17:07:55 +0100 Subject: [PATCH 007/107] Make Spaces cypress tests less flaky (#9138) --- cypress/e2e/spaces/spaces.spec.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cypress/e2e/spaces/spaces.spec.ts b/cypress/e2e/spaces/spaces.spec.ts index 029a123737d..0a8212ab8dd 100644 --- a/cypress/e2e/spaces/spaces.spec.ts +++ b/cypress/e2e/spaces/spaces.spec.ts @@ -100,9 +100,9 @@ describe("Spaces", () => { cy.get(".mx_AccessibleButton").contains("Go to my first room").click(); // Assert rooms exist in the room list - cy.get(".mx_RoomTile").contains("General").should("exist"); - cy.get(".mx_RoomTile").contains("Random").should("exist"); - cy.get(".mx_RoomTile").contains("Jokes").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "General").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "Random").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "Jokes").should("exist"); }); it("should allow user to create private space", () => { @@ -128,14 +128,14 @@ describe("Spaces", () => { cy.get(".mx_AccessibleButton").contains("Skip for now").click(); // Assert rooms exist in the room list - cy.get(".mx_RoomTile").contains("General").should("exist"); - cy.get(".mx_RoomTile").contains("Random").should("exist"); - cy.get(".mx_RoomTile").contains("Projects").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "General").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "Random").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "Projects").should("exist"); // Assert rooms exist in the space explorer - cy.get(".mx_SpaceHierarchy_roomTile").contains("General").should("exist"); - cy.get(".mx_SpaceHierarchy_roomTile").contains("Random").should("exist"); - cy.get(".mx_SpaceHierarchy_roomTile").contains("Projects").should("exist"); + cy.get(".mx_SpaceHierarchy_list").contains(".mx_SpaceHierarchy_roomTile", "General").should("exist"); + cy.get(".mx_SpaceHierarchy_list").contains(".mx_SpaceHierarchy_roomTile", "Random").should("exist"); + cy.get(".mx_SpaceHierarchy_list").contains(".mx_SpaceHierarchy_roomTile", "Projects").should("exist"); }); it("should allow user to create just-me space", () => { @@ -157,8 +157,8 @@ describe("Spaces", () => { cy.get(".mx_AddExistingToSpace_entry").click(); cy.get(".mx_AccessibleButton").contains("Add").click(); - cy.get(".mx_RoomTile").contains("Sample Room").should("exist"); - cy.get(".mx_SpaceHierarchy_roomTile").contains("Sample Room").should("exist"); + cy.get(".mx_RoomList").contains(".mx_RoomTile", "Sample Room").should("exist"); + cy.get(".mx_SpaceHierarchy_list").contains(".mx_SpaceHierarchy_roomTile", "Sample Room").should("exist"); }); it("should allow user to invite another to a space", () => { @@ -233,8 +233,8 @@ describe("Spaces", () => { cy.viewSpaceHomeByName(spaceName); }); cy.get(".mx_SpaceRoomView .mx_SpaceHierarchy_list").within(() => { - cy.get(".mx_SpaceHierarchy_roomTile").contains("Music").should("exist"); - cy.get(".mx_SpaceHierarchy_roomTile").contains("Gaming").should("exist"); + cy.contains(".mx_SpaceHierarchy_roomTile", "Music").should("exist"); + cy.contains(".mx_SpaceHierarchy_roomTile", "Gaming").should("exist"); }); }); }); From f467d94603abfad9fe41f088baf2f67f65ca4bdf Mon Sep 17 00:00:00 2001 From: Kerry Date: Mon, 8 Aug 2022 18:40:46 +0200 Subject: [PATCH 008/107] Live location sharing: share location at most every 5 seconds (PSG-655) (#9148) --- src/stores/OwnBeaconStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/OwnBeaconStore.ts b/src/stores/OwnBeaconStore.ts index f3184153f23..18d25ffad38 100644 --- a/src/stores/OwnBeaconStore.ts +++ b/src/stores/OwnBeaconStore.ts @@ -55,7 +55,7 @@ export enum OwnBeaconStoreEvent { BeaconUpdateError = 'BeaconUpdateError', } -const MOVING_UPDATE_INTERVAL = 2000; +const MOVING_UPDATE_INTERVAL = 5000; const STATIC_UPDATE_INTERVAL = 30000; const BAIL_AFTER_CONSECUTIVE_ERROR_COUNT = 2; From 32478db57e7cf39be2e1e79f03d4e3ef0f59e925 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 8 Aug 2022 15:48:28 -0400 Subject: [PATCH 009/107] Migrate the hidden read receipts flag to new "send read receipts" option (#9141) * Migrate the hidden read receipts flag to new "send read receipts" option For safety. * Appease linter & ignore guests * `void` --- .../e2e/settings/hidden-rr-migration.spec.ts | 90 +++++++++++++++++++ cypress/support/login.ts | 12 ++- cypress/support/settings.ts | 7 +- src/Lifecycle.ts | 3 + src/settings/SettingsStore.ts | 41 +++++++++ .../handlers/DeviceSettingsHandler.ts | 6 +- 6 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 cypress/e2e/settings/hidden-rr-migration.spec.ts diff --git a/cypress/e2e/settings/hidden-rr-migration.spec.ts b/cypress/e2e/settings/hidden-rr-migration.spec.ts new file mode 100644 index 00000000000..1d5419af339 --- /dev/null +++ b/cypress/e2e/settings/hidden-rr-migration.spec.ts @@ -0,0 +1,90 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import { SynapseInstance } from "../../plugins/synapsedocker"; + +function seedLabs(synapse: SynapseInstance, labsVal: boolean | null): void { + cy.initTestUser(synapse, "Sally", () => { + // seed labs flag + cy.window({ log: false }).then(win => { + if (typeof labsVal === "boolean") { + // stringify boolean + win.localStorage.setItem("mx_labs_feature_feature_hidden_read_receipts", `${labsVal}`); + } + }); + }); +} + +function testForVal(settingVal: boolean | null): void { + const testRoomName = "READ RECEIPTS"; + cy.createRoom({ name: testRoomName }).as("roomId"); + cy.all([cy.get("@roomId")]).then(() => { + cy.viewRoomByName(testRoomName).then(() => { + // if we can see the room, then sync is working for us. It's time to see if the + // migration even ran. + + cy.getSettingValue("sendReadReceipts", null, true).should("satisfy", (val) => { + if (typeof settingVal === "boolean") { + return val === settingVal; + } else { + return !val; // falsy - we don't actually care if it's undefined, null, or a literal false + } + }); + }); + }); +} + +describe("Hidden Read Receipts Setting Migration", () => { + // We run this as a full-blown end-to-end test to ensure it works in an integration + // sense. If we unit tested it, we'd be testing that the code works but not that the + // migration actually runs. + // + // Here, we get to test that not only the code works but also that it gets run. Most + // of our interactions are with the JS console as we're honestly just checking that + // things got set correctly. + // + // For a security-sensitive feature like hidden read receipts, it's absolutely vital + // that we migrate the setting appropriately. + + let synapse: SynapseInstance; + + beforeEach(() => { + cy.startSynapse("default").then(data => { + synapse = data; + }); + }); + + afterEach(() => { + cy.stopSynapse(synapse); + }); + + it('should not migrate the lack of a labs flag', () => { + seedLabs(synapse, null); + testForVal(null); + }); + + it('should migrate labsHiddenRR=false as sendRR=true', () => { + seedLabs(synapse, false); + testForVal(true); + }); + + it('should migrate labsHiddenRR=true as sendRR=false', () => { + seedLabs(synapse, true); + testForVal(false); + }); +}); diff --git a/cypress/support/login.ts b/cypress/support/login.ts index da72ec69a46..4cdfd6a84df 100644 --- a/cypress/support/login.ts +++ b/cypress/support/login.ts @@ -35,13 +35,19 @@ declare global { * Generates a test user and instantiates an Element session with that user. * @param synapse the synapse returned by startSynapse * @param displayName the displayName to give the test user + * @param prelaunchFn optional function to run before the app is visited */ - initTestUser(synapse: SynapseInstance, displayName: string): Chainable; + initTestUser( + synapse: SynapseInstance, + displayName: string, + prelaunchFn?: () => void, + ): Chainable; } } } -Cypress.Commands.add("initTestUser", (synapse: SynapseInstance, displayName: string): Chainable => { +// eslint-disable-next-line max-len +Cypress.Commands.add("initTestUser", (synapse: SynapseInstance, displayName: string, prelaunchFn?: () => void): Chainable => { // XXX: work around Cypress not clearing IDB between tests cy.window({ log: false }).then(win => { win.indexedDB.databases().then(databases => { @@ -87,6 +93,8 @@ Cypress.Commands.add("initTestUser", (synapse: SynapseInstance, displayName: str win.localStorage.setItem("mx_local_settings", '{"language":"en"}'); }); + prelaunchFn?.(); + return cy.visit("/").then(() => { // wait for the app to load return cy.get(".mx_MatrixChat", { timeout: 15000 }); diff --git a/cypress/support/settings.ts b/cypress/support/settings.ts index a44f3f06d25..06ec815364b 100644 --- a/cypress/support/settings.ts +++ b/cypress/support/settings.ts @@ -96,7 +96,7 @@ declare global { * value. * @return {*} The value, or null if not found */ - getSettingValue(name: string, roomId?: string): Chainable; + getSettingValue(name: string, roomId?: string, excludeDefault?: boolean): Chainable; } } } @@ -116,9 +116,10 @@ Cypress.Commands.add("setSettingValue", ( }); }); -Cypress.Commands.add("getSettingValue", (name: string, roomId?: string): Chainable => { +// eslint-disable-next-line max-len +Cypress.Commands.add("getSettingValue", (name: string, roomId?: string, excludeDefault?: boolean): Chainable => { return cy.getSettingsStore().then((store: typeof SettingsStore) => { - return store.getValue(name, roomId); + return store.getValue(name, roomId, excludeDefault); }); }); diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index df9dec9f5ed..94b2e257067 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -826,6 +826,9 @@ async function startMatrixClient(startSyncing = true): Promise { await MatrixClientPeg.assign(); } + // Run the migrations after the MatrixClientPeg has been assigned + SettingsStore.runMigrations(); + // This needs to be started after crypto is set up DeviceListener.sharedInstance().start(); // Similarly, don't start sending presence updates until we've started diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index b6f63d4e9c6..76b12b62bbe 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -35,6 +35,9 @@ import SettingsHandler from "./handlers/SettingsHandler"; import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload"; import { Action } from "../dispatcher/actions"; import PlatformSettingsHandler from "./handlers/PlatformSettingsHandler"; +import dispatcher from "../dispatcher/dispatcher"; +import { ActionPayload } from "../dispatcher/payloads"; +import { MatrixClientPeg } from "../MatrixClientPeg"; const defaultWatchManager = new WatchManager(); @@ -565,6 +568,44 @@ export default class SettingsStore { return null; } + /** + * Runs or queues any setting migrations needed. + */ + public static runMigrations(): void { + // Dev notes: to add your migration, just add a new `migrateMyFeature` function, call it, and + // add a comment to note when it can be removed. + + SettingsStore.migrateHiddenReadReceipts(); // Can be removed after October 2022. + } + + private static migrateHiddenReadReceipts(): void { + if (MatrixClientPeg.get().isGuest()) return; // not worth it + + // We wait for the first sync to ensure that the user's existing account data has loaded, as otherwise + // getValue() for an account-level setting like sendReadReceipts will return `null`. + const disRef = dispatcher.register((payload: ActionPayload) => { + if (payload.action === "MatrixActions.sync") { + dispatcher.unregister(disRef); + + const rrVal = SettingsStore.getValue("sendReadReceipts", null, true); + if (typeof rrVal !== "boolean") { + // new setting isn't set - see if the labs flag was. We have to manually reach into the + // handler for this because it isn't a setting anymore (`getValue` will yell at us). + const handler = LEVEL_HANDLERS[SettingLevel.DEVICE] as DeviceSettingsHandler; + const labsVal = handler.readFeature("feature_hidden_read_receipts"); + if (typeof labsVal === "boolean") { + // Inverse of labs flag because negative->positive language switch in setting name + const newVal = !labsVal; + console.log(`Setting sendReadReceipts to ${newVal} because of previously-set labs flag`); + + // noinspection JSIgnoredPromiseFromCall + SettingsStore.setValue("sendReadReceipts", null, SettingLevel.ACCOUNT, newVal); + } + } + } + }); + } + /** * Debugging function for reading explicit setting values without going through the * complicated/biased functions in the SettingsStore. This will print information to diff --git a/src/settings/handlers/DeviceSettingsHandler.ts b/src/settings/handlers/DeviceSettingsHandler.ts index 138457d5a9f..4be80b764d0 100644 --- a/src/settings/handlers/DeviceSettingsHandler.ts +++ b/src/settings/handlers/DeviceSettingsHandler.ts @@ -114,12 +114,16 @@ export default class DeviceSettingsHandler extends AbstractLocalStorageSettingsH // Note: features intentionally don't use the same key as settings to avoid conflicts // and to be backwards compatible. - private readFeature(featureName: string): boolean | null { + // public for access to migrations - not exposed from the SettingsHandler interface + public readFeature(featureName: string): boolean | null { if (MatrixClientPeg.get() && MatrixClientPeg.get().isGuest()) { // Guests should not have any labs features enabled. return false; } + // XXX: This turns they key names into `mx_labs_feature_feature_x` (double feature). + // This is because all feature names start with `feature_` as a matter of policy. + // Oh well. return this.getBoolean("mx_labs_feature_" + featureName); } From eb2e61e9cfa8d54760dddc82b7e8f98a58d5df8a Mon Sep 17 00:00:00 2001 From: Kerry Date: Tue, 9 Aug 2022 10:14:30 +0200 Subject: [PATCH 010/107] Device manager: generic settings subsection component (PSG-636) (#9147) * add feature_new_device_manager labs flag * add generic settings tab container * settingstab section styles * add session manager tab to user settings * add sessions tab case to UserSettingDialog test * fussy import ordering * remove posthog tracking * i18n * add generic settings subsection component --- res/css/_components.pcss | 1 + .../settings/shared/_SettingsSubsection.pcss | 36 +++++++++ res/css/views/settings/tabs/_SettingsTab.pcss | 2 +- .../settings/shared/SettingsSubsection.tsx | 37 +++++++++ .../settings/tabs/user/SessionManagerTab.tsx | 9 ++- src/i18n/strings/en_EN.json | 1 + .../shared/SettingsSubsection-test.tsx | 45 +++++++++++ .../SettingsSubsection-test.tsx.snap | 81 +++++++++++++++++++ 8 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 res/css/components/views/settings/shared/_SettingsSubsection.pcss create mode 100644 src/components/views/settings/shared/SettingsSubsection.tsx create mode 100644 test/components/views/settings/shared/SettingsSubsection-test.tsx create mode 100644 test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap diff --git a/res/css/_components.pcss b/res/css/_components.pcss index fe23a1c3882..d4d7ecc3163 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -28,6 +28,7 @@ @import "./components/views/messages/_MBeaconBody.pcss"; @import "./components/views/messages/shared/_MediaProcessingError.pcss"; @import "./components/views/settings/devices/_DeviceTile.pcss"; +@import "./components/views/settings/shared/_SettingsSubsection.pcss"; @import "./components/views/spaces/_QuickThemeSwitcher.pcss"; @import "./structures/_AutoHideScrollbar.pcss"; @import "./structures/_BackdropPanel.pcss"; diff --git a/res/css/components/views/settings/shared/_SettingsSubsection.pcss b/res/css/components/views/settings/shared/_SettingsSubsection.pcss new file mode 100644 index 00000000000..9eb51696bab --- /dev/null +++ b/res/css/components/views/settings/shared/_SettingsSubsection.pcss @@ -0,0 +1,36 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SettingsSubsection { + width: 100%; + box-sizing: border-box; +} + +.mx_SettingsSubsection_heading { + padding-bottom: $spacing-8; +} + +.mx_SettingsSubsection_description { + width: 100%; + box-sizing: inherit; + line-height: $font-24px; + margin-bottom: $spacing-32; + color: $secondary-content; +} + +.mx_SettingsSubsection_content { + width: 100%; +} diff --git a/res/css/views/settings/tabs/_SettingsTab.pcss b/res/css/views/settings/tabs/_SettingsTab.pcss index 8b4d17d8e97..544b5c623b2 100644 --- a/res/css/views/settings/tabs/_SettingsTab.pcss +++ b/res/css/views/settings/tabs/_SettingsTab.pcss @@ -103,5 +103,5 @@ limitations under the License. grid-template-columns: 1fr; grid-gap: $spacing-32; - padding: 0 $spacing-16; + padding: $spacing-16 0; } diff --git a/src/components/views/settings/shared/SettingsSubsection.tsx b/src/components/views/settings/shared/SettingsSubsection.tsx new file mode 100644 index 00000000000..5dcdc9dad6f --- /dev/null +++ b/src/components/views/settings/shared/SettingsSubsection.tsx @@ -0,0 +1,37 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; + +import Heading from "../../typography/Heading"; + +export interface SettingsSubsectionProps { + heading: string; + description?: string | React.ReactNode; + children?: React.ReactNode; +} + +const SettingsSubsection: React.FC = ({ heading, description, children }) => ( +
+ { heading } + { !!description &&
{ description }
} +
+ { children } +
+
+); + +export default SettingsSubsection; diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index afa663392ba..17c09aeb7a3 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -17,10 +17,17 @@ limitations under the License. import React from 'react'; import { _t } from "../../../../../languageHandler"; +import SettingsSubsection from '../../shared/SettingsSubsection'; import SettingsTab from '../SettingsTab'; const SessionManagerTab: React.FC = () => { - return ; + return + + ; }; export default SessionManagerTab; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9716fc98139..e601003ecb4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1563,6 +1563,7 @@ "Where you're signed in": "Where you're signed in", "Manage your signed-in devices below. A device's name is visible to people you communicate with.": "Manage your signed-in devices below. A device's name is visible to people you communicate with.", "Sessions": "Sessions", + "Current session": "Current session", "Sidebar": "Sidebar", "Spaces to show": "Spaces to show", "Spaces are ways to group rooms and people. Alongside the spaces you're in, you can use some pre-built ones too.": "Spaces are ways to group rooms and people. Alongside the spaces you're in, you can use some pre-built ones too.", diff --git a/test/components/views/settings/shared/SettingsSubsection-test.tsx b/test/components/views/settings/shared/SettingsSubsection-test.tsx new file mode 100644 index 00000000000..acc2e6db957 --- /dev/null +++ b/test/components/views/settings/shared/SettingsSubsection-test.tsx @@ -0,0 +1,45 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { render } from '@testing-library/react'; + +import SettingsSubsection from '../../../../../src/components/views/settings/shared/SettingsSubsection'; + +describe('', () => { + const defaultProps = { + heading: 'Test', + children:
test settings content
, + }; + const getComponent = (props = {}): React.ReactElement => + (); + + it('renders without description', () => { + const { container } = render(getComponent()); + expect(container).toMatchSnapshot(); + }); + + it('renders with plain text description', () => { + const { container } = render(getComponent({ description: 'This describes the subsection' })); + expect(container).toMatchSnapshot(); + }); + + it('renders with react element description', () => { + const description =

This describes the section link

; + const { container } = render(getComponent({ description })); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap b/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap new file mode 100644 index 00000000000..10309d2f676 --- /dev/null +++ b/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders with plain text description 1`] = ` +
+
+

+ Test +

+
+ This describes the subsection +
+
+
+ test settings content +
+
+
+
+`; + +exports[` renders with react element description 1`] = ` +
+
+

+ Test +

+
+

+ This describes the section + + link + +

+
+
+
+ test settings content +
+
+
+
+`; + +exports[` renders without description 1`] = ` +
+
+

+ Test +

+
+
+ test settings content +
+
+
+
+`; From 5fbeb20df864721e2acbb7bff56ff234abf9e3fb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Aug 2022 12:55:49 +0100 Subject: [PATCH 011/107] Some small tidying up (#9149) * Remove stale comment * Fix typing * Install katex type definitions --- package.json | 1 + src/components/structures/MatrixChat.tsx | 2 +- src/linkify-matrix.ts | 4 ++-- yarn.lock | 5 +++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1973a2f5b58..933ccfd7ada 100644 --- a/package.json +++ b/package.json @@ -150,6 +150,7 @@ "@types/flux": "^3.1.9", "@types/fs-extra": "^9.0.13", "@types/jest": "^26.0.20", + "@types/katex": "^0.14.0", "@types/lodash": "^4.14.168", "@types/modernizr": "^3.5.3", "@types/node": "^14.14.22", diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 41be62e1e69..8c173a36301 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -157,7 +157,7 @@ interface IScreen { params?: QueryDict; } -interface IProps { // TODO type things better +interface IProps { config: IConfigOptions; serverConfig?: ValidatedServerConfig; onNewScreen: (screen: string, replaceLast: boolean) => void; diff --git a/src/linkify-matrix.ts b/src/linkify-matrix.ts index b626756f7c6..896784cb454 100644 --- a/src/linkify-matrix.ts +++ b/src/linkify-matrix.ts @@ -130,8 +130,8 @@ function onAliasClick(event: MouseEvent, roomAlias: string) { }); } -const escapeRegExp = function(string): string { - return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +const escapeRegExp = function(s: string): string { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; // Recognise URLs from both our local and official Element deployments. diff --git a/yarn.lock b/yarn.lock index de8172c7287..f2f623b7edf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2094,6 +2094,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/katex@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.14.0.tgz#b84c0afc3218069a5ad64fe2a95321881021b5fe" + integrity sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA== + "@types/lodash@^4.14.168": version "4.14.182" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" From 5a9c2e530a9c0ff59742aef0ad3a571c4944336d Mon Sep 17 00:00:00 2001 From: Kerry Date: Tue, 9 Aug 2022 15:07:25 +0200 Subject: [PATCH 012/107] Device manager - selectable device tile wrapper (PSG-637) (#9153) * add selectabledevicetile wrapper * set pointer cursor * line up own device icon with new checkboxes --- res/css/_components.pcss | 1 + .../devices/_SelectableDeviceTile.pcss | 28 ++++++ res/css/views/settings/_DevicesPanel.pcss | 6 +- .../views/elements/StyledCheckbox.tsx | 8 +- .../views/settings/DevicesPanelEntry.tsx | 26 +++--- .../views/settings/devices/DeviceTile.tsx | 9 +- .../settings/devices/SelectableDeviceTile.tsx | 42 +++++++++ .../LabelledCheckbox-test.tsx.snap | 2 - .../devices/SelectableDeviceTile-test.tsx | 85 +++++++++++++++++++ .../SelectableDeviceTile-test.tsx.snap | 71 ++++++++++++++++ 10 files changed, 255 insertions(+), 23 deletions(-) create mode 100644 res/css/components/views/settings/devices/_SelectableDeviceTile.pcss create mode 100644 src/components/views/settings/devices/SelectableDeviceTile.tsx create mode 100644 test/components/views/settings/devices/SelectableDeviceTile-test.tsx create mode 100644 test/components/views/settings/devices/__snapshots__/SelectableDeviceTile-test.tsx.snap diff --git a/res/css/_components.pcss b/res/css/_components.pcss index d4d7ecc3163..d6445f01435 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -28,6 +28,7 @@ @import "./components/views/messages/_MBeaconBody.pcss"; @import "./components/views/messages/shared/_MediaProcessingError.pcss"; @import "./components/views/settings/devices/_DeviceTile.pcss"; +@import "./components/views/settings/devices/_SelectableDeviceTile.pcss"; @import "./components/views/settings/shared/_SettingsSubsection.pcss"; @import "./components/views/spaces/_QuickThemeSwitcher.pcss"; @import "./structures/_AutoHideScrollbar.pcss"; diff --git a/res/css/components/views/settings/devices/_SelectableDeviceTile.pcss b/res/css/components/views/settings/devices/_SelectableDeviceTile.pcss new file mode 100644 index 00000000000..5d6a497e02c --- /dev/null +++ b/res/css/components/views/settings/devices/_SelectableDeviceTile.pcss @@ -0,0 +1,28 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SelectableDeviceTile { + display: flex; + flex-direction: row; + align-items: center; + width: 100%; + cursor: pointer; +} + +.mx_SelectableDeviceTile_checkbox { + flex: 0 0; + margin-right: $spacing-16; +} diff --git a/res/css/views/settings/_DevicesPanel.pcss b/res/css/views/settings/_DevicesPanel.pcss index 9cbdb6a2a1b..8581225cee8 100644 --- a/res/css/views/settings/_DevicesPanel.pcss +++ b/res/css/views/settings/_DevicesPanel.pcss @@ -56,10 +56,12 @@ limitations under the License. align-items: flex-start; margin-block: 10px; min-height: 35px; + padding: 0 $spacing-8; } -.mx_DevicesPanel_icon, .mx_DevicesPanel_checkbox { - margin-left: 9px; +.mx_DevicesPanel_icon { + margin-left: 0px; + margin-right: $spacing-16; margin-top: 2px; } diff --git a/src/components/views/elements/StyledCheckbox.tsx b/src/components/views/elements/StyledCheckbox.tsx index 333fbb8adbc..35422366822 100644 --- a/src/components/views/elements/StyledCheckbox.tsx +++ b/src/components/views/elements/StyledCheckbox.tsx @@ -70,9 +70,11 @@ export default class StyledCheckbox extends React.PureComponent
-
- { this.props.children } -
+ { !!this.props.children && +
+ { this.props.children } +
+ } ; } diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index 5a5330fd3ee..b0301214b9b 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -20,7 +20,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import StyledCheckbox, { CheckboxStyle } from '../elements/StyledCheckbox'; import AccessibleButton from "../elements/AccessibleButton"; import Field from "../elements/Field"; import Modal from "../../../Modal"; @@ -28,6 +27,7 @@ import SetupEncryptionDialog from '../dialogs/security/SetupEncryptionDialog'; import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDialog'; import LogoutDialog from '../dialogs/LogoutDialog'; import DeviceTile from './devices/DeviceTile'; +import SelectableDeviceTile from './devices/SelectableDeviceTile'; interface IProps { device: IMyDevice; @@ -133,14 +133,6 @@ export default class DevicesPanelEntry extends React.Component { ; } - const left = this.props.isOwnDevice ? -
- -
: -
- -
; - const buttons = this.state.renaming ? { ; - return ( -
- { left } + if (this.props.isOwnDevice) { + return
+
+ +
{ buttons } +
; + } + + return ( +
+ + { buttons } +
); } diff --git a/src/components/views/settings/devices/DeviceTile.tsx b/src/components/views/settings/devices/DeviceTile.tsx index 03d952fbb1e..33f9fc40a85 100644 --- a/src/components/views/settings/devices/DeviceTile.tsx +++ b/src/components/views/settings/devices/DeviceTile.tsx @@ -23,15 +23,16 @@ import TooltipTarget from "../../elements/TooltipTarget"; import { Alignment } from "../../elements/Tooltip"; import Heading from "../../typography/Heading"; -interface Props { +export interface DeviceTileProps { device: IMyDevice; children?: React.ReactNode; + onClick?: () => void; } const DeviceTileName: React.FC<{ device: IMyDevice }> = ({ device }) => { if (device.display_name) { return @@ -59,7 +60,7 @@ const DeviceMetadata: React.FC<{ value: string, id: string }> = ({ value, id }) value ? { value } : null ); -const DeviceTile: React.FC = ({ device, children }) => { +const DeviceTile: React.FC = ({ device, children, onClick }) => { const lastActivity = device.last_seen_ts && `${_t('Last activity')} ${formatLastActivity(device.last_seen_ts)}`; const metadata = [ { id: 'lastActivity', value: lastActivity }, @@ -67,7 +68,7 @@ const DeviceTile: React.FC = ({ device, children }) => { ]; return
-
+
{ metadata.map(({ id, value }, index) => diff --git a/src/components/views/settings/devices/SelectableDeviceTile.tsx b/src/components/views/settings/devices/SelectableDeviceTile.tsx new file mode 100644 index 00000000000..e232e5ff50a --- /dev/null +++ b/src/components/views/settings/devices/SelectableDeviceTile.tsx @@ -0,0 +1,42 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; + +import StyledCheckbox, { CheckboxStyle } from '../../elements/StyledCheckbox'; +import DeviceTile, { DeviceTileProps } from './DeviceTile'; + +interface Props extends DeviceTileProps { + isSelected: boolean; + onClick: () => void; +} + +const SelectableDeviceTile: React.FC = ({ children, device, isSelected, onClick }) => { + return
+ + + { children } + +
; +}; + +export default SelectableDeviceTile; diff --git a/test/components/views/elements/__snapshots__/LabelledCheckbox-test.tsx.snap b/test/components/views/elements/__snapshots__/LabelledCheckbox-test.tsx.snap index 34cdbe59be9..286c69a8d02 100644 --- a/test/components/views/elements/__snapshots__/LabelledCheckbox-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/LabelledCheckbox-test.tsx.snap @@ -34,7 +34,6 @@ exports[` should render with byline of "this is a byline" 1` className="mx_Checkbox_checkmark" />
-
@@ -90,7 +89,6 @@ exports[` should render with byline of null 1`] = ` className="mx_Checkbox_checkmark" />
-
diff --git a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx new file mode 100644 index 00000000000..77dad3e1383 --- /dev/null +++ b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx @@ -0,0 +1,85 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; + +import SelectableDeviceTile from '../../../../../src/components/views/settings/devices/SelectableDeviceTile'; + +describe('', () => { + const device = { + display_name: 'My Device', + device_id: 'my-device', + last_seen_ip: '123.456.789', + }; + const defaultProps = { + onClick: jest.fn(), + device, + children:
test
, + isSelected: false, + }; + const getComponent = (props = {}) => + (); + + it('renders unselected device tile with checkbox', () => { + const { container } = render(getComponent()); + expect(container).toMatchSnapshot(); + }); + + it('renders selected tile', () => { + const { container } = render(getComponent({ isSelected: true })); + expect(container.querySelector(`#device-tile-checkbox-${device.device_id}`)).toMatchSnapshot(); + }); + + it('calls onClick on checkbox click', () => { + const onClick = jest.fn(); + const { container } = render(getComponent({ onClick })); + + act(() => { + fireEvent.click(container.querySelector(`#device-tile-checkbox-${device.device_id}`)); + }); + + expect(onClick).toHaveBeenCalled(); + }); + + it('calls onClick on device tile info click', () => { + const onClick = jest.fn(); + const { getByText } = render(getComponent({ onClick })); + + act(() => { + fireEvent.click(getByText(device.display_name)); + }); + + expect(onClick).toHaveBeenCalled(); + }); + + it('does not call onClick when clicking device tiles actions', () => { + const onClick = jest.fn(); + const onDeviceActionClick = jest.fn(); + const children = ; + const { getByTestId } = render(getComponent({ onClick, children })); + + act(() => { + fireEvent.click(getByTestId('device-action-button')); + }); + + // action click handler called + expect(onDeviceActionClick).toHaveBeenCalled(); + // main click handler not called + expect(onClick).not.toHaveBeenCalled(); + }); +}); diff --git a/test/components/views/settings/devices/__snapshots__/SelectableDeviceTile-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/SelectableDeviceTile-test.tsx.snap new file mode 100644 index 00000000000..09b81870a1f --- /dev/null +++ b/test/components/views/settings/devices/__snapshots__/SelectableDeviceTile-test.tsx.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders selected tile 1`] = ` + +`; + +exports[` renders unselected device tile with checkbox 1`] = ` +
+
+ + +
+`; From 147ec49ff57e9f9c7736798e43e865527daf0f30 Mon Sep 17 00:00:00 2001 From: kegsay Date: Tue, 9 Aug 2022 15:22:32 +0100 Subject: [PATCH 013/107] cypress: log stdout/stderr when docker exec fails (#9154) Otherwise you cannot debug anything with errors like: ``` > Command failed: docker exec 134c9a0afd7dadd0b82ce69b4d72d3d6d8ca1b211540d4390a88357b68fa03b9 pg_isready -U postgres ``` Now we include the stdout/err prior to logging this. --- cypress/plugins/docker/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cypress/plugins/docker/index.ts b/cypress/plugins/docker/index.ts index 2f3c6464083..98f8c2584db 100644 --- a/cypress/plugins/docker/index.ts +++ b/cypress/plugins/docker/index.ts @@ -61,9 +61,14 @@ export function dockerExec(args: { childProcess.execFile("docker", [ "exec", args.containerId, ...args.params, - ], { encoding: 'utf8' }, err => { - if (err) reject(err); - else resolve(); + ], { encoding: 'utf8' }, (err, stdout, stderr) => { + if (err) { + console.log(stdout); + console.log(stderr); + reject(err); + return; + } + resolve(); }); }); } From 48ae16b5a55091ee71645fc2def6dc179e28af79 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Aug 2022 15:37:13 +0100 Subject: [PATCH 014/107] Fix pillification sometimes doubling up (#9152) * Fix pillification sometimes doubling up * Remove redundant assignment * Add unit tests around pillification * Kill ts-ignore --- src/utils/pillify.tsx | 4 +- test/utils/pillify-test.tsx | 93 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 test/utils/pillify-test.tsx diff --git a/src/utils/pillify.tsx b/src/utils/pillify.tsx index 87b95007e32..b7a1b4e5583 100644 --- a/src/utils/pillify.tsx +++ b/src/utils/pillify.tsx @@ -44,8 +44,8 @@ export function pillifyLinks(nodes: ArrayLike, mxEvent: MatrixEvent, pi while (node) { let pillified = false; - if (node.tagName === "PRE" || node.tagName === "CODE") { - // Skip code blocks + if (node.tagName === "PRE" || node.tagName === "CODE" || pills.includes(node)) { + // Skip code blocks and existing pills node = node.nextSibling as Element; continue; } else if (node.tagName === "A" && node.getAttribute("href")) { diff --git a/test/utils/pillify-test.tsx b/test/utils/pillify-test.tsx new file mode 100644 index 00000000000..1ceff1cb848 --- /dev/null +++ b/test/utils/pillify-test.tsx @@ -0,0 +1,93 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import { render } from "@testing-library/react"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { ConditionKind, EventType, PushRuleActionName, Room, TweakName } from "matrix-js-sdk/src/matrix"; + +import { pillifyLinks } from "../../src/utils/pillify"; +import { stubClient } from "../test-utils"; +import { MatrixClientPeg } from "../../src/MatrixClientPeg"; +import DMRoomMap from "../../src/utils/DMRoomMap"; + +describe("pillify", () => { + const roomId = "!room:id"; + const event = new MatrixEvent({ + room_id: roomId, + type: EventType.RoomMessage, + content: { + body: "@room", + }, + }); + + beforeEach(() => { + stubClient(); + const cli = MatrixClientPeg.get(); + (cli.getRoom as jest.Mock).mockReturnValue(new Room(roomId, cli, cli.getUserId())); + cli.pushRules.global = { + override: [ + { + rule_id: ".m.rule.roomnotif", + default: true, + enabled: true, + conditions: [{ + kind: ConditionKind.EventMatch, + key: "content.body", + pattern: "@room", + }], + actions: [ + PushRuleActionName.Notify, + { + set_tweak: TweakName.Highlight, + value: true, + }, + ], + }, + ], + }; + + DMRoomMap.makeShared(); + }); + + it("should do nothing for empty element", () => { + const { container } = render(
); + const originalHtml = container.outerHTML; + const containers: Element[] = []; + pillifyLinks([container], event, containers); + expect(containers).toHaveLength(0); + expect(container.outerHTML).toEqual(originalHtml); + }); + + it("should pillify @room", () => { + const { container } = render(
@room
); + const containers: Element[] = []; + pillifyLinks([container], event, containers); + expect(containers).toHaveLength(1); + expect(container.querySelector(".mx_Pill.mx_AtRoomPill").textContent).toBe("!@room"); + }); + + it("should not double up pillification on repeated calls", () => { + const { container } = render(
@room
); + const containers: Element[] = []; + pillifyLinks([container], event, containers); + pillifyLinks([container], event, containers); + pillifyLinks([container], event, containers); + pillifyLinks([container], event, containers); + expect(containers).toHaveLength(1); + expect(container.querySelector(".mx_Pill.mx_AtRoomPill").textContent).toBe("!@room"); + }); +}); From e63072e21fa7001aa9d8ba2162ce7c51967b791c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Aug 2022 15:37:55 +0100 Subject: [PATCH 015/107] Fixes around URL tooltips and in-app matrix.to link handling (#9139) * Add regression test for tooltipify exposing raw HTML * Handle m.to links involving children better * Comments * Fix mistaken assertion --- .../pills-click-in-app.spec.ts | 5 +++-- src/components/views/messages/TextualBody.tsx | 16 ++++++++++----- src/utils/tooltipify.tsx | 20 ++++++++----------- test/utils/tooltipify-test.tsx | 15 ++++++++++++++ 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/cypress/e2e/regression-tests/pills-click-in-app.spec.ts b/cypress/e2e/regression-tests/pills-click-in-app.spec.ts index 33c86cbf3ac..cebbc86ed8b 100644 --- a/cypress/e2e/regression-tests/pills-click-in-app.spec.ts +++ b/cypress/e2e/regression-tests/pills-click-in-app.spec.ts @@ -59,8 +59,9 @@ describe("Pills", () => { // find the pill in the timeline and click it cy.get(".mx_EventTile_body .mx_Pill").click(); + const localUrl = `/#/room/#${targetLocalpart}:`; // verify we landed at a sane place - cy.url().should("contain", `/#/room/#${targetLocalpart}:`); + cy.url().should("contain", localUrl); cy.wait(250); // let the room list settle @@ -69,7 +70,7 @@ describe("Pills", () => { cy.get(".mx_EventTile_body .mx_Pill .mx_Pill_linkText") .should("have.css", "pointer-events", "none") .click({ force: true }); // force is to ensure we bypass pointer-events - cy.url().should("contain", `https://matrix.to/#/#${targetLocalpart}:`); + cy.url().should("contain", localUrl); }); }); }); diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 459f3845580..23ba901acdf 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -432,11 +432,17 @@ export default class TextualBody extends React.Component { * to start with (e.g. pills, links in the content). */ private onBodyLinkClick = (e: MouseEvent): void => { - const target = e.target as Element; - if (target.nodeName !== "A" || target.classList.contains(linkifyOpts.className)) return; - const { href } = target as HTMLLinkElement; - const localHref = tryTransformPermalinkToLocalHref(href); - if (localHref !== href) { + let target = e.target as HTMLLinkElement; + // links processed by linkifyjs have their own handler so don't handle those here + if (target.classList.contains(linkifyOpts.className)) return; + if (target.nodeName !== "A") { + // Jump to parent as the `` may contain children, e.g. an anchor wrapping an inline code section + target = target.closest("a"); + } + if (!target) return; + + const localHref = tryTransformPermalinkToLocalHref(target.href); + if (localHref !== target.href) { // it could be converted to a localHref -> therefore handle locally e.preventDefault(); window.location.hash = localHref; diff --git a/src/utils/tooltipify.tsx b/src/utils/tooltipify.tsx index 3f7042e1575..afdcf29609b 100644 --- a/src/utils/tooltipify.tsx +++ b/src/utils/tooltipify.tsx @@ -39,9 +39,7 @@ export function tooltipifyLinks(rootNodes: ArrayLike, ignoredNodes: Ele let node = rootNodes[0]; while (node) { - let tooltipified = false; - - if (ignoredNodes.indexOf(node) >= 0) { + if (ignoredNodes.includes(node) || containers.includes(node)) { node = node.nextSibling as Element; continue; } @@ -49,20 +47,18 @@ export function tooltipifyLinks(rootNodes: ArrayLike, ignoredNodes: Ele if (node.tagName === "A" && node.getAttribute("href") && node.getAttribute("href") !== node.textContent.trim() ) { - const container = document.createElement("span"); const href = node.getAttribute("href"); + // The node's innerHTML was already sanitized before being rendered in the first place, here we are just + // wrapping the link with the LinkWithTooltip component, keeping the same children. Ideally we'd do this + // without the superfluous span but this is not something React trivially supports at this time. const tooltip = - { node.innerHTML } + ; - ReactDOM.render(tooltip, container); - node.replaceChildren(container); - containers.push(container); - tooltipified = true; - } - - if (node.childNodes?.length && !tooltipified) { + ReactDOM.render(tooltip, node); + containers.push(node); + } else if (node.childNodes?.length) { tooltipifyLinks(node.childNodes as NodeListOf, ignoredNodes, containers); } diff --git a/test/utils/tooltipify-test.tsx b/test/utils/tooltipify-test.tsx index 8e3784e7fdf..1cad2a0ea25 100644 --- a/test/utils/tooltipify-test.tsx +++ b/test/utils/tooltipify-test.tsx @@ -57,4 +57,19 @@ describe('tooltipify', () => { expect(containers).toHaveLength(0); expect(root.outerHTML).toEqual(originalHtml); }); + + it("does not re-wrap if called multiple times", () => { + const component = mount(); + const root = component.getDOMNode(); + const containers: Element[] = []; + tooltipifyLinks([root], [], containers); + tooltipifyLinks([root], [], containers); + tooltipifyLinks([root], [], containers); + tooltipifyLinks([root], [], containers); + expect(containers).toHaveLength(1); + const anchor = root.querySelector("a"); + expect(anchor?.getAttribute("href")).toEqual("/foo"); + const tooltip = anchor.querySelector(".mx_TextWithTooltip_target"); + expect(tooltip).toBeDefined(); + }); }); From 736d8dfec7300b5d77a94faaf8d42e33f8bcb087 Mon Sep 17 00:00:00 2001 From: Element Translate Bot Date: Tue, 9 Aug 2022 17:43:13 +0200 Subject: [PATCH 016/107] Translations update from Weblate (#9159) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (German) Currently translated at 97.4% (3346 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.4% (3347 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.4% (3347 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.4% (3348 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.4% (3348 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.4% (3349 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.4% (3349 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.0% (3367 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.0% (3367 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.1% (3371 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.1% (3371 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.1% (3372 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.1% (3372 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.1% (3373 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.1% (3373 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.3% (3377 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 98.3% (3377 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (Spanish) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/es/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Italian) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Galician) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ * Translated using Weblate (French) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Dutch) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/nl/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Korean) Currently translated at 35.5% (1220 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ko/ * Translated using Weblate (Korean) Currently translated at 35.5% (1220 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ko/ * Translated using Weblate (Czech) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3435 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3432 of 3435 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Czech) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3433 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Persian) Currently translated at 72.7% (2499 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fa/ * Translated using Weblate (Persian) Currently translated at 72.7% (2499 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fa/ * Translated using Weblate (French) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Galician) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ * Translated using Weblate (French) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 88.3% (3036 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Italian) Currently translated at 100.0% (3436 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Swedish) Currently translated at 99.4% (3416 of 3436 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sv/ * Translated using Weblate (Vietnamese) Currently translated at 87.7% (3015 of 3437 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/vi/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3434 of 3437 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Vietnamese) Currently translated at 89.3% (3072 of 3437 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/vi/ * Translated using Weblate (Czech) Currently translated at 100.0% (3437 of 3437 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 80.2% (2758 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/pt_BR/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Italian) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Czech) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3438 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3435 of 3438 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (French) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Italian) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Czech) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Galician) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 89.3% (3072 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 89.3% (3072 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 89.4% (3076 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Swedish) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sv/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 92.2% (3172 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 92.5% (3183 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 95.0% (3268 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Dutch) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/nl/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 96.5% (3322 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Polish) Currently translated at 61.1% (2103 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/pl/ * Translated using Weblate (Spanish) Currently translated at 100.0% (3439 of 3439 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/es/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 96.5% (3323 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3440 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3440 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3440 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Czech) Currently translated at 100.0% (3440 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3440 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Vietnamese) Currently translated at 89.7% (3086 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/vi/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3437 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (French) Currently translated at 100.0% (3440 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Albanian) Currently translated at 99.6% (3427 of 3440 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sq/ * Translated using Weblate (French) Currently translated at 99.9% (3469 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Russian) Currently translated at 96.9% (3365 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 95.9% (3331 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3470 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3470 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Czech) Currently translated at 100.0% (3470 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3470 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Icelandic) Currently translated at 87.7% (3044 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/is/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3467 of 3470 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Russian) Currently translated at 97.0% (3367 of 3471 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3471 of 3471 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Italian) Currently translated at 100.0% (3471 of 3471 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Czech) Currently translated at 100.0% (3471 of 3471 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (Russian) Currently translated at 97.0% (3371 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 95.9% (3332 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Russian) Currently translated at 97.3% (3380 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3473 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3473 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Italian) Currently translated at 100.0% (3473 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Galician) Currently translated at 100.0% (3473 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ * Translated using Weblate (French) Currently translated at 99.9% (3472 of 3473 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (German) Currently translated at 96.9% (3376 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (French) Currently translated at 99.9% (3481 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Hungarian) Currently translated at 98.6% (3436 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ * Translated using Weblate (Russian) Currently translated at 97.0% (3380 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 95.8% (3339 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3482 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3482 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3482 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Italian) Currently translated at 100.0% (3482 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (Estonian) Currently translated at 99.9% (3479 of 3482 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 96.4% (3358 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (French) Currently translated at 99.9% (3482 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Dutch) Currently translated at 100.0% (3483 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/nl/ * Translated using Weblate (Russian) Currently translated at 97.0% (3380 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3483 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3483 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Korean) Currently translated at 35.1% (1223 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ko/ * Translated using Weblate (Galician) Currently translated at 100.0% (3483 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ * Translated using Weblate (Korean) Currently translated at 38.0% (1325 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ko/ * Translated using Weblate (Italian) Currently translated at 100.0% (3483 of 3483 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ * Translated using Weblate (French) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (French) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Russian) Currently translated at 96.9% (3380 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/ru/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 96.3% (3360 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Galician) Currently translated at 100.0% (3486 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/gl/ * Translated using Weblate (Estonian) Currently translated at 99.8% (3482 of 3486 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3485 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (German) Currently translated at 96.8% (3374 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 96.8% (3375 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 96.8% (3375 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3485 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Czech) Currently translated at 99.5% (3470 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/cs/ * Translated using Weblate (German) Currently translated at 96.9% (3380 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.0% (3382 of 3485 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3487 of 3487 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3487 of 3487 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Slovak) Currently translated at 100.0% (3487 of 3487 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/sk/ * Translated using Weblate (Estonian) Currently translated at 99.8% (3483 of 3487 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3487 of 3487 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/zh_Hant/ * Translated using Weblate (German) Currently translated at 97.0% (3385 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (French) Currently translated at 100.0% (3488 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/fr/ * Translated using Weblate (Hungarian) Currently translated at 99.3% (3467 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (3488 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/uk/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (3488 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/id/ * Translated using Weblate (Estonian) Currently translated at 99.8% (3484 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/et/ * Translated using Weblate (German) Currently translated at 97.2% (3393 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (German) Currently translated at 97.2% (3393 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/de/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (3488 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (3488 of 3488 strings) Translation: Element Web/matrix-react-sdk Translate-URL: https://translate.element.io/projects/element-web/matrix-react-sdk/it/ Co-authored-by: libexus Co-authored-by: jejo86 Co-authored-by: iaiz Co-authored-by: Jeff Huang Co-authored-by: Ihor Hordiichuk Co-authored-by: random Co-authored-by: Jozef Gaal Co-authored-by: Xose M Co-authored-by: Weblate Co-authored-by: Glandos Co-authored-by: Johan Smits Co-authored-by: revblue Co-authored-by: Ryo Co-authored-by: waclaw66 Co-authored-by: Linerly Co-authored-by: Priit Jõerüüt Co-authored-by: nafi3h Co-authored-by: Hivaa Co-authored-by: Szimszon Co-authored-by: c-cal Co-authored-by: Percy Co-authored-by: LinAGKar Co-authored-by: Dinh Quang Tuyen Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com> Co-authored-by: c1bebff3 Co-authored-by: phardyle Co-authored-by: phardyle Co-authored-by: krzmaciek Co-authored-by: Besnik Bleta Co-authored-by: Nui Harime Co-authored-by: Trendyne Co-authored-by: JokerGermany Co-authored-by: notramo Co-authored-by: Figurant16 Co-authored-by: Vri Co-authored-by: Michael Weimann --- src/i18n/strings/cs.json | 4 +- src/i18n/strings/de_DE.json | 29 ++++++-- src/i18n/strings/et.json | 22 +++++- src/i18n/strings/fr.json | 23 ++++++- src/i18n/strings/gl.json | 16 ++++- src/i18n/strings/hu.json | 84 ++++++++++++++++++----- src/i18n/strings/id.json | 53 ++++++++++++++- src/i18n/strings/it.json | 20 +++++- src/i18n/strings/ko.json | 123 +++++++++++++++++++++++++++++++--- src/i18n/strings/nl.json | 47 ++++++++++++- src/i18n/strings/ru.json | 66 +++++++++--------- src/i18n/strings/sk.json | 22 +++++- src/i18n/strings/uk.json | 20 +++++- src/i18n/strings/zh_Hans.json | 71 ++++++++++++++------ src/i18n/strings/zh_Hant.json | 23 ++++++- 15 files changed, 527 insertions(+), 96 deletions(-) diff --git a/src/i18n/strings/cs.json b/src/i18n/strings/cs.json index ef39579c83a..e068de9d5c1 100644 --- a/src/i18n/strings/cs.json +++ b/src/i18n/strings/cs.json @@ -3478,5 +3478,7 @@ "It’s what you’re here for, so lets get to it": "Kvůli tomu jste tady, tak se do toho pusťte", "Find and invite your friends": "Najděte a pozvěte své přátele", "You made it!": "Zvládli jste to!", - "Help": "Nápověda" + "Help": "Nápověda", + "iOS": "iOS", + "Android": "Android" } diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index cf72b7ed8d6..4cb248cb20f 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -412,7 +412,7 @@ "Key request sent.": "Schlüsselanfrage gesendet.", "Submit debug logs": "Fehlerbericht abschicken", "Code": "Code", - "Opens the Developer Tools dialog": "Entwickler-Werkzeuge öffnen", + "Opens the Developer Tools dialog": "Öffnet die Entwicklerwerkzeuge", "You don't currently have any stickerpacks enabled": "Keine Stickerpakete aktiviert", "Stickerpack": "Stickerpaket", "Fetching third party location failed": "Das Abrufen des Drittanbieterstandorts ist fehlgeschlagen", @@ -812,7 +812,7 @@ "The user must be unbanned before they can be invited.": "Verbannte Nutzer können nicht eingeladen werden.", "Show read receipts sent by other users": "Lesebestätigungen anzeigen", "Scissors": "Schere", - "Upgrade to your own domain": "Upgrade zu deiner eigenen Domain", + "Upgrade to your own domain": "Zu deiner eigenen Domain aufwerten", "Accept all %(invitedRooms)s invites": "Akzeptiere alle %(invitedRooms)s Einladungen", "Change room avatar": "Raumbild ändern", "Change room name": "Raumname ändern", @@ -918,7 +918,7 @@ "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Diese Handlung erfordert es, auf den Standardidentitätsserver zuzugreifen, um eine E-Mail-Adresse oder Telefonnummer zu validieren, aber der Server hat keine Nutzungsbedingungen.", "Only continue if you trust the owner of the server.": "Fahre nur fort, wenn du den Betreibern des Servers vertraust.", "Trust": "Vertrauen", - "Custom (%(level)s)": "Selbstdefiniert (%(level)s)", + "Custom (%(level)s)": "Benutzerdefiniert (%(level)s)", "Sends a message as plain text, without interpreting it as markdown": "Verschickt eine Nachricht in Rohtext, ohne sie als Markdown darzustellen", "Use an identity server to invite by email. Manage in Settings.": "Mit einem Identitätsserver kannst du über E-Mail Einladungen zu verschicken. Verwalte ihn in den Einstellungen.", "%(name)s (%(userId)s)": "%(name)s (%(userId)s)", @@ -1855,7 +1855,7 @@ "Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their avatar.": "Nachrichten hier sind Ende-zu-Ende-verschlüsselt. Verifiziere %(displayName)s im deren Profil - klicke auf deren Avatar.", "Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their avatar.": "Nachrichten in diesem Raum sind Ende-zu-Ende-verschlüsselt. Wenn Personen beitreten, kannst du sie in ihrem Profil verifizieren, indem du auf deren Avatar klickst.", "Comment": "Kommentar", - "Please view existing bugs on Github first. No match? Start a new one.": "Bitte wirf einen Blick auf existierende Programmfehler auf Github. Keinen gefunden? Erstelle einen neuen.", + "Please view existing bugs on Github first. No match? Start a new one.": "Bitte wirf einen Blick auf existierende Programmfehler auf Github. Keinen passenden gefunden? Erstelle einen neuen.", "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.": "PRO TIPP: Wenn du einen Programmfehler meldest, füge bitte Debug-Logs hinzu um uns zu helfen das Problem zu finden.", "Invite by email": "Via Email einladen", "Start a conversation with someone using their name, email address or username (like ).": "Beginne eine Konversation mit jemanden unter Benutzung des Namens, der Email-Adresse oder der Matrix-ID (wie ).", @@ -3390,5 +3390,24 @@ "Failed to set direct message tag": "Fehler beim Setzen der Nachrichtenmarkierung", "Resent!": "Verschickt!", "Did not receive it? Resend it": "Nicht angekommen? Erneut senden", - "Unread email icon": "Ungelesene E-Mail Symbol" + "Unread email icon": "Ungelesene E-Mail Symbol", + "Video rooms are always-on VoIP channels embedded within a room in %(brand)s.": "Videoräume sind dauerhaft aktive VoIP-Kanäle, die in einem Raum in %(brand)s eingebettet sind.", + "A new way to chat over voice and video in %(brand)s.": "Eine neue Art in %(brand)s über Audio und Video zu kommunizieren.", + "Coworkers and teams": "Kollegen und Gruppen", + "Friends and family": "Freunde und Familie", + "iOS": "iOS", + "Android": "Android", + "You can't disable this later. The room will be encrypted but the embedded call will not.": "Dies kann später nicht deaktiviert werden. Der Raum wird verschlüsselt sein, nicht aber der eingebettete Anruf.", + "You need to have the right permissions in order to share locations in this room.": "Du brauchst du richtigen Berechtigungen, um deinen Live-Standort in diesem Raum zu teilen.", + "Who will you chat to the most?": "Mit wem wirst du am meisten chatten?", + "We're creating a room with %(names)s": "Wir erstellen einen Raum mit %(names)s", + "Messages in this chat will be end-to-end encrypted.": "Nachrichten in dieser Konversation werden Ende-zu-Ende verschlüsselt.", + "Send your first message to invite to chat": "Schreibe die erste Nachricht, um zur Konversation einzuladen", + "Your server doesn't support disabling sending read receipts.": "Dein Server unterstützt das deaktivieren von Lesebestätigungen nicht.", + "Send read receipts": "Sende Lesebestätigungen", + "Share your activity and status with others.": "Teile anderen deine Aktivität und deinen Status mit.", + "Presence": "Anwesenheit", + "Deactivating your account is a permanent action — be careful!": "Die Deaktivierung deines Kontos ist unwiderruflich - sei vorsichtig!", + "Favourite Messages (under active development)": "Favorisierte Nachrichten (in aktiver Entwicklung)", + "Use new session manager (under active development)": "Benutze neue Sitzungsverwaltung (in aktiver Entwicklung)" } diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index b96a1e1451e..f98bc3a873f 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -3474,5 +3474,25 @@ "Get stuff done by finding your teammates": "Saa tööd tehtud üheskoos oma kaasteelistega", "Find and invite your co-workers": "Leia kolleege ja saada neile kutse", "Find friends": "Leia sõpru", - "Find and invite your friends": "Leia sõpru ja saada neile kutse" + "Find and invite your friends": "Leia sõpru ja saada neile kutse", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play ja Google Play logo on Google LLC kaubamärgid.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® ja Apple logo® on Apple Inc kaubamärgid.", + "Get it on F-Droid": "Laadi alla F-Droid'ist", + "Get it on Google Play": "Laadi alla Google Play'st", + "Android": "Android", + "Download on the App Store": "Laadi alla App Store'st", + "iOS": "iOS", + "Download %(brand)s Desktop": "Laadi alla %(brand)s töölaua rakendusena", + "Download %(brand)s": "Laadi alla %(brand)s", + "We’d appreciate any feedback on how you’re finding Element.": "Meile meeldiks kui sa saadad meile oma arvamuse Element'i kohta.", + "How are you finding Element so far?": "Mis mulje sulle Element seni on jätnud?", + "Help": "Abiteave", + "Your server doesn't support disabling sending read receipts.": "Sinu koduserver ei võimalda lugemisteatiste keelamist.", + "Share your activity and status with others.": "Jaga teistega oma olekut ja tegevusi.", + "Presence": "Olek võrgus", + "Send read receipts": "Saada lugemisteatiseid", + "Last activity": "Viimased tegevused", + "Sessions": "Sessionid", + "Use new session manager (under active development)": "Uus sessioonihaldur (aktiivselt arendamisel)", + "Current session": "Praegune sessioon" } diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index c384660c359..f48c7468d26 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -3454,7 +3454,7 @@ "Only %(count)s steps to go|other": "Plus que %(count)s étapes", "Welcome to %(brand)s": "Bienvenue sur %(brand)s", "Find your people": "Trouvez vos contacts", - "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.": "Gardez le contrôle sur la discussion de votre communauté.\nPrend en charge des millions de messages, avec une interopérabilité et une modération efficaces.", + "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.": "Gardez le contrôle sur la discussion de votre communauté.\nPrend en charge des millions de messages, avec une interopérabilité et une modération efficace.", "Find your co-workers": "Trouver vos collègues", "Secure messaging for work": "Messagerie sécurisée pour le travail", "Start your first chat": "Démarrer votre première conversation", @@ -3479,5 +3479,24 @@ "You made it!": "Vous avez réussi !", "Help": "Aide", "We’d appreciate any feedback on how you’re finding Element.": "Nous apprécierions toutes vos remarques sur Element.", - "How are you finding Element so far?": "Comment trouvez-vous Element jusque-là ?" + "How are you finding Element so far?": "Comment trouvez-vous Element jusque-là ?", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play et le logo Google Play sont des marques déposées de Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® et le logo Apple® sont des marques déposées de Apple Inc.", + "Get it on F-Droid": "Récupérez-le sur F-Droid", + "Get it on Google Play": "Récupérez-le sur Google Play", + "Android": "Android", + "Download on the App Store": "Télécharger sur l’App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "Télécharger %(brand)s Desktop", + "Download %(brand)s": "Télécharger %(brand)s", + "We're creating a room with %(names)s": "Nous créons un salon avec %(names)s", + "Community ownership": "Propriété de la communauté", + "Your server doesn't support disabling sending read receipts.": "Votre serveur ne supporte pas la désactivation de l’envoi des accusés de réception.", + "Share your activity and status with others.": "Partager votre activité et votre statut avec les autres.", + "Presence": "Présence", + "Send read receipts": "Envoyer les accusés de réception", + "Last activity": "Dernière activité", + "Current session": "Cette session", + "Sessions": "Sessions", + "Use new session manager (under active development)": "Utiliser un nouveau gestionnaire de session (en cours de développement)" } diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 08d11fe7a53..1ceacdc5735 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -3480,5 +3480,19 @@ "Find friends": "Atopar amizades", "It’s what you’re here for, so lets get to it": "É a razón de que estés aquí, asi que imos", "Find and invite your friends": "Atopa e convida ás túas amizades", - "You made it!": "Conseguíchelo!" + "You made it!": "Conseguíchelo!", + "We're creating a room with %(names)s": "Estamos creando unha sala con %(names)s", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play e o logo de Google Play son marcas de Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® e o Apple logo® son marcas de Apple Inc.", + "Get it on F-Droid": "Descargar desde F-Droid", + "Get it on Google Play": "Descargar desde Google Play", + "Android": "Android", + "Download on the App Store": "Descargar na App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "Descargar %(brand)s Desktop", + "Download %(brand)s": "Descargar %(brand)s", + "Your server doesn't support disabling sending read receipts.": "O teu servidor non ten soporte para desactivar o envío de resgardos de lectura.", + "Share your activity and status with others.": "Comparte a túa actividade e estado con outras persoas.", + "Presence": "Presenza", + "Send read receipts": "Enviar resgardos de lectura" } diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index 4c7f0129f57..dec3bbc71d8 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -3418,31 +3418,85 @@ "Video rooms are always-on VoIP channels embedded within a room in %(brand)s.": "Videó szobák állandó VoIP csatornák a szobákba ágyazva itt: %(brand)s.", "A new way to chat over voice and video in %(brand)s.": "Új lehetőség hanggal és videóval csevegni itt: %(brand)s.", "Video rooms": "Videó szobák", - "Stop and close": "Megállít és kilép", + "Stop and close": "Befejezés és kilépés", "You can't disable this later. The room will be encrypted but the embedded call will not.": "Ezt később nem lehet kikapcsolni. A szoba titkosítva lesz de a hívások nem.", - "Online community members": "Online tagok a közösségekből", - "Coworkers and teams": "Munkatársak és csapatok", + "Online community members": "Online közösségek tagjai", + "Coworkers and teams": "Munkatársak és csoportok", "Friends and family": "Barátok és család", "We'll help you get connected.": "Segítünk a kapcsolatteremtésben.", "Who will you chat to the most?": "Kivel beszélget a legtöbbet?", - "You're in": "Ön belépett", - "You need to have the right permissions in order to share locations in this room.": "A helymegosztáshoz ebben a szobában megfelelő jogosultságokra van szüksége.", - "You don't have permission to share locations": "Nincs jogosultsága a helymegosztáshoz", - "Join the room to participate": "Belépés a szobába a részvételhez", + "You're in": "Itt vagy:", + "You need to have the right permissions in order to share locations in this room.": "A helymegosztáshoz ebben a szobában megfelelő jogosultságokra van szükséged.", + "You don't have permission to share locations": "Nincs jogosultságod a helymegosztáshoz", + "Join the room to participate": "Csatlakozz a szobához, hogy részt vehess", "Favourite Messages (under active development)": "Kedvenc üzenetek (aktív fejlesztés alatt)", "Reset bearing to north": "Északi irányba állítás", - "Mapbox logo": "Térkép logó", + "Mapbox logo": "Mapbox logó", "Location not available": "Földrajzi helyzet nem meghatározható", "Find my location": "Jelenlegi helyzetem megkeresése", "Exit fullscreen": "Kilépés a teljes képernyőből", "Enter fullscreen": "Teljes képernyőre váltás", "Map feedback": "Térkép visszajelzés", "Toggle attribution": "Tulajdonságok átkapcsolása", - "In %(spaceName)s and %(count)s other spaces.|one": "Itt: %(spaceName)s és %(count)s másik tér.", - "In %(spaceName)s and %(count)s other spaces.|zero": "Téren: %(spaceName)s.", - "In %(spaceName)s and %(count)s other spaces.|other": "Itt: %(spaceName)s és %(count)s másik tér.", - "In spaces %(space1Name)s and %(space2Name)s.": "A téren: %(space1Name)s és %(space2Name)s.", - "Developer command: Discards the current outbound group session and sets up new Olm sessions": "Fejlesztői parancs: Eldobja a jelenlegi kimenő csoport kapcsolatot és új Olm kapcsolatot hoz létre", - "Send your first message to invite to chat": "Az első üzeneteddel hívd meg ide őt: ", - "Messages in this chat will be end-to-end encrypted.": "Az üzenetek a beszélgetésben végponttól végpontig titkosítottak." + "In %(spaceName)s and %(count)s other spaces.|one": "Itt: %(spaceName)s és %(count)s másik térben.", + "In %(spaceName)s and %(count)s other spaces.|zero": "Ebben a térben: %(spaceName)s.", + "In %(spaceName)s and %(count)s other spaces.|other": "Itt: %(spaceName)s és %(count)s másik térben.", + "In spaces %(space1Name)s and %(space2Name)s.": "Ezekben a terekben: %(space1Name)s és %(space2Name)s.", + "Developer command: Discards the current outbound group session and sets up new Olm sessions": "Fejlesztői parancs: Eldobja a jelenlegi kimenő csoport kapcsolatot és új Olm munkamenetet hoz létre", + "Send your first message to invite to chat": "Küldj egy üzenetet ahhoz, hogy meghívd felhasználót", + "Messages in this chat will be end-to-end encrypted.": "Az üzenetek ebben a beszélgetésben végponti titkosítással vannak védve.", + "You did it!": "Kész!", + "Only %(count)s steps to go|one": "Még %(count)s lépés", + "Only %(count)s steps to go|other": "Még %(count)s lépés", + "Welcome to %(brand)s": "Üdvözlöm itt: %(brand)s", + "Find your people": "Találja meg az embereket", + "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.": "Tartsa meg a beszélgetés feletti irányítást.\nMilliók támogatása erős moderációs képességekkel és együttműködési lehetőségekkel.", + "Community ownership": "Közösség tulajdonjoga", + "Find your co-workers": "Találja meg a munkatársait", + "Secure messaging for work": "Biztonságos üzenetküldés munkához", + "Start your first chat": "Az első beszélgetés elkezdése", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "Ingyenes végpontok közötti titkosított üzenetküldés és korlátlan hang és videó hívás, %(brand)s használata jó lehetőség a kapcsolattartáshoz.", + "Secure messaging for friends and family": "Biztonságos üzenetküldés barátokkal, családdal", + "We’d appreciate any feedback on how you’re finding Element.": "Minden visszajelzésnek örülünk azzal kapcsolatban, hogy milyennek találja Elementet.", + "How are you finding Element so far?": "Eddig milyennek találja Elementet?", + "Enable notifications": "Értesítések engedélyezése", + "Don’t miss a reply or important message": "Ne maradjon le válaszról vagy fontos üzenetről", + "Turn on notifications": "Értesítések bekapcsolása", + "Your profile": "Profil", + "Make sure people know it’s really you": "Biztosítsa a többieket arról, hogy Ön valójában Ön", + "Set up your profile": "Profil beállítása", + "Download apps": "Alkalmazások letöltése", + "Don’t miss a thing by taking Element with you": "Ne maradjon le semmiről vigye magával Elementet", + "Download Element": "Element letöltése", + "Find and invite your community members": "Közösség tagjának megkeresése és meghívása", + "Find people": "Emberek megkeresése", + "Get stuff done by finding your teammates": "Fejezzen be dolgokat csoporttárs megtalálásával", + "Find and invite your co-workers": "Munkatárs keresése és meghívása", + "Find friends": "Barátok keresése", + "It’s what you’re here for, so lets get to it": "Kezdjük amiért itt van", + "Find and invite your friends": "Keresse meg és hívja meg barátait", + "You made it!": "Elkészült!", + "Use new session manager (under active development)": "Új munkamenet kezelő használata (aktív fejlesztés alatt)", + "Send read receipts": "Olvasás visszajelzés küldése", + "We're creating a room with %(names)s": "Szobát készítünk: %(names)s", + "Google Play and the Google Play logo are trademarks of Google LLC.": "A Google Play és a Google Play logó a Google LLC védjegye.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "Az App Store® és az Apple logo® az Apple Inc. védjegyei.", + "Get it on F-Droid": "Letöltés az F-Droidról", + "Get it on Google Play": "Letöltés a Google Play-ből", + "Android": "Android", + "Download on the App Store": "Letöltés az App Store-ból", + "iOS": "iOS", + "Download %(brand)s Desktop": "Asztali %(brand)s letöltése", + "Download %(brand)s": "%(brand)s eltöltése", + "Choose a locale": "Válasszon nyelvet", + "Help": "Segítség", + "Saved Items": "Mentett elemek", + "Last activity": "Utolsó tevékenység", + "Current session": "Jelenlegi munkamenet", + "Sessions": "Munkamenetek", + "Your server doesn't support disabling sending read receipts.": "A matrix szervere nem támogatja az olvasás visszajelzések elküldésének tiltását.", + "Share your activity and status with others.": "Ossza meg a tevékenységét és állapotát másokkal.", + "Presence": "Állapot", + "Spell check": "Helyesírás ellenőrzés", + "Complete these to get the most out of %(brand)s": "Ezen lépések befejezésével hozhatod ki a legtöbbet %(brand)s alkalmazásból" } diff --git a/src/i18n/strings/id.json b/src/i18n/strings/id.json index 0794a0b34d7..e4761685980 100644 --- a/src/i18n/strings/id.json +++ b/src/i18n/strings/id.json @@ -3447,5 +3447,56 @@ "Favourite Messages (under active development)": "Pesan Favorit (dalam pengembangan aktif)", "Saved Items": "Item yang Tersimpan", "Choose a locale": "Pilih locale", - "Spell check": "Pemeriksa ejaan" + "Spell check": "Pemeriksa ejaan", + "Download %(brand)s": "Unduh %(brand)s", + "Complete these to get the most out of %(brand)s": "Selesaikan untuk mendapatkan hasil yang maksimal dari %(brand)s", + "Welcome to %(brand)s": "Selamat datang di %(brand)s", + "We're creating a room with %(names)s": "Kami sedang membuat sebuah ruangan dengan %(names)s", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play dan logo Google Play adalah merek dagang dari Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® dan logo Apple® adalah merek dagang dari Apple Inc.", + "Get it on F-Droid": "Dapatkan di F-Droid", + "Get it on Google Play": "Dapatkan di Google Play", + "Android": "Android", + "Download on the App Store": "Unduh di App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "Unduh %(brand)s Desktop", + "Help": "Bantuan", + "Your server doesn't support disabling sending read receipts.": "Server Anda tidak mendukung penonaktifkan pengiriman laporan dibaca.", + "Share your activity and status with others.": "Bagikan aktivitas dan status Anda dengan orang lain.", + "Presence": "Presensi", + "You did it!": "Anda berhasil!", + "Only %(count)s steps to go|one": "Hanya %(count)s langkah lagi untuk dilalui", + "Only %(count)s steps to go|other": "Hanya %(count)s langkah lagi untuk dilalui", + "Find your people": "Temukan orang-orang Anda", + "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.": "Tetap miliki kemilikan dan kendali atas diskusi komunitas.\nBesar untuk mendukung jutaan anggota, dengan moderasi dan interoperabilitas berdaya.", + "Community ownership": "Kemilikan komunitas", + "Find your co-workers": "Temukan rekan kerja Anda", + "Secure messaging for work": "Perpesanan aman untuk berkerja", + "Start your first chat": "Mulai obrolan pertama Anda", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "Dengan perpesanan terenkripsi ujung-ke-ujung gratis, dan panggilan suara & video tidak terbatas, %(brand)s adalah cara yang baik untuk tetap terhubung.", + "Secure messaging for friends and family": "Perpesanan aman untuk teman dan keluarga", + "We’d appreciate any feedback on how you’re finding Element.": "Kami akan menghargai masukan apa pun tentang bagaimana Anda menemukan Element.", + "How are you finding Element so far?": "Bagaimana Anda menemukan Element sejauh ini?", + "Enable notifications": "Nyalakan notifikasi", + "Don’t miss a reply or important message": "Jangan lewatkan sebuah balasan atau pesan yang penting", + "Turn on notifications": "Nyalakan notifikasi", + "Your profile": "Profil Anda", + "Make sure people know it’s really you": "Pastikan orang-orang tahu bahwa itu memang Anda", + "Set up your profile": "Siapkan profil Anda", + "Download apps": "Unduh aplikasi", + "Don’t miss a thing by taking Element with you": "Jangan lewatkan apa pun dengan membawa Element dengan Anda", + "Download Element": "Unduh Element", + "Find and invite your community members": "Temukan dan undang anggota komunitas Anda", + "Find people": "Temukan orang-orang", + "Get stuff done by finding your teammates": "Selesaikan hal-hal dengan menemukan rekan setim Anda", + "Find and invite your co-workers": "Temukan dan undang rekan kerja Anda", + "Find friends": "Temukan teman-teman", + "It’s what you’re here for, so lets get to it": "Untuk itulah Anda di sini, jadi mari kita lakukan", + "Find and invite your friends": "Temukan dan undang teman Anda", + "You made it!": "Anda berhasil!", + "Send read receipts": "Kirim laporan dibaca", + "Last activity": "Aktivitas terakhir", + "Sessions": "Sesi", + "Use new session manager (under active development)": "Gunakan pengelola sesi baru (dalam pengembangan aktif)", + "Current session": "Sesi saat ini" } diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index 079415783d0..5ffaf3df34c 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -3480,5 +3480,23 @@ "Find and invite your friends": "Trova e invita i tuoi amici", "You made it!": "Ce l'hai fatta!", "We’d appreciate any feedback on how you’re finding Element.": "Ci piacerebbe avere una tua opinione riguardo Element.", - "How are you finding Element so far?": "Come ti sta sembrando Element?" + "How are you finding Element so far?": "Come ti sta sembrando Element?", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play e il logo Google Play sono marchi registrati di Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® e il logo Apple® sono marchi registrati di Apple Inc.", + "Get it on F-Droid": "Ottienilo su F-Droid", + "Get it on Google Play": "Ottienilo su Google Play", + "Android": "Android", + "Download on the App Store": "Scarica dall'App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "Scarica %(brand)s Desktop", + "Download %(brand)s": "Scarica %(brand)s", + "We're creating a room with %(names)s": "Stiamo creando una stanza con %(names)s", + "Last activity": "Ultima attività", + "Current session": "Sessione attuale", + "Sessions": "Sessioni", + "Your server doesn't support disabling sending read receipts.": "Il tuo server non supporta la disattivazione delle conferme di lettura.", + "Share your activity and status with others.": "Condividi la tua attività e lo stato con gli altri.", + "Presence": "Presenza", + "Use new session manager (under active development)": "Usa il nuovo gestore di sessioni (in sviluppo attivo)", + "Send read receipts": "Invia le conferme di lettura" } diff --git a/src/i18n/strings/ko.json b/src/i18n/strings/ko.json index 5056fb30b13..042036debb2 100644 --- a/src/i18n/strings/ko.json +++ b/src/i18n/strings/ko.json @@ -429,17 +429,17 @@ "Jump to read receipt": "읽은 기록으로 건너뛰기", "Share room": "방 공유하기", "Members only (since they joined)": "구성원만(구성원들이 참여한 시점부터)", - "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s이 참가했습니다", - "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s이 %(count)s번 참가했습니다", - "%(oneUser)sjoined %(count)s times|other": "%(oneUser)s님이 %(count)s번 참가했습니다", - "%(oneUser)sjoined %(count)s times|one": "%(oneUser)s님이 참가했습니다", + "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s이 참여했습니다", + "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s이 %(count)s번 참여했습니다", + "%(oneUser)sjoined %(count)s times|other": "%(oneUser)s님이 %(count)s번 참여했습니다", + "%(oneUser)sjoined %(count)s times|one": "%(oneUser)s님이 참여했습니다", "%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s이 %(count)s번 참가하다가 떠났습니다", "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s이 참가하다가 떠났습니다", "%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s님이 %(count)s번 참가하다가 떠났습니다", "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s님이 참가하다가 떠났습니다", - "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s이 떠나고 다시 참가했습니다", - "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s님이 %(count)s번 떠나고 다시 참가했습니다", - "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s님이 떠나고 다시 참가했습니다", + "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s이 떠나고 다시 참여했습니다", + "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s님이 %(count)s번 떠나고 다시 참여했습니다", + "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s님이 떠나고 다시 참여했습니다", "%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s이 %(count)s번 떠났습니다", "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s이 떠났습니다", "%(oneUser)sleft %(count)s times|other": "%(oneUser)s님이 %(count)s번 떠났습니다", @@ -511,7 +511,7 @@ "This room is a continuation of another conversation.": "이 방은 다른 대화방의 연장선입니다.", "Click here to see older messages.": "여길 눌러 오래된 메시지를 보세요.", "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", - "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s이 %(count)s번 떠나고 다시 참가했습니다", + "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s이 %(count)s번 떠나고 다시 참여했습니다", "In reply to ": "관련 대화 ", "Updating %(brand)s": "%(brand)s 업데이트 중", "Upgrade this room to version %(version)s": "이 방을 %(version)s 버전으로 업그레이드", @@ -1219,5 +1219,110 @@ "Pin to sidebar": "사이드바 고정", "Developer tools": "개발자 도구", "All settings": "전체 설정", - "Use Single Sign On to continue": "SSO로 계속하기" + "Use Single Sign On to continue": "SSO로 계속하기", + "Join public room": "공개 방 참가하기", + "New room": "새로운 방 만들기", + "Start new chat": "새로운 대화 시작하기", + "Room options": "방 옵션", + "Mentions & Keywords": "멘션 및 키워드", + "Mentions & keywords": "멘션 및 키워드", + "Use default": "기본 설정 사용", + "Results not as expected? Please give feedback.": "예상한 결과가 아닌가요? 피드백 부탁드립니다.", + "Get notified only with mentions and keywords as set up in your settings": "설정에서 지정한 멘션과 키워드인 경우에만 알림을 받습니다", + "Get notifications as set up in your settings": "설정에서 지정한 알림만 받습니다", + "Get notified for every message": "모든 메세지 알림을 받습니다", + "You won't get any notifications": "어떤 알람도 받지 않습니다", + "Public": "공개", + "Space members": "스페이스 멤버 목록", + "Private room (invite only)": "비공개 방 (초대 필요)", + "Private (invite only)": "비공개 (초대 필요)", + "Never send encrypted messages to unverified sessions in this room from this session": "이 채팅방의 현재 세션에서 확인되지 않은 세션으로 암호화된 메시지를 보내지 않음", + "Decide who can view and join %(spaceName)s.": "누가 %(spaceName)s를 보거나 참여할 수 있는지 결정합니다.", + "Accessibility": "접근성", + "Access": "접근", + "Recommended for public spaces.": "공개 스페이스에 권장 합니다.", + "Allow people to preview your space before they join.": "스페이스에 참여하기 전에 미리볼 수 있도록 허용합니다.", + "Preview Space": "스페이스 미리보기", + "Message Previews": "메세지 미리보기", + "Anyone can find and join.": "누구나 찾고 참여할 수 있습니다.", + "Anyone in can find and join. You can select other spaces too.": "에 소속된 누구나 찾고 참여할 수 있습니다. 다른 스페이스도 선택 가능합니다.", + "Only invited people can join.": "초대한 경우에만 참여할 수 있습니다.", + "Visibility": "가시성", + "Explore Public Rooms": "공개 방 살펴보기", + "Manage & explore rooms": "관리 및 방 목록 보기", + "Space home": "스페이스 홈", + "Clear": "지우기", + "Clear notifications": "알림 지우기", + "Search for": "검색 기준", + "Search for rooms or people": "방 또는 사람 검색", + "Search for rooms": "방 검색", + "Search for spaces": "스페이스 검색", + "Recently Direct Messaged": "최근 다이렉트 메세지", + "No recently visited rooms": "최근에 방문하지 않은 방 목록", + "Recently visited rooms": "최근 방문한 방 목록", + "Recently viewed": "최근에 확인한", + "%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s님이 표시이름을 제거했습니다 (%(oldDisplayName)s)", + "%(senderName)s changed their profile picture": "%(senderName)s님이 프로필 사진을 변경했습니다", + "%(senderName)s invited %(targetName)s": "%(senderName)s님이 %(targetName)s님을 초대했습니다", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s님이 방 이름을 %(oldRoomName)s에서 %(newRoomName)s(으)로 변경했습니다.", + "%(oldDisplayName)s changed their display name to %(displayName)s": "%(oldDisplayName)s님이 표시이름을 %(displayName)s(으)로 변경했습니다", + "%(targetName)s left the room": "%(targetName)s님이 방을 떠났습니다", + "%(targetName)s joined the room": "%(targetName)s님이 방에 참여했습니다", + "%(senderName)s set a profile picture": "%(senderName)s님이 프로필 사진을 설정했습니다", + "Scroll to most recent messages": "가장 최근 메세지로 스크롤", + "Room Info": "방 정보", + "If you can't find the room you're looking for, ask for an invite or create a new room.": "만약 찾고 있는 방이 없다면, 초대를 요청하거나 새로운 방을 만드세요.", + "If you can't find the room you're looking for, ask for an invite or create a new room.": "만약 찾고 있는 방이 없다면, 초대를 요청하거나 새로운 방을 만드세요.", + "Unable to copy a link to the room to the clipboard.": "방 링크를 클립보드에 복사할 수 없습니다.", + "Unable to copy room link": "방 링크를 복사할 수 없습니다", + "Copy room link": "방 링크 복사", + "Share your public space": "당신의 공개 스페이스 공유하기", + "Your public space": "당신의 공개 스페이스", + "Public space": "공개 스페이스", + "Private space (invite only)": "비공개 스페이스 (초대 필요)", + "Your private space": "당신의 비공개 스페이스", + "Private space": "비공개 스페이스", + "Rooms and spaces": "방 및 스페이스 목록", + "Sidebar": "사이드바", + "Keyboard": "키보드 (단축키)", + "Send feedback": "피드백 보내기", + "Feedback sent": "피드백 보내기", + "Feedback": "피드백", + "Show all rooms in Home": "모든 방을 홈에서 보기", + "Leave all rooms": "모든 방에서 떠나기", + "Show all rooms": "모든 방 목록 보기", + "All rooms": "모든 방 목록", + "Expand": "펼치기", + "Create a new space": "새로운 스페이스 만들기", + "Create a space": "스페이스 만들기", + "Export Chat": "대화 내보내기", + "Export chat": "대화 내보내기", + "Room settings": "방 설정", + "Hide Widgets": "위젯 숨기기", + "Widgets": "위젯", + "Nothing pinned, yet": "아직 고정된 것이 없습니다", + "Pinned messages": "고정된 메세지", + "Pinned": "고정됨", + "Files": "파일 목록", + "Poll": "투표", + "Send voice message": "음성 메세지 보내기", + "Voice Message": "음성 메세지", + "Sticker": "스티커", + "View source": "소스 보기", + "Report a bug": "버그 보고", + "Report": "보고", + "Forward message": "전달 메세지", + "Forward": "전달", + "about a day ago": "약 1일 전", + "%(num)s days ago": "%(num)s일 전", + "about an hour ago": "약 1 시간 전", + "%(num)s minutes ago": "%(num)s분 전", + "Or send invite link": "또는 초대 링크 보내기", + "Suggestions": "추천 목록", + "Recent Conversations": "최근 대화 목록", + "Start a conversation with someone using their name or username (like ).": "이름이나 사용자명( 형식)을 사용하는 사람들과 대화를 시작하세요.", + "Direct Messages": "다이렉트 메세지", + "Explore public rooms": "공개 방 목록 살펴보기", + "Show %(count)s more|other": "%(count)s개 더 보기", + "People": "사람들" } diff --git a/src/i18n/strings/nl.json b/src/i18n/strings/nl.json index e85cf626e2f..ac3b344ef5d 100644 --- a/src/i18n/strings/nl.json +++ b/src/i18n/strings/nl.json @@ -3445,5 +3445,50 @@ "Messages in this chat will be end-to-end encrypted.": "Berichten in deze chat worden eind-tot-eind versleuteld.", "Saved Items": "Opgeslagen items", "Send your first message to invite to chat": "Stuur uw eerste bericht om uit te nodigen om te chatten", - "Favourite Messages (under active development)": "Favoriete berichten (in actieve ontwikkeling)" + "Favourite Messages (under active development)": "Favoriete berichten (in actieve ontwikkeling)", + "We're creating a room with %(names)s": "We maken een kamer aan met %(names)s", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play en het Google Play-logo zijn handelsmerken van Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® en het Apple logo® zijn handelsmerken van Apple Inc.", + "Get it on F-Droid": "Download het op F-Droid", + "Get it on Google Play": "Verkrijg het via Google Play", + "Android": "Android", + "Download on the App Store": "Te downloaden in de App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "%(brand)s Desktop downloaden", + "Download %(brand)s": "%(brand)s downloaden", + "Choose a locale": "Kies een landinstelling", + "Help": "Help", + "Spell check": "Spellingscontrole", + "Complete these to get the most out of %(brand)s": "Voltooi deze om het meeste uit %(brand)s te halen", + "You did it!": "Het is u gelukt!", + "Only %(count)s steps to go|one": "Nog maar %(count)s stap te gaan", + "Only %(count)s steps to go|other": "Nog maar %(count)s stappen te gaan", + "Welcome to %(brand)s": "Welkom bij %(brand)s", + "Find your people": "Vind uw mensen", + "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.": "Houd het eigendom en de controle over de discussie in de gemeenschap.\nSchaal om miljoenen te ondersteunen, met krachtige beheersbaarheid en interoperabiliteit.", + "Community ownership": "Gemeenschapseigendom", + "Find your co-workers": "Vind uw collega's", + "Secure messaging for work": "Veilig berichten versturen voor werk", + "Start your first chat": "Start uw eerste chat", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "Met gratis eind-tot-eind versleutelde berichten en onbeperkte spraak- en video-oproepen, is %(brand)s een geweldige manier om in contact te blijven.", + "Secure messaging for friends and family": "Veilig berichten versturen voor vrienden en familie", + "We’d appreciate any feedback on how you’re finding Element.": "We stellen het op prijs als u feedback geeft over hoe u Element vindt.", + "How are you finding Element so far?": "Hoe vind u Element tot nu toe?", + "Enable notifications": "Meldingen inschakelen", + "Don’t miss a reply or important message": "Mis geen antwoord of belangrijk bericht", + "Turn on notifications": "Meldingen aanzetten", + "Your profile": "Uw profiel", + "Make sure people know it’s really you": "Zorg ervoor dat mensen weten dat u het echt bent", + "Set up your profile": "Stel uw profiel in", + "Download apps": "Apps downloaden", + "Don’t miss a thing by taking Element with you": "Mis niets door Element mee te nemen", + "Download Element": "Element downloaden", + "Find and invite your community members": "Vind en nodig uw communityleden uit", + "Find people": "Zoek mensen", + "Get stuff done by finding your teammates": "Krijg dingen gedaan door uw teamgenoten te vinden", + "Find and invite your co-workers": "Vind en nodig uw collega's uit", + "Find friends": "Zoek vrienden", + "It’s what you’re here for, so lets get to it": "Daar bent u voor, dus laten we beginnen", + "Find and invite your friends": "Zoek uw vrienden en nodig ze uit", + "You made it!": "Het is u gelukt!" } diff --git a/src/i18n/strings/ru.json b/src/i18n/strings/ru.json index 781b22ea5a4..7c2d4ffb17b 100644 --- a/src/i18n/strings/ru.json +++ b/src/i18n/strings/ru.json @@ -328,22 +328,22 @@ "Members only (since they joined)": "Только участники (с момента их входа)", "A text message has been sent to %(msisdn)s": "Текстовое сообщение отправлено на %(msisdn)s", "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", - "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sвошли %(count)s раз(а)", - "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)sвошли", - "%(oneUser)sjoined %(count)s times|other": "%(oneUser)sвошёл(-ла) %(count)s раз(а)", - "%(oneUser)sjoined %(count)s times|one": "%(oneUser)sвошёл(-ла)", - "%(severalUsers)sleft %(count)s times|other": "%(severalUsers)sвышли %(count)s раз(а)", - "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)sвышли", - "%(oneUser)sleft %(count)s times|other": "%(oneUser)sвышел(-ла) %(count)s раз(а)", - "%(oneUser)sleft %(count)s times|one": "%(oneUser)sвышел(-ла)", - "%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)sвошли и вышли %(count)s раз(а)", - "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)sвошли и вышли", - "%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)sвошёл(-ла) и вышел(-ла) %(count)s раз(а)", - "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)sвошёл(-ла) и вышел(-ла)", - "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)sвышли и снова вошли %(count)s раз(а)", - "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)sвышли и снова вошли", - "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)sвышел(-ла) и снова вошёл(-ла) %(count)s раз(а)", - "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)sвышел(-ла) и снова вошёл(-ла)", + "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s присоединились %(count)s раз(а)", + "%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s присоединились", + "%(oneUser)sjoined %(count)s times|other": "%(oneUser)s присоединился(лась) %(count)s раз(а)", + "%(oneUser)sjoined %(count)s times|one": "%(oneUser)s присоединился(лась)", + "%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s покинули %(count)s раз(а)", + "%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s покинули", + "%(oneUser)sleft %(count)s times|other": "%(oneUser)s покинул(а) %(count)s раз(а)", + "%(oneUser)sleft %(count)s times|one": "%(oneUser)s покинул(а)", + "%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s присоединились и покинули %(count)s раз(а)", + "%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s присоединились и покинули", + "%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s присоединился(лась) и покинул(а) %(count)s раз(а)", + "%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s присоединился(лась) и покинул(а)", + "%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s покинули и снова присоединились %(count)s раз(а)", + "%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s покинули и снова присоединились", + "%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s покинул(а) и снова присоединился(лась) %(count)s раз(а)", + "%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s покинул(а) и снова присоединился(лась)", "were invited %(count)s times|other": "приглашены %(count)s раз(а)", "were invited %(count)s times|one": "приглашены", "was invited %(count)s times|other": "приглашен(а) %(count)s раз(а)", @@ -526,10 +526,10 @@ "Put a link back to the old room at the start of the new room so people can see old messages": "Разместим ссылку на старую комнату, чтобы люди могли видеть старые сообщения", "Please contact your service administrator to continue using this service.": "Пожалуйста, обратитесь к вашему администратору, чтобы продолжить использовать этот сервис.", "Unable to load! Check your network connectivity and try again.": "Не удалось загрузить! Проверьте подключение к сети и попробуйте снова.", - "Upgrades a room to a new version": "Модернизирует комнату до новой версии", + "Upgrades a room to a new version": "Обновляет комнату до новой версии", "Sets the room name": "Устанавливает название комнаты", "Forces the current outbound group session in an encrypted room to be discarded": "Принудительно отбрасывает текущую групповую сессию для отправки сообщений в зашифрованную комнату", - "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s модернизировал эту комнату.", + "%(senderDisplayName)s upgraded this room.": "%(senderDisplayName)s обновил(а) эту комнату.", "%(senderName)s set the main address for this room to %(address)s.": "%(senderName)s установил(а) %(address)s в качестве главного адреса комнаты.", "%(senderName)s removed the main address for this room.": "%(senderName)s удалил главный адрес комнаты.", "%(displayName)s is typing …": "%(displayName)s печатает…", @@ -1472,7 +1472,7 @@ "Ok": "Хорошо", "New login. Was this you?": "Новый вход в вашу учётную запись. Это были Вы?", "You joined the call": "Вы присоединились к звонку", - "%(senderName)s joined the call": "%(senderName)s присоединился(-ась) к звонку", + "%(senderName)s joined the call": "%(senderName)s присоединился(лась) к звонку", "Call in progress": "Звонок в процессе", "Call ended": "Звонок завершён", "You started a call": "Вы начали звонок", @@ -1594,8 +1594,8 @@ "Confirm by comparing the following with the User Settings in your other session:": "Подтвердите, сравнив следующие параметры с настройками пользователя в другой вашей сессии:", "Confirm this user's session by comparing the following with their User Settings:": "Подтвердите сессию этого пользователя, сравнив следующие параметры с его пользовательскими настройками:", "If they don't match, the security of your communication may be compromised.": "Если они не совпадают, безопасность вашего общения может быть поставлена под угрозу.", - "Upgrade private room": "Модернизировать приватную комнату", - "Upgrade public room": "Модернизировать публичную комнату", + "Upgrade private room": "Обновить приватную комнату", + "Upgrade public room": "Обновить публичную комнату", "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Модернизация комнаты - это расширенное действие, которое обычно рекомендуется, когда комната нестабильна из-за ошибок, отсутствующих функций или уязвимостей безопасности.", "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "Обычно это влияет только на то, как комната обрабатывается на сервере. Если у вас возникли проблемы с вашим %(brand)s, пожалуйста, сообщите об ошибке.", "You'll upgrade this room from to .": "Вы модернизируете эту комнату с до .", @@ -1792,7 +1792,7 @@ "Add a topic to help people know what it is about.": "Добавьте тему, чтобы люди знали, о чём комната.", "Topic: %(topic)s ": "Тема: %(topic)s ", "Topic: %(topic)s (edit)": "Тема: %(topic)s (изменить)", - "This is the beginning of your direct message history with .": "Это начало вашей истории прямых сообщений с .", + "This is the beginning of your direct message history with .": "Это начало вашей переписки с .", "Only the two of you are in this conversation, unless either of you invites anyone to join.": "В этом разговоре только вы двое, если только кто-нибудь из вас не пригласит кого-нибудь присоединиться.", "Takes the call in the current room off hold": "Прекратить удержание вызова в текущей комнате", "Places the call in the current room on hold": "Перевести вызов в текущей комнате на удержание", @@ -2342,7 +2342,7 @@ "A private space to organise your rooms": "Приватное пространство для организации ваших комнат", "Just me": "Только я", "Make sure the right people have access to %(name)s": "Убедитесь, что правильные люди имеют доступ к %(name)s", - "Who are you working with?": "С кем ты работаешь?", + "Who are you working with?": "С кем вы работаете?", "Go to my first room": "Перейти в мою первую комнату", "It's just you at the moment, it will be even better with others.": "Сейчас здесь только ты, с другими будет ещё лучше.", "Share %(name)s": "Поделиться %(name)s", @@ -2513,7 +2513,7 @@ "Zoom in": "Увеличить", "Zoom out": "Уменьшить", "%(count)s people you know have already joined|one": "%(count)s человек, которого вы знаете, уже присоединился", - "%(count)s people you know have already joined|other": "%(count)s человек, которых вы знаете, уже присоединились", + "%(count)s people you know have already joined|other": "%(count)s человек(а), которых вы знаете, уже присоединились", "Including %(commaSeparatedMembers)s": "Включая %(commaSeparatedMembers)s", "View all %(count)s members|one": "Посмотреть 1 участника", "View all %(count)s members|other": "Просмотреть всех %(count)s участников", @@ -2613,7 +2613,7 @@ "Give feedback.": "Оставить отзыв.", "Thank you for trying Spaces. Your feedback will help inform the next versions.": "Спасибо, что попробовали пространства. Ваши отзывы помогут при разработке следующих версий.", "Spaces feedback": "Отзыв о пространствах", - "Spaces are a new feature.": "Пространства - это новая функция.", + "Spaces are a new feature.": "Пространства — это новая функция.", "Please enter a name for the space": "Пожалуйста, введите название пространства", "Your camera is still enabled": "Ваша камера всё ещё включена", "Your camera is turned off": "Ваша камера выключена", @@ -2650,7 +2650,7 @@ "%(targetName)s left the room": "%(targetName)s покинул(а) комнату", "%(targetName)s left the room: %(reason)s": "%(targetName)s покинул(а) комнату: %(reason)s", "%(targetName)s rejected the invitation": "%(targetName)s отклонил(а) приглашение", - "%(targetName)s joined the room": "%(targetName)s вошёл(-ла) в комнату", + "%(targetName)s joined the room": "%(targetName)s присоединился(лась) к комнате", "%(senderName)s made no change": "%(senderName)s не сделал(а) изменений", "%(senderName)s set a profile picture": "%(senderName)s установил(а) аватар", "%(senderName)s changed their profile picture": "%(senderName)s изменил(а) аватар", @@ -3206,7 +3206,7 @@ "Can I use text chat alongside the video call?": "Можно ли использовать текстовый чат одновременно с видеозвонком?", "Use the “+” button in the room section of the left panel.": "Используйте кнопку \"+\" в разделе комнат на левой панели.", "How can I create a video room?": "Как создать видеокомнату?", - "A new way to chat over voice and video in %(brand)s.": "Новый способ голосового и видео общения в %(brand)s.", + "A new way to chat over voice and video in %(brand)s.": "Новый способ голосового и видеообщения в %(brand)s.", "Video rooms": "Видеокомнаты", "Connection lost": "Соединение потеряно", "The person who invited you has already left, or their server is offline.": "Пригласивший вас человек уже ушёл, или его сервер не подключён к сети.", @@ -3247,8 +3247,8 @@ "Video devices": "Видеоустройства", "Mute microphone": "Отключить микрофон", "Audio devices": "Аудиоустройства", - "%(count)s people joined|one": "%(count)s присоединившихся", - "%(count)s people joined|other": "%(count)s присоединившихся", + "%(count)s people joined|one": "%(count)s человек присоединился", + "%(count)s people joined|other": "%(count)s человек(а) присоединились", "sends hearts": "отправляет сердечки", "Enable hardware acceleration": "Включить аппаратное ускорение", "Remove from space": "Исключить из пространства", @@ -3264,7 +3264,7 @@ "Signing out your devices will delete the message encryption keys stored on them, making encrypted chat history unreadable.": "При выходе из устройств удаляются хранящиеся на них ключи шифрования сообщений, что сделает зашифрованную историю чатов нечитаемой.", "Resetting your password on this homeserver will cause all of your devices to be signed out. This will delete the message encryption keys stored on them, making encrypted chat history unreadable.": "Сброс пароля на этом домашнем сервере приведет к тому, что все ваши устройства будут отключены. Это приведет к удалению хранящихся на них ключей шифрования сообщений, что сделает зашифрованную историю чата нечитаемой.", "Event ID: %(eventId)s": "ID события: %(eventId)s", - "Threads are a beta feature": "Ветки - бета-функция", + "Threads are a beta feature": "Обсуждения — бета-функция", "Your message wasn't sent because this homeserver has been blocked by its administrator. Please contact your service administrator to continue using the service.": "Ваше сообщение не отправлено, поскольку домашний сервер заблокирован его администратором. Обратитесь к администратору службы, чтобы продолжить её использование.", "Resent!": "Отправлено повторно!", "Did not receive it? Resend it": "Не получили? Отправить его повторно", @@ -3372,8 +3372,8 @@ "%(brand)s was denied permission to fetch your location. Please allow location access in your browser settings.": "%(brand)s не получил доступа к вашему местонахождению. Разрешите доступ к местоположению в настройках браузера.", "Share for %(duration)s": "Поделиться на %(duration)s", "Live location sharing": "Отправка местонахождения в реальном времени", - "Beta feature. Click to learn more.": "Бетафункция. Нажмите, чтобы узнать больше.", - "Beta feature": "Бетафункция", + "Beta feature. Click to learn more.": "Бета-функция. Нажмите, чтобы узнать больше.", + "Beta feature": "Бета-функция", "Ban from room": "Заблокировать в комнате", "Unban from room": "Разблокировать в комнате", "Ban from space": "Заблокировать в пространстве", @@ -3403,7 +3403,7 @@ "If you've submitted a bug via GitHub, debug logs can help us track down the problem. ": "Если вы отправили ошибку через GitHub, журналы отладки могут помочь нам отследить проблему. ", "Right-click message context menu": "Правая кнопка мыши – контекстное меню сообщения", "Show HTML representation of room topics": "Показать HTML-представление тем комнаты", - "Video rooms are always-on VoIP channels embedded within a room in %(brand)s.": "Видеокомнаты - это постоянные VoIP-каналы, встроенные в комнату в %(brand)s.", + "Video rooms are always-on VoIP channels embedded within a room in %(brand)s.": "Видеокомнаты — это постоянные VoIP-каналы, встроенные в комнату в %(brand)s.", "You're trying to access a community link (%(groupId)s).
Communities are no longer supported and have been replaced by spaces.Learn more about spaces here.": "Вы пытаетесь получить доступ к ссылке на сообщество (%(groupId)s).
Сообщества больше не поддерживаются и их заменили пространствами.Узнайте больше о пространствах здесь.", "Enable live location sharing": "Включить функцию \"Поделиться трансляцией местоположения\"", "Live location ended": "Трансляция местоположения завершена", diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index 5304644a25d..05285a15234 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -3477,5 +3477,25 @@ "Find friends": "Nájsť priateľov", "It’s what you’re here for, so lets get to it": "Kvôli tomu ste tu, tak sa do toho pustite", "Find and invite your friends": "Nájdite a pozvite svojich priateľov", - "You made it!": "Zvládli ste to!" + "You made it!": "Zvládli ste to!", + "We're creating a room with %(names)s": "Vytvárame miestnosť s %(names)s", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play a logo Google Play sú ochranné známky spoločnosti Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® a logo Apple® sú ochranné známky spoločnosti Apple Inc.", + "Get it on F-Droid": "Získajte ho v službe F-Droid", + "Get it on Google Play": "Získajte ho v službe Google Play", + "Android": "Android", + "Download on the App Store": "Stiahnuť v obchode App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "Stiahnuť %(brand)s Desktop", + "Download %(brand)s": "Stiahnuť %(brand)s", + "Help": "Pomocník", + "Your server doesn't support disabling sending read receipts.": "Váš server nepodporuje vypnutie odosielania potvrdení o prečítaní.", + "Share your activity and status with others.": "Zdieľajte svoju aktivitu a stav s ostatnými.", + "Presence": "Prítomnosť", + "We’d appreciate any feedback on how you’re finding Element.": "Budeme vďační za akúkoľvek spätnú väzbu o tom, ako sa vám Element osvedčil.", + "How are you finding Element so far?": "Ako sa vám zatiaľ páči Element?", + "Send read receipts": "Odosielať potvrdenia o prečítaní", + "Last activity": "Posledná aktivita", + "Sessions": "Relácie", + "Use new session manager (under active development)": "Použiť nového správcu relácií (v štádiu aktívneho vývoja)" } diff --git a/src/i18n/strings/uk.json b/src/i18n/strings/uk.json index dd087f8506e..d86168051bc 100644 --- a/src/i18n/strings/uk.json +++ b/src/i18n/strings/uk.json @@ -3480,5 +3480,23 @@ "You made it!": "Ви це зробили!", "Help": "Довідка", "We’d appreciate any feedback on how you’re finding Element.": "Ми будемо вдячні за ваш відгук про Element.", - "How are you finding Element so far?": "Як вам Element?" + "How are you finding Element so far?": "Як вам Element?", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play і логотип Google Play є товарними знаками Google LLC.", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® і логотип Apple® є товарними знаками Apple Inc.", + "Get it on F-Droid": "Отримати з F-Droid", + "Get it on Google Play": "Отримати з Google Play", + "Android": "Android", + "Download on the App Store": "Завантажити з App Store", + "iOS": "iOS", + "Download %(brand)s Desktop": "Завантажити %(brand)s для компʼютера", + "Download %(brand)s": "Завантажити %(brand)s", + "We're creating a room with %(names)s": "Ми створюємо кімнату з %(names)s", + "Your server doesn't support disabling sending read receipts.": "Ваш сервер не підтримує вимкнення надсилання сповіщень про прочитання.", + "Share your activity and status with others.": "Діліться своєю активністю та станом з іншими.", + "Presence": "Присутність", + "Send read receipts": "Надсилати підтвердження прочитання", + "Last activity": "Остання активність", + "Sessions": "Сеанси", + "Use new session manager (under active development)": "Використовувати новий менеджер сеансів (в активній розробці)", + "Current session": "Поточний сеанс" } diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index ddc03caf37a..50f04c4800f 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -493,7 +493,7 @@ "Muted Users": "被禁言的用户", "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.": "在加密的房间中,比如这个,默认禁用URL预览,以确保主服务器(生成预览的地方)无法获知你在此房间中看到的链接的有关的信息。", "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.": "当有人发送一条带有链接的消息后,可显示链接的预览,链接预览可包含此链接的网页标题、描述以及图片。", - "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "你确定要移除(删除)此事件吗?注意,如果删除了房间名称或话题的修改事件,就会撤销此更改。", + "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "你确定要移除(删除)此事件吗?注意,如果删除房间名称或话题的更改,更改会被撤销。", "Clear Storage and Sign Out": "清除数据并退出登录", "Send Logs": "发送日志", "Refresh": "刷新", @@ -756,7 +756,7 @@ "Unable to load commit detail: %(msg)s": "无法加载提交详情:%(msg)s", "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this": "为避免丢失聊天记录,你必须在登出前导出房间密钥。你需要切换至新版 %(brand)s 方可继续执行此操作", "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "验证此用户并将其标记为已信任。在收发端到端加密消息时,信任用户可让你更加放心。", - "Waiting for partner to confirm...": "等待对方确认中...", + "Waiting for partner to confirm...": "等待对方确认中……", "Incoming Verification Request": "收到验证请求", "You've previously used %(brand)s on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, %(brand)s needs to resync your account.": "你之前在 %(host)s 上开启了 %(brand)s 的成员列表延迟加载设置。目前版本中延迟加载功能已被停用。因为本地缓存在这两个设置项上不相容,%(brand)s 需要重新同步你的账户。", "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "通过仅在需要时加载其他用户的信息,%(brand)s 现在使用的内存减少到了原来的三分之一至五分之一。 请等待与服务器重新同步!", @@ -794,7 +794,7 @@ "Create account": "创建账户", "Registration has been disabled on this homeserver.": "此主服务器已禁止注册。", "Unable to query for supported registration methods.": "无法查询支持的注册方法。", - "Keep going...": "请继续...", + "Keep going...": "请继续……", "For maximum security, this should be different from your account password.": "为确保最大的安全性,它应该与你的账户密码不同。", "That matches!": "匹配成功!", "That doesn't match.": "不匹配。", @@ -835,8 +835,8 @@ "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.": "房间加密一经启用,便无法禁用。在加密房间中,发送的消息无法被服务器看到,只能被房间的参与者看到。启用加密可能会使许多机器人和桥接无法正常运作。 详细了解加密。", "Power level": "权力级别", "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "警告:升级房间 不会自动将房间成员转移到新版房间中。 我们将会在旧版房间中发布一个新版房间的链接——房间成员必须点击此链接以加入新房间。", - "Adds a custom widget by URL to the room": "通过链接为房间添加自定义挂件", - "Please supply a https:// or http:// widget URL": "请提供一个 https:// 或 http:// 形式的插件", + "Adds a custom widget by URL to the room": "通过URL添加自定义挂件到房间", + "Please supply a https:// or http:// widget URL": "请提供一个 https:// 或 http:// 挂件URL", "You cannot modify widgets in this room.": "你无法修改此房间的插件。", "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s 撤销了对 %(targetDisplayName)s 加入房间的邀请。", "Upgrade this room to the recommended room version": "升级此房间至推荐版本", @@ -883,7 +883,7 @@ "Use your account or create a new one to continue.": "使用已有账户或创建一个新账户。", "Create Account": "创建账户", "Sign In": "登录", - "Custom (%(level)s)": "访客(%(level)s)", + "Custom (%(level)s)": "自定义(%(level)s)", "Messages": "信息", "Actions": "动作", "Sends a message as plain text, without interpreting it as markdown": "以纯文本形式发送消息,不将其作为 markdown 处理", @@ -899,7 +899,7 @@ "Use an identity server to invite by email. Manage in Settings.": "使用身份服务器以通过电子邮件邀请其他用户。在设置中进行管理。", "Unbans user with given ID": "按照 ID 解封用户", "Could not find user in room": "房间中无用户", - "Please supply a widget URL or embed code": "请提供一个插件或嵌入代码", + "Please supply a widget URL or embed code": "请提供一个挂件URL或嵌入代码", "Verifies a user, session, and pubkey tuple": "验证用户、会话和公钥元组", "Session already verified!": "会话已验证!", "WARNING: Session already verified, but keys do NOT MATCH!": "警告:会话已验证,但密钥不匹配!", @@ -946,7 +946,7 @@ "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s(%(userId)s)登录到未验证的新会话:", "Ask this user to verify their session, or manually verify it below.": "要求此用户验证其会话,或在下面手动进行验证。", "Not Trusted": "不可信任", - "Manually Verify by Text": "手动验证文字", + "Manually Verify by Text": "用文本手动验证", "Interactively verify by Emoji": "通过表情符号进行交互式验证", "Done": "完成", "Cannot reach homeserver": "无法连接到主服务器", @@ -996,7 +996,7 @@ "Call ended": "通话结束", "You started a call": "你开始了通话", "%(senderName)s started a call": "%(senderName)s开始了通话", - "Waiting for answer": "等待接听", + "Waiting for answer": "正在等待接听", "%(senderName)s is calling": "%(senderName)s正在通话", "Support adding custom themes": "支持添加自定义主题", "Font size": "字体大小", @@ -1017,8 +1017,8 @@ "My Ban List": "我的封禁列表", "This is your list of users/servers you have blocked - don't leave the room!": "这是你屏蔽的用户/服务器的列表——不要离开此房间!", "Unknown caller": "未知来电人", - "Waiting for %(displayName)s to verify…": "等待 %(displayName)s 进行验证…", - "Cancelling…": "正在取消…", + "Waiting for %(displayName)s to verify…": "正在等待%(displayName)s进行验证……", + "Cancelling…": "正在取消……", "They match": "它们匹配", "They don't match": "它们不匹配", "To be secure, do this in person or use a trusted way to communicate.": "为了安全,请当面完成或使用信任的方法交流。", @@ -1096,7 +1096,7 @@ "Custom font size can only be between %(min)s pt and %(max)s pt": "自定义字体大小只能介于 %(min)s pt 和 %(max)s pt 之间", "Error downloading theme information.": "下载主题信息时发生错误。", "Theme added!": "主题已添加!", - "Custom theme URL": "自定义主题链接", + "Custom theme URL": "自定义主题URL", "Add theme": "添加主题", "Message layout": "信息布局", "Modern": "现代", @@ -1145,7 +1145,7 @@ "Sounds": "声音", "Notification sound": "通知声音", "Reset": "重置", - "Set a new custom sound": "使用新的自定义声音", + "Set a new custom sound": "设置新的自定义声音", "Browse": "浏览", "Upgrade the room": "更新房间", "Enable room encryption": "启用房间加密", @@ -1223,7 +1223,7 @@ "Room %(name)s": "房间 %(name)s", "No recently visited rooms": "没有最近访问过的房间", "People": "联系人", - "Joining room …": "正在加入房间…", + "Joining room …": "正在加入房间……", "Loading …": "正在加载……", "Rejecting invite …": "正在拒绝邀请……", "Join the conversation with an account": "使用一个账户加入对话", @@ -1294,7 +1294,7 @@ "New published address (e.g. #alias:server)": "新的发布的地址(例如 #alias:server)", "Local Addresses": "本地地址", "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)": "为此房间设置地址以便用户通过你的主服务器(%(localDomain)s)找到此房间", - "Waiting for %(displayName)s to accept…": "等待%(displayName)s接受……", + "Waiting for %(displayName)s to accept…": "正在等待%(displayName)s接受……", "Accepting…": "正在接受……", "Start Verification": "开始验证", "Messages in this room are end-to-end encrypted.": "此房间内的消息是端对端加密的。", @@ -2198,7 +2198,7 @@ "Decrypted event source": "解密的事件源码", "Original event source": "原始事件源码", "Invite by username": "按照用户名邀请", - "Inviting...": "正在邀请…", + "Inviting...": "正在邀请……", "Welcome to ": "欢迎来到 ", "Share %(name)s": "分享 %(name)s", "Add a topic to help people know what it is about.": "添加话题,让大家知道这里是讨论什么的。", @@ -2306,7 +2306,7 @@ "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "这通常仅影响服务器如何处理房间。如果你的 %(brand)s 遇到问题,请回报错误。", "Just a heads up, if you don't add an email and forget your password, you could permanently lose access to your account.": "请注意,如果你不添加电子邮箱并且忘记密码,你将永远失去对你账户的访问权。", "Continuing without email": "不使用电子邮箱并继续", - "Data on this screen is shared with %(widgetDomain)s": "在此画面上的资料会与 %(widgetDomain)s 分享", + "Data on this screen is shared with %(widgetDomain)s": "此屏幕上的数据与%(widgetDomain)s分享", "Consult first": "先询问", "Invited people will be able to read old messages.": "被邀请的人将能够阅读过去的消息。", "Invite someone using their name, username (like ) or share this room.": "使用某人的名字、用户名(如 )或分享此房间来邀请他们。", @@ -2498,7 +2498,7 @@ "Collapse": "折叠", "Expand": "展开", "Recommended for public spaces.": "建议用于公开空间。", - "Allow people to preview your space before they join.": "允许在加入前预览你的空间。", + "Allow people to preview your space before they join.": "允许人们在加入前预览你的空间。", "Preview Space": "预览空间", "Decide who can view and join %(spaceName)s.": "决定谁可以查看和加入 %(spaceName)s。", "Visibility": "可见性", @@ -2528,7 +2528,7 @@ "%(senderName)s removed their profile picture": "%(senderName)s 已移除他们的资料图片", "%(senderName)s removed their display name (%(oldDisplayName)s)": "%(senderName)s 已将他们的昵称移除(%(oldDisplayName)s)", "%(senderName)s set their display name to %(displayName)s": "%(senderName)s 已将他们的昵称设置为 %(displayName)s", - "%(oldDisplayName)s changed their display name to %(displayName)s": "%(oldDisplayName)s 已将他们的昵称更改为 %(displayName)s", + "%(oldDisplayName)s changed their display name to %(displayName)s": "%(oldDisplayName)s将其显示名称改为%(displayName)s", "%(senderName)s banned %(targetName)s": "%(senderName)s 已封禁 %(targetName)s", "%(senderName)s banned %(targetName)s: %(reason)s": "%(senderName)s 已封禁 %(targetName)s: %(reason)s", "%(senderName)s invited %(targetName)s": "%(senderName)s 已邀请 %(targetName)s", @@ -2876,7 +2876,7 @@ "Files": "文件", "You won't get any notifications": "你不会收到任何通知", "Get notified only with mentions and keywords as set up in your settings": "如设置中设定的那样仅通知提及和关键词", - "@mentions & keywords": "@提及 & 关键词", + "@mentions & keywords": "@提及和关键词", "Get notified for every message": "获得每条消息的通知", "Get notifications as set up in your settings": "如设置中设定的那样获取通知", "sends rainfall": "发送降雨", @@ -3339,5 +3339,34 @@ "Set up your profile": "设置你的用户资料", "Download apps": "下载应用", "Download Element": "下载Element", - "Help": "帮助" + "Help": "帮助", + "Results are only revealed when you end the poll": "结果仅在你结束投票后展示", + "Voters see results as soon as they have voted": "投票者一投完票就能看到结果", + "Closed poll": "封闭式投票", + "Open poll": "开放式投票", + "Poll type": "投票类型", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store®和Apple logo®是Apple Inc.的商标", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play及其logo是Google LLC的商标。", + "Community ownership": "社群所有权", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "%(brand)s提供免费的端到端加密消息传递以及无限制的语音和视频通话,是保持联系的绝佳方式。", + "We’d appreciate any feedback on how you’re finding Element.": "对于您如何找到Element的任何反馈,我们将不胜感激。", + "Don’t miss a reply or important message": "不要错过回复或重要消息", + "Make sure people know it’s really you": "确保人们知道这真的是你", + "Don’t miss a thing by taking Element with you": "随身携带Element,不要错过任何事", + "Find and invite your community members": "发现并邀请你的社群成员", + "Find people": "找人", + "Find and invite your co-workers": "发现并邀请你的同事", + "Find friends": "发现朋友", + "Find and invite your friends": "发现并邀请你的朋友", + "Find your people": "寻找你的人", + "Welcome to %(brand)s": "欢迎来到%(brand)s", + "Only %(count)s steps to go|other": "仅需%(count)s步", + "Only %(count)s steps to go|one": "仅需%(count)s步", + "How are you finding Element so far?": "你是如何发现Element的?", + "Download %(brand)s": "下载%(brand)s", + "Download %(brand)s Desktop": "下载%(brand)s桌面版", + "Download on the App Store": "在App Store下载", + "Send read receipts": "发送已读回执", + "Share your activity and status with others.": "与别人分享你的活动和状态。", + "Your server doesn't support disabling sending read receipts.": "你的服务器不支持禁用发送已读回执。" } diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 06dac88d787..e17feb2b9a9 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -264,7 +264,7 @@ "Reject all %(invitedRooms)s invites": "拒絕所有 %(invitedRooms)s 邀請", "Failed to invite": "邀請失敗", "Confirm Removal": "確認移除", - "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "您確定您想要移除(刪除)此活動嗎?注意若您刪除房間名稱或主題變更,還是可以復原變更。", + "Are you sure you wish to remove (delete) this event? Note that if you delete a room name or topic change, it could undo the change.": "您確定您想要移除(刪除)此活動嗎?注意,若您刪除房間名稱或主題的變更,變更會被復原。", "Unknown error": "未知的錯誤", "Incorrect password": "不正確的密碼", "Unable to restore session": "無法復原工作階段", @@ -3129,7 +3129,7 @@ "Results are only revealed when you end the poll": "結果僅在您結束投票時顯示", "Voters see results as soon as they have voted": "投票者可以在投票後立刻看到結果", "Open poll": "開放投票", - "Closed poll": "已關閉的投票", + "Closed poll": "封闭式投票", "Poll type": "投票類型", "Results will be visible when the poll is ended": "結果將在投票結束時可見", "Open user settings": "開啟使用者設定", @@ -3480,5 +3480,22 @@ "You made it!": "您做到了!", "Help": "說明", "We’d appreciate any feedback on how you’re finding Element.": "對於您如何找到 Element 的任何回饋,我們將不勝感激。", - "How are you finding Element so far?": "您是如何找到 Element 的?" + "How are you finding Element so far?": "您是如何找到 Element 的?", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play 與 Google Play logo 是 Google 公司的商標。", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® 與 Apple logo® 是蘋果公司的商標。", + "Get it on F-Droid": "在 F-Droid 上取得", + "Get it on Google Play": "在 Google Play 上取得", + "Android": "Android", + "Download on the App Store": "在 App Store 上下載", + "iOS": "iOS", + "Download %(brand)s Desktop": "下載 %(brand)s 桌面版", + "Download %(brand)s": "下載 %(brand)s", + "We're creating a room with %(names)s": "我們正在建立一個包含 %(names)s 的聊天室", + "Your server doesn't support disabling sending read receipts.": "您的伺服器不支援停用傳送讀取回條。", + "Share your activity and status with others.": "與他人分享您的活動與狀態。", + "Presence": "在場", + "Send read receipts": "傳送讀取回條", + "Last activity": "上次活動", + "Sessions": "工作階段", + "Use new session manager (under active development)": "使用新的工作階段管理程式(正在積極開發中)" } From 2cae2be90964f2779f0259335d8e0596be2e0ee5 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 9 Aug 2022 17:11:26 +0100 Subject: [PATCH 017/107] Upgrade matrix-js-sdk to 19.3.0-rc.1 --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 933ccfd7ada..1bf41275ee4 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "maplibre-gl": "^1.15.2", "matrix-encrypt-attachment": "^1.0.3", "matrix-events-sdk": "^0.0.1-beta.7", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "19.3.0-rc.1", "matrix-widget-api": "^0.1.0-beta.18", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index f2f623b7edf..37037a01489 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6776,9 +6776,10 @@ matrix-events-sdk@^0.0.1-beta.7: resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934" integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "19.2.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/cf33569a2187628dd7954ac771995cce3e804af4" +matrix-js-sdk@19.3.0-rc.1: + version "19.3.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-19.3.0-rc.1.tgz#dda9f140dca076a77c5c00315b53bf2d417e5946" + integrity sha512-sK5bdn0AClJD+Cgned/rHcqMNRy1HEDuU/4SiRFoO+ROhBnNTsN3L7nOosS318WTCCpTwu2OdJCcMSJ9BBridw== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 8cf9b357f8d1eb69242c9290df465cd8c2d494c1 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 9 Aug 2022 17:14:58 +0100 Subject: [PATCH 018/107] Prepare changelog for v3.52.0-rc.1 --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c27429be7f3..cec9cf24ebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +Changes in [3.52.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.52.0-rc.1) (2022-08-09) +=============================================================================================================== + +## ✨ Features + * Device manager generic settings subsection component ([\#9147](https://github.com/matrix-org/matrix-react-sdk/pull/9147)). + * Migrate the hidden read receipts flag to new "send read receipts" option ([\#9141](https://github.com/matrix-org/matrix-react-sdk/pull/9141)). + * Live location sharing - share location at most every 5 seconds ([\#9148](https://github.com/matrix-org/matrix-react-sdk/pull/9148)). + * Increase max length of voice messages to 15m ([\#9133](https://github.com/matrix-org/matrix-react-sdk/pull/9133)). Fixes vector-im/element-web#18620. + * Move pin drop out of labs ([\#9135](https://github.com/matrix-org/matrix-react-sdk/pull/9135)). + * Device manager - New device tile info design ([\#9122](https://github.com/matrix-org/matrix-react-sdk/pull/9122)). + * Start DM on first message ([\#8612](https://github.com/matrix-org/matrix-react-sdk/pull/8612)). Fixes vector-im/element-web#14736. + * Remove "Add Space" button from RoomListHeader when user cannot create spaces ([\#9129](https://github.com/matrix-org/matrix-react-sdk/pull/9129)). + * The Welcome Home Screen: Dedicated Download Apps Dialog ([\#9120](https://github.com/matrix-org/matrix-react-sdk/pull/9120)). Fixes vector-im/element-web#22921. + * The Welcome Home Screen: "Submit Feedback" pane ([\#9090](https://github.com/matrix-org/matrix-react-sdk/pull/9090)). Fixes vector-im/element-web#22918. + * New User Onboarding Task List ([\#9083](https://github.com/matrix-org/matrix-react-sdk/pull/9083)). Fixes vector-im/element-web#22919. + * Add support for disabling spell checking ([\#8604](https://github.com/matrix-org/matrix-react-sdk/pull/8604)). Fixes vector-im/element-web#21901. + * Live location share - leave maximised map open when beacons expire ([\#9098](https://github.com/matrix-org/matrix-react-sdk/pull/9098)). + +## 🐛 Bug Fixes + * Fix pillification sometimes doubling up ([\#9152](https://github.com/matrix-org/matrix-react-sdk/pull/9152)). Fixes vector-im/element-web#23036. + * Use stable reference for active tab in tabbedView ([\#9145](https://github.com/matrix-org/matrix-react-sdk/pull/9145)). + * Fix composer padding ([\#9137](https://github.com/matrix-org/matrix-react-sdk/pull/9137)). Fixes vector-im/element-web#22992. + * Fix highlights not being applied to plaintext messages ([\#9126](https://github.com/matrix-org/matrix-react-sdk/pull/9126)). Fixes vector-im/element-web#22787. + * Fix dismissing edit composer when change was undone ([\#9109](https://github.com/matrix-org/matrix-react-sdk/pull/9109)). Fixes vector-im/element-web#22932. + * 1-to-1 DM rooms with bots now act like DM rooms instead of multi-user-rooms before ([\#9124](https://github.com/matrix-org/matrix-react-sdk/pull/9124)). Fixes vector-im/element-web#22894. + * Apply inline start padding to selected lines on modern layout only ([\#9006](https://github.com/matrix-org/matrix-react-sdk/pull/9006)). Fixes vector-im/element-web#22768. Contributed by @luixxiul. + * Peek into world-readable rooms from spotlight ([\#9115](https://github.com/matrix-org/matrix-react-sdk/pull/9115)). Fixes vector-im/element-web#22862. + * Use default styling on nested numbered lists due to MD being sensitive ([\#9110](https://github.com/matrix-org/matrix-react-sdk/pull/9110)). Fixes vector-im/element-web#22935. + * Fix replying using chat effect commands ([\#9101](https://github.com/matrix-org/matrix-react-sdk/pull/9101)). Fixes vector-im/element-web#22824. + Changes in [3.51.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.51.0) (2022-08-02) ===================================================================================================== From dcc12a142dff6796597fffea33496e4b130bcc09 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 9 Aug 2022 17:14:59 +0100 Subject: [PATCH 019/107] v3.52.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1bf41275ee4..cf88aa102c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.51.0", + "version": "3.52.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -23,7 +23,7 @@ "package.json", ".stylelintrc.js" ], - "main": "./src/index.ts", + "main": "./lib/index.ts", "matrix_src_main": "./src/index.ts", "matrix_lib_main": "./lib/index.ts", "matrix_lib_typings": "./lib/index.d.ts", @@ -252,5 +252,6 @@ "jestSonar": { "reportPath": "coverage", "sonar56x": true - } + }, + "typings": "./lib/index.d.ts" } From 7c2134ed430dbaeed20f3884521c4a2abec369a6 Mon Sep 17 00:00:00 2001 From: t3chguy Date: Tue, 9 Aug 2022 16:44:57 +0000 Subject: [PATCH 020/107] [create-pull-request] automated change --- yarn.lock | 1204 ++++++++++++++++++++++++++--------------------------- 1 file changed, 602 insertions(+), 602 deletions(-) diff --git a/yarn.lock b/yarn.lock index f2f623b7edf..0aaf76475c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,12 @@ "@actions/core@^1.4.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.9.0.tgz#20c1baac5d4bd2508ba1fc3e5f3fc4b8a80d4082" - integrity sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA== + version "1.9.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.9.1.tgz#97c0201b1f9856df4f7c3a375cdcdb0c2a2f750b" + integrity sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA== dependencies: "@actions/http-client" "^2.0.1" + uuid "^8.3.2" "@actions/github@^5.0.0": version "5.0.3" @@ -35,15 +36,15 @@ "@jridgewell/trace-mapping" "^0.3.9" "@babel/cli@^7.12.10": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.6.tgz#b1228eb9196b34d608155a47508011d9e47ab1f2" - integrity sha512-jXNHoYCbxZ8rKy+2lyy0VjcaGxS4NPbN0qc95DjIiGZQL/mTNx3o2/yI0TG+X0VrrTuwmO7zH52T9NcNdbF9Uw== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.10.tgz#4211adfc45ffa7d4f3cee6b60bb92e9fe68fe56a" + integrity sha512-dLvWH+ZDFAkd2jPBSghrsFBuXrREvFwjpDycXbmUoeochqKYe4zNSLEJYErpLg8dvxvZYe79/MkN461XCwpnGw== dependencies: "@jridgewell/trace-mapping" "^0.3.8" commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" - glob "^7.0.0" + glob "^7.2.0" make-dir "^2.1.0" slash "^2.0.0" optionalDependencies: @@ -57,26 +58,26 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.18.6": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": version "7.18.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== "@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.6.tgz#54a107a3c298aee3fe5e1947a6464b9b6faca03d" - integrity sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" + integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.6" - "@babel/helper-compilation-targets" "^7.18.6" - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helpers" "^7.18.6" - "@babel/parser" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.10" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.10" + "@babel/types" "^7.18.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -84,27 +85,27 @@ semver "^6.3.0" "@babel/eslint-parser@^7.12.10": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.18.2.tgz#e14dee36c010edfb0153cf900c2b0815e82e3245" - integrity sha512-oFQYkE8SuH14+uR51JVAmdqwKYXGRjEXx7s+WiagVjqQ+HPE+nnwyF2qlVG8evUsUHmPcA+6YXMEDbIhEyQc5A== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.18.9.tgz#255a63796819a97b7578751bb08ab9f2a375a031" + integrity sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ== dependencies: eslint-scope "^5.1.1" eslint-visitor-keys "^2.1.0" semver "^6.3.0" "@babel/eslint-plugin@^7.12.10": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.17.7.tgz#4ee1d5b29b79130f3bb5a933358376bcbee172b8" - integrity sha512-JATUoJJXSgwI0T8juxWYtK1JSgoLpIGUsCHIv+NMXcUDA2vIe6nvAHR9vnuJgs/P1hOFw7vPwibixzfqBBLIVw== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.18.10.tgz#11f454b5d1aa64c42fcfd64abe93071c15ebea3c" + integrity sha512-iV1OZj/7eg4wZIcsVEkXS3MUWdhmpLsu2h+9Zr2ppywKWdCRs6VfjxbRzmHHYeurTizrrnaJ9ZkbO8KOv4lauQ== dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.18.6", "@babel/generator@^7.18.7", "@babel/generator@^7.7.2": - version "7.18.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.7.tgz#2aa78da3c05aadfc82dbac16c99552fc802284bd" - integrity sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A== +"@babel/generator@^7.18.10", "@babel/generator@^7.7.2": + version "7.18.12" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" + integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== dependencies: - "@babel/types" "^7.18.7" + "@babel/types" "^7.18.10" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -116,34 +117,34 @@ "@babel/types" "^7.18.6" "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.6.tgz#f14d640ed1ee9246fb33b8255f08353acfe70e6a" - integrity sha512-KT10c1oWEpmrIRYnthbzHgoOf6B+Xd6a5yhdbNtdhtG7aO1or5HViuf1TQR36xY/QprXA5nvxO6nAjhJ4y38jw== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== dependencies: "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz#18d35bfb9f83b1293c22c55b3d576c1315b6ed96" - integrity sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" + integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== dependencies: - "@babel/compat-data" "^7.18.6" + "@babel/compat-data" "^7.18.8" "@babel/helper-validator-option" "^7.18.6" browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz#6f15f8459f3b523b39e00a99982e2c040871ed72" - integrity sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" + integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-function-name" "^7.18.6" - "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.9" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-create-regexp-features-plugin@^7.18.6": @@ -154,24 +155,22 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== +"@babel/helper-define-polyfill-provider@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" + integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" debug "^4.1.1" lodash.debounce "^4.0.8" resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7" - integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q== +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" @@ -180,13 +179,13 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz#8334fecb0afba66e6d87a7e8c6bb7fed79926b83" - integrity sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw== +"@babel/helper-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" + integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== dependencies: "@babel/template" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/types" "^7.18.9" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -195,33 +194,33 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.6.tgz#44802d7d602c285e1692db0bad9396d007be2afc" - integrity sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng== +"@babel/helper-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6": +"@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.8.tgz#4f8408afead0188cfa48672f9d0e5787b61778c8" - integrity sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" + integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== dependencies: - "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-simple-access" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.18.6" "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.8" - "@babel/types" "^7.18.8" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -230,31 +229,31 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz#9448974dd4fb1d80fefe72e8a0af37809cd30d6d" - integrity sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" + integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== -"@babel/helper-remap-async-to-generator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.6.tgz#fa1f81acd19daee9d73de297c0308783cd3cfc23" - integrity sha512-z5wbmV55TveUPZlCLZvxWHtrjuJd+8inFhk7DG0WW87/oJuGDcjDiu7HIvGcpf5464L6xKCg3vNkmlVVz9hwyQ== +"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-wrap-function" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.6.tgz#efedf51cfccea7b7b8c0f00002ab317e7abfe420" - integrity sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" + integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== dependencies: - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helper-simple-access@^7.18.6": version "7.18.6" @@ -263,12 +262,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.6.tgz#7dff00a5320ca4cf63270e5a0eca4b268b7380d9" - integrity sha512-4KoLhwGS9vGethZpAhYnMejWkX64wsnHPDwvOsKWU6Fg4+AlK2Jz3TyjQLMEPvz+1zemi/WBdkYxCD0bAfIkiw== +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" + integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.18.9" "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" @@ -277,6 +276,11 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + "@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" @@ -287,24 +291,24 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== -"@babel/helper-wrap-function@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.6.tgz#ec44ea4ad9d8988b90c3e465ba2382f4de81a073" - integrity sha512-I5/LZfozwMNbwr/b1vhhuYD+J/mU+gfGAj5td7l5Rv9WYmH6i3Om69WGKNmlIpsVW/mF6O5bvTKbvDQZVgjqOw== +"@babel/helper-wrap-function@^7.18.9": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz#bff23ace436e3f6aefb61f85ffae2291c80ed1fb" + integrity sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w== dependencies: - "@babel/helper-function-name" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/helper-function-name" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.11" + "@babel/types" "^7.18.10" -"@babel/helpers@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.6.tgz#4c966140eaa1fcaa3d5a8c09d7db61077d4debfd" - integrity sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ== +"@babel/helpers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" + integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== dependencies: "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/highlight@^7.18.6": version "7.18.6" @@ -315,10 +319,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.14.7", "@babel/parser@^7.18.5", "@babel/parser@^7.18.6", "@babel/parser@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.8.tgz#822146080ac9c62dac0823bb3489622e0bc1cbdf" - integrity sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA== +"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.18.5": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" + integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -327,23 +331,23 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.6.tgz#b4e4dbc2cd1acd0133479918f7c6412961c9adb8" - integrity sha512-Udgu8ZRgrBrttVz6A0EVL0SJ1z+RLbIeqsu632SA1hf0awEppD6TvdznoH+orIF8wtFFAV/Enmw9Y+9oV8TQcw== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" -"@babel/plugin-proposal-async-generator-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17" - integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w== +"@babel/plugin-proposal-async-generator-functions@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz#85ea478c98b0095c3e4102bff3b67d306ed24952" + integrity sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew== dependencies: - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.18.6": @@ -372,19 +376,19 @@ "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-default-from@^7.12.1": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.6.tgz#121b3ba0152d0020865bc86271c8150e5115abc7" - integrity sha512-oTvzWB16T9cB4j5kX8c8DuUHo/4QtR2P9vnUNKed9xqFP8Jos/IRniz1FiIryn6luDYoltDJSYF7RCpbm2doMg== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz#091f4794dbce4027c03cf4ebc64d3fb96b75c206" + integrity sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-default-from" "^7.18.6" -"@babel/plugin-proposal-export-namespace-from@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.6.tgz#1016f0aa5ab383bbf8b3a85a2dcaedf6c8ee7491" - integrity sha512-zr/QcUlUo7GPo6+X1wC98NJADqmy5QTFWWhqeQWiki4XHafJtLl/YMGkmRB2szDD2IYJCCdBTd4ElwhId9T7Xw== +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.18.6": @@ -395,12 +399,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.6.tgz#3b9cac6f1ffc2aa459d111df80c12020dfc6b665" - integrity sha512-zMo66azZth/0tVd7gmkxOkOjs2rpHyhpcFo565PUP37hSp6hSd9uUKIfTDFMz58BwqgQKhJ9YxtM5XddjXVn+Q== +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": @@ -419,16 +423,16 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.6.tgz#ec93bba06bfb3e15ebd7da73e953d84b094d5daf" - integrity sha512-9yuM6wr4rIsKa1wlUAbZEazkCrgw2sMPEXCr4Rnwetu7cEW1NydkCWytLuYletbf8vFxdJxFhwEZqMpOx2eZyw== +"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" + integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== dependencies: - "@babel/compat-data" "^7.18.6" - "@babel/helper-compilation-targets" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" "@babel/plugin-proposal-optional-catch-binding@^7.18.6": version "7.18.6" @@ -438,13 +442,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.6.tgz#46d4f2ffc20e87fad1d98bc4fa5d466366f6aa0b" - integrity sha512-PatI6elL5eMzoypFAiYDpYQyMtXTn+iMhuxxQt5mAXD4fEmKorpSI3PHd+i3JXBJN3xyA6MvJv7at23HffFHwA== +"@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.18.6": @@ -636,40 +640,40 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-block-scoping@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.6.tgz#b5f78318914615397d86a731ef2cc668796a726c" - integrity sha512-pRqwb91C42vs1ahSAWJkxOxU1RHWDn16XAa6ggQ72wjLlWyYeAcLvTtE0aM8ph3KNydy9CQF2nLYcjq1WysgxQ== +"@babel/plugin-transform-block-scoping@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" + integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-classes@^7.18.6": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.8.tgz#7e85777e622e979c85c701a095280360b818ce49" - integrity sha512-RySDoXdF6hgHSHuAW4aLGyVQdmvEX/iJtjVre52k0pxRq4hzqze+rAVP++NmNv596brBpYmaiKgTZby7ziBnVg== +"@babel/plugin-transform-classes@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" + integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-function-name" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-replace-supers" "^7.18.9" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.6.tgz#5d15eb90e22e69604f3348344c91165c5395d032" - integrity sha512-9repI4BhNrR0KenoR9vm3/cIc1tSBIo+u1WVjKCAynahj25O8zfbiE6JtAtHPGQSs4yZ+bA8mRasRP+qc+2R5A== +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-destructuring@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.6.tgz#a98b0e42c7ffbf5eefcbcf33280430f230895c6f" - integrity sha512-tgy3u6lRp17ilY8r1kP4i2+HDUwxlVqq3RTc943eAWSzGgpU1qhiKpqZ5CMyHReIYPHdo3Kg8v8edKtDqSVEyQ== +"@babel/plugin-transform-destructuring@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" + integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.18.6" @@ -679,12 +683,12 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-duplicate-keys@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.6.tgz#e6c94e8cd3c9dd8a88144f7b78ae22975a7ff473" - integrity sha512-NJU26U/208+sxYszf82nmGYqVF9QN8py2HFTblPT9hbawi8+1C5a9JubODLTGFuT0qlkqVinmkwOD13s0sZktg== +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-exponentiation-operator@^7.18.6": version "7.18.6" @@ -694,28 +698,28 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-for-of@^7.18.6": +"@babel/plugin-transform-for-of@^7.18.8": version "7.18.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-function-name@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.6.tgz#6a7e4ae2893d336fd1b8f64c9f92276391d0f1b4" - integrity sha512-kJha/Gbs5RjzIu0CxZwf5e3aTTSlhZnHMT8zPWnJMjNpLOUgqevg+PN5oMH68nMCXnfiMo4Bhgxqj59KHTlAnA== +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-compilation-targets" "^7.18.6" - "@babel/helper-function-name" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.6.tgz#9d6af353b5209df72960baf4492722d56f39a205" - integrity sha512-x3HEw0cJZVDoENXOp20HlypIHfl0zMIhMVZEBVTfmqbObIpsMxMbmU5nOEO8R7LYT+z5RORKPlTI5Hj4OsO9/Q== +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-member-expression-literals@^7.18.6": version "7.18.6" @@ -743,14 +747,14 @@ "@babel/helper-simple-access" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.6.tgz#026511b7657d63bf5d4cf2fd4aeb963139914a54" - integrity sha512-UbPYpXxLjTw6w6yXX2BYNxF3p6QY225wcTkfQCy3OMnSlS/C3xGtwUjEzGkldb/sy6PWLiCQ3NbYfjWUTI3t4g== +"@babel/plugin-transform-modules-systemjs@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06" + integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A== dependencies: "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/helper-validator-identifier" "^7.18.6" babel-plugin-dynamic-import-node "^2.3.3" @@ -785,7 +789,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.18.6": +"@babel/plugin-transform-parameters@^7.18.8": version "7.18.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== @@ -814,15 +818,15 @@ "@babel/plugin-transform-react-jsx" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" - integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz#ea47b2c4197102c196cbd10db9b3bb20daa820f1" + integrity sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/types" "^7.18.10" "@babel/plugin-transform-react-pure-annotations@^7.18.6": version "7.18.6" @@ -848,15 +852,15 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-runtime@^7.12.10": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.6.tgz#77b14416015ea93367ca06979710f5000ff34ccb" - integrity sha512-8uRHk9ZmRSnWqUgyae249EJZ94b0yAGLBIqzZzl+0iEdbno55Pmlt/32JZsHwXD9k/uZj18Aqqk35wBX4CBTXA== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz#37d14d1fa810a368fd635d4d1476c0154144a96f" + integrity sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" + "@babel/helper-plugin-utils" "^7.18.9" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.18.6": @@ -866,13 +870,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-spread@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.6.tgz#82b080241965f1689f0a60ecc6f1f6575dbdb9d6" - integrity sha512-ayT53rT/ENF8WWexIRg9AiV9h0aIteyWn5ptfZTZQrjk/+f3WdrJGCY4c9wcgl2+MKkKPhzbYp97FTsquZpDCw== +"@babel/plugin-transform-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" + integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-transform-sticky-regex@^7.18.6": version "7.18.6" @@ -881,35 +885,35 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-template-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.6.tgz#b763f4dc9d11a7cce58cf9a490d82e80547db9c2" - integrity sha512-UuqlRrQmT2SWRvahW46cGSany0uTlcj8NYOS5sRGYi8FxPYPoLd5DDmMd32ZXEj2Jq+06uGVQKHxa/hJx2EzKw== +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-typeof-symbol@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.6.tgz#486bb39d5a18047358e0d04dc0d2f322f0b92e92" - integrity sha512-7m71iS/QhsPk85xSjFPovHPcH3H9qeyzsujhTc+vcdnsXavoWYJ74zx0lP5RhpC5+iDnVLO+PPMHzC11qels1g== +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" - integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== + version "7.18.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.12.tgz#712e9a71b9e00fde9f8c0238e0cceee86ab2f8fd" + integrity sha512-2vjjam0cum0miPkenUbQswKowuxs/NjMwIKEq0zwegRxXk12C9YOF9STXnaUptITOtOJHKHpzvvWYOjbm6tc0w== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-typescript" "^7.18.6" -"@babel/plugin-transform-unicode-escapes@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27" - integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw== +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" @@ -920,28 +924,28 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.12.11": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.6.tgz#953422e98a5f66bc56cd0b9074eaea127ec86ace" - integrity sha512-WrthhuIIYKrEFAwttYzgRNQ5hULGmwTj+D6l7Zdfsv5M7IWV/OZbUfbeL++Qrzx1nVJwWROIFhCHRYQV4xbPNw== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.10.tgz#83b8dfe70d7eea1aae5a10635ab0a5fe60dfc0f4" + integrity sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA== dependencies: - "@babel/compat-data" "^7.18.6" - "@babel/helper-compilation-targets" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.6" - "@babel/plugin-proposal-async-generator-functions" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.18.10" "@babel/plugin-proposal-class-properties" "^7.18.6" "@babel/plugin-proposal-class-static-block" "^7.18.6" "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.18.9" "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" "@babel/plugin-proposal-private-methods" "^7.18.6" "@babel/plugin-proposal-private-property-in-object" "^7.18.6" "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" @@ -963,40 +967,40 @@ "@babel/plugin-transform-arrow-functions" "^7.18.6" "@babel/plugin-transform-async-to-generator" "^7.18.6" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.18.6" - "@babel/plugin-transform-classes" "^7.18.6" - "@babel/plugin-transform-computed-properties" "^7.18.6" - "@babel/plugin-transform-destructuring" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.18.9" + "@babel/plugin-transform-classes" "^7.18.9" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.18.9" "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.18.6" - "@babel/plugin-transform-function-name" "^7.18.6" - "@babel/plugin-transform-literals" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" "@babel/plugin-transform-member-expression-literals" "^7.18.6" "@babel/plugin-transform-modules-amd" "^7.18.6" "@babel/plugin-transform-modules-commonjs" "^7.18.6" - "@babel/plugin-transform-modules-systemjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.18.9" "@babel/plugin-transform-modules-umd" "^7.18.6" "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" "@babel/plugin-transform-new-target" "^7.18.6" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" "@babel/plugin-transform-property-literals" "^7.18.6" "@babel/plugin-transform-regenerator" "^7.18.6" "@babel/plugin-transform-reserved-words" "^7.18.6" "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.18.6" + "@babel/plugin-transform-spread" "^7.18.9" "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.6" - "@babel/plugin-transform-typeof-symbol" "^7.18.6" - "@babel/plugin-transform-unicode-escapes" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.6" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" + "@babel/types" "^7.18.10" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" core-js-compat "^3.22.1" semver "^6.3.0" @@ -1033,9 +1037,9 @@ "@babel/plugin-transform-typescript" "^7.18.6" "@babel/register@^7.12.10": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.6.tgz#48a4520f1b2a7d7ac861e8148caeb0cefe6c59db" - integrity sha512-tkYtONzaO8rQubZzpBnvZPFcHgh8D9F55IjOsYton4X2IBoyRn2ZSWQqySTZnUn2guZbxbQiAB27hJEbvXamhQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" + integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -1044,50 +1048,51 @@ source-map-support "^0.5.16" "@babel/runtime-corejs3@^7.10.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.6.tgz#6f02c5536911f4b445946a2179554b95c8838635" - integrity sha512-cOu5wH2JFBgMjje+a+fz2JNIWU4GzYpl05oSob3UDvBEh6EuIn+TXFHMmBbhSb+k/4HMzgKCQfEEDArAWNF9Cw== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae" + integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A== dependencies: core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580" - integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.9", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.18.6", "@babel/template@^7.3.3": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" - integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== +"@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" -"@babel/traverse@^7.12.12", "@babel/traverse@^7.13.0", "@babel/traverse@^7.18.5", "@babel/traverse@^7.18.6", "@babel/traverse@^7.18.8", "@babel/traverse@^7.7.2": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.8.tgz#f095e62ab46abf1da35e5a2011f43aee72d8d5b0" - integrity sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg== +"@babel/traverse@^7.12.12", "@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.5", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" + integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.7" - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-function-name" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.8" - "@babel/types" "^7.18.8" + "@babel/parser" "^7.18.11" + "@babel/types" "^7.18.10" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.18.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.8.tgz#c5af199951bf41ba4a6a9a6d0d8ad722b30cd42f" - integrity sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw== +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== dependencies: + "@babel/helper-string-parser" "^7.18.10" "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" @@ -1588,17 +1593,17 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^12.7.0": - version "12.8.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.8.0.tgz#f4708cf948724d6e8f7d878cfd91584c1c5c0523" - integrity sha512-ydcKLs2KKcxlhpdWLzJxEBDEk/U5MUeqtqkXlrtAUXXFPs6vLl1PEGghFC/BbpleosB7iXs0Z4P2DGe7ZT5ZNg== +"@octokit/openapi-types@^12.11.0": + version "12.11.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" + integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== "@octokit/plugin-paginate-rest@^2.16.8", "@octokit/plugin-paginate-rest@^2.17.0": - version "2.21.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.2.tgz#070be9bb18cb78e52b471ddc3551d28355e2d5e2" - integrity sha512-S24H0a6bBVreJtoTaRHT/gnVASbOHVTRMOVIqd9zrJBP3JozsxJB56TDuTUmd1xLI4/rAE2HNmThvVKtIdLLEw== + version "2.21.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" + integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== dependencies: - "@octokit/types" "^6.39.0" + "@octokit/types" "^6.40.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" @@ -1644,17 +1649,17 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0": - version "6.39.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.39.0.tgz#46ce28ca59a3d4bac0e487015949008302e78eee" - integrity sha512-Mq4N9sOAYCitTsBtDdRVrBE80lIrMBhL9Jbrw0d+j96BAzlq4V+GLHFJbHokEsVvO/9tQupQdoFdgVYhD2C8UQ== +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": + version "6.41.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" + integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: - "@octokit/openapi-types" "^12.7.0" + "@octokit/openapi-types" "^12.11.0" "@peculiar/asn1-schema@^2.1.6": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.2.0.tgz#d8a54527685c8dee518e6448137349444310ad64" - integrity sha512-1ENEJNY7Lwlua/1wvzpYP194WtjQBfFxvde2FlzfBFh/ln6wvChrtxlORhbKEnYswzn6fOC4c7HdC5izLPMTJg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.0.tgz#5368416eb336138770c692ffc2bab119ee3ae917" + integrity sha512-DtNLAG4vmDrdSJFPe7rypkcj597chNQL7u+2dBtYo5mh7VW2+im6ke+O0NVr8W1f4re4C3F71LhoMb0Yxqa48Q== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -1678,96 +1683,96 @@ tslib "^2.4.0" webcrypto-core "^1.7.4" -"@percy/cli-build@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-build/-/cli-build-1.6.1.tgz#f49e6df1ca3b2f548c853c66f75ebb407e2302ea" - integrity sha512-YP4+uLfT14zI1bfjU+VE8fhu6RkwuhRK1AHIFcbonj/HlIhCdFWqyrWcmLp6HYU06JoiNtbI4QQNM3spxWrolg== +"@percy/cli-build@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli-build/-/cli-build-1.8.0.tgz#58c08e85b56370bfebb6651a9fec2a3a7fe199cf" + integrity sha512-Ezp/WVv4edHpx/VVuVKvCKvfsuVjjuCTDiw5SIh6qr31nbx3IBUhWfuTWlU+/y6f6g39fQ7rbS0oKXr+ZtDeww== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.0" -"@percy/cli-command@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-command/-/cli-command-1.6.1.tgz#e41b47d038e9ce52ffeeabba19d707468670829d" - integrity sha512-5jeKsKM+it3gh93vV48dBi725ewVGOfxUsjZU8slKcK9IaohXkjr0/aWor9LWgQcug87sLS9VjNlc+UyhTW0KA== +"@percy/cli-command@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli-command/-/cli-command-1.8.0.tgz#44dc5c229e5d15b2990d13f4a8a086979684e160" + integrity sha512-cA/qJL8ZcICZPngqfzaSey9u3ys0y+rz/iTtDwLvFk8W+h6AGcYzGL44VJtdOCT8KBrsU4qpYWiykZegROWQvw== dependencies: - "@percy/config" "1.6.1" - "@percy/core" "1.6.1" - "@percy/logger" "1.6.1" + "@percy/config" "1.8.0" + "@percy/core" "1.8.0" + "@percy/logger" "1.8.0" -"@percy/cli-config@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-config/-/cli-config-1.6.1.tgz#f34c72fda6f9903770bb725693b8c7dbbad21150" - integrity sha512-fUO14gra2EdvY0RT5eSxH8QhammDtclcb2vjTwMG6QUW+sGDQQCu8M8s7YBxRKo/aTq+tJy1hfqd6W/rsx0mWQ== +"@percy/cli-config@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli-config/-/cli-config-1.8.0.tgz#c5cd66589ba89d1c1b16f9e4aa05554cd132c576" + integrity sha512-VPwgFYijoWnf3IRQJYLhzN30Bav/g/iFC5YVnu8XajXZg5DtO8TW1vyeOrQAYvK5Fo065dIdoWmw5CqCO5cUww== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.0" -"@percy/cli-exec@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-exec/-/cli-exec-1.6.1.tgz#736e5f2c09080e19ebbcf84c1ded13c5e7db2b0b" - integrity sha512-F8j+reJWPu0s+zQuEU2Gm8SZ1QP7cf04MS5hmL1CaE7G7O88ec+yrS6eiSlNrY1+D45B7NYy7l6zWzsP+nQ3TA== +"@percy/cli-exec@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli-exec/-/cli-exec-1.8.0.tgz#88bb40bfb2e84bb51a438d2ce7a97d93f394be8a" + integrity sha512-Ce6e2kO9ShIDRbW/4QGJZaofy8mgc3U71BGWovJGR2+ChekRnNKKyVIOMgtSkQRZ9Yyt5yYUnVaoLijmoVt4hQ== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.0" cross-spawn "^7.0.3" which "^2.0.2" -"@percy/cli-snapshot@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-snapshot/-/cli-snapshot-1.6.1.tgz#0da90a1cf6f7e79898f4f68f1e180c8c7c8f2894" - integrity sha512-lrZR/CIL1zkZdeOPmnidkVtWY6foOq3ekWbaoMgDu0agMjf2lxEX2DvkCS/WwdmF/JvDcquLKO6gS5EHE6mbBQ== +"@percy/cli-snapshot@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli-snapshot/-/cli-snapshot-1.8.0.tgz#d75947564109ce349176cd2aad6ef4ee9e17bc74" + integrity sha512-Yz5HKKrDuXB5EMf3Eeo1Sx7aki6VkMMFj8EimDA1WMatW3UC7/pmUmDoxJXkrEJP/CuOIAAi4GKBmDU5/aKHzw== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.0" yaml "^2.0.0" -"@percy/cli-upload@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-upload/-/cli-upload-1.6.1.tgz#ffd95ca03d15081a77d9090d1c33b32044db849e" - integrity sha512-F+n7gy5CTfR0BUSsFdxEpfb9CNDnnhnFO0AN9v2FfyiSEei3CXrS2Te6DJS/YXnVI58YeFRbiW6G1LKhP+Vi4g== +"@percy/cli-upload@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli-upload/-/cli-upload-1.8.0.tgz#3bbef12bf163c85e9dfc5310a861e3d56e9f68b2" + integrity sha512-d4DRxHZgyL+0pyQVKEh3OHhVYbSxUxHASdsPWsDGZksmOiAyE0VMaeN3m40D/4FbJuylMC2pJ5XaYQv/kCM9Ew== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.0" fast-glob "^3.2.11" image-size "^1.0.0" "@percy/cli@^1.3.0": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli/-/cli-1.6.1.tgz#d3910dd63d2b2b67e57e4c9c675eafb4d471322a" - integrity sha512-BwCcigFixUhi2Wn6X+oucJrqnk/6e4FOsYQI4+lDzZU416+WpsYF0CjmhQVpa9Us278L+qc7eIsDHJwuJeurFw== - dependencies: - "@percy/cli-build" "1.6.1" - "@percy/cli-command" "1.6.1" - "@percy/cli-config" "1.6.1" - "@percy/cli-exec" "1.6.1" - "@percy/cli-snapshot" "1.6.1" - "@percy/cli-upload" "1.6.1" - "@percy/client" "1.6.1" - "@percy/logger" "1.6.1" - -"@percy/client@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.6.1.tgz#6bb136c2fe0ab0490acc898de21f8ab3508420a5" - integrity sha512-e5ToG88O1gDDOuQ+i4989vKCfF+CsPSGG9VA06IKSYNP4QBATe9/RYERZ5jk118uEutjxi8lIjzet0eg8rv7BA== + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/cli/-/cli-1.8.0.tgz#3502c361a3e1db5c7516456308fca7f1ce58eb7f" + integrity sha512-7wSYSx4awUvXjenO/jo0yNyFdR+Napj2NX994HDyc2EbCBjnSVPNxRWUGHJxlhWAHB5yv3yjnXhijPm6zb1Kng== + dependencies: + "@percy/cli-build" "1.8.0" + "@percy/cli-command" "1.8.0" + "@percy/cli-config" "1.8.0" + "@percy/cli-exec" "1.8.0" + "@percy/cli-snapshot" "1.8.0" + "@percy/cli-upload" "1.8.0" + "@percy/client" "1.8.0" + "@percy/logger" "1.8.0" + +"@percy/client@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.8.0.tgz#b56206432d5f6d4ee71877f1f277e11178951587" + integrity sha512-PMkST2X57m9paTZ22yv8yNeO2X33FeDTnEo99r4TKriiIs5Vm4UKurKYJdztS8OrYh/xl7tx1ATpc6n3vvNkEg== dependencies: - "@percy/env" "1.6.1" - "@percy/logger" "1.6.1" + "@percy/env" "1.8.0" + "@percy/logger" "1.8.0" -"@percy/config@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.6.1.tgz#11fb960dca4ecc0575349382a973b6c5c941363a" - integrity sha512-UiB4gpt01VgPUF4ObZBixR1Wwi/ZUaMXBUxmE3wOa3zZrtZXOzbZwQGcntw5ToEq6OQBP100q1vetnP8xhFQtQ== +"@percy/config@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.8.0.tgz#f0aaff8b04e5b6e3809ba9f41177027ddc4b3db1" + integrity sha512-B4cfcNpwqMKKC2US9k+y3sfsgD9JcspNDT+VNPA+9zp1PEbyW5dDe3FQY5bjoLRtQHcNtepBOTMurmWVhjViWw== dependencies: - "@percy/logger" "1.6.1" + "@percy/logger" "1.8.0" ajv "^8.6.2" cosmiconfig "^7.0.0" yaml "^2.0.0" -"@percy/core@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.6.1.tgz#f18681ba4ba1d8f25cfe2c624424749ae0b2590a" - integrity sha512-a4EeoynE4pU7qExfLr56nmZNu3ozeCNFk6rCBQqoXbSJy6UVG8nj7U8AWEPeBHqtdk2M+30YAVr6mJCQjkZZ7w== +"@percy/core@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.8.0.tgz#a8ce0be3edf7c10dfb6d71825ddaba3b20aaa340" + integrity sha512-v6kyC89iXYCWPuuG0XGtBKXVRc92/xZxKZQiehNC6I2/svCAW3GPWWQro8R+HX5ZUqVY6uqPHNdt2oJNKAEJNg== dependencies: - "@percy/client" "1.6.1" - "@percy/config" "1.6.1" - "@percy/dom" "1.6.1" - "@percy/logger" "1.6.1" + "@percy/client" "1.8.0" + "@percy/config" "1.8.0" + "@percy/dom" "1.8.0" + "@percy/logger" "1.8.0" content-disposition "^0.5.4" cross-spawn "^7.0.3" extract-zip "^2.0.1" @@ -1785,25 +1790,25 @@ dependencies: "@percy/sdk-utils" "^1.3.1" -"@percy/dom@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.6.1.tgz#8b8e82817dd88f8497ffce2d8fc5480466cbfef3" - integrity sha512-TAVGiE/7imR3Q6z1ogZFufX+SsPrElBOYNmj+MOFVJyzJ/cTjA9B510Blx1nXTu2VNdK2GAmRGQPbZDty34YMg== +"@percy/dom@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.8.0.tgz#6bd4bc72fd71c6fd445f2a223d5f860af9b36183" + integrity sha512-Q/a5uq3h7xUOZoFEYGhYxaNv6N/wxnQi9K3oaMfa1I8JvBqEVKzuw4izY5Q0OsOmiz78JJFmsLn8IbLh9NuP2w== -"@percy/env@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.6.1.tgz#5119de7526e006242b76688c24e94822113af139" - integrity sha512-AguYuqRcKHkCnWndvq1pTrxeUXT0mNULSi9p7D0nN1744036RcdVrE/EhZbgK2fshSHxIrnfPSj0Lnt4Of46lg== +"@percy/env@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.8.0.tgz#a3c3fa9cb071df0f94b7c197b9235aaf4e62a747" + integrity sha512-njfRWRrmyGVOWo1U+Zd8tyniOiNVYWvQvyodHWzqz7AoNz0Zr/+sCs8TsuMfF9QAb3MLcWn4LJoZE09mBIFBwg== -"@percy/logger@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.6.1.tgz#754e3bdaa4419aadc2d7fa95b63f4ef757483cd4" - integrity sha512-cOMzDbg6Or1SnyzT9xCnwIiMDQ4sUR7Ha91CE6NWpV273Ef5PtvBA0joIB7wWSe7t2/a8hQrYJ6eGU4rUcsuTw== +"@percy/logger@1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.8.0.tgz#e85f60dff1b118644f91f6d73c08a455169f57af" + integrity sha512-z1PNQFXDr4+y+a6FPzsZBuMttr0VrRpYSpxRhvhTZTjnLsBvgQ6VP2GxyFlp7E3Y4aG9Lmzau71s2Nlb35Ylgg== "@percy/sdk-utils@^1.3.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/sdk-utils/-/sdk-utils-1.6.1.tgz#257549ced3549188aebdb37ffb6e46f77dca77eb" - integrity sha512-H1a5UzlYYYUYj0I+hm3o1chgx1XMjTjeKHPneg25APfaDn5fCK1sm4xddurYFMk2sIv5Gxx8pvVMFSl4SlA/ZA== + version "1.8.0" + resolved "https://registry.yarnpkg.com/@percy/sdk-utils/-/sdk-utils-1.8.0.tgz#8db2aefbb211747f8f622db6daf25b781464a308" + integrity sha512-ehyYs7L4MDmqjBAfTHA8wDGrsDHiPrJ5SwMA9g9tT06VUey1JJtjSK30VidoouZQm+Sus9r5lW/K73CQZc3lSg== "@sentry/browser@^6.11.0": version "6.19.7" @@ -1869,9 +1874,9 @@ tslib "^1.9.3" "@sinclair/typebox@^0.24.1": - version "0.24.19" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.19.tgz#5297278e0d8a1aea084685a3216074910ac6c113" - integrity sha512-gHJu8cdYTD5p4UqmQHrxaWrtb/jkH5imLXzuBypWhKzNkW0qfmgz+w1xaJccWVuJta1YYUdlDiPHXRTR4Ku0MQ== + version "0.24.27" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.27.tgz#d55643516a1546174e10da681a8aaa81e757452d" + integrity sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg== "@sinonjs/commons@^1.7.0": version "1.8.3" @@ -1895,9 +1900,9 @@ "@sinonjs/commons" "^1.7.0" "@testing-library/dom@^8.0.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.16.0.tgz#d6fc50250aed17b1035ca1bd64655e342db3936a" - integrity sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w== + version "8.17.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.17.1.tgz#2d7af4ff6dad8d837630fecd08835aee08320ad7" + integrity sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -1954,9 +1959,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.17.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314" - integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA== + version "7.18.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.0.tgz#8134fd78cb39567465be65b9fdc16d378095f41f" + integrity sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw== dependencies: "@babel/types" "^7.3.0" @@ -2038,9 +2043,9 @@ "@types/node" "*" "@types/geojson@^7946.0.8": - version "7946.0.9" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.9.tgz#dd5d4c39c989c01f49c1b05df5826ffd4ddae1e9" - integrity sha512-snVY+R7d0VfUEHdfEe/NTFnz0+Qei0kEGIV8ErryWQwDXNS7uzhVEMcUtZ+uBHXSBcJa0zBcevBE0u41acbodQ== + version "7946.0.10" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" + integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== "@types/graceful-fs@^4.1.2": version "4.1.5" @@ -2115,14 +2120,14 @@ integrity sha512-jhMOZSS0UGYTS9pqvt6q3wtT3uvOSve5piTEmTMx3zzTuBLvSIMxSIBIc3d5lajVD5h4xc41AMZD2M5orN3PxA== "@types/node@*": - version "18.0.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199" - integrity sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ== + version "18.6.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.5.tgz#06caea822caf9e59d5034b695186ee74154d2802" + integrity sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw== "@types/node@^14.14.22", "@types/node@^14.14.31": - version "14.18.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41" - integrity sha512-x5W9s+8P4XteaxT/jKF0PSb7XEvo5VmqEWgsMlyeY4ZlLK8I6aH6g5TPPyDlLAep+GYf4kefb7HFyc7PAO3m+Q== + version "14.18.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.23.tgz#70f5f20b0b1b38f696848c1d3647bb95694e615e" + integrity sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2145,9 +2150,9 @@ integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== "@types/prettier@^2.1.5": - version "2.6.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" - integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== + version "2.7.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" + integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== "@types/prop-types@*": version "15.7.5" @@ -2267,9 +2272,9 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" - integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.11.tgz#5e10ca33e219807c0eee0f08b5efcba9b6a42c06" + integrity sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA== dependencies: "@types/yargs-parser" "*" @@ -2286,13 +2291,13 @@ integrity sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w== "@typescript-eslint/eslint-plugin@^5.6.0": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.6.tgz#9c6017b6c1d04894141b4a87816388967f64c359" - integrity sha512-J4zYMIhgrx4MgnZrSDD7sEnQp7FmhKNOaqaOpaoQ/SfdMfRB/0yvK74hTnvH+VQxndZynqs5/Hn4t+2/j9bADg== + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz#059798888720ec52ffa96c5f868e31a8f70fa3ec" + integrity sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg== dependencies: - "@typescript-eslint/scope-manager" "5.30.6" - "@typescript-eslint/type-utils" "5.30.6" - "@typescript-eslint/utils" "5.30.6" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/type-utils" "5.33.0" + "@typescript-eslint/utils" "5.33.0" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -2301,68 +2306,68 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.6.0": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.30.6.tgz#add440db038fa9d777e4ebdaf66da9e7fb7abe92" - integrity sha512-gfF9lZjT0p2ZSdxO70Xbw8w9sPPJGfAdjK7WikEjB3fcUI/yr9maUVEdqigBjKincUYNKOmf7QBMiTf719kbrA== + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.0.tgz#26ec3235b74f0667414613727cb98f9b69dc5383" + integrity sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w== dependencies: - "@typescript-eslint/scope-manager" "5.30.6" - "@typescript-eslint/types" "5.30.6" - "@typescript-eslint/typescript-estree" "5.30.6" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/typescript-estree" "5.33.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.30.6": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.30.6.tgz#ce1b49ff5ce47f55518d63dbe8fc9181ddbd1a33" - integrity sha512-Hkq5PhLgtVoW1obkqYH0i4iELctEKixkhWLPTYs55doGUKCASvkjOXOd/pisVeLdO24ZX9D6yymJ/twqpJiG3g== +"@typescript-eslint/scope-manager@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d" + integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw== dependencies: - "@typescript-eslint/types" "5.30.6" - "@typescript-eslint/visitor-keys" "5.30.6" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/visitor-keys" "5.33.0" -"@typescript-eslint/type-utils@5.30.6": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.30.6.tgz#a64aa9acbe609ab77f09f53434a6af2b9685f3af" - integrity sha512-GFVVzs2j0QPpM+NTDMXtNmJKlF842lkZKDSanIxf+ArJsGeZUIaeT4jGg+gAgHt7AcQSFwW7htzF/rbAh2jaVA== +"@typescript-eslint/type-utils@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz#92ad1fba973c078d23767ce2d8d5a601baaa9338" + integrity sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA== dependencies: - "@typescript-eslint/utils" "5.30.6" + "@typescript-eslint/utils" "5.33.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.30.6": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.30.6.tgz#86369d0a7af8c67024115ac1da3e8fb2d38907e1" - integrity sha512-HdnP8HioL1F7CwVmT4RaaMX57RrfqsOMclZc08wGMiDYJBsLGBM7JwXM4cZJmbWLzIR/pXg1kkrBBVpxTOwfUg== +"@typescript-eslint/types@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b" + integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw== -"@typescript-eslint/typescript-estree@5.30.6": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.6.tgz#a84a0d6a486f9b54042da1de3d671a2c9f14484e" - integrity sha512-Z7TgPoeYUm06smfEfYF0RBkpF8csMyVnqQbLYiGgmUSTaSXTP57bt8f0UFXstbGxKIreTwQCujtaH0LY9w9B+A== +"@typescript-eslint/typescript-estree@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf" + integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ== dependencies: - "@typescript-eslint/types" "5.30.6" - "@typescript-eslint/visitor-keys" "5.30.6" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/visitor-keys" "5.33.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.30.6": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.30.6.tgz#1de2da14f678e7d187daa6f2e4cdb558ed0609dc" - integrity sha512-xFBLc/esUbLOJLk9jKv0E9gD/OH966M40aY9jJ8GiqpSkP2xOV908cokJqqhVd85WoIvHVHYXxSFE4cCSDzVvA== +"@typescript-eslint/utils@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.0.tgz#46797461ce3146e21c095d79518cc0f8ec574038" + integrity sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.30.6" - "@typescript-eslint/types" "5.30.6" - "@typescript-eslint/typescript-estree" "5.30.6" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/typescript-estree" "5.33.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.30.6": - version "5.30.6" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.6.tgz#94dd10bb481c8083378d24de1742a14b38a2678c" - integrity sha512-41OiCjdL2mCaSDi2SvYbzFLlqqlm5v1ZW9Ym55wXKL/Rx6OOB1IbuFGo71Fj6Xy90gJDFTlgOS+vbmtGHPTQQA== +"@typescript-eslint/visitor-keys@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484" + integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw== dependencies: - "@typescript-eslint/types" "5.30.6" + "@typescript-eslint/types" "5.33.0" eslint-visitor-keys "^3.3.0" "@wojtekmaj/enzyme-adapter-react-17@^0.6.1": @@ -2415,10 +2420,10 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.7.1: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.2.4, acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== agent-base@6: version "6.0.2" @@ -2714,11 +2719,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axe-core@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.2.tgz#dcf7fb6dea866166c3eab33d68208afe4d5f670c" - integrity sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA== - axe-core@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" @@ -2795,29 +2795,29 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== +babel-plugin-polyfill-corejs2@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" + integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.2" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== +babel-plugin-polyfill-corejs3@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" + integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" -babel-plugin-polyfill-regenerator@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-plugin-polyfill-regenerator@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz#8f51809b6d5883e07e71548d75966ff7635527fe" + integrity sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" babel-preset-current-node-syntax@^1.0.0: version "1.0.1" @@ -2974,15 +2974,15 @@ browser-request@^0.3.3: resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" integrity sha512-YyNI4qJJ+piQG6MMEuo7J3Bzaqssufx04zpEKYfSrl/1Op59HWali9zMtBpXnkmqMcOuWJPZvudrm9wISmnCbg== -browserslist@^4.20.2, browserslist@^4.21.1: - version "4.21.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.1.tgz#c9b9b0a54c7607e8dc3e01a0d311727188011a00" - integrity sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ== +browserslist@^4.20.2, browserslist@^4.21.3: + version "4.21.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: - caniuse-lite "^1.0.30001359" - electron-to-chromium "^1.4.172" - node-releases "^2.0.5" - update-browserslist-db "^1.0.4" + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" + node-releases "^2.0.6" + update-browserslist-db "^1.0.5" bs58@^5.0.0: version "5.0.0" @@ -3086,10 +3086,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001359: - version "1.0.30001366" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001366.tgz#c73352c83830a9eaf2dea0ff71fb4b9a4bbaa89c" - integrity sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA== +caniuse-lite@^1.0.30001370: + version "1.0.30001374" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" + integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== capture-exit@^2.0.0: version "2.0.0" @@ -3403,17 +3403,17 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.23.4" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.4.tgz#56ad4a352884317a15f6b04548ff7139d23b917f" - integrity sha512-RkSRPe+JYEoflcsuxJWaiMPhnZoFS51FcIxm53k4KzhISCBTmaGlto9dTIrYuk0hnJc3G6pKufAKepHnBq6B6Q== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" + integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== dependencies: - browserslist "^4.21.1" + browserslist "^4.21.3" semver "7.0.0" core-js-pure@^3.20.2: - version "3.23.4" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.4.tgz#aba5c7fb297063444f6bf93afb0362151679a012" - integrity sha512-lizxkcgj3XDmi7TUBFe+bQ1vNpD5E4t76BrBWI3HdUxdw/Mq1VF4CkiHzIKyieECKtcODK2asJttoofEeUKICQ== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3" + integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg== core-js@^1.0.0: version "1.2.7" @@ -3560,9 +3560,9 @@ cypress-real-events@^1.7.1: integrity sha512-/Bg15RgJ0SYsuXc6lPqH08x19z6j2vmhWN4wXfJqm3z8BTAFiK2MvipZPzxT8Z0jJP0q7kuniWrLIvz/i/8lCQ== cypress@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.3.0.tgz#fae8d32f0822fcfb938e79c7c31ef344794336ae" - integrity sha512-txkQWKzvBVnWdCuKs5Xc08gjpO89W2Dom2wpZgT9zWZT5jXxqPIxqP/NC1YArtkpmp3fN5HW8aDjYBizHLUFvg== + version "10.4.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.4.0.tgz#bb5b3b6588ad49eff172fecf5778cc0da2980e4e" + integrity sha512-OM7F8MRE01SHQRVVzunid1ZK1m90XTxYnl+7uZfIrB4CYqUDCrZEeSyCXzIbsS6qcaijVCAhqDL60SxG8N6hew== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -3642,9 +3642,9 @@ date-names@^0.1.11: integrity sha512-IxxoeD9tdx8pXVcmqaRlPvrXIsSrSrIZzfzlOkm9u+hyzKp5Wk/odt9O/gd7Ockzy8n/WHeEpTVJ2bF3mMV4LA== dayjs@^1.10.4: - version "1.11.3" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258" - integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A== + version "1.11.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e" + integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g== debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" @@ -3756,9 +3756,9 @@ detect-node-es@^1.1.0: integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== diff-dom@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/diff-dom/-/diff-dom-4.2.3.tgz#c6234b49c1b49e41601d2f08dbb26cd57842de45" - integrity sha512-8OZPIbTWVhkQVlUlsb+VuMEMpTpKKhO5FTwds2bYVIaBiPNJCG1YW5qXUyLWMux5gC2UGyYXjtX05SPivnGMCw== + version "4.2.5" + resolved "https://registry.yarnpkg.com/diff-dom/-/diff-dom-4.2.5.tgz#5e093486d4ce706c702f0151c1b674aa015ac0a6" + integrity sha512-muGbiH5Mkj+bCigiG4x8tGES1JQQHp8UpAEaemOqfQkiwtCxKqDYPOeqBzoTRG+L7mKwHgTPY2WBlgOnnnUmAw== diff-match-patch@^1.0.5: version "1.0.5" @@ -3899,10 +3899,10 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.4.172: - version "1.4.188" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.188.tgz#1954bc444e6f6662cb39f24810f9e5022fcd5bb3" - integrity sha512-Zpa1+E+BVmD/orkyz1Z2dAT1XNUuVAHB3GrogfyY66dXN0ZWSsygI8+u6QTDai1ZayLcATDJpcv2Z2AZjEcr1A== +electron-to-chromium@^1.4.202: + version "1.4.212" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.212.tgz#20cd48e88288fd2428138c108804edb1961bf559" + integrity sha512-LjQUg1SpLj2GfyaPDVBUHdhmlDU1vDB4f0mJWSGkISoXQrn5/lH3ECPCuo2Bkvf6Y30wO+b69te+rZK/llZmjg== emittery@^0.8.1: version "0.8.1" @@ -4083,9 +4083,9 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.61" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.61.tgz#311de37949ef86b6b0dcea894d1ffedb909d3269" - integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA== + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== dependencies: es6-iterator "^2.0.3" es6-symbol "^3.1.3" @@ -4201,20 +4201,20 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.14.1" eslint-plugin-jsx-a11y@^6.5.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.0.tgz#2c5ac12e013eb98337b9aa261c3b355275cc6415" - integrity sha512-kTeLuIzpNhXL2CwLlc8AHI0aFRwWHcg483yepO9VQiHzM9bZwJdzTkzBszbuPrbgGmq2rlX/FaT2fJQsjUSHsw== + version "6.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" + integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== dependencies: - "@babel/runtime" "^7.18.3" + "@babel/runtime" "^7.18.9" aria-query "^4.2.2" array-includes "^3.1.5" ast-types-flow "^0.0.7" - axe-core "^4.4.2" + axe-core "^4.4.3" axobject-query "^2.2.0" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" has "^1.0.3" - jsx-ast-utils "^3.3.1" + jsx-ast-utils "^3.3.2" language-tags "^1.0.5" minimatch "^3.1.2" semver "^6.3.0" @@ -4329,11 +4329,11 @@ eslint@8.9.0: v8-compile-cache "^2.0.3" espree@^9.3.1, espree@^9.3.2: - version "9.3.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" - integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== + version "9.3.3" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" + integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== dependencies: - acorn "^8.7.1" + acorn "^8.8.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" @@ -4380,9 +4380,9 @@ event-emitter@^0.3.5: es5-ext "~0.10.14" eventemitter2@^6.4.3: - version "6.4.6" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.6.tgz#92d56569cc147a4d9b9da9e942e89b20ce236b0a" - integrity sha512-OHqo4wbHX5VbvlbB6o6eDwhYmiTjrpWACjF8Pmof/GTD6rdBNdZFNck3xlhqOiQFGCOoq3uzHvA0cQpFHIGVAQ== + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== events@^3.2.0: version "3.3.0" @@ -4586,9 +4586,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastest-levenshtein@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" - integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: version "1.13.0" @@ -4959,7 +4959,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5000,9 +5000,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.15.0, globals@^13.6.0: - version "13.16.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.16.0.tgz#9be4aca28f311aaeb974ea54978ebbb5e35ce46a" - integrity sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q== + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== dependencies: type-fest "^0.20.2" @@ -5073,7 +5073,7 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -5422,9 +5422,9 @@ is-ci@^3.0.0: ci-info "^3.2.0" is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== dependencies: has "^1.0.3" @@ -5723,9 +5723,9 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" - integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -6424,18 +6424,18 @@ jsprim@^2.0.2: json-schema "0.4.0" verror "1.10.0" -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" - integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== dependencies: array-includes "^3.1.5" - object.assign "^4.1.2" + object.assign "^4.1.3" jszip@^3.7.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.0.tgz#faf3db2b4b8515425e34effcdbb086750a346061" - integrity sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q== + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -6778,7 +6778,7 @@ matrix-events-sdk@^0.0.1-beta.7: "matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "19.2.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/cf33569a2187628dd7954ac771995cce3e804af4" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/3e37c7426420786c53992d48b85fe3ed37fcf40f" dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" @@ -6793,9 +6793,9 @@ matrix-events-sdk@^0.0.1-beta.7: unhomoglyph "^1.0.6" matrix-mock-request@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.1.0.tgz#86f5b0ef846865d0767d3a8e64f5bcd6ca94c178" - integrity sha512-Cjpl3yP6h0yu5GKG89m1XZXZlm69Kg/qHV41N/t6SrQsgcfM3Bfavqx9YrtG0UnuXGy4bBSZIe1QiWVeFPZw1A== + version "2.1.2" + resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.1.2.tgz#11e38ed1233dced88a6f2bfba1684d5c5b3aa2c2" + integrity sha512-/OXCIzDGSLPJ3fs+uzDrtaOHI/Sqp4iEuniRn31U8S06mPXbvAnXknHqJ4c6A/KVwJj/nPFbGXpK4wPM038I6A== dependencies: expect "^28.1.0" @@ -7050,7 +7050,7 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.5: +node-releases@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== @@ -7157,14 +7157,14 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" + integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" object-keys "^1.1.1" object.entries@^1.1.1, object.entries@^1.1.5: @@ -7532,9 +7532,9 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.3.11, postcss@^8.4.14: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== + version "8.4.16" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" + integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== dependencies: nanoid "^3.3.4" picocolors "^1.0.0" @@ -7857,9 +7857,9 @@ react-test-renderer@^17.0.0, react-test-renderer@^17.0.2: scheduler "^0.20.2" react-transition-group@^4.4.1: - version "4.4.2" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" - integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== dependencies: "@babel/runtime" "^7.5.5" dom-helpers "^5.0.1" @@ -8228,9 +8228,9 @@ sane@^4.0.3: walker "~1.0.5" sanitize-html@^2.3.2: - version "2.7.0" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279" - integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.1.tgz#a6c2c1a88054a79eeacfac9b0a43f1b393476901" + integrity sha512-oOpe8l4J8CaBk++2haoN5yNI5beekjuHv3JRPKUx/7h40Rdr85pemn4NkvUB3TcBP7yjat574sPlcMAyv4UQig== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" @@ -9028,9 +9028,9 @@ type@^1.0.1: integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" - integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -9125,10 +9125,10 @@ untildify@^4.0.0: resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -update-browserslist-db@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" - integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== +update-browserslist-db@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" + integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -9417,14 +9417,14 @@ write-file-atomic@^4.0.1: signal-exit "^3.0.7" ws@^7.4.6: - version "7.5.8" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" - integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.0.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" - integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== xml-name-validator@^3.0.0: version "3.0.0" @@ -9480,9 +9480,9 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^13.2.4: version "13.3.2" From 394e181854947f0be1d9e5b8f83a1edf3040da4d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Aug 2022 20:46:59 +0100 Subject: [PATCH 021/107] Define interface for RLS to ease wiring in Sliding Sync (#9150) * Define iface for RLS * Iterate interface --- src/@types/global.d.ts | 4 +- src/components/views/rooms/RoomSublist.tsx | 2 +- src/stores/room-list/Interface.ts | 107 ++++++++++++++++++ src/stores/room-list/RoomListStore.ts | 15 +-- test/components/views/rooms/RoomList-test.tsx | 4 +- 5 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 src/stores/room-list/Interface.ts diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 7c595640fd0..00758371112 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -23,7 +23,7 @@ import ContentMessages from "../ContentMessages"; import { IMatrixClientPeg } from "../MatrixClientPeg"; import ToastStore from "../stores/ToastStore"; import DeviceListener from "../DeviceListener"; -import { RoomListStoreClass } from "../stores/room-list/RoomListStore"; +import { RoomListStore } from "../stores/room-list/Interface"; import { PlatformPeg } from "../PlatformPeg"; import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore"; import { IntegrationManagers } from "../integrations/IntegrationManagers"; @@ -79,7 +79,7 @@ declare global { mxContentMessages: ContentMessages; mxToastStore: ToastStore; mxDeviceListener: DeviceListener; - mxRoomListStore: RoomListStoreClass; + mxRoomListStore: RoomListStore; mxRoomListLayoutStore: RoomListLayoutStore; mxPlatformPeg: PlatformPeg; mxIntegrationManagers: typeof IntegrationManagers; diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index 677b63bcf95..bd09ecb4a01 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -372,7 +372,7 @@ export default class RoomSublist extends React.Component { }; private onTagSortChanged = async (sort: SortAlgorithm) => { - await RoomListStore.instance.setTagSorting(this.props.tagId, sort); + RoomListStore.instance.setTagSorting(this.props.tagId, sort); this.forceUpdate(); }; diff --git a/src/stores/room-list/Interface.ts b/src/stores/room-list/Interface.ts new file mode 100644 index 00000000000..ab538709896 --- /dev/null +++ b/src/stores/room-list/Interface.ts @@ -0,0 +1,107 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import type { Room } from "matrix-js-sdk/src/models/room"; +import type { EventEmitter } from "events"; +import { ITagMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models"; +import { RoomUpdateCause, TagID } from "./models"; +import { IFilterCondition } from "./filters/IFilterCondition"; + +export enum RoomListStoreEvent { + // The event/channel which is called when the room lists have been changed. + ListsUpdate = "lists_update", +} + +export interface RoomListStore extends EventEmitter { + /** + * Gets an ordered set of rooms for the all known tags. + * @returns {ITagMap} The cached list of rooms, ordered, + * for each tag. May be empty, but never null/undefined. + */ + get orderedLists(): ITagMap; + + /** + * Set the sort algorithm for the specified tag. + * @param tagId the tag to set the algorithm for + * @param sort the sort algorithm to set to + */ + setTagSorting(tagId: TagID, sort: SortAlgorithm): void; + + /** + * Get the sort algorithm for the specified tag. + * @param tagId tag to get the sort algorithm for + * @returns the sort algorithm + */ + getTagSorting(tagId: TagID): SortAlgorithm; + + /** + * Set the list algorithm for the specified tag. + * @param tagId the tag to set the algorithm for + * @param order the list algorithm to set to + */ + setListOrder(tagId: TagID, order: ListAlgorithm): void; + + /** + * Get the list algorithm for the specified tag. + * @param tagId tag to get the list algorithm for + * @returns the list algorithm + */ + getListOrder(tagId: TagID): ListAlgorithm; + + /** + * Regenerates the room whole room list, discarding any previous results. + * + * Note: This is only exposed externally for the tests. Do not call this from within + * the app. + * @param params.trigger Set to false to prevent a list update from being sent. Should only + * be used if the calling code will manually trigger the update. + */ + regenerateAllLists(params: { trigger: boolean }): void; + + /** + * Adds a filter condition to the room list store. Filters may be applied async, + * and thus might not cause an update to the store immediately. + * @param {IFilterCondition} filter The filter condition to add. + */ + addFilter(filter: IFilterCondition): Promise; + + /** + * Removes a filter condition from the room list store. If the filter was + * not previously added to the room list store, this will no-op. The effects + * of removing a filter may be applied async and therefore might not cause + * an update right away. + * @param {IFilterCondition} filter The filter condition to remove. + */ + removeFilter(filter: IFilterCondition): void; + + /** + * Gets the tags for a room identified by the store. The returned set + * should never be empty, and will contain DefaultTagID.Untagged if + * the store is not aware of any tags. + * @param room The room to get the tags for. + * @returns The tags for the room. + */ + getTagsForRoom(room: Room): TagID[]; + + /** + * Manually update a room with a given cause. This should only be used if the + * room list store would otherwise be incapable of doing the update itself. Note + * that this may race with the room list's regular operation. + * @param {Room} room The room to update. + * @param {RoomUpdateCause} cause The cause to update for. + */ + manualRoomUpdate(room: Room, cause: RoomUpdateCause): Promise; +} diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index c15567afdc8..9083943ed9f 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -37,18 +37,15 @@ import { RoomNotificationStateStore } from "../notifications/RoomNotificationSta import { VisibilityProvider } from "./filters/VisibilityProvider"; import { SpaceWatcher } from "./SpaceWatcher"; import { IRoomTimelineActionPayload } from "../../actions/MatrixActionCreators"; +import { RoomListStore as Interface, RoomListStoreEvent } from "./Interface"; interface IState { // state is tracked in underlying classes } -/** - * The event/channel which is called when the room lists have been changed. Raised - * with one argument: the instance of the store. - */ -export const LISTS_UPDATE_EVENT = "lists_update"; +export const LISTS_UPDATE_EVENT = RoomListStoreEvent.ListsUpdate; -export class RoomListStoreClass extends AsyncStoreWithClient { +export class RoomListStoreClass extends AsyncStoreWithClient implements Interface { /** * Set to true if you're running tests on the store. Should not be touched in * any other environment. @@ -365,7 +362,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { this.algorithm.updatesInhibited = false; } - public async setTagSorting(tagId: TagID, sort: SortAlgorithm) { + public setTagSorting(tagId: TagID, sort: SortAlgorithm) { this.setAndPersistTagSorting(tagId, sort); this.updateFn.trigger(); } @@ -602,9 +599,9 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } export default class RoomListStore { - private static internalInstance: RoomListStoreClass; + private static internalInstance: Interface; - public static get instance(): RoomListStoreClass { + public static get instance(): Interface { if (!RoomListStore.internalInstance) { RoomListStore.internalInstance = new RoomListStoreClass(); } diff --git a/test/components/views/rooms/RoomList-test.tsx b/test/components/views/rooms/RoomList-test.tsx index 7b9d3ee3113..6fa3fe22cf4 100644 --- a/test/components/views/rooms/RoomList-test.tsx +++ b/test/components/views/rooms/RoomList-test.tsx @@ -137,7 +137,7 @@ describe('RoomList', () => { client.getRoom.mockImplementation((roomId) => roomMap[roomId]); // Now that everything has been set up, prepare and update the store - await RoomListStore.instance.makeReady(client); + await (RoomListStore.instance as RoomListStoreClass).makeReady(client); done(); }); @@ -150,7 +150,7 @@ describe('RoomList', () => { } await RoomListLayoutStore.instance.resetLayouts(); - await RoomListStore.instance.resetStore(); + await (RoomListStore.instance as RoomListStoreClass).resetStore(); done(); }); From 9ed555050185457f2dc80f452de534c30fc72146 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Wed, 10 Aug 2022 08:51:54 +0200 Subject: [PATCH 022/107] Implement GroupCallUtils (#9131) * Implement GroupCallUtils * Trigger CI * Use UnstableValue for new call event types * Implement PR feedback --- src/utils/GroupCallUtils.ts | 175 ++++++++ test/utils/GroupCallUtils-test.ts | 673 ++++++++++++++++++++++++++++++ 2 files changed, 848 insertions(+) create mode 100644 src/utils/GroupCallUtils.ts create mode 100644 test/utils/GroupCallUtils-test.ts diff --git a/src/utils/GroupCallUtils.ts b/src/utils/GroupCallUtils.ts new file mode 100644 index 00000000000..3af6a2b07a0 --- /dev/null +++ b/src/utils/GroupCallUtils.ts @@ -0,0 +1,175 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { EventTimeline, MatrixClient, MatrixEvent, RoomState } from "matrix-js-sdk/src/matrix"; +import { UnstableValue } from "matrix-js-sdk/src/NamespacedValue"; +import { deepCopy } from "matrix-js-sdk/src/utils"; + +export const STUCK_DEVICE_TIMEOUT_MS = 1000 * 60 * 60; // 1 hour + +export const CALL_STATE_EVENT_TYPE = new UnstableValue("m.call", "org.matrix.msc3401.call"); +export const CALL_MEMBER_STATE_EVENT_TYPE = new UnstableValue("m.call.member", "org.matrix.msc3401.call.member"); +const CALL_STATE_EVENT_TERMINATED = "m.terminated"; + +interface MDevice { + ["m.device_id"]: string; +} + +interface MCall { + ["m.call_id"]: string; + ["m.devices"]: Array; +} + +interface MCallMemberContent { + ["m.expires_ts"]: number; + ["m.calls"]: Array; +} + +const getRoomState = (client: MatrixClient, roomId: string): RoomState => { + return client.getRoom(roomId) + ?.getLiveTimeline() + ?.getState?.(EventTimeline.FORWARDS); +}; + +/** + * Returns all room state events for the stable and unstable type value. + */ +const getRoomStateEvents = ( + client: MatrixClient, + roomId: string, + type: UnstableValue, +): MatrixEvent[] => { + const roomState = getRoomState(client, roomId); + if (!roomState) return []; + + return [ + ...roomState.getStateEvents(type.name), + ...roomState.getStateEvents(type.altName), + ]; +}; + +/** + * Finds the latest, non-terminated call state event. + */ +export const getGroupCall = (client: MatrixClient, roomId: string): MatrixEvent => { + return getRoomStateEvents(client, roomId, CALL_STATE_EVENT_TYPE) + .sort((a: MatrixEvent, b: MatrixEvent) => b.getTs() - a.getTs()) + .find((event: MatrixEvent) => { + return !(CALL_STATE_EVENT_TERMINATED in event.getContent()); + }); +}; + +/** + * Finds the "m.call.member" events for an "m.call" event. + * + * @returns {MatrixEvent[]} non-expired "m.call.member" events for the call + */ +export const useConnectedMembers = (client: MatrixClient, callEvent: MatrixEvent): MatrixEvent[] => { + if (!CALL_STATE_EVENT_TYPE.matches(callEvent.getType())) return []; + + const callId = callEvent.getStateKey(); + const now = Date.now(); + + return getRoomStateEvents(client, callEvent.getRoomId(), CALL_MEMBER_STATE_EVENT_TYPE) + .filter((callMemberEvent: MatrixEvent): boolean => { + const { + ["m.expires_ts"]: expiresTs, + ["m.calls"]: calls, + } = callMemberEvent.getContent(); + + // state event expired + if (expiresTs && expiresTs < now) return false; + + return !!calls?.find((call: MCall) => call["m.call_id"] === callId); + }) || []; +}; + +/** + * Removes a list of devices from a call. + * Only works for the current user's devices. + */ +const removeDevices = async (client: MatrixClient, callEvent: MatrixEvent, deviceIds: string[]): Promise => { + if (!CALL_STATE_EVENT_TYPE.matches(callEvent.getType())) return; + + const roomId = callEvent.getRoomId(); + const roomState = getRoomState(client, roomId); + if (!roomState) return; + + const callMemberEvent = roomState.getStateEvents(CALL_MEMBER_STATE_EVENT_TYPE.name, client.getUserId()) + ?? roomState.getStateEvents(CALL_MEMBER_STATE_EVENT_TYPE.altName, client.getUserId()); + const callMemberEventContent = callMemberEvent?.getContent(); + if ( + !Array.isArray(callMemberEventContent?.["m.calls"]) + || callMemberEventContent?.["m.calls"].length === 0 + ) { + return; + } + + // copy the content to prevent mutations + const newContent = deepCopy(callMemberEventContent); + const callId = callEvent.getStateKey(); + let changed = false; + + newContent["m.calls"].forEach((call: MCall) => { + // skip other calls + if (call["m.call_id"] !== callId) return; + + call["m.devices"] = call["m.devices"]?.filter((device: MDevice) => { + if (deviceIds.includes(device["m.device_id"])) { + changed = true; + return false; + } + + return true; + }); + }); + + if (changed) { + // only send a new state event if there has been a change + newContent["m.expires_ts"] = Date.now() + STUCK_DEVICE_TIMEOUT_MS; + await client.sendStateEvent( + roomId, + CALL_MEMBER_STATE_EVENT_TYPE.name, + newContent, + client.getUserId(), + ); + } +}; + +/** + * Removes the current device from a call. + */ +export const removeOurDevice = async (client: MatrixClient, callEvent: MatrixEvent) => { + return removeDevices(client, callEvent, [client.getDeviceId()]); +}; + +/** + * Removes all devices of the current user that have not been seen within the STUCK_DEVICE_TIMEOUT_MS. + * Does per default not remove the current device unless includeCurrentDevice is true. + * + * @param {boolean} includeCurrentDevice - Whether to include the current device of this session here. + */ +export const fixStuckDevices = async (client: MatrixClient, callEvent: MatrixEvent, includeCurrentDevice: boolean) => { + const now = Date.now(); + const { devices: myDevices } = await client.getDevices(); + const currentDeviceId = client.getDeviceId(); + const devicesToBeRemoved = myDevices.filter(({ last_seen_ts: lastSeenTs, device_id: deviceId }) => { + return lastSeenTs + && (deviceId !== currentDeviceId || includeCurrentDevice) + && (now - lastSeenTs) > STUCK_DEVICE_TIMEOUT_MS; + }).map(d => d.device_id); + return removeDevices(client, callEvent, devicesToBeRemoved); +}; diff --git a/test/utils/GroupCallUtils-test.ts b/test/utils/GroupCallUtils-test.ts new file mode 100644 index 00000000000..971527e8037 --- /dev/null +++ b/test/utils/GroupCallUtils-test.ts @@ -0,0 +1,673 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked } from "jest-mock"; +import { IMyDevice, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; + +import { + CALL_MEMBER_STATE_EVENT_TYPE, + CALL_STATE_EVENT_TYPE, + fixStuckDevices, + getGroupCall, + removeOurDevice, + STUCK_DEVICE_TIMEOUT_MS, + useConnectedMembers, +} from "../../src/utils/GroupCallUtils"; +import { createTestClient, mkEvent } from "../test-utils"; + +[ + { + callStateEventType: CALL_STATE_EVENT_TYPE.name, + callMemberStateEventType: CALL_MEMBER_STATE_EVENT_TYPE.name, + }, + { + callStateEventType: CALL_STATE_EVENT_TYPE.altName, + callMemberStateEventType: CALL_MEMBER_STATE_EVENT_TYPE.altName, + }, +].forEach(({ callStateEventType, callMemberStateEventType }) => { + describe(`GroupCallUtils (${callStateEventType}, ${callMemberStateEventType})`, () => { + const roomId = "!room:example.com"; + let client: MatrixClient; + let callEvent: MatrixEvent; + const callId = "test call"; + const callId2 = "test call 2"; + const userId1 = "@user1:example.com"; + const now = 1654616071686; + + const setUpNonCallStateEvent = () => { + callEvent = mkEvent({ + room: roomId, + user: userId1, + event: true, + type: "test", + skey: userId1, + content: {}, + }); + }; + + const setUpEmptyStateKeyCallEvent = () => { + callEvent = mkEvent({ + room: roomId, + user: userId1, + event: true, + type: callStateEventType, + skey: "", + content: {}, + }); + }; + + const setUpValidCallEvent = () => { + callEvent = mkEvent({ + room: roomId, + user: userId1, + event: true, + type: callStateEventType, + skey: callId, + content: {}, + }); + }; + + beforeEach(() => { + client = createTestClient(); + }); + + describe("getGroupCall", () => { + describe("for a non-existing room", () => { + beforeEach(() => { + mocked(client.getRoom).mockReturnValue(null); + }); + + it("should return null", () => { + expect(getGroupCall(client, roomId)).toBeUndefined(); + }); + }); + + describe("for an existing room", () => { + let room: Room; + + beforeEach(() => { + room = new Room(roomId, client, client.getUserId()); + mocked(client.getRoom).mockImplementation((rid: string) => { + return rid === roomId + ? room + : null; + }); + }); + + it("should return null if no 'call' state event exist", () => { + expect(getGroupCall(client, roomId)).toBeUndefined(); + }); + + describe("with call state events", () => { + let callEvent1: MatrixEvent; + let callEvent2: MatrixEvent; + let callEvent3: MatrixEvent; + + beforeEach(() => { + callEvent1 = mkEvent({ + room: roomId, + user: client.getUserId(), + event: true, + type: callStateEventType, + content: {}, + ts: 150, + skey: "call1", + }); + room.getLiveTimeline().addEvent(callEvent1, { + toStartOfTimeline: false, + }); + + callEvent2 = mkEvent({ + room: roomId, + user: client.getUserId(), + event: true, + type: callStateEventType, + content: {}, + ts: 100, + skey: "call2", + }); + room.getLiveTimeline().addEvent(callEvent2, { + toStartOfTimeline: false, + }); + + // terminated call - should never be returned + callEvent3 = mkEvent({ + room: roomId, + user: client.getUserId(), + event: true, + type: callStateEventType, + content: { + ["m.terminated"]: "time's up", + }, + ts: 500, + skey: "call3", + }); + room.getLiveTimeline().addEvent(callEvent3, { + toStartOfTimeline: false, + }); + }); + + it("should return the newest call state event (1)", () => { + expect(getGroupCall(client, roomId)).toBe(callEvent1); + }); + + it("should return the newest call state event (2)", () => { + callEvent2.getTs = () => 200; + expect(getGroupCall(client, roomId)).toBe(callEvent2); + }); + }); + }); + }); + + describe("useConnectedMembers", () => { + describe("for a non-call event", () => { + beforeEach(() => { + setUpNonCallStateEvent(); + }); + + it("should return an empty list", () => { + expect(useConnectedMembers(client, callEvent)).toEqual([]); + }); + }); + + describe("for an empty state key", () => { + beforeEach(() => { + setUpEmptyStateKeyCallEvent(); + }); + + it("should return an empty list", () => { + expect(useConnectedMembers(client, callEvent)).toEqual([]); + }); + }); + + describe("for a valid call state event", () => { + beforeEach(() => { + setUpValidCallEvent(); + }); + + describe("and a non-existing room", () => { + beforeEach(() => { + mocked(client.getRoom).mockReturnValue(null); + }); + + it("should return an empty list", () => { + expect(useConnectedMembers(client, callEvent)).toEqual([]); + }); + }); + + describe("and an existing room", () => { + let room: Room; + + beforeEach(() => { + room = new Room(roomId, client, client.getUserId()); + mocked(client.getRoom).mockImplementation((rid: string) => { + return rid === roomId + ? room + : null; + }); + }); + + it("should return an empty list if no call member state events exist", () => { + expect(useConnectedMembers(client, callEvent)).toEqual([]); + }); + + describe("and some call member state events", () => { + const userId2 = "@user2:example.com"; + const userId3 = "@user3:example.com"; + const userId4 = "@user4:example.com"; + let expectedEvent1: MatrixEvent; + let expectedEvent2: MatrixEvent; + + beforeEach(() => { + jest.useFakeTimers() + .setSystemTime(now); + + expectedEvent1 = mkEvent({ + event: true, + room: roomId, + user: userId1, + skey: userId1, + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now + 100, + ["m.calls"]: [ + { + ["m.call_id"]: callId2, + }, + { + ["m.call_id"]: callId, + }, + ], + }, + }); + room.getLiveTimeline().addEvent(expectedEvent1, { toStartOfTimeline: false }); + + expectedEvent2 = mkEvent({ + event: true, + room: roomId, + user: userId2, + skey: userId2, + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now + 100, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + }, + ], + }, + }); + room.getLiveTimeline().addEvent(expectedEvent2, { toStartOfTimeline: false }); + + // expired event + const event3 = mkEvent({ + event: true, + room: roomId, + user: userId3, + skey: userId3, + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now - 100, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + }, + ], + }, + }); + room.getLiveTimeline().addEvent(event3, { toStartOfTimeline: false }); + + // other call + const event4 = mkEvent({ + event: true, + room: roomId, + user: userId4, + skey: userId4, + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now + 100, + ["m.calls"]: [ + { + ["m.call_id"]: callId2, + }, + ], + }, + }); + room.getLiveTimeline().addEvent(event4, { toStartOfTimeline: false }); + + // empty calls + const event5 = mkEvent({ + event: true, + room: roomId, + user: userId4, + skey: userId4, + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now + 100, + ["m.calls"]: [], + }, + }); + room.getLiveTimeline().addEvent(event5, { toStartOfTimeline: false }); + + // no calls prop + const event6 = mkEvent({ + event: true, + room: roomId, + user: userId4, + skey: userId4, + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now + 100, + }, + }); + room.getLiveTimeline().addEvent(event6, { toStartOfTimeline: false }); + }); + + it("should return the expected call member events", () => { + const callMemberEvents = useConnectedMembers(client, callEvent); + expect(callMemberEvents).toHaveLength(2); + expect(callMemberEvents).toContain(expectedEvent1); + expect(callMemberEvents).toContain(expectedEvent2); + }); + }); + }); + }); + }); + + describe("removeOurDevice", () => { + describe("for a non-call event", () => { + beforeEach(() => { + setUpNonCallStateEvent(); + }); + + it("should not update the state", () => { + removeOurDevice(client, callEvent); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + }); + + describe("for an empty state key", () => { + beforeEach(() => { + setUpEmptyStateKeyCallEvent(); + }); + + it("should not update the state", () => { + removeOurDevice(client, callEvent); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + }); + + describe("for a valid call state event", () => { + beforeEach(() => { + setUpValidCallEvent(); + }); + + describe("and a non-existing room", () => { + beforeEach(() => { + mocked(client.getRoom).mockReturnValue(null); + }); + + it("should not update the state", () => { + removeOurDevice(client, callEvent); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + }); + + describe("and an existing room", () => { + let room: Room; + + beforeEach(() => { + room = new Room(roomId, client, client.getUserId()); + room.getLiveTimeline().addEvent(callEvent, { toStartOfTimeline: false }); + mocked(client.getRoom).mockImplementation((rid: string) => { + return rid === roomId + ? room + : null; + }); + }); + + it("should not update the state if no call member event exists", () => { + removeOurDevice(client, callEvent); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + + describe("and a call member state event", () => { + beforeEach(() => { + jest.useFakeTimers() + .setSystemTime(now); + + const callMemberEvent = mkEvent({ + event: true, + room: roomId, + user: client.getUserId(), + skey: client.getUserId(), + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now - 100, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + ["m.devices"]: [ + // device to be removed + { "m.device_id": client.getDeviceId() }, + { "m.device_id": "device 2" }, + ], + }, + { + // no device list + ["m.call_id"]: callId, + }, + { + // other call + ["m.call_id"]: callId2, + ["m.devices"]: [ + { "m.device_id": client.getDeviceId() }, + ], + }, + ], + }, + }); + room.getLiveTimeline().addEvent(callMemberEvent, { toStartOfTimeline: false }); + }); + + it("should remove the device from the call", async () => { + await removeOurDevice(client, callEvent); + expect(client.sendStateEvent).toHaveBeenCalledTimes(1); + expect(client.sendStateEvent).toHaveBeenCalledWith( + roomId, + CALL_MEMBER_STATE_EVENT_TYPE.name, + { + ["m.expires_ts"]: now + STUCK_DEVICE_TIMEOUT_MS, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + ["m.devices"]: [ + { "m.device_id": "device 2" }, + ], + }, + { + // no device list + ["m.call_id"]: callId, + }, + { + // other call + ["m.call_id"]: callId2, + ["m.devices"]: [ + { "m.device_id": client.getDeviceId() }, + ], + }, + ], + }, + client.getUserId(), + ); + }); + }); + }); + }); + }); + + describe("fixStuckDevices", () => { + let thisDevice: IMyDevice; + let otherDevice: IMyDevice; + let noLastSeenTsDevice: IMyDevice; + let stuckDevice: IMyDevice; + + beforeEach(() => { + jest.useFakeTimers() + .setSystemTime(now); + + thisDevice = { device_id: "ABCDEFGHI", last_seen_ts: now - STUCK_DEVICE_TIMEOUT_MS - 100 }; + otherDevice = { device_id: "ABCDEFGHJ", last_seen_ts: now }; + noLastSeenTsDevice = { device_id: "ABCDEFGHK" }; + stuckDevice = { device_id: "ABCDEFGHL", last_seen_ts: now - STUCK_DEVICE_TIMEOUT_MS - 100 }; + + mocked(client.getDeviceId).mockReturnValue(thisDevice.device_id); + mocked(client.getDevices).mockResolvedValue({ + devices: [ + thisDevice, + otherDevice, + noLastSeenTsDevice, + stuckDevice, + ], + }); + }); + + describe("for a non-call event", () => { + beforeEach(() => { + setUpNonCallStateEvent(); + }); + + it("should not update the state", () => { + fixStuckDevices(client, callEvent, true); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + }); + + describe("for an empty state key", () => { + beforeEach(() => { + setUpEmptyStateKeyCallEvent(); + }); + + it("should not update the state", () => { + fixStuckDevices(client, callEvent, true); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + }); + + describe("for a valid call state event", () => { + beforeEach(() => { + setUpValidCallEvent(); + }); + + describe("and a non-existing room", () => { + beforeEach(() => { + mocked(client.getRoom).mockReturnValue(null); + }); + + it("should not update the state", () => { + fixStuckDevices(client, callEvent, true); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + }); + + describe("and an existing room", () => { + let room: Room; + + beforeEach(() => { + room = new Room(roomId, client, client.getUserId()); + room.getLiveTimeline().addEvent(callEvent, { toStartOfTimeline: false }); + mocked(client.getRoom).mockImplementation((rid: string) => { + return rid === roomId + ? room + : null; + }); + }); + + it("should not update the state if no call member event exists", () => { + fixStuckDevices(client, callEvent, true); + expect(client.sendStateEvent).not.toHaveBeenCalled(); + }); + + describe("and a call member state event", () => { + beforeEach(() => { + const callMemberEvent = mkEvent({ + event: true, + room: roomId, + user: client.getUserId(), + skey: client.getUserId(), + type: callMemberStateEventType, + content: { + ["m.expires_ts"]: now - 100, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + ["m.devices"]: [ + { "m.device_id": thisDevice.device_id }, + { "m.device_id": otherDevice.device_id }, + { "m.device_id": noLastSeenTsDevice.device_id }, + { "m.device_id": stuckDevice.device_id }, + ], + }, + { + // no device list + ["m.call_id"]: callId, + }, + { + // other call + ["m.call_id"]: callId2, + ["m.devices"]: [ + { "m.device_id": stuckDevice.device_id }, + ], + }, + ], + }, + }); + room.getLiveTimeline().addEvent(callMemberEvent, { toStartOfTimeline: false }); + }); + + it("should remove stuck devices from the call, except this device", async () => { + await fixStuckDevices(client, callEvent, false); + expect(client.sendStateEvent).toHaveBeenCalledTimes(1); + expect(client.sendStateEvent).toHaveBeenCalledWith( + roomId, + CALL_MEMBER_STATE_EVENT_TYPE.name, + { + ["m.expires_ts"]: now + STUCK_DEVICE_TIMEOUT_MS, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + ["m.devices"]: [ + { "m.device_id": thisDevice.device_id }, + { "m.device_id": otherDevice.device_id }, + { "m.device_id": noLastSeenTsDevice.device_id }, + ], + }, + { + // no device list + ["m.call_id"]: callId, + }, + { + // other call + ["m.call_id"]: callId2, + ["m.devices"]: [ + { "m.device_id": stuckDevice.device_id }, + ], + }, + ], + }, + client.getUserId(), + ); + }); + + it("should remove stuck devices from the call, including this device", async () => { + await fixStuckDevices(client, callEvent, true); + expect(client.sendStateEvent).toHaveBeenCalledTimes(1); + expect(client.sendStateEvent).toHaveBeenCalledWith( + roomId, + CALL_MEMBER_STATE_EVENT_TYPE.name, + { + ["m.expires_ts"]: now + STUCK_DEVICE_TIMEOUT_MS, + ["m.calls"]: [ + { + ["m.call_id"]: callId, + ["m.devices"]: [ + { "m.device_id": otherDevice.device_id }, + { "m.device_id": noLastSeenTsDevice.device_id }, + ], + }, + { + // no device list + ["m.call_id"]: callId, + }, + { + // other call + ["m.call_id"]: callId2, + ["m.devices"]: [ + { "m.device_id": stuckDevice.device_id }, + ], + }, + ], + }, + client.getUserId(), + ); + }); + }); + }); + }); + }); + }); +}); + From 3b64a7999c79ef2a2d8cdc91535756d034666e88 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 08:20:29 +0100 Subject: [PATCH 023/107] Override the disambiguated profile colour in percy tests for screenshot consistency (#9161) --- res/css/views/messages/_DisambiguatedProfile.pcss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/res/css/views/messages/_DisambiguatedProfile.pcss b/res/css/views/messages/_DisambiguatedProfile.pcss index ef4bc7cfb30..4863dc3518c 100644 --- a/res/css/views/messages/_DisambiguatedProfile.pcss +++ b/res/css/views/messages/_DisambiguatedProfile.pcss @@ -34,3 +34,10 @@ limitations under the License. color: $primary-content; } } + +@media only percy { + .mx_DisambiguatedProfile_displayName { + /* Override the colour in percy tests for screenshot consistency */ + color: $username-variant1-color !important; + } +} From fdde6b1428e86d604c3577dea3b386109db62d9b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 09:40:17 +0100 Subject: [PATCH 024/107] Cypress test stability improvements (#9156) * Make cypress hidden event test more reliable * Make timeline tests more stable --- cypress/e2e/timeline/timeline.spec.ts | 41 ++++++++++++++------------- cypress/support/settings.ts | 4 +-- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/cypress/e2e/timeline/timeline.spec.ts b/cypress/e2e/timeline/timeline.spec.ts index 6eacacfed23..73de28dd302 100644 --- a/cypress/e2e/timeline/timeline.spec.ts +++ b/cypress/e2e/timeline/timeline.spec.ts @@ -155,7 +155,7 @@ describe("Timeline", () => { cy.visit("/#/room/" + roomId); cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC); cy.contains(".mx_RoomView_body .mx_GenericEventListSummary[data-layout=irc] " + - ".mx_GenericEventListSummary_summary", "created and configured the room."); + ".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist"); cy.get(".mx_Spinner").should("not.exist"); cy.percySnapshot("Configured room on IRC layout"); }); @@ -166,7 +166,7 @@ describe("Timeline", () => { // Wait until configuration is finished cy.contains(".mx_RoomView_body .mx_GenericEventListSummary " + - ".mx_GenericEventListSummary_summary", "created and configured the room."); + ".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist"); // Click "expand" link button cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click(); @@ -193,14 +193,14 @@ describe("Timeline", () => { cy.visit("/#/room/" + roomId); cy.setSettingValue("showHiddenEventsInTimeline", null, SettingLevel.DEVICE, true); cy.contains(".mx_RoomView_body .mx_GenericEventListSummary .mx_GenericEventListSummary_summary", - "created and configured the room."); + "created and configured the room.").should("exist"); // Edit message cy.contains(".mx_RoomView_body .mx_EventTile .mx_EventTile_line", "Message").within(() => { cy.get('[aria-label="Edit"]').click({ force: true }); // Cypress has no ability to hover cy.get(".mx_BasicMessageComposer_input").type("Edit{enter}"); }); - cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile[data-scroll-tokens]", "MessageEdit"); + cy.contains(".mx_EventTile[data-scroll-tokens]", "MessageEdit").should("exist"); // Click timestamp to highlight hidden event line cy.get(".mx_RoomView_body .mx_EventTile_info .mx_MessageTimestamp").click(); @@ -228,18 +228,19 @@ describe("Timeline", () => { cy.visit("/#/room/" + roomId); cy.setSettingValue("showHiddenEventsInTimeline", null, SettingLevel.DEVICE, true); cy.contains(".mx_RoomView_body .mx_GenericEventListSummary " + - ".mx_GenericEventListSummary_summary", "created and configured the room."); + ".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist"); // Edit message cy.contains(".mx_RoomView_body .mx_EventTile .mx_EventTile_line", "Message").within(() => { cy.get('[aria-label="Edit"]').click({ force: true }); // Cypress has no ability to hover cy.get(".mx_BasicMessageComposer_input").type("Edit{enter}"); }); - cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "MessageEdit"); + cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "MessageEdit").should("exist"); // Click top left of the event toggle, which should not be covered by MessageActionBar's safe area - cy.get(".mx_EventTile .mx_ViewSourceEvent").realHover() - .get(".mx_EventTile .mx_ViewSourceEvent .mx_ViewSourceEvent_toggle").click('topLeft', { force: false }); + cy.get(".mx_EventTile .mx_ViewSourceEvent").realHover().within(() => { + cy.get(".mx_ViewSourceEvent_toggle").click('topLeft', { force: false }); + }); // Make sure the expand toggle worked cy.get(".mx_EventTile .mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle").should("be.visible"); @@ -249,17 +250,17 @@ describe("Timeline", () => { cy.visit("/#/room/" + roomId); cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.Bubble); cy.contains(".mx_RoomView_body .mx_GenericEventListSummary[data-layout=bubble] " + - ".mx_GenericEventListSummary_summary", "created and configured the room."); + ".mx_GenericEventListSummary_summary", "created and configured the room.").should("exist"); // Click "expand" link button cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").click(); // Click "collapse" link button on the first hovered info event line - cy.get(".mx_GenericEventListSummary_unstyledList .mx_EventTile_info:first-of-type").realHover() - .get(".mx_GenericEventListSummary_toggle[aria-expanded=true]").click({ force: false }); + cy.get(".mx_GenericEventListSummary_unstyledList .mx_EventTile_info:first-of-type").realHover(); + cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=true]").click({ force: false }); // Make sure "collapse" link button worked - cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]"); + cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]").should("exist"); }); it("should highlight search result words regardless of formatting", () => { @@ -285,7 +286,7 @@ describe("Timeline", () => { cy.getComposer().type(`${MESSAGE}{enter}`); // Reply to the message - cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile_line", "Hello world").within(() => { + cy.get(".mx_RoomView_body").contains(".mx_EventTile_line", "Hello world").within(() => { cy.get('[aria-label="Reply"]').click({ force: true }); // Cypress has no ability to hover }); }; @@ -296,20 +297,22 @@ describe("Timeline", () => { cy.getComposer().type(`${reply}{enter}`); - cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line").find(".mx_ReplyTile .mx_MTextBody") + cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line .mx_ReplyTile .mx_MTextBody") .should("contain", MESSAGE); - cy.get(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MTextBody").contains(reply) + cy.contains(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MTextBody", reply) .should("have.length", 1); }); - xit("can reply with a voice message", () => { + it("can reply with a voice message", () => { viewRoomSendMessageAndSetupReply(); - cy.openMessageComposerOptions().find(`[aria-label="Voice Message"]`).click(); + cy.openMessageComposerOptions().within(() => { + cy.get(`[aria-label="Voice Message"]`).click(); + }); cy.wait(3000); - cy.getComposer().find(".mx_MessageComposer_sendMessage").click(); + cy.get(".mx_RoomView_body .mx_MessageComposer .mx_MessageComposer_sendMessage").click(); - cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line").find(".mx_ReplyTile .mx_MTextBody") + cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line .mx_ReplyTile .mx_MTextBody") .should("contain", MESSAGE); cy.get(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MVoiceMessageBody") .should("have.length", 1); diff --git a/cypress/support/settings.ts b/cypress/support/settings.ts index 06ec815364b..ec07df93aa1 100644 --- a/cypress/support/settings.ts +++ b/cypress/support/settings.ts @@ -82,7 +82,7 @@ declare global { * @param {*} value The new value of the setting, may be null. * @return {Promise} Resolves when the setting has been changed. */ - setSettingValue(name: string, roomId: string, level: SettingLevel, value: any): Chainable; + setSettingValue(settingName: string, roomId: string, level: SettingLevel, value: any): Chainable; /** * Gets the value of a setting. The room ID is optional if the @@ -96,7 +96,7 @@ declare global { * value. * @return {*} The value, or null if not found */ - getSettingValue(name: string, roomId?: string, excludeDefault?: boolean): Chainable; + getSettingValue(settingName: string, roomId?: string, excludeDefault?: boolean): Chainable; } } } From 350341d13d6e6b2709ce19685e40228954056a6f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 11:22:15 +0100 Subject: [PATCH 025/107] Fix inverted logic for showing UserWelcomeTop component (#9164) --- src/components/structures/HomePage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index 200c28d159f..613625ae702 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -97,8 +97,8 @@ const HomePage: React.FC = ({ justRegistered = false }) => { return ; } - let introSection; - if (justRegistered || !!OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE)) { + let introSection: JSX.Element; + if (justRegistered || !OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE)) { introSection = ; } else { const brandingConfig = SdkConfig.getObject("branding"); From 3d0982e9a6032eae70e2293de374928c7c36b723 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 13:14:54 +0100 Subject: [PATCH 026/107] Space panel accessibility improvements (#9157) * Move the UserMenu out of the SpacePanel ul list * Apply aria-selected to the spacepanel treeview * Fix typing --- res/css/structures/_SpacePanel.pcss | 4 -- .../structures/AutoHideScrollbar.tsx | 40 ++++++++++--------- .../structures/IndicatorScrollbar.tsx | 19 ++++----- .../dialogs/AddExistingToSpaceDialog.tsx | 2 +- .../views/emojipicker/EmojiPicker.tsx | 2 +- src/components/views/spaces/SpacePanel.tsx | 12 +++--- .../views/spaces/SpaceTreeLevel.tsx | 4 +- 7 files changed, 44 insertions(+), 39 deletions(-) diff --git a/res/css/structures/_SpacePanel.pcss b/res/css/structures/_SpacePanel.pcss index 7fdb2500b45..72dbddf75e1 100644 --- a/res/css/structures/_SpacePanel.pcss +++ b/res/css/structures/_SpacePanel.pcss @@ -78,10 +78,6 @@ $activeBorderColor: $primary-content; margin: 0; list-style: none; padding: 0; - - > .mx_SpaceItem { - padding-left: 16px; - } } .mx_SpaceButton_toggleCollapse { diff --git a/src/components/structures/AutoHideScrollbar.tsx b/src/components/structures/AutoHideScrollbar.tsx index d2134c33fef..9a6f5b26a0a 100644 --- a/src/components/structures/AutoHideScrollbar.tsx +++ b/src/components/structures/AutoHideScrollbar.tsx @@ -15,18 +15,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { HTMLAttributes, WheelEvent } from "react"; +import classNames from "classnames"; +import React, { HTMLAttributes, ReactHTML, WheelEvent } from "react"; -interface IProps extends Omit, "onScroll"> { +type DynamicHtmlElementProps = + JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; +type DynamicElementProps = Partial>; + +export type IProps = DynamicHtmlElementProps & { + element?: T; className?: string; onScroll?: (event: Event) => void; onWheel?: (event: WheelEvent) => void; style?: React.CSSProperties; tabIndex?: number; wrappedRef?: (ref: HTMLDivElement) => void; -} +}; + +export default class AutoHideScrollbar extends React.Component> { + static defaultProps = { + element: 'div' as keyof ReactHTML, + }; -export default class AutoHideScrollbar extends React.Component { public readonly containerRef: React.RefObject = React.createRef(); public componentDidMount() { @@ -36,9 +46,7 @@ export default class AutoHideScrollbar extends React.Component { this.containerRef.current.addEventListener("scroll", this.props.onScroll, { passive: true }); } - if (this.props.wrappedRef) { - this.props.wrappedRef(this.containerRef.current); - } + this.props.wrappedRef?.(this.containerRef.current); } public componentWillUnmount() { @@ -49,19 +57,15 @@ export default class AutoHideScrollbar extends React.Component { public render() { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { className, onScroll, onWheel, style, tabIndex, wrappedRef, children, ...otherProps } = this.props; + const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props; - return (
- { children } -
); + tabIndex: tabIndex ?? -1, + }, children); } } diff --git a/src/components/structures/IndicatorScrollbar.tsx b/src/components/structures/IndicatorScrollbar.tsx index 4b122345b32..ea876f9ae29 100644 --- a/src/components/structures/IndicatorScrollbar.tsx +++ b/src/components/structures/IndicatorScrollbar.tsx @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentProps, createRef } from "react"; +import React, { createRef } from "react"; -import AutoHideScrollbar from "./AutoHideScrollbar"; +import AutoHideScrollbar, { IProps as AutoHideScrollbarProps } from "./AutoHideScrollbar"; import UIStore, { UI_EVENTS } from "../../stores/UIStore"; -interface IProps extends Omit, "onWheel"> { +export type IProps = Omit, "onWheel"> & { // If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator // and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning // by the parent element. @@ -31,21 +31,22 @@ interface IProps extends Omit, "onWheel verticalScrollsHorizontally?: boolean; children: React.ReactNode; - className: string; -} +}; interface IState { leftIndicatorOffset: string; rightIndicatorOffset: string; } -export default class IndicatorScrollbar extends React.Component { - private autoHideScrollbar = createRef(); +export default class IndicatorScrollbar< + T extends keyof JSX.IntrinsicElements, +> extends React.Component, IState> { + private autoHideScrollbar = createRef>(); private scrollElement: HTMLDivElement; private likelyTrackpadUser: boolean = null; private checkAgainForTrackpad = 0; // ts in milliseconds to recheck this._likelyTrackpadUser - constructor(props: IProps) { + constructor(props: IProps) { super(props); this.state = { @@ -65,7 +66,7 @@ export default class IndicatorScrollbar extends React.Component } }; - public componentDidUpdate(prevProps: IProps): void { + public componentDidUpdate(prevProps: IProps): void { const prevLen = React.Children.count(prevProps.children); const curLen = React.Children.count(this.props.children); // check overflow only if amount of children changes. diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index 606f96553d7..6251faee41f 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -130,7 +130,7 @@ export const AddExistingToSpace: React.FC = ({ const cli = useContext(MatrixClientContext); const visibleRooms = useMemo(() => cli.getVisibleRooms().filter(r => r.getMyMembership() === "join"), [cli]); - const scrollRef = useRef(); + const scrollRef = useRef>(); const [scrollState, setScrollState] = useState({ // these are estimates which update as soon as it mounts scrollTop: 0, diff --git a/src/components/views/emojipicker/EmojiPicker.tsx b/src/components/views/emojipicker/EmojiPicker.tsx index e32f20b78b6..c178084826a 100644 --- a/src/components/views/emojipicker/EmojiPicker.tsx +++ b/src/components/views/emojipicker/EmojiPicker.tsx @@ -55,7 +55,7 @@ class EmojiPicker extends React.Component { private readonly memoizedDataByCategory: Record; private readonly categories: ICategory[]; - private scrollRef = React.createRef(); + private scrollRef = React.createRef>(); constructor(props: IProps) { super(props); diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index e1f62445f74..129e6f3584e 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -132,6 +132,7 @@ const MetaSpaceButton = ({ selected, isPanelCollapsed, ...props }: IMetaSpaceBut "collapsed": isPanelCollapsed, })} role="treeitem" + aria-selected={selected} > ; @@ -282,6 +283,9 @@ const InnerSpacePanel = React.memo(({ style={isDraggingOver ? { pointerEvents: "none", } : undefined} + element="ul" + role="tree" + aria-label={_t("Spaces")} > { metaSpacesSection } { invites.map(s => ( @@ -321,7 +325,7 @@ const InnerSpacePanel = React.memo(({ const SpacePanel = () => { const [isPanelCollapsed, setPanelCollapsed] = useState(true); - const ref = useRef(); + const ref = useRef(); useLayoutEffect(() => { UIStore.instance.trackElementDimensions("SpacePanel", ref.current); return () => UIStore.instance.stopTrackingElementDimensions("SpacePanel"); @@ -340,11 +344,9 @@ const SpacePanel = () => { }}> { ({ onKeyDownHandler }) => ( -
    @@ -381,7 +383,7 @@ const SpacePanel = () => { -
+
) } diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index 80d678d3b61..cab7bc3c76b 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -315,6 +315,7 @@ export class SpaceItem extends React.PureComponent { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { tabIndex, ...restDragHandleProps } = dragHandleProps || {}; + const selected = activeSpaces.includes(space.roomId); return (
  • { className={itemClasses} ref={innerRef} aria-expanded={hasChildren ? !collapsed : undefined} + aria-selected={selected} role="treeitem" > Date: Wed, 10 Aug 2022 08:57:56 -0400 Subject: [PATCH 027/107] Implement MSC3819: Allowing widgets to send/receive to-device messages (#8885) * Implement MSC3819: Allowing widgets to send/receive to-device messages * Don't change the room events and state events drivers * Update to latest matrix-widget-api changes * Support sending encrypted to-device messages * Use queueToDevice for better reliability * Update types for latest WidgetDriver changes * Upgrade matrix-widget-api * Add tests * Test StopGapWidget * Fix a potential memory leak --- package.json | 2 +- src/stores/widgets/StopGapWidget.ts | 37 ++++++--- src/stores/widgets/StopGapWidgetDriver.ts | 48 ++++++++++- src/widgets/CapabilityText.tsx | 9 +- test/stores/widgets/StopGapWidget-test.ts | 70 ++++++++++++++++ .../widgets/StopGapWidgetDriver-test.ts | 79 ++++++++++++++++++ .../StopGapWidgetDriver-test.ts.snap | 82 +++++++++++++++++++ test/test-utils/test-utils.ts | 11 ++- yarn.lock | 8 +- 9 files changed, 322 insertions(+), 24 deletions(-) create mode 100644 test/stores/widgets/StopGapWidget-test.ts create mode 100644 test/stores/widgets/StopGapWidgetDriver-test.ts create mode 100644 test/stores/widgets/__snapshots__/StopGapWidgetDriver-test.ts.snap diff --git a/package.json b/package.json index 933ccfd7ada..ca203360c63 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "matrix-encrypt-attachment": "^1.0.3", "matrix-events-sdk": "^0.0.1-beta.7", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", - "matrix-widget-api": "^0.1.0-beta.18", + "matrix-widget-api": "^1.0.0", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", "pako": "^2.0.3", diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 7a1c5e0ba53..889a050ebfd 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -33,6 +33,7 @@ import { WidgetKind, } from "matrix-widget-api"; import { EventEmitter } from "events"; +import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event"; import { logger } from "matrix-js-sdk/src/logger"; import { ClientEvent } from "matrix-js-sdk/src/client"; @@ -148,6 +149,7 @@ export class ElementWidget extends Widget { } export class StopGapWidget extends EventEmitter { + private client: MatrixClient; private messaging: ClientWidgetApi; private mockWidget: ElementWidget; private scalarToken: string; @@ -157,12 +159,13 @@ export class StopGapWidget extends EventEmitter { constructor(private appTileProps: IAppTileProps) { super(); - let app = appTileProps.app; + this.client = MatrixClientPeg.get(); + let app = appTileProps.app; // Backwards compatibility: not all old widgets have a creatorUserId if (!app.creatorUserId) { app = objectShallowClone(app); // clone to prevent accidental mutation - app.creatorUserId = MatrixClientPeg.get().getUserId(); + app.creatorUserId = this.client.getUserId(); } this.mockWidget = new ElementWidget(app); @@ -203,7 +206,7 @@ export class StopGapWidget extends EventEmitter { const fromCustomisation = WidgetVariableCustomisations?.provideVariables?.() ?? {}; const defaults: ITemplateParams = { widgetRoomId: this.roomId, - currentUserId: MatrixClientPeg.get().getUserId(), + currentUserId: this.client.getUserId(), userDisplayName: OwnProfileStore.instance.displayName, userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(), clientId: ELEMENT_CLIENT_ID, @@ -260,8 +263,10 @@ export class StopGapWidget extends EventEmitter { */ public startMessaging(iframe: HTMLIFrameElement): any { if (this.started) return; + const allowedCapabilities = this.appTileProps.whitelistCapabilities || []; const driver = new StopGapWidgetDriver(allowedCapabilities, this.mockWidget, this.kind, this.roomId); + this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver); this.messaging.on("preparing", () => this.emit("preparing")); this.messaging.on("ready", () => this.emit("ready")); @@ -302,7 +307,7 @@ export class StopGapWidget extends EventEmitter { // Populate the map of "read up to" events for this widget with the current event in every room. // This is a bit inefficient, but should be okay. We do this for all rooms in case the widget // requests timeline capabilities in other rooms down the road. It's just easier to manage here. - for (const room of MatrixClientPeg.get().getRooms()) { + for (const room of this.client.getRooms()) { // Timelines are most recent last const events = room.getLiveTimeline()?.getEvents() || []; const roomEvent = events[events.length - 1]; @@ -311,8 +316,9 @@ export class StopGapWidget extends EventEmitter { } // Attach listeners for feeding events - the underlying widget classes handle permissions for us - MatrixClientPeg.get().on(ClientEvent.Event, this.onEvent); - MatrixClientPeg.get().on(MatrixEventEvent.Decrypted, this.onEventDecrypted); + this.client.on(ClientEvent.Event, this.onEvent); + this.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted); + this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent); this.messaging.on(`action:${WidgetApiFromWidgetAction.UpdateAlwaysOnScreen}`, (ev: CustomEvent) => { @@ -363,7 +369,7 @@ export class StopGapWidget extends EventEmitter { // noinspection JSIgnoredPromiseFromCall IntegrationManagers.sharedInstance().getPrimaryManager().open( - MatrixClientPeg.get().getRoom(RoomViewStore.instance.getRoomId()), + this.client.getRoom(RoomViewStore.instance.getRoomId()), `type_${integType}`, integId, ); @@ -428,14 +434,13 @@ export class StopGapWidget extends EventEmitter { WidgetMessagingStore.instance.stopMessaging(this.mockWidget, this.roomId); this.messaging = null; - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().off(ClientEvent.Event, this.onEvent); - MatrixClientPeg.get().off(MatrixEventEvent.Decrypted, this.onEventDecrypted); - } + this.client.off(ClientEvent.Event, this.onEvent); + this.client.off(MatrixEventEvent.Decrypted, this.onEventDecrypted); + this.client.off(ClientEvent.ToDeviceEvent, this.onToDeviceEvent); } private onEvent = (ev: MatrixEvent) => { - MatrixClientPeg.get().decryptEventIfNeeded(ev); + this.client.decryptEventIfNeeded(ev); if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; this.feedEvent(ev); }; @@ -445,6 +450,12 @@ export class StopGapWidget extends EventEmitter { this.feedEvent(ev); }; + private onToDeviceEvent = async (ev: MatrixEvent) => { + await this.client.decryptEventIfNeeded(ev); + if (ev.isDecryptionFailure()) return; + await this.messaging.feedToDevice(ev.getEffectiveEvent(), ev.isEncrypted()); + }; + private feedEvent(ev: MatrixEvent) { if (!this.messaging) return; @@ -465,7 +476,7 @@ export class StopGapWidget extends EventEmitter { // Timelines are most recent last, so reverse the order and limit ourselves to 100 events // to avoid overusing the CPU. - const timeline = MatrixClientPeg.get().getRoom(ev.getRoomId()).getLiveTimeline(); + const timeline = this.client.getRoom(ev.getRoomId()).getLiveTimeline(); const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100); for (const timelineEvent of events) { diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 3b617e6f314..ee69f0ca9c2 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -20,6 +20,7 @@ import { IOpenIDCredentials, IOpenIDUpdate, ISendEventDetails, + IRoomEvent, MatrixCapabilities, OpenIDRequestState, SimpleObservable, @@ -182,6 +183,49 @@ export class StopGapWidgetDriver extends WidgetDriver { return { roomId, eventId: r.event_id }; } + public async sendToDevice( + eventType: string, + encrypted: boolean, + contentMap: { [userId: string]: { [deviceId: string]: object } }, + ): Promise { + const client = MatrixClientPeg.get(); + + if (encrypted) { + const deviceInfoMap = await client.crypto.deviceList.downloadKeys(Object.keys(contentMap), false); + + await Promise.all( + Object.entries(contentMap).flatMap(([userId, userContentMap]) => + Object.entries(userContentMap).map(async ([deviceId, content]) => { + if (deviceId === "*") { + // Send the message to all devices we have keys for + await client.encryptAndSendToDevices( + Object.values(deviceInfoMap[userId]).map(deviceInfo => ({ + userId, deviceInfo, + })), + content, + ); + } else { + // Send the message to a specific device + await client.encryptAndSendToDevices( + [{ userId, deviceInfo: deviceInfoMap[userId][deviceId] }], + content, + ); + } + }), + ), + ); + } else { + await client.queueToDevice({ + eventType, + batch: Object.entries(contentMap).flatMap(([userId, userContentMap]) => + Object.entries(userContentMap).map(([deviceId, content]) => + ({ userId, deviceId, payload: content }), + ), + ), + }); + } + } + private pickRooms(roomIds: (string | Symbols.AnyRoom)[] = null): Room[] { const client = MatrixClientPeg.get(); if (!client) throw new Error("Not attached to a client"); @@ -197,7 +241,7 @@ export class StopGapWidgetDriver extends WidgetDriver { msgtype: string | undefined, limitPerRoom: number, roomIds: (string | Symbols.AnyRoom)[] = null, - ): Promise { + ): Promise { limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary const rooms = this.pickRooms(roomIds); @@ -224,7 +268,7 @@ export class StopGapWidgetDriver extends WidgetDriver { stateKey: string | undefined, limitPerRoom: number, roomIds: (string | Symbols.AnyRoom)[] = null, - ): Promise { + ): Promise { limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary const rooms = this.pickRooms(roomIds); diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx index cd442f213b8..e4790eaad2a 100644 --- a/src/widgets/CapabilityText.tsx +++ b/src/widgets/CapabilityText.tsx @@ -17,6 +17,7 @@ limitations under the License. import { Capability, EventDirection, + EventKind, getTimelineRoomIDFromCapability, isTimelineCapability, isTimelineCapabilityFor, @@ -134,7 +135,7 @@ export class CapabilityText { }; private static bylineFor(eventCap: WidgetEventCapability): TranslatedString { - if (eventCap.isState) { + if (eventCap.kind === EventKind.State) { return !eventCap.keyStr ? _t("with an empty state key") : _t("with state key %(stateKey)s", { stateKey: eventCap.keyStr }); @@ -143,6 +144,8 @@ export class CapabilityText { } public static for(capability: Capability, kind: WidgetKind): TranslatedCapabilityText { + // TODO: Support MSC3819 (to-device capabilities) + // First see if we have a super simple line of text to provide back if (CapabilityText.simpleCaps[capability]) { const textForKind = CapabilityText.simpleCaps[capability]; @@ -184,13 +187,13 @@ export class CapabilityText { // Special case room messages so they show up a bit cleaner to the user. Result is // effectively "Send images" instead of "Send messages... of type images" if we were // to handle the msgtype nuances in this function. - if (!eventCap.isState && eventCap.eventType === EventType.RoomMessage) { + if (eventCap.kind === EventKind.Event && eventCap.eventType === EventType.RoomMessage) { return CapabilityText.forRoomMessageCap(eventCap, kind); } // See if we have a static line of text to provide for the given event type and // direction. The hope is that we do for common event types for friendlier copy. - const evSendRecv = eventCap.isState + const evSendRecv = eventCap.kind === EventKind.State ? CapabilityText.stateSendRecvCaps : CapabilityText.nonStateSendRecvCaps; if (evSendRecv[eventCap.eventType]) { diff --git a/test/stores/widgets/StopGapWidget-test.ts b/test/stores/widgets/StopGapWidget-test.ts new file mode 100644 index 00000000000..40292e451be --- /dev/null +++ b/test/stores/widgets/StopGapWidget-test.ts @@ -0,0 +1,70 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked, MockedObject } from "jest-mock"; +import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client"; +import { ClientWidgetApi } from "matrix-widget-api"; + +import { stubClient, mkRoom, mkEvent } from "../../test-utils"; +import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; +import { StopGapWidget } from "../../../src/stores/widgets/StopGapWidget"; + +jest.mock("matrix-widget-api/lib/ClientWidgetApi"); + +describe("StopGapWidget", () => { + let client: MockedObject; + let widget: StopGapWidget; + let messaging: MockedObject; + + beforeEach(() => { + stubClient(); + client = mocked(MatrixClientPeg.get()); + + widget = new StopGapWidget({ + app: { + id: "test", + creatorUserId: "@alice:example.org", + type: "example", + url: "https://example.org", + }, + room: mkRoom(client, "!1:example.org"), + userId: "@alice:example.org", + creatorUserId: "@alice:example.org", + waitForIframeLoad: true, + userWidget: false, + }); + // Start messaging without an iframe, since ClientWidgetApi is mocked + widget.startMessaging(null as unknown as HTMLIFrameElement); + messaging = mocked(mocked(ClientWidgetApi).mock.instances[0]); + }); + + afterEach(() => { + widget.stopMessaging(); + }); + + it("feeds incoming to-device messages to the widget", async () => { + const event = mkEvent({ + event: true, + type: "org.example.foo", + user: "@alice:example.org", + content: { hello: "world" }, + }); + + client.emit(ClientEvent.ToDeviceEvent, event); + await Promise.resolve(); // flush promises + expect(messaging.feedToDevice).toHaveBeenCalledWith(event.getEffectiveEvent(), false); + }); +}); diff --git a/test/stores/widgets/StopGapWidgetDriver-test.ts b/test/stores/widgets/StopGapWidgetDriver-test.ts new file mode 100644 index 00000000000..7dab35052b4 --- /dev/null +++ b/test/stores/widgets/StopGapWidgetDriver-test.ts @@ -0,0 +1,79 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked, MockedObject } from "jest-mock"; +import { Widget, WidgetKind, WidgetDriver } from "matrix-widget-api"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; + +import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; +import { StopGapWidgetDriver } from "../../../src/stores/widgets/StopGapWidgetDriver"; +import { stubClient } from "../../test-utils"; + +describe("StopGapWidgetDriver", () => { + let client: MockedObject; + let driver: WidgetDriver; + + beforeEach(() => { + stubClient(); + client = mocked(MatrixClientPeg.get()); + + driver = new StopGapWidgetDriver( + [], + new Widget({ + id: "test", + creatorUserId: "@alice:example.org", + type: "example", + url: "https://example.org", + }), + WidgetKind.Room, + ); + }); + + describe("sendToDevice", () => { + const contentMap = { + "@alice:example.org": { + "*": { + hello: "alice", + }, + }, + "@bob:example.org": { + "bobDesktop": { + hello: "bob", + }, + }, + }; + + it("sends unencrypted messages", async () => { + await driver.sendToDevice("org.example.foo", false, contentMap); + expect(client.queueToDevice.mock.calls).toMatchSnapshot(); + }); + + it("sends encrypted messages", async () => { + const aliceWeb = new DeviceInfo("aliceWeb"); + const aliceMobile = new DeviceInfo("aliceMobile"); + const bobDesktop = new DeviceInfo("bobDesktop"); + + mocked(client.crypto.deviceList).downloadKeys.mockResolvedValue({ + "@alice:example.org": { aliceWeb, aliceMobile }, + "@bob:example.org": { bobDesktop }, + }); + + await driver.sendToDevice("org.example.foo", true, contentMap); + expect(client.encryptAndSendToDevices.mock.calls).toMatchSnapshot(); + }); + }); +}); diff --git a/test/stores/widgets/__snapshots__/StopGapWidgetDriver-test.ts.snap b/test/stores/widgets/__snapshots__/StopGapWidgetDriver-test.ts.snap new file mode 100644 index 00000000000..5f19dbb793d --- /dev/null +++ b/test/stores/widgets/__snapshots__/StopGapWidgetDriver-test.ts.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StopGapWidgetDriver sendToDevice sends encrypted messages 1`] = ` +Array [ + Array [ + Array [ + Object { + "deviceInfo": DeviceInfo { + "algorithms": undefined, + "deviceId": "aliceWeb", + "keys": Object {}, + "known": false, + "signatures": Object {}, + "unsigned": Object {}, + "verified": 0, + }, + "userId": "@alice:example.org", + }, + Object { + "deviceInfo": DeviceInfo { + "algorithms": undefined, + "deviceId": "aliceMobile", + "keys": Object {}, + "known": false, + "signatures": Object {}, + "unsigned": Object {}, + "verified": 0, + }, + "userId": "@alice:example.org", + }, + ], + Object { + "hello": "alice", + }, + ], + Array [ + Array [ + Object { + "deviceInfo": DeviceInfo { + "algorithms": undefined, + "deviceId": "bobDesktop", + "keys": Object {}, + "known": false, + "signatures": Object {}, + "unsigned": Object {}, + "verified": 0, + }, + "userId": "@bob:example.org", + }, + ], + Object { + "hello": "bob", + }, + ], +] +`; + +exports[`StopGapWidgetDriver sendToDevice sends unencrypted messages 1`] = ` +Array [ + Array [ + Object { + "batch": Array [ + Object { + "deviceId": "*", + "payload": Object { + "hello": "alice", + }, + "userId": "@alice:example.org", + }, + Object { + "deviceId": "bobDesktop", + "payload": Object { + "hello": "bob", + }, + "userId": "@bob:example.org", + }, + ], + "eventType": "org.example.foo", + }, + ], +] +`; diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 39f8e8a7ece..391683d5d15 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -91,6 +91,12 @@ export function createTestClient(): MatrixClient { removeRoom: jest.fn(), }, + crypto: { + deviceList: { + downloadKeys: jest.fn(), + }, + }, + getPushActionsForEvent: jest.fn(), getRoom: jest.fn().mockImplementation(mkStubRoom), getRooms: jest.fn().mockReturnValue([]), @@ -163,6 +169,9 @@ export function createTestClient(): MatrixClient { downloadKeys: jest.fn(), fetchRoomEvent: jest.fn(), makeTxnId: jest.fn().mockImplementation(() => `t${txnId++}`), + sendToDevice: jest.fn().mockResolvedValue(undefined), + queueToDevice: jest.fn().mockResolvedValue(undefined), + encryptAndSendToDevices: jest.fn().mockResolvedValue(undefined), } as unknown as MatrixClient; } @@ -176,7 +185,7 @@ type MakeEventPassThruProps = { type MakeEventProps = MakeEventPassThruProps & { type: string; content: IContent; - room: Room["roomId"]; + room?: Room["roomId"]; // to-device messages are roomless // eslint-disable-next-line camelcase prev_content?: IContent; unsigned?: IUnsigned; diff --git a/yarn.lock b/yarn.lock index f2f623b7edf..56d63e5a17a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6813,10 +6813,10 @@ matrix-web-i18n@^1.3.0: "@babel/traverse" "^7.18.5" walk "^2.3.15" -matrix-widget-api@^0.1.0-beta.18: - version "0.1.0-beta.18" - resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.18.tgz#4efd30edec3eeb4211285985464c062fcab59795" - integrity sha512-kCpcs6rrB94Mmr2/1gBJ+6auWyZ5UvOMOn5K2VFafz2/NDMzZg9OVWj9KFYnNAuwwBE5/tCztYEj6OQ+hgbwOQ== +matrix-widget-api@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.0.0.tgz#0cde6839cca66ad817ab12aca3490ccc8bac97d1" + integrity sha512-cy8p/8EteRPTFIAw7Q9EgPUJc2jD19ZahMR8bMKf2NkILDcjuPMC0UWnsJyB3fSnlGw+VbGepttRpULM31zX8Q== dependencies: "@types/events" "^3.0.0" events "^3.2.0" From 28ed87bffeea84bcb6bb3621c2f9efdefd3cb9d1 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 10 Aug 2022 09:26:42 -0400 Subject: [PATCH 028/107] Implement MSC3846: Allowing widgets to access TURN servers (#9061) * Implement MSC3819: Allowing widgets to send/receive to-device messages * Don't change the room events and state events drivers * Implement MSC3846: Allowing widgets to access TURN servers * Update to latest matrix-widget-api changes * Support sending encrypted to-device messages * Yield a TURN server immediately * Use queueToDevice for better reliability * Update types for latest WidgetDriver changes * Upgrade matrix-widget-api * Add tests * Test StopGapWidget * Fix a potential memory leak * Add tests * Empty commit to retry CI --- src/stores/widgets/StopGapWidgetDriver.ts | 42 ++++++++++++- .../widgets/StopGapWidgetDriver-test.ts | 59 ++++++++++++++++++- test/test-utils/test-utils.ts | 9 ++- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index ee69f0ca9c2..8fe18dbc8c0 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import { IOpenIDCredentials, IOpenIDUpdate, ISendEventDetails, + ITurnServer, IRoomEvent, MatrixCapabilities, OpenIDRequestState, @@ -30,6 +31,7 @@ import { WidgetEventCapability, WidgetKind, } from "matrix-widget-api"; +import { ClientEvent, ITurnServer as IClientTurnServer } from "matrix-js-sdk/src/client"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { IContent, IEvent, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; @@ -62,6 +64,12 @@ function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) localStorage.setItem(`widget_${widget.id}_approved_caps`, JSON.stringify(caps)); } +const normalizeTurnServer = ({ urls, username, credential }: IClientTurnServer): ITurnServer => ({ + uris: urls, + username, + password: credential, +}); + export class StopGapWidgetDriver extends WidgetDriver { private allowedCapabilities: Set; @@ -326,4 +334,36 @@ export class StopGapWidgetDriver extends WidgetDriver { public async navigate(uri: string): Promise { navigateToPermalink(uri); } + + public async* getTurnServers(): AsyncGenerator { + const client = MatrixClientPeg.get(); + if (!client.pollingTurnServers || !client.getTurnServers().length) return; + + let setTurnServer: (server: ITurnServer) => void; + let setError: (error: Error) => void; + + const onTurnServers = ([server]: IClientTurnServer[]) => setTurnServer(normalizeTurnServer(server)); + const onTurnServersError = (error: Error, fatal: boolean) => { if (fatal) setError(error); }; + + client.on(ClientEvent.TurnServers, onTurnServers); + client.on(ClientEvent.TurnServersError, onTurnServersError); + + try { + const initialTurnServer = client.getTurnServers()[0]; + yield normalizeTurnServer(initialTurnServer); + + // Repeatedly listen for new TURN servers until an error occurs or + // the caller stops this generator + while (true) { + yield await new Promise((resolve, reject) => { + setTurnServer = resolve; + setError = reject; + }); + } + } finally { + // The loop was broken - clean up + client.off(ClientEvent.TurnServers, onTurnServers); + client.off(ClientEvent.TurnServersError, onTurnServersError); + } + } } diff --git a/test/stores/widgets/StopGapWidgetDriver-test.ts b/test/stores/widgets/StopGapWidgetDriver-test.ts index 7dab35052b4..79046294282 100644 --- a/test/stores/widgets/StopGapWidgetDriver-test.ts +++ b/test/stores/widgets/StopGapWidgetDriver-test.ts @@ -15,8 +15,8 @@ limitations under the License. */ import { mocked, MockedObject } from "jest-mock"; -import { Widget, WidgetKind, WidgetDriver } from "matrix-widget-api"; -import { MatrixClient } from "matrix-js-sdk/src/client"; +import { Widget, WidgetKind, WidgetDriver, ITurnServer } from "matrix-widget-api"; +import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "matrix-js-sdk/src/client"; import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; @@ -76,4 +76,59 @@ describe("StopGapWidgetDriver", () => { expect(client.encryptAndSendToDevices.mock.calls).toMatchSnapshot(); }); }); + + describe("getTurnServers", () => { + it("stops if VoIP isn't supported", async () => { + jest.spyOn(client, "pollingTurnServers", "get").mockReturnValue(false); + const servers = driver.getTurnServers(); + expect(await servers.next()).toEqual({ value: undefined, done: true }); + }); + + it("stops if the homeserver provides no TURN servers", async () => { + const servers = driver.getTurnServers(); + expect(await servers.next()).toEqual({ value: undefined, done: true }); + }); + + it("gets TURN servers", async () => { + const server1: ITurnServer = { + uris: [ + "turn:turn.example.com:3478?transport=udp", + "turn:10.20.30.40:3478?transport=tcp", + "turns:10.20.30.40:443?transport=tcp", + ], + username: "1443779631:@user:example.com", + password: "JlKfBy1QwLrO20385QyAtEyIv0=", + }; + const server2: ITurnServer = { + uris: [ + "turn:turn.example.com:3478?transport=udp", + "turn:10.20.30.40:3478?transport=tcp", + "turns:10.20.30.40:443?transport=tcp", + ], + username: "1448999322:@user:example.com", + password: "hunter2", + }; + const clientServer1: IClientTurnServer = { + urls: server1.uris, + username: server1.username, + credential: server1.password, + }; + const clientServer2: IClientTurnServer = { + urls: server2.uris, + username: server2.username, + credential: server2.password, + }; + + client.getTurnServers.mockReturnValue([clientServer1]); + const servers = driver.getTurnServers(); + expect(await servers.next()).toEqual({ value: server1, done: false }); + + const nextServer = servers.next(); + client.getTurnServers.mockReturnValue([clientServer2]); + client.emit(ClientEvent.TurnServers, [clientServer2]); + expect(await nextServer).toEqual({ value: server2, done: false }); + + await servers.return(undefined); + }); + }); }); diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 391683d5d15..fd120a077b0 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -74,7 +74,7 @@ export function createTestClient(): MatrixClient { const eventEmitter = new EventEmitter(); let txnId = 1; - return { + const client = { getHomeserverUrl: jest.fn(), getIdentityServerUrl: jest.fn(), getDomain: jest.fn().mockReturnValue("matrix.org"), @@ -118,6 +118,7 @@ export function createTestClient(): MatrixClient { getThirdpartyProtocols: jest.fn().mockResolvedValue({}), getClientWellKnown: jest.fn().mockReturnValue(null), supportsVoip: jest.fn().mockReturnValue(true), + getTurnServers: jest.fn().mockReturnValue([]), getTurnServersExpiry: jest.fn().mockReturnValue(2 ^ 32), getThirdpartyUser: jest.fn().mockResolvedValue([]), getAccountData: (type) => { @@ -173,6 +174,12 @@ export function createTestClient(): MatrixClient { queueToDevice: jest.fn().mockResolvedValue(undefined), encryptAndSendToDevices: jest.fn().mockResolvedValue(undefined), } as unknown as MatrixClient; + + Object.defineProperty(client, "pollingTurnServers", { + configurable: true, + get: () => true, + }); + return client; } type MakeEventPassThruProps = { From 2e32a4d4b655e94c821dabe71190474cc2c715c9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 14:33:13 +0100 Subject: [PATCH 029/107] Fix invisible power levels tile when showing hidden events (#9162) * Fix invisible power levels tile when showing hidden events * Add regression test --- src/TextForEvent.tsx | 36 ++++++++++++++-------------- src/events/EventTileFactory.tsx | 4 ++++ test/events/EventTileFactory-test.ts | 36 ++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 test/events/EventTileFactory-test.ts diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx index 10530d7c9b4..9310391e3e2 100644 --- a/src/TextForEvent.tsx +++ b/src/TextForEvent.tsx @@ -434,29 +434,29 @@ function textForHistoryVisibilityEvent(event: MatrixEvent): () => string | null // Currently will only display a change if a user's power level is changed function textForPowerEvent(event: MatrixEvent): () => string | null { const senderName = getSenderName(event); - if (!event.getPrevContent() || !event.getPrevContent().users || - !event.getContent() || !event.getContent().users) { + if (!event.getPrevContent()?.users || !event.getContent()?.users) { return null; } - const previousUserDefault = event.getPrevContent().users_default || 0; - const currentUserDefault = event.getContent().users_default || 0; + const previousUserDefault: number = event.getPrevContent().users_default || 0; + const currentUserDefault: number = event.getContent().users_default || 0; // Construct set of userIds - const users = []; - Object.keys(event.getContent().users).forEach( - (userId) => { - if (users.indexOf(userId) === -1) users.push(userId); - }, - ); - Object.keys(event.getPrevContent().users).forEach( - (userId) => { - if (users.indexOf(userId) === -1) users.push(userId); - }, - ); - - const diffs = []; + const users: string[] = []; + Object.keys(event.getContent().users).forEach((userId) => { + if (users.indexOf(userId) === -1) users.push(userId); + }); + Object.keys(event.getPrevContent().users).forEach((userId) => { + if (users.indexOf(userId) === -1) users.push(userId); + }); + + const diffs: { + userId: string; + name: string; + from: number; + to: number; + }[] = []; users.forEach((userId) => { // Previous power level - let from = event.getPrevContent().users[userId]; + let from: number = event.getPrevContent().users[userId]; if (!Number.isInteger(from)) { from = previousUserDefault; } diff --git a/src/events/EventTileFactory.tsx b/src/events/EventTileFactory.tsx index 9315741c593..88982b373fe 100644 --- a/src/events/EventTileFactory.tsx +++ b/src/events/EventTileFactory.tsx @@ -225,6 +225,10 @@ export function pickFactory( return noEventFactoryFactory(); // improper event type to render } + if (STATE_EVENT_TILE_TYPES[evType] === TextualEventFactory && !hasText(mxEvent, showHiddenEvents)) { + return noEventFactoryFactory(); + } + return STATE_EVENT_TILE_TYPES[evType] ?? noEventFactoryFactory(); } diff --git a/test/events/EventTileFactory-test.ts b/test/events/EventTileFactory-test.ts new file mode 100644 index 00000000000..ebda574dfc2 --- /dev/null +++ b/test/events/EventTileFactory-test.ts @@ -0,0 +1,36 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix"; + +import { JSONEventFactory, pickFactory } from "../../src/events/EventTileFactory"; +import { createTestClient } from "../test-utils"; + +const roomId = "!room:example.com"; + +describe("pickFactory", () => { + it("should return JSONEventFactory for a no-op m.room.power_levels event", () => { + const cli = createTestClient(); + const event = new MatrixEvent({ + type: EventType.RoomPowerLevels, + state_key: "", + content: {}, + sender: cli.getUserId(), + room_id: roomId, + }); + expect(pickFactory(event, cli, true)).toBe(JSONEventFactory); + }); +}); From df016ff5f6524c27187d7d13757e10a79d32fbac Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 16:10:08 +0100 Subject: [PATCH 030/107] Upgrade deps (#9165) --- yarn.lock | 313 +++++++++++++++++++++++++++++------------------------- 1 file changed, 171 insertions(+), 142 deletions(-) diff --git a/yarn.lock b/yarn.lock index 56d63e5a17a..c8ac572177f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1044,20 +1044,27 @@ source-map-support "^0.5.16" "@babel/runtime-corejs3@^7.10.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.6.tgz#6f02c5536911f4b445946a2179554b95c8838635" - integrity sha512-cOu5wH2JFBgMjje+a+fz2JNIWU4GzYpl05oSob3UDvBEh6EuIn+TXFHMmBbhSb+k/4HMzgKCQfEEDArAWNF9Cw== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae" + integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A== dependencies: core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.9", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580" integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.18.6", "@babel/template@^7.3.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" @@ -1678,96 +1685,96 @@ tslib "^2.4.0" webcrypto-core "^1.7.4" -"@percy/cli-build@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-build/-/cli-build-1.6.1.tgz#f49e6df1ca3b2f548c853c66f75ebb407e2302ea" - integrity sha512-YP4+uLfT14zI1bfjU+VE8fhu6RkwuhRK1AHIFcbonj/HlIhCdFWqyrWcmLp6HYU06JoiNtbI4QQNM3spxWrolg== +"@percy/cli-build@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli-build/-/cli-build-1.8.1.tgz#15752ed826ffcfb4160ad5d931ec35b5bdb3e1ea" + integrity sha512-tVn2sDboRepzuf2n96hlyV84q0metkRwN81wusNicPcoZjXagKPfkbLIe/Js5JIDYFGWaTuFxErdFkLnaK2ogQ== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.1" -"@percy/cli-command@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-command/-/cli-command-1.6.1.tgz#e41b47d038e9ce52ffeeabba19d707468670829d" - integrity sha512-5jeKsKM+it3gh93vV48dBi725ewVGOfxUsjZU8slKcK9IaohXkjr0/aWor9LWgQcug87sLS9VjNlc+UyhTW0KA== +"@percy/cli-command@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli-command/-/cli-command-1.8.1.tgz#feee384f7414771482fa86cd1fb9e0f2e0b821b4" + integrity sha512-iCb5o2ZuiY9j3gQDN/Q+b+B2T/BKImfil5ve9kA4I5S2Uv8ujVX2u4tHznrb89hw2xlkBUTbN2nCETH9HR7gcA== dependencies: - "@percy/config" "1.6.1" - "@percy/core" "1.6.1" - "@percy/logger" "1.6.1" + "@percy/config" "1.8.1" + "@percy/core" "1.8.1" + "@percy/logger" "1.8.1" -"@percy/cli-config@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-config/-/cli-config-1.6.1.tgz#f34c72fda6f9903770bb725693b8c7dbbad21150" - integrity sha512-fUO14gra2EdvY0RT5eSxH8QhammDtclcb2vjTwMG6QUW+sGDQQCu8M8s7YBxRKo/aTq+tJy1hfqd6W/rsx0mWQ== +"@percy/cli-config@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli-config/-/cli-config-1.8.1.tgz#e4aafac25676ed9dcdeaf44824c95f2a34fe2ab7" + integrity sha512-cjb7rZMjnvnGGJD+z4yCX8u2//Q2CAI3MbltG7ipuEmj2MtzdNwQS1Y8v57fO2g0b0ALp2PqwTXacwhoIKiWCg== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.1" -"@percy/cli-exec@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-exec/-/cli-exec-1.6.1.tgz#736e5f2c09080e19ebbcf84c1ded13c5e7db2b0b" - integrity sha512-F8j+reJWPu0s+zQuEU2Gm8SZ1QP7cf04MS5hmL1CaE7G7O88ec+yrS6eiSlNrY1+D45B7NYy7l6zWzsP+nQ3TA== +"@percy/cli-exec@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli-exec/-/cli-exec-1.8.1.tgz#8452c515d5d4a20e8837171f63b1313ffa4a3907" + integrity sha512-zN3ulxYexsmMDwnx6BfNzaRbsKg08zCyIYF3Kv7Ut18J2FnqZXuF89fO2k/BQnvwzCIvwlkB4YzZxEP27jPORg== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.1" cross-spawn "^7.0.3" which "^2.0.2" -"@percy/cli-snapshot@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-snapshot/-/cli-snapshot-1.6.1.tgz#0da90a1cf6f7e79898f4f68f1e180c8c7c8f2894" - integrity sha512-lrZR/CIL1zkZdeOPmnidkVtWY6foOq3ekWbaoMgDu0agMjf2lxEX2DvkCS/WwdmF/JvDcquLKO6gS5EHE6mbBQ== +"@percy/cli-snapshot@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli-snapshot/-/cli-snapshot-1.8.1.tgz#404d25467069befaf77bcd93118dc7ee22c1c66e" + integrity sha512-VvV+MTTkd6GRkkCLB02BAGKleCt1j3xcyVF2EgUaX8av+9LruONwR8oIpeGc+rCSwlzcpQKAsCduq9cyiz26iw== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.1" yaml "^2.0.0" -"@percy/cli-upload@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli-upload/-/cli-upload-1.6.1.tgz#ffd95ca03d15081a77d9090d1c33b32044db849e" - integrity sha512-F+n7gy5CTfR0BUSsFdxEpfb9CNDnnhnFO0AN9v2FfyiSEei3CXrS2Te6DJS/YXnVI58YeFRbiW6G1LKhP+Vi4g== +"@percy/cli-upload@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli-upload/-/cli-upload-1.8.1.tgz#01f08f513933c91618de324e12be6a5a19f4e44f" + integrity sha512-2R4pEfOhYMTp2cdQeBzssoHESjTVcfdtLN2cQiAcV86jpBC+A5tVW7FQnRBjrefOdnLWRCO9HQn69sRdxrH28g== dependencies: - "@percy/cli-command" "1.6.1" + "@percy/cli-command" "1.8.1" fast-glob "^3.2.11" image-size "^1.0.0" "@percy/cli@^1.3.0": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/cli/-/cli-1.6.1.tgz#d3910dd63d2b2b67e57e4c9c675eafb4d471322a" - integrity sha512-BwCcigFixUhi2Wn6X+oucJrqnk/6e4FOsYQI4+lDzZU416+WpsYF0CjmhQVpa9Us278L+qc7eIsDHJwuJeurFw== - dependencies: - "@percy/cli-build" "1.6.1" - "@percy/cli-command" "1.6.1" - "@percy/cli-config" "1.6.1" - "@percy/cli-exec" "1.6.1" - "@percy/cli-snapshot" "1.6.1" - "@percy/cli-upload" "1.6.1" - "@percy/client" "1.6.1" - "@percy/logger" "1.6.1" - -"@percy/client@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.6.1.tgz#6bb136c2fe0ab0490acc898de21f8ab3508420a5" - integrity sha512-e5ToG88O1gDDOuQ+i4989vKCfF+CsPSGG9VA06IKSYNP4QBATe9/RYERZ5jk118uEutjxi8lIjzet0eg8rv7BA== - dependencies: - "@percy/env" "1.6.1" - "@percy/logger" "1.6.1" - -"@percy/config@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.6.1.tgz#11fb960dca4ecc0575349382a973b6c5c941363a" - integrity sha512-UiB4gpt01VgPUF4ObZBixR1Wwi/ZUaMXBUxmE3wOa3zZrtZXOzbZwQGcntw5ToEq6OQBP100q1vetnP8xhFQtQ== - dependencies: - "@percy/logger" "1.6.1" + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/cli/-/cli-1.8.1.tgz#adf3e47ced38a563b065a3f4c153f40f57045ac9" + integrity sha512-vVpLC8fgPAabw9APNIuM00+f9bwgR3BEFln9LJVtqp1L9ILspKLoUl2qt6nO0SgmxPcHJ3/HO6EGoVFoMClZTQ== + dependencies: + "@percy/cli-build" "1.8.1" + "@percy/cli-command" "1.8.1" + "@percy/cli-config" "1.8.1" + "@percy/cli-exec" "1.8.1" + "@percy/cli-snapshot" "1.8.1" + "@percy/cli-upload" "1.8.1" + "@percy/client" "1.8.1" + "@percy/logger" "1.8.1" + +"@percy/client@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/client/-/client-1.8.1.tgz#bbc81c2a41ab44e5f79c93aac6afa68c17a90af8" + integrity sha512-2T0SdIUFwMCsnaUTM0hNQx5lrkg2qPHPKBL/MRrbbTPJVJe3J9PzeR84RJtKviyy+ciEkkm6UaQsWBngbyXXsQ== + dependencies: + "@percy/env" "1.8.1" + "@percy/logger" "1.8.1" + +"@percy/config@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/config/-/config-1.8.1.tgz#46d73b8cdb0e1d7400e8d91fe12e9f6ea08d2b31" + integrity sha512-yKdKx0kh5xyVxBdNVExXsoNuSmKcmpma31DkqqacDUu2nYSjuCGxr3j3y8BRAKURfj59fdhwpFNmjVVB1xiVWA== + dependencies: + "@percy/logger" "1.8.1" ajv "^8.6.2" cosmiconfig "^7.0.0" yaml "^2.0.0" -"@percy/core@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.6.1.tgz#f18681ba4ba1d8f25cfe2c624424749ae0b2590a" - integrity sha512-a4EeoynE4pU7qExfLr56nmZNu3ozeCNFk6rCBQqoXbSJy6UVG8nj7U8AWEPeBHqtdk2M+30YAVr6mJCQjkZZ7w== +"@percy/core@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/core/-/core-1.8.1.tgz#b1e419da98251b370cdcea436bb702ee3461d203" + integrity sha512-wwTSJWoKK8tr8AbORlJGJ7uz55v8oUdOrfZtCnRPssWiceaWFXecRbjLXcNCCz0/2nT6Y1ilDsEOjlExYnKhww== dependencies: - "@percy/client" "1.6.1" - "@percy/config" "1.6.1" - "@percy/dom" "1.6.1" - "@percy/logger" "1.6.1" + "@percy/client" "1.8.1" + "@percy/config" "1.8.1" + "@percy/dom" "1.8.1" + "@percy/logger" "1.8.1" content-disposition "^0.5.4" cross-spawn "^7.0.3" extract-zip "^2.0.1" @@ -1785,20 +1792,20 @@ dependencies: "@percy/sdk-utils" "^1.3.1" -"@percy/dom@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.6.1.tgz#8b8e82817dd88f8497ffce2d8fc5480466cbfef3" - integrity sha512-TAVGiE/7imR3Q6z1ogZFufX+SsPrElBOYNmj+MOFVJyzJ/cTjA9B510Blx1nXTu2VNdK2GAmRGQPbZDty34YMg== +"@percy/dom@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/dom/-/dom-1.8.1.tgz#b3daa0f6f8d95a5b72df4edfdca615ed04f14f93" + integrity sha512-h9XJV+VcVHrMkfIgJ6sJLujtYLzkvRy3aBvUY/iS7yh88qM16pSuTgEHG9fzbOp8ZNZpu6zZdxy4QmeACqzGzw== -"@percy/env@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.6.1.tgz#5119de7526e006242b76688c24e94822113af139" - integrity sha512-AguYuqRcKHkCnWndvq1pTrxeUXT0mNULSi9p7D0nN1744036RcdVrE/EhZbgK2fshSHxIrnfPSj0Lnt4Of46lg== +"@percy/env@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/env/-/env-1.8.1.tgz#338d87479dc48fc2ca3e0a66c98f0b5235f322c2" + integrity sha512-noxlV3fesivvXxnWEODUzdoToIurOolvwlEk+ETmpB933zKTpo55UFD5leV5LWP5Oxwrv4uen2i7ZdHRXPD47Q== -"@percy/logger@1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.6.1.tgz#754e3bdaa4419aadc2d7fa95b63f4ef757483cd4" - integrity sha512-cOMzDbg6Or1SnyzT9xCnwIiMDQ4sUR7Ha91CE6NWpV273Ef5PtvBA0joIB7wWSe7t2/a8hQrYJ6eGU4rUcsuTw== +"@percy/logger@1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@percy/logger/-/logger-1.8.1.tgz#91e1fb3ec2952dfd170201ab101aa1a7f505c778" + integrity sha512-O1GpuuN6pzBk/dso57LS90APbRNfc4y0qsGEIph+8f1zK6dBZmalwKLzhgrvb267rLe3pduYM5fCiYel0pqh7Q== "@percy/sdk-utils@^1.3.1": version "1.6.1" @@ -1869,9 +1876,9 @@ tslib "^1.9.3" "@sinclair/typebox@^0.24.1": - version "0.24.19" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.19.tgz#5297278e0d8a1aea084685a3216074910ac6c113" - integrity sha512-gHJu8cdYTD5p4UqmQHrxaWrtb/jkH5imLXzuBypWhKzNkW0qfmgz+w1xaJccWVuJta1YYUdlDiPHXRTR4Ku0MQ== + version "0.24.27" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.27.tgz#d55643516a1546174e10da681a8aaa81e757452d" + integrity sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg== "@sinonjs/commons@^1.7.0": version "1.8.3" @@ -2038,9 +2045,9 @@ "@types/node" "*" "@types/geojson@^7946.0.8": - version "7946.0.9" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.9.tgz#dd5d4c39c989c01f49c1b05df5826ffd4ddae1e9" - integrity sha512-snVY+R7d0VfUEHdfEe/NTFnz0+Qei0kEGIV8ErryWQwDXNS7uzhVEMcUtZ+uBHXSBcJa0zBcevBE0u41acbodQ== + version "7946.0.10" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" + integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== "@types/graceful-fs@^4.1.2": version "4.1.5" @@ -2115,14 +2122,14 @@ integrity sha512-jhMOZSS0UGYTS9pqvt6q3wtT3uvOSve5piTEmTMx3zzTuBLvSIMxSIBIc3d5lajVD5h4xc41AMZD2M5orN3PxA== "@types/node@*": - version "18.0.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.3.tgz#463fc47f13ec0688a33aec75d078a0541a447199" - integrity sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ== + version "18.6.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.5.tgz#06caea822caf9e59d5034b695186ee74154d2802" + integrity sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw== "@types/node@^14.14.22", "@types/node@^14.14.31": - version "14.18.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41" - integrity sha512-x5W9s+8P4XteaxT/jKF0PSb7XEvo5VmqEWgsMlyeY4ZlLK8I6aH6g5TPPyDlLAep+GYf4kefb7HFyc7PAO3m+Q== + version "14.18.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.23.tgz#70f5f20b0b1b38f696848c1d3647bb95694e615e" + integrity sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2267,9 +2274,9 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" - integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.11.tgz#5e10ca33e219807c0eee0f08b5efcba9b6a42c06" + integrity sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA== dependencies: "@types/yargs-parser" "*" @@ -2714,11 +2721,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axe-core@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.2.tgz#dcf7fb6dea866166c3eab33d68208afe4d5f670c" - integrity sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA== - axe-core@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" @@ -3411,9 +3413,9 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1: semver "7.0.0" core-js-pure@^3.20.2: - version "3.23.4" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.4.tgz#aba5c7fb297063444f6bf93afb0362151679a012" - integrity sha512-lizxkcgj3XDmi7TUBFe+bQ1vNpD5E4t76BrBWI3HdUxdw/Mq1VF4CkiHzIKyieECKtcODK2asJttoofEeUKICQ== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3" + integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg== core-js@^1.0.0: version "1.2.7" @@ -3560,9 +3562,9 @@ cypress-real-events@^1.7.1: integrity sha512-/Bg15RgJ0SYsuXc6lPqH08x19z6j2vmhWN4wXfJqm3z8BTAFiK2MvipZPzxT8Z0jJP0q7kuniWrLIvz/i/8lCQ== cypress@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.3.0.tgz#fae8d32f0822fcfb938e79c7c31ef344794336ae" - integrity sha512-txkQWKzvBVnWdCuKs5Xc08gjpO89W2Dom2wpZgT9zWZT5jXxqPIxqP/NC1YArtkpmp3fN5HW8aDjYBizHLUFvg== + version "10.4.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.4.0.tgz#bb5b3b6588ad49eff172fecf5778cc0da2980e4e" + integrity sha512-OM7F8MRE01SHQRVVzunid1ZK1m90XTxYnl+7uZfIrB4CYqUDCrZEeSyCXzIbsS6qcaijVCAhqDL60SxG8N6hew== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -3642,9 +3644,9 @@ date-names@^0.1.11: integrity sha512-IxxoeD9tdx8pXVcmqaRlPvrXIsSrSrIZzfzlOkm9u+hyzKp5Wk/odt9O/gd7Ockzy8n/WHeEpTVJ2bF3mMV4LA== dayjs@^1.10.4: - version "1.11.3" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258" - integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A== + version "1.11.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e" + integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g== debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" @@ -3756,9 +3758,9 @@ detect-node-es@^1.1.0: integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== diff-dom@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/diff-dom/-/diff-dom-4.2.3.tgz#c6234b49c1b49e41601d2f08dbb26cd57842de45" - integrity sha512-8OZPIbTWVhkQVlUlsb+VuMEMpTpKKhO5FTwds2bYVIaBiPNJCG1YW5qXUyLWMux5gC2UGyYXjtX05SPivnGMCw== + version "4.2.5" + resolved "https://registry.yarnpkg.com/diff-dom/-/diff-dom-4.2.5.tgz#5e093486d4ce706c702f0151c1b674aa015ac0a6" + integrity sha512-muGbiH5Mkj+bCigiG4x8tGES1JQQHp8UpAEaemOqfQkiwtCxKqDYPOeqBzoTRG+L7mKwHgTPY2WBlgOnnnUmAw== diff-match-patch@^1.0.5: version "1.0.5" @@ -4201,20 +4203,20 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.14.1" eslint-plugin-jsx-a11y@^6.5.1: - version "6.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.0.tgz#2c5ac12e013eb98337b9aa261c3b355275cc6415" - integrity sha512-kTeLuIzpNhXL2CwLlc8AHI0aFRwWHcg483yepO9VQiHzM9bZwJdzTkzBszbuPrbgGmq2rlX/FaT2fJQsjUSHsw== + version "6.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" + integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== dependencies: - "@babel/runtime" "^7.18.3" + "@babel/runtime" "^7.18.9" aria-query "^4.2.2" array-includes "^3.1.5" ast-types-flow "^0.0.7" - axe-core "^4.4.2" + axe-core "^4.4.3" axobject-query "^2.2.0" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" has "^1.0.3" - jsx-ast-utils "^3.3.1" + jsx-ast-utils "^3.3.2" language-tags "^1.0.5" minimatch "^3.1.2" semver "^6.3.0" @@ -4380,9 +4382,9 @@ event-emitter@^0.3.5: es5-ext "~0.10.14" eventemitter2@^6.4.3: - version "6.4.6" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.6.tgz#92d56569cc147a4d9b9da9e942e89b20ce236b0a" - integrity sha512-OHqo4wbHX5VbvlbB6o6eDwhYmiTjrpWACjF8Pmof/GTD6rdBNdZFNck3xlhqOiQFGCOoq3uzHvA0cQpFHIGVAQ== + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== events@^3.2.0: version "3.3.0" @@ -6424,7 +6426,7 @@ jsprim@^2.0.2: json-schema "0.4.0" verror "1.10.0" -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.1: +"jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.2" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== @@ -6432,10 +6434,18 @@ jsprim@^2.0.2: array-includes "^3.1.5" object.assign "^4.1.2" +jsx-ast-utils@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.3" + jszip@^3.7.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.0.tgz#faf3db2b4b8515425e34effcdbb086750a346061" - integrity sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q== + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -6793,9 +6803,9 @@ matrix-events-sdk@^0.0.1-beta.7: unhomoglyph "^1.0.6" matrix-mock-request@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.1.0.tgz#86f5b0ef846865d0767d3a8e64f5bcd6ca94c178" - integrity sha512-Cjpl3yP6h0yu5GKG89m1XZXZlm69Kg/qHV41N/t6SrQsgcfM3Bfavqx9YrtG0UnuXGy4bBSZIe1QiWVeFPZw1A== + version "2.1.2" + resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.1.2.tgz#11e38ed1233dced88a6f2bfba1684d5c5b3aa2c2" + integrity sha512-/OXCIzDGSLPJ3fs+uzDrtaOHI/Sqp4iEuniRn31U8S06mPXbvAnXknHqJ4c6A/KVwJj/nPFbGXpK4wPM038I6A== dependencies: expect "^28.1.0" @@ -7157,7 +7167,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.2: +object.assign@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -7167,6 +7177,16 @@ object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.2, object.assign@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" + integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.entries@^1.1.1, object.entries@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" @@ -7531,7 +7551,16 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.3.11, postcss@^8.4.14: +postcss@^8.3.11: + version "8.4.16" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" + integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^8.4.14: version "8.4.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== @@ -7857,9 +7886,9 @@ react-test-renderer@^17.0.0, react-test-renderer@^17.0.2: scheduler "^0.20.2" react-transition-group@^4.4.1: - version "4.4.2" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" - integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== dependencies: "@babel/runtime" "^7.5.5" dom-helpers "^5.0.1" @@ -8228,9 +8257,9 @@ sane@^4.0.3: walker "~1.0.5" sanitize-html@^2.3.2: - version "2.7.0" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279" - integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.1.tgz#a6c2c1a88054a79eeacfac9b0a43f1b393476901" + integrity sha512-oOpe8l4J8CaBk++2haoN5yNI5beekjuHv3JRPKUx/7h40Rdr85pemn4NkvUB3TcBP7yjat574sPlcMAyv4UQig== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" @@ -9422,9 +9451,9 @@ ws@^7.4.6: integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== ws@^8.0.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" - integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== xml-name-validator@^3.0.0: version "3.0.0" From 4e30d3c0fc4b62ef1271989c68dbd6539f3b2726 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 16:29:53 +0100 Subject: [PATCH 031/107] Fix space panel subspace indentation going missing (#9167) * Fix space panel subspace indentation going missing * Add cypress test around subspaces in space panel * Add cypress test around subspaces in space panel * Fix bad selector * Fix aria axe violation heading-order * Fix test * Remove it.only --- cypress/e2e/spaces/spaces.spec.ts | 38 ++++++++++++++++++++++++++ res/css/structures/_HomePage.pcss | 6 ++-- res/css/structures/_SpacePanel.pcss | 5 ++++ src/components/structures/HomePage.tsx | 6 ++-- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/spaces/spaces.spec.ts b/cypress/e2e/spaces/spaces.spec.ts index 0a8212ab8dd..e7767de9421 100644 --- a/cypress/e2e/spaces/spaces.spec.ts +++ b/cypress/e2e/spaces/spaces.spec.ts @@ -237,4 +237,42 @@ describe("Spaces", () => { cy.contains(".mx_SpaceHierarchy_roomTile", "Gaming").should("exist"); }); }); + + it("should render subspaces in the space panel only when expanded", () => { + cy.injectAxe(); + + cy.createSpace({ + name: "Child Space", + initial_state: [], + }).then(spaceId => { + cy.createSpace({ + name: "Root Space", + initial_state: [ + spaceChildInitialState(spaceId), + ], + }).as("spaceId"); + }); + cy.get('.mx_SpacePanel .mx_SpaceButton[aria-label="Root Space"]').should("exist"); + cy.get('.mx_SpacePanel .mx_SpaceButton[aria-label="Child Space"]').should("not.exist"); + + const axeOptions = { + rules: { + // Disable this check as it triggers on nested roving tab index elements which are in practice fine + 'nested-interactive': { + enabled: false, + }, + }, + }; + cy.checkA11y(undefined, axeOptions); + cy.get(".mx_SpacePanel").percySnapshotElement("Space panel collapsed", { widths: [68] }); + + cy.get(".mx_SpaceButton_toggleCollapse").click({ force: true }); + cy.get(".mx_SpacePanel:not(.collapsed)").should("exist"); + + cy.contains(".mx_SpaceItem", "Root Space").should("exist") + .contains(".mx_SpaceItem", "Child Space").should("exist"); + + cy.checkA11y(undefined, axeOptions); + cy.get(".mx_SpacePanel").percySnapshotElement("Space panel expanded", { widths: [258] }); + }); }); diff --git a/res/css/structures/_HomePage.pcss b/res/css/structures/_HomePage.pcss index 6bfabd9c87f..f35de9919ce 100644 --- a/res/css/structures/_HomePage.pcss +++ b/res/css/structures/_HomePage.pcss @@ -37,15 +37,15 @@ limitations under the License. } h1 { - font-weight: 600; + font-weight: $font-semi-bold; font-size: $font-32px; line-height: $font-44px; margin-bottom: 4px; } - h4 { + h2 { margin-top: 4px; - font-weight: 600; + font-weight: $font-semi-bold; font-size: $font-18px; line-height: $font-25px; color: $muted-fg-color; diff --git a/res/css/structures/_SpacePanel.pcss b/res/css/structures/_SpacePanel.pcss index 72dbddf75e1..6517aec5e10 100644 --- a/res/css/structures/_SpacePanel.pcss +++ b/res/css/structures/_SpacePanel.pcss @@ -286,6 +286,11 @@ $activeBorderColor: $primary-content; visibility: hidden; } } + + .mx_SpaceTreeLevel { + // Indent subspaces + padding-left: 16px; + } } .mx_SpaceButton_avatarWrapper { diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index 613625ae702..b2596cee435 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -85,7 +85,7 @@ const UserWelcomeTop = () => {

    { _tDom("Welcome %(name)s", { name: ownProfile.displayName }) }

    -

    { _tDom("Now, let's help you get started") }

    +

    { _tDom("Now, let's help you get started") }

  • ; }; @@ -107,11 +107,11 @@ const HomePage: React.FC = ({ justRegistered = false }) => { introSection = {config.brand}

    { _tDom("Welcome to %(appName)s", { appName: config.brand }) }

    -

    { _tDom("Own your conversations.") }

    +

    { _tDom("Own your conversations.") }

    ; } - return + return
    { introSection }
    From b7872f2ff7abef6b5cc28d7866d2265f403ec937 Mon Sep 17 00:00:00 2001 From: Kerry Date: Wed, 10 Aug 2022 18:14:59 +0200 Subject: [PATCH 032/107] Device manager - data fetching (PSG-637) (#9151) * add session manager tab to user settings * fussy import ordering * i18n * extract device fetching logic into hook * use new extended device type in device tile, add verified metadata * add current session section, test * tidy * update types for DeviceWithVerification --- .../views/settings/DevicesPanelEntry.tsx | 9 +- .../views/settings/devices/DeviceTile.tsx | 10 +- .../views/settings/devices/useOwnDevices.ts | 105 ++++++++++++ .../settings/shared/SettingsSubsection.tsx | 8 +- .../settings/tabs/user/SessionManagerTab.tsx | 16 +- src/i18n/strings/en_EN.json | 2 + .../settings/devices/DeviceTile-test.tsx | 6 + .../devices/SelectableDeviceTile-test.tsx | 1 + .../__snapshots__/DeviceTile-test.tsx.snap | 54 +++++++ .../SelectableDeviceTile-test.tsx.snap | 7 + .../tabs/user/SessionManagerTab-test.tsx | 152 ++++++++++++++++++ .../SessionManagerTab-test.tsx.snap | 77 +++++++++ 12 files changed, 434 insertions(+), 13 deletions(-) create mode 100644 src/components/views/settings/devices/useOwnDevices.ts create mode 100644 test/components/views/settings/tabs/user/SessionManagerTab-test.tsx create mode 100644 test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index b0301214b9b..beb7a8e86e6 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -154,12 +154,17 @@ export default class DevicesPanelEntry extends React.Component { ; + const deviceWithVerification = { + ...this.props.device, + isVerified: this.props.verified, + }; + if (this.props.isOwnDevice) { return
    - + { buttons }
    ; @@ -167,7 +172,7 @@ export default class DevicesPanelEntry extends React.Component { return (
    - + { buttons }
    diff --git a/src/components/views/settings/devices/DeviceTile.tsx b/src/components/views/settings/devices/DeviceTile.tsx index 33f9fc40a85..9e9a520fcea 100644 --- a/src/components/views/settings/devices/DeviceTile.tsx +++ b/src/components/views/settings/devices/DeviceTile.tsx @@ -15,21 +15,21 @@ limitations under the License. */ import React, { Fragment } from "react"; -import { IMyDevice } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../../languageHandler"; import { formatDate, formatRelativeTime } from "../../../../DateUtils"; import TooltipTarget from "../../elements/TooltipTarget"; import { Alignment } from "../../elements/Tooltip"; import Heading from "../../typography/Heading"; +import { DeviceWithVerification } from "./useOwnDevices"; export interface DeviceTileProps { - device: IMyDevice; + device: DeviceWithVerification; children?: React.ReactNode; onClick?: () => void; } -const DeviceTileName: React.FC<{ device: IMyDevice }> = ({ device }) => { +const DeviceTileName: React.FC<{ device: DeviceWithVerification }> = ({ device }) => { if (device.display_name) { return = ({ value, id }) const DeviceTile: React.FC = ({ device, children, onClick }) => { const lastActivity = device.last_seen_ts && `${_t('Last activity')} ${formatLastActivity(device.last_seen_ts)}`; + const verificationStatus = device.isVerified ? _t('Verified') : _t('Unverified'); const metadata = [ + { id: 'isVerified', value: verificationStatus }, { id: 'lastActivity', value: lastActivity }, { id: 'lastSeenIp', value: device.last_seen_ip }, ]; - return
    + return
    diff --git a/src/components/views/settings/devices/useOwnDevices.ts b/src/components/views/settings/devices/useOwnDevices.ts new file mode 100644 index 00000000000..ad9523cc14f --- /dev/null +++ b/src/components/views/settings/devices/useOwnDevices.ts @@ -0,0 +1,105 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { useContext, useEffect, useState } from "react"; +import { IMyDevice, MatrixClient } from "matrix-js-sdk/src/matrix"; +import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { logger } from "matrix-js-sdk/src/logger"; + +import MatrixClientContext from "../../../../contexts/MatrixClientContext"; + +export type DeviceWithVerification = IMyDevice & { isVerified: boolean | null }; + +const isDeviceVerified = ( + matrixClient: MatrixClient, + crossSigningInfo: CrossSigningInfo, + device: IMyDevice, +): boolean | null => { + try { + const deviceInfo = matrixClient.getStoredDevice(matrixClient.getUserId(), device.device_id); + return crossSigningInfo.checkDeviceTrust( + crossSigningInfo, + deviceInfo, + false, + true, + ).isCrossSigningVerified(); + } catch (error) { + logger.error("Error getting device cross-signing info", error); + return null; + } +}; + +const fetchDevicesWithVerification = async (matrixClient: MatrixClient): Promise => { + const { devices } = await matrixClient.getDevices(); + const crossSigningInfo = matrixClient.getStoredCrossSigningForUser(matrixClient.getUserId()); + + const devicesDict = devices.reduce((acc, device: IMyDevice) => ({ + ...acc, + [device.device_id]: { + ...device, + isVerified: isDeviceVerified(matrixClient, crossSigningInfo, device), + }, + }), {}); + + return devicesDict; +}; +export enum OwnDevicesError { + Unsupported = 'Unsupported', + Default = 'Default', +} +type DevicesState = { + devices: Record; + currentDeviceId: string; + isLoading: boolean; + error?: OwnDevicesError; +}; +export const useOwnDevices = (): DevicesState => { + const matrixClient = useContext(MatrixClientContext); + + const currentDeviceId = matrixClient.getDeviceId(); + + const [devices, setDevices] = useState({}); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(); + + useEffect(() => { + const getDevicesAsync = async () => { + setIsLoading(true); + try { + const devices = await fetchDevicesWithVerification(matrixClient); + setDevices(devices); + setIsLoading(false); + } catch (error) { + if (error.httpStatus == 404) { + // 404 probably means the HS doesn't yet support the API. + setError(OwnDevicesError.Unsupported); + } else { + logger.error("Error loading sessions:", error); + setError(OwnDevicesError.Default); + } + setIsLoading(false); + } + }; + getDevicesAsync(); + }, [matrixClient]); + + return { + devices, + currentDeviceId, + isLoading, + error, + }; +}; diff --git a/src/components/views/settings/shared/SettingsSubsection.tsx b/src/components/views/settings/shared/SettingsSubsection.tsx index 5dcdc9dad6f..6d23a080caa 100644 --- a/src/components/views/settings/shared/SettingsSubsection.tsx +++ b/src/components/views/settings/shared/SettingsSubsection.tsx @@ -14,18 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; +import React, { HTMLAttributes } from "react"; import Heading from "../../typography/Heading"; -export interface SettingsSubsectionProps { +export interface SettingsSubsectionProps extends HTMLAttributes { heading: string; description?: string | React.ReactNode; children?: React.ReactNode; } -const SettingsSubsection: React.FC = ({ heading, description, children }) => ( -
    +const SettingsSubsection: React.FC = ({ heading, description, children, ...rest }) => ( +
    { heading } { !!description &&
    { description }
    }
    diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index 17c09aeb7a3..7d65ce83da0 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -17,16 +17,26 @@ limitations under the License. import React from 'react'; import { _t } from "../../../../../languageHandler"; +import Spinner from '../../../elements/Spinner'; +import { useOwnDevices } from '../../devices/useOwnDevices'; +import DeviceTile from '../../devices/DeviceTile'; import SettingsSubsection from '../../shared/SettingsSubsection'; import SettingsTab from '../SettingsTab'; const SessionManagerTab: React.FC = () => { + const { devices, currentDeviceId, isLoading } = useOwnDevices(); + + const currentDevice = devices[currentDeviceId]; return + data-testid='current-session-section' + > + { isLoading && } + { !!currentDevice && } + ; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e601003ecb4..c15c81273cf 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1693,6 +1693,8 @@ "Verification code": "Verification code", "Discovery options will appear once you have added a phone number above.": "Discovery options will appear once you have added a phone number above.", "Last activity": "Last activity", + "Verified": "Verified", + "Unverified": "Unverified", "Unable to remove contact information": "Unable to remove contact information", "Remove %(email)s?": "Remove %(email)s?", "Invalid Email Address": "Invalid Email Address", diff --git a/test/components/views/settings/devices/DeviceTile-test.tsx b/test/components/views/settings/devices/DeviceTile-test.tsx index d688eca9135..4083945fd61 100644 --- a/test/components/views/settings/devices/DeviceTile-test.tsx +++ b/test/components/views/settings/devices/DeviceTile-test.tsx @@ -24,6 +24,7 @@ describe('', () => { const defaultProps = { device: { device_id: '123', + isVerified: false, }, }; const getComponent = (props = {}) => ( @@ -43,6 +44,11 @@ describe('', () => { expect(container).toMatchSnapshot(); }); + it('renders a verified device with no metadata', () => { + const { container } = render(getComponent()); + expect(container).toMatchSnapshot(); + }); + it('renders display name with a tooltip', () => { const device: IMyDevice = { device_id: '123', diff --git a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx index 77dad3e1383..5c0fe47828b 100644 --- a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx +++ b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx @@ -25,6 +25,7 @@ describe('', () => { display_name: 'My Device', device_id: 'my-device', last_seen_ip: '123.456.789', + isVerified: false, }; const defaultProps = { onClick: jest.fn(), diff --git a/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap index 299d72348c8..cafd47a8a74 100644 --- a/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap +++ b/test/components/views/settings/devices/__snapshots__/DeviceTile-test.tsx.snap @@ -4,6 +4,7 @@ exports[` renders a device with no metadata 1`] = `
    renders a device with no metadata 1`] = ` +
    +
    +
    +
    +`; + +exports[` renders a verified device with no metadata 1`] = ` +
    +
    +
    +

    + 123 +

    +
    @@ -30,6 +70,7 @@ exports[` renders display name with a tooltip 1`] = `
    renders display name with a tooltip 1`] = `
    @@ -60,6 +107,7 @@ exports[` separates metadata with a dot 1`] = `
    separates metadata with a dot 1`] = `