From 7c9c818f06a53743ccd6507991f56e682c1e0fa5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Tue, 13 Jun 2023 11:50:36 +0100 Subject: [PATCH 01/68] GHA: require kiwi creds in cypress.yaml (#11069) * GHA: require kiwi creds in cypress.yaml * prettify --- .github/workflows/cypress.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index 9c2f047c2e1..012a4b65bb2 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -23,6 +23,10 @@ on: secrets: CYPRESS_RECORD_KEY: required: true + TCMS_USERNAME: + required: true + TCMS_PASSWORD: + required: true concurrency: group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }} From 0ce3664434a885ca470fcaa9918f0fec69847d8f Mon Sep 17 00:00:00 2001 From: Suguru Hirahara Date: Tue, 13 Jun 2023 11:13:07 +0000 Subject: [PATCH 02/68] Conform to the naming policy - AppsContainer resizer (#11042) * Add a snapshot to track AppTile in AppsDrawer * Conform to the naming policy - AppsContainer resizer * Nest 'mx_AppsContainer_resizer_container_handle' to enforce the style - mx_AppsContainer_resizer - mx_AppsContainer_resizer_container - mx_AppsContainer_resizer_container_handle * Rename "mx_AppsContainer_resizer" to "mx_AppsDrawer_resizer" PersistentVResizer is defined by mx_AppsDrawer (see const "classes") --- cypress/e2e/widgets/layout.spec.ts | 2 +- res/css/views/rooms/_AppsDrawer.pcss | 54 ++++---- src/components/views/rooms/AppsDrawer.tsx | 8 +- .../views/elements/AppTile-test.tsx | 3 + .../__snapshots__/AppTile-test.tsx.snap | 116 ++++++++++++++++++ 5 files changed, 151 insertions(+), 32 deletions(-) diff --git a/cypress/e2e/widgets/layout.spec.ts b/cypress/e2e/widgets/layout.spec.ts index 0f18ce85c22..16470fd5a0b 100644 --- a/cypress/e2e/widgets/layout.spec.ts +++ b/cypress/e2e/widgets/layout.spec.ts @@ -102,7 +102,7 @@ describe("Widget Layout", () => { it("manually resize the height of the top container layout", () => { cy.get('iframe[title="widget"]').invoke("height").should("be.lessThan", 250); - cy.get(".mx_AppsContainer_resizerHandle") + cy.get(".mx_AppsDrawer_resizer_container_handle") .trigger("mousedown") .trigger("mousemove", { clientX: 0, clientY: 550, force: true }) .trigger("mouseup", { clientX: 0, clientY: 550, force: true }); diff --git a/res/css/views/rooms/_AppsDrawer.pcss b/res/css/views/rooms/_AppsDrawer.pcss index 1913ab799ca..20a68b4136d 100644 --- a/res/css/views/rooms/_AppsDrawer.pcss +++ b/res/css/views/rooms/_AppsDrawer.pcss @@ -32,43 +32,47 @@ limitations under the License. overflow: hidden; flex-grow: 1; - .mx_AppsContainer_resizerHandleContainer { + .mx_AppsDrawer_resizer { + margin-bottom: var(--container-gap-width); + } + + .mx_AppsDrawer_resizer_container { width: 100%; height: 10px; display: block; position: relative; - } - - .mx_AppsContainer_resizerHandle { - cursor: ns-resize; - /* Override styles from library, making the whole area the target area */ - width: 100% !important; - height: 100% !important; + .mx_AppsDrawer_resizer_container_handle { + cursor: ns-resize; - /* This is positioned directly below frame */ - position: absolute; - bottom: 50% !important; /* override from library */ + /* Override styles from library, making the whole area the target area */ + width: 100% !important; + height: 100% !important; - /* We then render the pill handle in an ::after to keep it in the handle's */ - /* area without being a massive line across the screen */ - &::after { - content: ""; + /* This is positioned directly below frame */ position: absolute; - border-radius: 3px; + bottom: 50% !important; /* override from library */ - height: 4px; - bottom: 0; + /* We then render the pill handle in an ::after to keep it in the handle's */ + /* area without being a massive line across the screen */ + &::after { + content: ""; + position: absolute; + border-radius: 3px; + + height: 4px; + bottom: 0; - /* Together, these make the bar 64px wide */ - /* These are also overridden from the library */ - left: calc(50% - 32px); - right: calc(50% - 32px); + /* Together, these make the bar 64px wide */ + /* These are also overridden from the library */ + left: calc(50% - 32px); + right: calc(50% - 32px); + } } } &:hover { - .mx_AppsContainer_resizerHandle::after { + .mx_AppsDrawer_resizer_container_handle::after { opacity: 0.8; background: $primary-content; } @@ -123,10 +127,6 @@ limitations under the License. } } -.mx_AppsContainer_resizer { - margin-bottom: var(--container-gap-width); -} - .mx_AppsContainer { display: flex; flex-direction: row; diff --git a/src/components/views/rooms/AppsDrawer.tsx b/src/components/views/rooms/AppsDrawer.tsx index 378538af182..d68a606c9d5 100644 --- a/src/components/views/rooms/AppsDrawer.tsx +++ b/src/components/views/rooms/AppsDrawer.tsx @@ -283,9 +283,9 @@ export default class AppsDrawer extends React.Component { room={this.props.room} minHeight={100} maxHeight={this.props.maxHeight - 50} - handleClass="mx_AppsContainer_resizerHandle" - handleWrapperClass="mx_AppsContainer_resizerHandleContainer" - className="mx_AppsContainer_resizer" + className="mx_AppsDrawer_resizer" + handleWrapperClass="mx_AppsDrawer_resizer_container" + handleClass="mx_AppsDrawer_resizer_container_handle" resizeNotifier={this.props.resizeNotifier} > {appContainers} @@ -358,9 +358,9 @@ const PersistentVResizer: React.FC = ({ resizeNotifier.stopResizing(); }} + className={className} handleWrapperClass={handleWrapperClass} handleClasses={{ bottom: handleClass }} - className={className} enable={{ bottom: true }} > {children} diff --git a/test/components/views/elements/AppTile-test.tsx b/test/components/views/elements/AppTile-test.tsx index 7743a002e9d..8f77db9f1ea 100644 --- a/test/components/views/elements/AppTile-test.tsx +++ b/test/components/views/elements/AppTile-test.tsx @@ -327,6 +327,9 @@ describe("AppTile", () => { expect(renderResult.getByText("Example 1")).toBeInTheDocument(); expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(true); + const { asFragment } = renderResult; + expect(asFragment()).toMatchSnapshot(); // Take snapshot of AppsDrawer with AppTile + // We want to verify that as we move the widget to the center container, // the widget frame remains running. diff --git a/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap b/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap index 95e1ce929aa..9f345ff82c1 100644 --- a/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap @@ -162,3 +162,119 @@ exports[`AppTile for a pinned widget should render 1`] = ` `; + +exports[`AppTile preserves non-persisted widget on container move 1`] = ` + +
+
+
+
+
+ + + + + Example 1 + + + + + +
+
+
+
+
+
+ +
+
+
+
+ Loading… +
+   +
+
+
+
+
+
+
+
+
+
+
+ +`; From ba28174ac09f9eaee612f22cdf9ed9c775608a38 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 Jun 2023 08:42:40 +0100 Subject: [PATCH 03/68] Prevent escape in threads from sending focus to main timeline composer (#11061) --- src/components/views/rooms/SendMessageComposer.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 33526c36780..263e7d13293 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -356,6 +356,8 @@ export class SendMessageComposer extends React.Component Date: Wed, 14 Jun 2023 08:45:19 +0100 Subject: [PATCH 04/68] Allow specifying help URLs in config.json (#11070) * Allow specifying help URLs in config.json * Fix test --- src/IConfigOptions.ts | 2 ++ src/SdkConfig.ts | 2 ++ .../views/settings/tabs/room/SecurityRoomSettingsTab.tsx | 3 ++- .../views/settings/tabs/user/HelpUserSettingsTab.tsx | 8 ++++++-- test/components/structures/MatrixChat-test.tsx | 2 ++ 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/IConfigOptions.ts b/src/IConfigOptions.ts index 1fa873ec87a..9efb490cc1c 100644 --- a/src/IConfigOptions.ts +++ b/src/IConfigOptions.ts @@ -152,6 +152,8 @@ export interface IConfigOptions { enable_presence_by_hs_url?: Record; // terms_and_conditions_links?: { url: string; text: string }[]; + help_url: string; + help_encryption_url: string; latex_maths_delims?: { inline?: { diff --git a/src/SdkConfig.ts b/src/SdkConfig.ts index be94bb5b08d..a12eb34d785 100644 --- a/src/SdkConfig.ts +++ b/src/SdkConfig.ts @@ -26,6 +26,8 @@ import { DeepReadonly, Defaultize } from "./@types/common"; // see element-web config.md for docs, or the IConfigOptions interface for dev docs export const DEFAULTS: DeepReadonly = { brand: "Element", + help_url: "https://element.io/help", + help_encryption_url: "https://element.io/help#encryption", integrations_ui_url: "https://scalar.vector.im/", integrations_rest_url: "https://scalar.vector.im/api", uisi_autorageshake_app: "element-auto-uisi", diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index e4ffd3ae5e3..eb209c9a866 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -43,6 +43,7 @@ import PosthogTrackers from "../../../../../PosthogTrackers"; import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; import { SettingsSection } from "../../shared/SettingsSection"; import SettingsTab from "../SettingsTab"; +import SdkConfig from "../../../../../SdkConfig"; interface IProps { room: Room; @@ -163,7 +164,7 @@ export default class SecurityRoomSettingsTab extends React.ComponentLearn more about encryption.", {}, { - a: (sub) => {sub}, + a: (sub) => {sub}, }, ), onFinished: (confirm) => { diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx index 6a8018bee95..34da8288639 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx @@ -251,7 +251,7 @@ export default class HelpUserSettingsTab extends React.Component brand, }, { - a: (sub) => {sub}, + a: (sub) => {sub}, }, ); if (SdkConfig.get("welcome_user_id") && getCurrentLanguage().startsWith("en")) { @@ -265,7 +265,11 @@ export default class HelpUserSettingsTab extends React.Component }, { a: (sub) => ( - + {sub} ), diff --git a/test/components/structures/MatrixChat-test.tsx b/test/components/structures/MatrixChat-test.tsx index cc80057cf8b..dd81ecf68ca 100644 --- a/test/components/structures/MatrixChat-test.tsx +++ b/test/components/structures/MatrixChat-test.tsx @@ -70,6 +70,8 @@ describe("", () => { const defaultProps: ComponentProps = { config: { brand: "Test", + help_url: "help_url", + help_encryption_url: "help_encryption_url", element_call: {}, feedback: { existing_issues_url: "https://feedback.org/existing", From 788c1c8f13c368f3a74c01c67b5f7940d2176746 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 14 Jun 2023 10:12:01 +0200 Subject: [PATCH 05/68] Cypress: improve secure key backup test (#11086) * Check account data after secure key backup * Add passphrase cypress test --- cypress/e2e/crypto/crypto.spec.ts | 93 ++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index 0a332a376de..0a79c8f0e1a 100644 --- a/cypress/e2e/crypto/crypto.spec.ts +++ b/cypress/e2e/crypto/crypto.spec.ts @@ -171,28 +171,91 @@ describe("Cryptography", function () { cy.stopHomeserver(this.homeserver); }); - it("setting up secure key backup should work", () => { - skipIfRustCrypto(); - cy.openUserSettings("Security & Privacy"); - cy.findByRole("button", { name: "Set up Secure Backup" }).click(); - cy.get(".mx_Dialog").within(() => { - cy.findByRole("button", { name: "Continue" }).click(); - cy.get(".mx_CreateSecretStorageDialog_recoveryKey code").invoke("text").as("securityKey"); + describe("setting up secure key backup should work", () => { + /** + * Verify that the `m.cross_signing.${keyType}` key is available on the account data on the server + * @param keyType + */ + function verifyKey(keyType: string) { + return cy + .getClient() + .then((cli) => cy.wrap(cli.getAccountDataFromServer(`m.cross_signing.${keyType}`))) + .then((accountData: { encrypted: Record> }) => { + expect(accountData.encrypted).to.exist; + const keys = Object.keys(accountData.encrypted); + const key = accountData.encrypted[keys[0]]; + expect(key.ciphertext).to.exist; + expect(key.iv).to.exist; + expect(key.mac).to.exist; + }); + } + + /** + * Click on download button and continue + */ + function downloadKey() { // Clicking download instead of Copy because of https://github.com/cypress-io/cypress/issues/2851 cy.findByRole("button", { name: "Download" }).click(); cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); - cy.get(".mx_InteractiveAuthDialog").within(() => { - cy.get(".mx_Dialog_title").within(() => { - cy.findByText("Setting up keys").should("exist"); - cy.findByText("Setting up keys").should("not.exist"); + } + + it("by recovery code", () => { + skipIfRustCrypto(); + cy.openUserSettings("Security & Privacy"); + cy.findByRole("button", { name: "Set up Secure Backup" }).click(); + cy.get(".mx_Dialog").within(() => { + // Recovery key is selected by default + cy.findByRole("button", { name: "Continue" }).click(); + cy.get(".mx_CreateSecretStorageDialog_recoveryKey code").invoke("text").as("securityKey"); + + downloadKey(); + + cy.get(".mx_InteractiveAuthDialog").within(() => { + cy.get(".mx_Dialog_title").within(() => { + cy.findByText("Setting up keys").should("exist"); + cy.findByText("Setting up keys").should("not.exist"); + }); }); + + cy.findByText("Secure Backup successful").should("exist"); + cy.findByRole("button", { name: "Done" }).click(); + cy.findByText("Secure Backup successful").should("not.exist"); + }); + + // Verify that the SSSS keys are in the account data stored in the server + verifyKey("master"); + verifyKey("self_signing"); + verifyKey("user_signing"); + }); + + it("by passphrase", () => { + skipIfRustCrypto(); + cy.openUserSettings("Security & Privacy"); + cy.findByRole("button", { name: "Set up Secure Backup" }).click(); + cy.get(".mx_Dialog").within(() => { + // Select passphrase option + cy.findByText("Enter a Security Phrase").click(); + cy.findByRole("button", { name: "Continue" }).click(); + + // Fill passphrase input + cy.get("input").type("new passphrase for setting up a secure key backup"); + cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); + // Confirm passphrase + cy.get("input").type("new passphrase for setting up a secure key backup"); + cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); + + downloadKey(); + + cy.findByText("Secure Backup successful").should("exist"); + cy.findByRole("button", { name: "Done" }).click(); + cy.findByText("Secure Backup successful").should("not.exist"); }); - cy.findByText("Secure Backup successful").should("exist"); - cy.findByRole("button", { name: "Done" }).click(); - cy.findByText("Secure Backup successful").should("not.exist"); + // Verify that the SSSS keys are in the account data stored in the server + verifyKey("master"); + verifyKey("self_signing"); + verifyKey("user_signing"); }); - return; }); it("creating a DM should work, being e2e-encrypted / user verification", function (this: CryptoTestContext) { From 3f52de2f5b2bc5bbc63ae267dca892e127e7b8aa Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 Jun 2023 09:49:17 +0100 Subject: [PATCH 06/68] Don't warn about the effects of redacting state events when redacting non-state-events (#11071) --- .../views/dialogs/ConfirmAndWaitRedactDialog.tsx | 4 +++- src/components/views/dialogs/ConfirmRedactDialog.tsx | 12 ++++++++---- src/components/views/messages/EditHistoryMessage.tsx | 1 + src/i18n/strings/en_EN.json | 3 ++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx index a792ccbd634..9989dc54344 100644 --- a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx @@ -15,6 +15,7 @@ limitations under the License. */ import React from "react"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from "../../../languageHandler"; import ConfirmRedactDialog from "./ConfirmRedactDialog"; @@ -23,6 +24,7 @@ import BaseDialog from "./BaseDialog"; import Spinner from "../elements/Spinner"; interface IProps { + event: MatrixEvent; redact: () => Promise; onFinished: (success?: boolean) => void; } @@ -91,7 +93,7 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent; + return ; } } } diff --git a/src/components/views/dialogs/ConfirmRedactDialog.tsx b/src/components/views/dialogs/ConfirmRedactDialog.tsx index f51b89131c8..9c1fa2b1812 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmRedactDialog.tsx @@ -26,6 +26,7 @@ import ErrorDialog from "./ErrorDialog"; import TextInputDialog from "./TextInputDialog"; interface IProps { + event: MatrixEvent; onFinished(success?: false, reason?: void): void; onFinished(success: true, reason?: string): void; } @@ -35,14 +36,16 @@ interface IProps { */ export default class ConfirmRedactDialog extends React.Component { public render(): React.ReactNode { + let description = _t("Are you sure you wish to remove (delete) this event?"); + if (this.props.event.isState()) { + description += " " + _t("Note that removing room changes like this could undo the change."); + } + return ( => { if (!proceed) return; diff --git a/src/components/views/messages/EditHistoryMessage.tsx b/src/components/views/messages/EditHistoryMessage.tsx index 3531e6e5fa5..0b1d830fdfd 100644 --- a/src/components/views/messages/EditHistoryMessage.tsx +++ b/src/components/views/messages/EditHistoryMessage.tsx @@ -79,6 +79,7 @@ export default class EditHistoryMessage extends React.PureComponent { await cli.redactEvent(event.getRoomId()!, event.getId()!); }, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 84ca822e85d..4b5c15f333d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2739,8 +2739,9 @@ "Changelog": "Changelog", "You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)", "Removing…": "Removing…", + "Are you sure you wish to remove (delete) this event?": "Are you sure you wish to remove (delete) this event?", + "Note that removing room changes like this could undo the change.": "Note that removing room changes like this could undo the change.", "Confirm Removal": "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.", "Reason (optional)": "Reason (optional)", "Clear all data in this session?": "Clear all data in this session?", "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.": "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.", From 127b54223398b4e6e3191eff402daedce8e4a415 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 14 Jun 2023 11:46:14 +0200 Subject: [PATCH 07/68] Use `crypto.isCrossSigningReady` in `CrossSigningPanel` (#11080) --- src/components/views/settings/CrossSigningPanel.tsx | 2 +- test/components/views/settings/CrossSigningPanel-test.tsx | 6 +----- test/test-utils/client.ts | 1 + 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/views/settings/CrossSigningPanel.tsx b/src/components/views/settings/CrossSigningPanel.tsx index c9208ca9034..f5b893e4ea3 100644 --- a/src/components/views/settings/CrossSigningPanel.tsx +++ b/src/components/views/settings/CrossSigningPanel.tsx @@ -102,7 +102,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { const homeserverSupportsCrossSigning = await cli.doesServerSupportUnstableFeature( "org.matrix.e2e_cross_signing", ); - const crossSigningReady = await cli.isCrossSigningReady(); + const crossSigningReady = await crypto.isCrossSigningReady(); this.setState({ crossSigningPublicKeysOnDevice, diff --git a/test/components/views/settings/CrossSigningPanel-test.tsx b/test/components/views/settings/CrossSigningPanel-test.tsx index b9fd22dc44c..96c65fd5c61 100644 --- a/test/components/views/settings/CrossSigningPanel-test.tsx +++ b/test/components/views/settings/CrossSigningPanel-test.tsx @@ -59,10 +59,6 @@ describe("", () => { }); describe("when cross signing is ready", () => { - beforeEach(() => { - mockClient.isCrossSigningReady.mockResolvedValue(true); - }); - it("should render when keys are not backed up", async () => { getComponent(); await flushPromises(); @@ -93,7 +89,7 @@ describe("", () => { describe("when cross signing is not ready", () => { beforeEach(() => { - mockClient.isCrossSigningReady.mockResolvedValue(false); + mocked(mockClient.getCrypto()!.isCrossSigningReady).mockResolvedValue(false); }); it("should render when keys are not backed up", async () => { diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts index 177e06abc1b..582099d6833 100644 --- a/test/test-utils/client.ts +++ b/test/test-utils/client.ts @@ -167,5 +167,6 @@ export const mockClientMethodsCrypto = (): Partial< userSigningKey: true, }, }), + isCrossSigningReady: jest.fn().mockResolvedValue(true), }), }); From b40f29f04cc86db8147f511af8a9f994ac997f1a Mon Sep 17 00:00:00 2001 From: Suguru Hirahara Date: Wed, 14 Jun 2023 11:11:06 +0000 Subject: [PATCH 08/68] Fix visual regressions around widget permissions (#10954) * Add a Jest snapshot of AppPermission * Move the test inside 'for a pinned widget' category * Make only spinner message bold * Set font size specified with "mx_AppPermission_smallText" by default - Add "mx_AppPermission_largeText" for elements whose size has not been specified with mx_AppPermission_smallText - Create _AppWarning.pcss for AppWarning * Make AppPermission panel scrollable, keeping the content at the center * Run prettier * Use Heading component * Use Icon component * Fix the test --- res/css/_components.pcss | 1 + .../views/elements/_AppPermission.pcss | 46 +++--- .../views/elements/_AppWarning.pcss | 25 +++ res/css/views/rooms/_AppsDrawer.pcss | 8 - res/img/feather-customised/help-circle.svg | 2 +- .../views/elements/AppPermission.tsx | 35 +++-- .../views/elements/AppTile-test.tsx | 61 +++++--- .../__snapshots__/AppTile-test.tsx.snap | 143 ++++++++++++++++++ 8 files changed, 251 insertions(+), 70 deletions(-) create mode 100644 res/css/components/views/elements/_AppWarning.pcss diff --git a/res/css/_components.pcss b/res/css/_components.pcss index 56628095f2f..82f8cda556b 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -21,6 +21,7 @@ @import "./components/views/dialogs/polls/_PollListItem.pcss"; @import "./components/views/dialogs/polls/_PollListItemEnded.pcss"; @import "./components/views/elements/_AppPermission.pcss"; +@import "./components/views/elements/_AppWarning.pcss"; @import "./components/views/elements/_FilterDropdown.pcss"; @import "./components/views/elements/_FilterTabGroup.pcss"; @import "./components/views/elements/_LearnMore.pcss"; diff --git a/res/css/components/views/elements/_AppPermission.pcss b/res/css/components/views/elements/_AppPermission.pcss index be78efa43b4..71f282ebeee 100644 --- a/res/css/components/views/elements/_AppPermission.pcss +++ b/res/css/components/views/elements/_AppPermission.pcss @@ -16,41 +16,29 @@ limitations under the License. */ .mx_AppPermission { - > div { - margin-bottom: 12px; - } - - h4 { - margin: 0; - padding: 0; - } + font-size: $font-12px; + width: 100%; /* make mx_AppPermission fill width of mx_AppTileBody so that scroll bar appears on the edge */ + overflow-y: scroll; - .mx_AppPermission_smallText { - font-size: $font-12px; - } + .mx_AppPermission_content { + margin-block: auto; /* place at the center */ - .mx_AppPermission_bolder { - font-weight: var(--font-semi-bold); - } + > div { + margin-block: 12px; + } - .mx_AppPermission_helpIcon { - margin-top: 1px; - margin-right: 2px; - width: 10px; - height: 10px; - display: inline-block; + .mx_AppPermission_content_bolder { + font-weight: var(--font-semi-bold); + } - &::before { + .mx_TextWithTooltip_target--helpIcon { display: inline-block; - background-color: $accent; - mask-repeat: no-repeat; - mask-size: 12px; - width: 12px; - height: 12px; - mask-position: center; - content: ""; + height: $font-14px; /* align with characters on the same line */ vertical-align: middle; - mask-image: url("$(res)/img/feather-customised/help-circle.svg"); + + .mx_Icon { + color: $accent; + } } } } diff --git a/res/css/components/views/elements/_AppWarning.pcss b/res/css/components/views/elements/_AppWarning.pcss new file mode 100644 index 00000000000..8d859d12a86 --- /dev/null +++ b/res/css/components/views/elements/_AppWarning.pcss @@ -0,0 +1,25 @@ +/* +Copyright 2023 Suguru Hirahara + +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_AppWarning { + font-size: $font-16px; + justify-content: center; + + h4 { + margin: 0; + padding: 0; + } +} diff --git a/res/css/views/rooms/_AppsDrawer.pcss b/res/css/views/rooms/_AppsDrawer.pcss index 20a68b4136d..306e4c103c8 100644 --- a/res/css/views/rooms/_AppsDrawer.pcss +++ b/res/css/views/rooms/_AppsDrawer.pcss @@ -311,14 +311,7 @@ limitations under the License. display: flex; height: 100%; flex-direction: column; - justify-content: center; align-items: center; - font-size: $font-16px; - - h4 { - margin: 0; - padding: 0; - } } .mx_AppTile_loading { @@ -326,7 +319,6 @@ limitations under the License. flex-direction: column; justify-content: center; align-items: center; - font-weight: bold; position: relative; height: 100%; diff --git a/res/img/feather-customised/help-circle.svg b/res/img/feather-customised/help-circle.svg index 7ecb0a8f358..61b853aae86 100644 --- a/res/img/feather-customised/help-circle.svg +++ b/res/img/feather-customised/help-circle.svg @@ -1,5 +1,5 @@ - + diff --git a/src/components/views/elements/AppPermission.tsx b/src/components/views/elements/AppPermission.tsx index e73c8ddf8a1..f57a5e4a29b 100644 --- a/src/components/views/elements/AppPermission.tsx +++ b/src/components/views/elements/AppPermission.tsx @@ -25,9 +25,11 @@ import WidgetUtils from "../../../utils/WidgetUtils"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import MemberAvatar from "../avatars/MemberAvatar"; import BaseAvatar from "../avatars/BaseAvatar"; +import Heading from "../typography/Heading"; import AccessibleButton from "./AccessibleButton"; import TextWithTooltip from "./TextWithTooltip"; import { parseUrl } from "../../../utils/UrlUtils"; +import { Icon as HelpIcon } from "../../../../res/img/feather-customised/help-circle.svg"; interface IProps { url: string; @@ -117,8 +119,9 @@ export default class AppPermission extends React.Component { - + ); @@ -139,20 +142,22 @@ export default class AppPermission extends React.Component { return (
-
{_t("Widget added by")}
-
- {avatar} -

{displayName}

-
{userId}
-
-
{warning}
-
- {_t("This widget may use cookies.")} {encryptionWarning} -
-
- - {_t("Continue")} - +
+
{_t("Widget added by")}
+
+ {avatar} + {displayName} +
{userId}
+
+
{warning}
+
+ {_t("This widget may use cookies.")} {encryptionWarning} +
+
+ + {_t("Continue")} + +
); diff --git a/test/components/views/elements/AppTile-test.tsx b/test/components/views/elements/AppTile-test.tsx index 8f77db9f1ea..5a825b2a8b8 100644 --- a/test/components/views/elements/AppTile-test.tsx +++ b/test/components/views/elements/AppTile-test.tsx @@ -62,6 +62,11 @@ jest.mock("../../../../src/stores/OwnProfileStore", () => ({ }, })); +// Fake random strings to give a predictable snapshot +jest.mock("matrix-js-sdk/src/randomstring", () => ({ + randomString: () => "abdefghi", +})); + describe("AppTile", () => { let cli: MatrixClient; let r1: Room; @@ -387,6 +392,45 @@ describe("AppTile", () => { expect(moveToContainerSpy).toHaveBeenCalledWith(r1, app1, Container.Center); }); + it("should render permission request", () => { + jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts, widgetInfo) => { + if (lifecycleEvent === WidgetLifecycle.PreLoadRequest && (widgetInfo as WidgetInfo).id === app1.id) { + (opts as ApprovalOpts).approved = false; + } + }); + + // userId and creatorUserId are different + const renderResult = render( + + + , + ); + + const { container, asFragment } = renderResult; + + expect(container.querySelector(".mx_Spinner")).toBeFalsy(); + expect(asFragment()).toMatchSnapshot(); + + expect(renderResult.queryByRole("button", { name: "Continue" })).toBeInTheDocument(); + }); + + it("should not display 'Continue' button on permission load", () => { + jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts, widgetInfo) => { + if (lifecycleEvent === WidgetLifecycle.PreLoadRequest && (widgetInfo as WidgetInfo).id === app1.id) { + (opts as ApprovalOpts).approved = true; + } + }); + + // userId and creatorUserId are different + const renderResult = render( + + + , + ); + + expect(renderResult.queryByRole("button", { name: "Continue" })).not.toBeInTheDocument(); + }); + describe("for a maximised (centered) widget", () => { beforeEach(() => { jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockImplementation( @@ -446,21 +490,4 @@ describe("AppTile", () => { expect(asFragment()).toMatchSnapshot(); }); }); - - it("for a pinned widget permission load", () => { - jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts, widgetInfo) => { - if (lifecycleEvent === WidgetLifecycle.PreLoadRequest && (widgetInfo as WidgetInfo).id === app1.id) { - (opts as ApprovalOpts).approved = true; - } - }); - - // userId and creatorUserId are different - const renderResult = render( - - - , - ); - - expect(renderResult.queryByRole("button", { name: "Continue" })).not.toBeInTheDocument(); - }); }); diff --git a/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap b/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap index 9f345ff82c1..dac8883dece 100644 --- a/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap @@ -163,6 +163,149 @@ exports[`AppTile for a pinned widget should render 1`] = ` `; +exports[`AppTile for a pinned widget should render permission request 1`] = ` + +
+
+ + + + + Example 1 + + + + + +
+
+
+
+
+
+ +
+
+
+
+ Widget added by +
+
+ + + + +

+ @userAnother +

+
+
+
+ + Using this widget may share data +
+
+
+ with example.com. + +
+
+ This widget may use cookies.  +
+
+
+ Continue +
+
+
+
+
+
+ +`; + exports[`AppTile preserves non-persisted widget on container move 1`] = `
Date: Wed, 14 Jun 2023 13:42:07 +0100 Subject: [PATCH 09/68] Prefer MatrixClientContext over MatrixClientPeg (#10986) --- src/components/structures/UserView.tsx | 8 +++-- .../views/messages/EditHistoryMessage.tsx | 14 +++++--- .../views/right_panel/EncryptionPanel.tsx | 10 +++--- .../settings/tabs/room/BridgeSettingsTab.tsx | 11 ++++--- .../tabs/room/NotificationSettingsTab.tsx | 3 +- .../tabs/room/RolesRoomSettingsTab.tsx | 32 +++++++++++-------- .../tabs/room/VoipRoomSettingsTab.tsx | 24 +++++++------- .../tabs/user/AppearanceUserSettingsTab.tsx | 7 ++-- .../tabs/user/GeneralUserSettingsTab.tsx | 16 +++++----- .../tabs/user/HelpUserSettingsTab.tsx | 23 ++++++------- .../tabs/user/VoiceUserSettingsTab.tsx | 9 ++++-- src/hooks/useIsInitialSyncComplete.ts | 4 +-- src/hooks/useUserOnboardingContext.ts | 4 +-- .../components/VoiceBroadcastBody.tsx | 4 +-- .../tabs/room/BridgeSettingsTab-test.tsx | 5 +-- .../tabs/room/RolesRoomSettingsTab-test.tsx | 4 +-- .../user/AppearanceUserSettingsTab-test.tsx | 9 ++++-- .../UserOnboardingPage-test.tsx | 4 +-- test/test-utils/wrappers.tsx | 13 ++++++++ .../components/VoiceBroadcastBody-test.tsx | 3 +- 20 files changed, 120 insertions(+), 87 deletions(-) diff --git a/src/components/structures/UserView.tsx b/src/components/structures/UserView.tsx index 4cff508dfba..e9a05880f60 100644 --- a/src/components/structures/UserView.tsx +++ b/src/components/structures/UserView.tsx @@ -20,7 +20,6 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from "../../MatrixClientPeg"; import Modal from "../../Modal"; import { _t } from "../../languageHandler"; import ErrorDialog from "../views/dialogs/ErrorDialog"; @@ -30,6 +29,7 @@ import Spinner from "../views/elements/Spinner"; import ResizeNotifier from "../../utils/ResizeNotifier"; import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases"; import { UserOnboardingPage } from "../views/user-onboarding/UserOnboardingPage"; +import MatrixClientContext from "../../contexts/MatrixClientContext"; interface IProps { userId: string; @@ -42,6 +42,9 @@ interface IState { } export default class UserView extends React.Component { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + public constructor(props: IProps) { super(props); this.state = { @@ -65,11 +68,10 @@ export default class UserView extends React.Component { } private async loadProfileInfo(): Promise { - const cli = MatrixClientPeg.get(); this.setState({ loading: true }); let profileInfo: Awaited>; try { - profileInfo = await cli.getProfileInfo(this.props.userId); + profileInfo = await this.context.getProfileInfo(this.props.userId); } catch (err) { Modal.createDialog(ErrorDialog, { title: _t("Could not load user profile"), diff --git a/src/components/views/messages/EditHistoryMessage.tsx b/src/components/views/messages/EditHistoryMessage.tsx index 0b1d830fdfd..6c06321e69a 100644 --- a/src/components/views/messages/EditHistoryMessage.tsx +++ b/src/components/views/messages/EditHistoryMessage.tsx @@ -25,13 +25,13 @@ import { formatTime } from "../../../DateUtils"; import { pillifyLinks, unmountPills } from "../../../utils/pillify"; import { tooltipifyLinks, unmountTooltips } from "../../../utils/tooltipify"; import { _t } from "../../../languageHandler"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; import Modal from "../../../Modal"; import RedactedBody from "./RedactedBody"; import AccessibleButton from "../elements/AccessibleButton"; import ConfirmAndWaitRedactDialog from "../dialogs/ConfirmAndWaitRedactDialog"; import ViewSource from "../../structures/ViewSource"; import SettingsStore from "../../../settings/SettingsStore"; +import MatrixClientContext from "../../../contexts/MatrixClientContext"; function getReplacedContent(event: MatrixEvent): IContent { const originalContent = event.getOriginalContent(); @@ -52,14 +52,18 @@ interface IState { } export default class EditHistoryMessage extends React.PureComponent { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + private content = createRef(); private pills: Element[] = []; private tooltips: Element[] = []; - public constructor(props: IProps) { + public constructor(props: IProps, context: React.ContextType) { super(props); + this.context = context; - const cli = MatrixClientPeg.get(); + const cli = this.context; const userId = cli.getSafeUserId(); const event = this.props.mxEvent; const room = cli.getRoom(event.getRoomId()); @@ -74,7 +78,7 @@ export default class EditHistoryMessage extends React.PureComponent => { const event = this.props.mxEvent; - const cli = MatrixClientPeg.get(); + const cli = this.context; Modal.createDialog( ConfirmAndWaitRedactDialog, @@ -102,7 +106,7 @@ export default class EditHistoryMessage extends React.PureComponent = (props: IProps) => { + const cli = useMatrixClientContext(); const { verificationRequest, verificationRequestPromise, member, onClose, layout, isRoomEncrypted } = props; const [request, setRequest] = useState(verificationRequest); // state to show a spinner immediately after clicking "start verification", @@ -106,7 +107,6 @@ const EncryptionPanel: React.FC = (props: IProps) => { const onStartVerification = useCallback(async (): Promise => { setRequesting(true); - const cli = MatrixClientPeg.get(); let verificationRequest_: VerificationRequest; try { const roomId = await ensureDMExists(cli, member.userId); @@ -135,14 +135,12 @@ const EncryptionPanel: React.FC = (props: IProps) => { }); } if (!RightPanelStore.instance.isOpen) RightPanelStore.instance.togglePanel(null); - }, [member]); + }, [cli, member]); const requested: boolean = (!request && isRequesting) || (!!request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined)); - const isSelfVerification = request - ? request.isSelfVerification - : member.userId === MatrixClientPeg.get().getUserId(); + const isSelfVerification = request ? request.isSelfVerification : member.userId === cli.getUserId(); if (!request || requested) { const initiatedByMe = (!request && isRequesting) || (!!request && request.initiatedByMe); diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx b/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx index 04b75cb4cc6..fc04d534706 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx @@ -17,12 +17,13 @@ limitations under the License. import React, { ReactNode } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../../../languageHandler"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import BridgeTile from "../../BridgeTile"; import SettingsTab from "../SettingsTab"; import { SettingsSection } from "../../shared/SettingsSection"; +import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; const BRIDGE_EVENT_TYPES = [ "uk.half-shot.bridge", @@ -36,14 +37,16 @@ interface IProps { } export default class BridgeSettingsTab extends React.Component { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + private renderBridgeCard(event: MatrixEvent, room: Room | null): ReactNode { const content = event.getContent(); if (!room || !content?.channel || !content.protocol) return null; return ; } - public static getBridgeStateEvents(roomId: string): MatrixEvent[] { - const client = MatrixClientPeg.get(); + public static getBridgeStateEvents(client: MatrixClient, roomId: string): MatrixEvent[] { const roomState = client.getRoom(roomId)?.currentState; if (!roomState) return []; @@ -53,7 +56,7 @@ export default class BridgeSettingsTab extends React.Component { public render(): React.ReactNode { // This settings tab will only be invoked if the following function returns more // than 0 events, so no validation is needed at this stage. - const bridgeEvents = BridgeSettingsTab.getBridgeStateEvents(this.props.room.roomId); + const bridgeEvents = BridgeSettingsTab.getBridgeStateEvents(this.context, this.props.room.roomId); const room = this.props.room; let content: JSX.Element; diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx b/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx index 627971eaf3a..1ba3a2f17c4 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx @@ -18,7 +18,6 @@ import React, { createRef } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../../../languageHandler"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; import SettingsStore from "../../../../../settings/SettingsStore"; @@ -116,7 +115,7 @@ export default class NotificationsSettingsTab extends React.Component { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + private onUnbanClick = (): void => { - MatrixClientPeg.get() - .unban(this.props.member.roomId, this.props.member.userId) - .catch((err) => { - logger.error("Failed to unban: " + err); - Modal.createDialog(ErrorDialog, { - title: _t("Error"), - description: _t("Failed to unban"), - }); + this.context.unban(this.props.member.roomId, this.props.member.userId).catch((err) => { + logger.error("Failed to unban: " + err); + Modal.createDialog(ErrorDialog, { + title: _t("Error"), + description: _t("Failed to unban"), }); + }); }; public render(): React.ReactNode { @@ -136,12 +137,15 @@ interface IProps { } export default class RolesRoomSettingsTab extends React.Component { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + public componentDidMount(): void { - MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate); + this.context.on(RoomStateEvent.Update, this.onRoomStateUpdate); } public componentWillUnmount(): void { - const client = MatrixClientPeg.get(); + const client = this.context; if (client) { client.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); } @@ -173,7 +177,7 @@ export default class RolesRoomSettingsTab extends React.Component { } private onPowerLevelsChanged = (value: number, powerLevelKey: string): void => { - const client = MatrixClientPeg.get(); + const client = this.context; const room = this.props.room; const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, ""); let plContent = plEvent?.getContent() ?? {}; @@ -215,7 +219,7 @@ export default class RolesRoomSettingsTab extends React.Component { }; private onUserPowerLevelChanged = (value: number, powerLevelKey: string): void => { - const client = MatrixClientPeg.get(); + const client = this.context; const room = this.props.room; const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, ""); let plContent = plEvent?.getContent() ?? {}; @@ -241,7 +245,7 @@ export default class RolesRoomSettingsTab extends React.Component { }; public render(): React.ReactNode { - const client = MatrixClientPeg.get(); + const client = this.context; const room = this.props.room; const isSpaceRoom = room.isSpaceRoom(); diff --git a/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx index 94f89be331c..71c0d21184f 100644 --- a/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx @@ -21,7 +21,6 @@ import { RoomState } from "matrix-js-sdk/src/models/room-state"; import { Room } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../../../languageHandler"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import SettingsSubsection from "../../shared/SettingsSubsection"; import SettingsTab from "../SettingsTab"; @@ -38,14 +37,17 @@ const ElementCallSwitch: React.FC = ({ room }) => { const isPublic = useMemo(() => room.getJoinRule() === JoinRule.Public, [room]); const [content, events, maySend] = useRoomState( room, - useCallback((state: RoomState) => { - const content = state?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent(); - return [ - content ?? {}, - content?.["events"] ?? {}, - state?.maySendStateEvent(EventType.RoomPowerLevels, MatrixClientPeg.get().getSafeUserId()), - ]; - }, []), + useCallback( + (state: RoomState) => { + const content = state?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent(); + return [ + content ?? {}, + content?.["events"] ?? {}, + state?.maySendStateEvent(EventType.RoomPowerLevels, room.client.getSafeUserId()), + ]; + }, + [room.client], + ), ); const [elementCallEnabled, setElementCallEnabled] = useState(() => { @@ -69,12 +71,12 @@ const ElementCallSwitch: React.FC = ({ room }) => { events[ElementCall.MEMBER_EVENT_TYPE.name] = adminLevel; } - MatrixClientPeg.get().sendStateEvent(room.roomId, EventType.RoomPowerLevels, { + room.client.sendStateEvent(room.roomId, EventType.RoomPowerLevels, { events: events, ...content, }); }, - [room.roomId, content, events, isPublic], + [room.client, room.roomId, content, events, isPublic], ); const brand = SdkConfig.get("element_call").brand ?? DEFAULTS.element_call.brand; diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 135c0c4a65f..29ad966a402 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -19,7 +19,6 @@ import React, { ChangeEvent, ReactNode } from "react"; import { _t } from "../../../../../languageHandler"; import SdkConfig from "../../../../../SdkConfig"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import SettingsStore from "../../../../../settings/SettingsStore"; import SettingsFlag from "../../../elements/SettingsFlag"; import Field from "../../../elements/Field"; @@ -34,6 +33,7 @@ import ImageSizePanel from "../../ImageSizePanel"; import SettingsTab from "../SettingsTab"; import { SettingsSection } from "../../shared/SettingsSection"; import SettingsSubsection, { SettingsSubsectionText } from "../../shared/SettingsSubsection"; +import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; interface IProps {} @@ -49,6 +49,9 @@ interface IState { } export default class AppearanceUserSettingsTab extends React.Component { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + private readonly MESSAGE_PREVIEW_TEXT = _t("Hey you. You're the best!"); private unmounted = false; @@ -66,7 +69,7 @@ export default class AppearanceUserSettingsTab extends React.Component { // Fetch the current user profile for the message preview - const client = MatrixClientPeg.get(); + const client = this.context; const userId = client.getUserId()!; const profileInfo = await client.getProfileInfo(userId); if (this.unmounted) return; diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx index 13a44fcaa64..c6c3bb9287e 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx @@ -33,7 +33,6 @@ import SpellCheckSettings from "../../SpellCheckSettings"; import AccessibleButton from "../../../elements/AccessibleButton"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog"; import PlatformPeg from "../../../../../PlatformPeg"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import Modal from "../../../../../Modal"; import dis from "../../../../../dispatcher/dispatcher"; import { Service, ServicePolicyPair, startTermsFlow } from "../../../../../Terms"; @@ -102,14 +101,15 @@ export default class GeneralUserSettingsTab extends React.Component) { super(props); + this.context = context; this.state = { language: languageHandler.getCurrentLanguage(), spellCheckEnabled: false, spellCheckLanguages: [], - haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()), + haveIdServer: Boolean(this.context.getIdentityServerUrl()), idServerHasUnsignedTerms: false, requiredPolicyInfo: { // This object is passed along to a component for handling @@ -151,7 +151,7 @@ export default class GeneralUserSettingsTab extends React.Component { if (payload.action === "id_server_changed") { - this.setState({ haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()) }); + this.setState({ haveIdServer: Boolean(this.context.getIdentityServerUrl()) }); this.getThreepidState(); } }; @@ -165,7 +165,7 @@ export default class GeneralUserSettingsTab extends React.Component { - const cli = MatrixClientPeg.get(); + const cli = this.context; const serverSupportsSeparateAddAndBind = await cli.doesServerSupportSeparateAddAndBind(); @@ -184,7 +184,7 @@ export default class GeneralUserSettingsTab extends React.Component { - const cli = MatrixClientPeg.get(); + const cli = this.context; // Check to see if terms need accepting this.checkTerms(); @@ -195,7 +195,7 @@ export default class GeneralUserSettingsTab extends React.Component { // By starting the terms flow we get the logic for checking which terms the user has signed // for free. So we might as well use that for our own purposes. - const idServerUrl = MatrixClientPeg.get().getIdentityServerUrl(); + const idServerUrl = this.context.getIdentityServerUrl(); if (!this.state.haveIdServer || !idServerUrl) { this.setState({ idServerHasUnsignedTerms: false }); return; diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx index 34da8288639..7f690ce53e4 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx @@ -19,7 +19,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import AccessibleButton from "../../../elements/AccessibleButton"; import { _t, getCurrentLanguage } from "../../../../../languageHandler"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import SdkConfig from "../../../../../SdkConfig"; import createRoom from "../../../../../createRoom"; import Modal from "../../../../../Modal"; @@ -77,7 +76,7 @@ export default class HelpUserSettingsTab extends React.Component private getVersionInfo(): { appVersion: string; olmVersion: string } { const brand = SdkConfig.get().brand; const appVersion = this.state.appVersion || "unknown"; - const olmVersionTuple = MatrixClientPeg.get().olmVersion; + const olmVersionTuple = this.context.olmVersion; const olmVersion = olmVersionTuple ? `${olmVersionTuple[0]}.${olmVersionTuple[1]}.${olmVersionTuple[2]}` : ""; @@ -94,12 +93,10 @@ export default class HelpUserSettingsTab extends React.Component // Dev note: please keep this log line, it's useful when troubleshooting a MatrixClient suddenly // stopping in the middle of the logs. logger.log("Clear cache & reload clicked"); - MatrixClientPeg.get().stopClient(); - MatrixClientPeg.get() - .store.deleteAllData() - .then(() => { - PlatformPeg.get()?.reload(); - }); + this.context.stopClient(); + this.context.store.deleteAllData().then(() => { + PlatformPeg.get()?.reload(); + }); }; private onBugReport = (): void => { @@ -362,19 +359,19 @@ export default class HelpUserSettingsTab extends React.Component {_t( "Homeserver is %(homeserverUrl)s", { - homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(), + homeserverUrl: this.context.getHomeserverUrl(), }, { code: (sub) => {sub}, }, )} - {MatrixClientPeg.get().getIdentityServerUrl() && ( + {this.context.getIdentityServerUrl() && ( {_t( "Identity server is %(identityServerUrl)s", { - identityServerUrl: MatrixClientPeg.get().getIdentityServerUrl(), + identityServerUrl: this.context.getIdentityServerUrl(), }, { code: (sub) => {sub}, @@ -391,8 +388,8 @@ export default class HelpUserSettingsTab extends React.Component " Do not share it with anyone.", )} - MatrixClientPeg.get().getAccessToken()}> - {MatrixClientPeg.get().getAccessToken()} + this.context.getAccessToken()}> + {this.context.getAccessToken()} diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index b9ff171c4c8..bf8540516a1 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -23,7 +23,6 @@ import { _t } from "../../../../../languageHandler"; import MediaDeviceHandler, { IMediaDevices, MediaDeviceKindEnum } from "../../../../../MediaDeviceHandler"; import Field from "../../../elements/Field"; import AccessibleButton from "../../../elements/AccessibleButton"; -import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import SettingsFlag from "../../../elements/SettingsFlag"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; @@ -31,6 +30,7 @@ import { requestMediaPermissions } from "../../../../../utils/media/requestMedia import SettingsTab from "../SettingsTab"; import { SettingsSection } from "../../shared/SettingsSection"; import SettingsSubsection from "../../shared/SettingsSubsection"; +import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; interface IState { mediaDevices: IMediaDevices | null; @@ -58,6 +58,9 @@ const mapDeviceKindToHandlerValue = (deviceKind: MediaDeviceKindEnum): string | }; export default class VoiceUserSettingsTab extends React.Component<{}, IState> { + public static contextType = MatrixClientContext; + public context!: React.ContextType; + public constructor(props: {}) { super(props); @@ -114,11 +117,11 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { }; private changeWebRtcMethod = (p2p: boolean): void => { - MatrixClientPeg.get().setForceTURN(!p2p); + this.context.setForceTURN(!p2p); }; private changeFallbackICEServerAllowed = (allow: boolean): void => { - MatrixClientPeg.get().setFallbackICEServerAllowed(allow); + this.context.setFallbackICEServerAllowed(allow); }; private renderDeviceOptions(devices: Array, category: MediaDeviceKindEnum): Array { diff --git a/src/hooks/useIsInitialSyncComplete.ts b/src/hooks/useIsInitialSyncComplete.ts index 33174fa1821..3f78e13e72f 100644 --- a/src/hooks/useIsInitialSyncComplete.ts +++ b/src/hooks/useIsInitialSyncComplete.ts @@ -16,10 +16,10 @@ limitations under the License. import { ClientEvent } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from "../MatrixClientPeg"; import { useEventEmitterState } from "./useEventEmitter"; +import { useMatrixClientContext } from "../contexts/MatrixClientContext"; export function useInitialSyncComplete(): boolean { - const cli = MatrixClientPeg.get(); + const cli = useMatrixClientContext(); return useEventEmitterState(cli, ClientEvent.Sync, () => cli.isInitialSyncComplete()); } diff --git a/src/hooks/useUserOnboardingContext.ts b/src/hooks/useUserOnboardingContext.ts index a3ea8ed3193..1d622173f65 100644 --- a/src/hooks/useUserOnboardingContext.ts +++ b/src/hooks/useUserOnboardingContext.ts @@ -18,9 +18,9 @@ import { logger } from "matrix-js-sdk/src/logger"; import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/matrix"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { MatrixClientPeg } from "../MatrixClientPeg"; import { Notifier } from "../Notifier"; import DMRoomMap from "../utils/DMRoomMap"; +import { useMatrixClientContext } from "../contexts/MatrixClientContext"; export interface UserOnboardingContext { hasAvatar: boolean; @@ -47,7 +47,7 @@ function useRefOf(value: (...values: T) => R): (...values: T function useUserOnboardingContextValue(defaultValue: T, callback: (cli: MatrixClient) => Promise): T { const [value, setValue] = useState(defaultValue); - const cli = MatrixClientPeg.get(); + const cli = useMatrixClientContext(); const handler = useRefOf(callback); diff --git a/src/voice-broadcast/components/VoiceBroadcastBody.tsx b/src/voice-broadcast/components/VoiceBroadcastBody.tsx index 9b6ca990e02..5433154bdff 100644 --- a/src/voice-broadcast/components/VoiceBroadcastBody.tsx +++ b/src/voice-broadcast/components/VoiceBroadcastBody.tsx @@ -25,13 +25,13 @@ import { VoiceBroadcastInfoState, } from ".."; import { IBodyProps } from "../../components/views/messages/IBodyProps"; -import { MatrixClientPeg } from "../../MatrixClientPeg"; import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper"; import { SDKContext } from "../../contexts/SDKContext"; +import { useMatrixClientContext } from "../../contexts/MatrixClientContext"; export const VoiceBroadcastBody: React.FC = ({ mxEvent }) => { const sdkContext = useContext(SDKContext); - const client = MatrixClientPeg.get(); + const client = useMatrixClientContext(); const [infoState, setInfoState] = useState(mxEvent.getContent()?.state || VoiceBroadcastInfoState.Stopped); useEffect(() => { diff --git a/test/components/views/settings/tabs/room/BridgeSettingsTab-test.tsx b/test/components/views/settings/tabs/room/BridgeSettingsTab-test.tsx index bf41be073a2..35a54e9761b 100644 --- a/test/components/views/settings/tabs/room/BridgeSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/BridgeSettingsTab-test.tsx @@ -19,7 +19,7 @@ import { render } from "@testing-library/react"; import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import BridgeSettingsTab from "../../../../../../src/components/views/settings/tabs/room/BridgeSettingsTab"; -import { getMockClientWithEventEmitter } from "../../../../../test-utils"; +import { getMockClientWithEventEmitter, withClientContextRenderOptions } from "../../../../../test-utils"; describe("", () => { const userId = "@alice:server.org"; @@ -28,7 +28,8 @@ describe("", () => { }); const roomId = "!room:server.org"; - const getComponent = (room: Room) => render(); + const getComponent = (room: Room) => + render(, withClientContextRenderOptions(client)); it("renders when room is not bridging messages to any platform", () => { const room = new Room(roomId, client, userId); diff --git a/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx b/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx index 9ee9df774c6..c9c82ab5470 100644 --- a/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx @@ -24,7 +24,7 @@ import { mocked } from "jest-mock"; import { RoomMember } from "matrix-js-sdk/src/matrix"; import RolesRoomSettingsTab from "../../../../../../src/components/views/settings/tabs/room/RolesRoomSettingsTab"; -import { mkStubRoom, stubClient } from "../../../../../test-utils"; +import { mkStubRoom, withClientContextRenderOptions, stubClient } from "../../../../../test-utils"; import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg"; import { VoiceBroadcastInfoEventType } from "../../../../../../src/voice-broadcast"; import SettingsStore from "../../../../../../src/settings/SettingsStore"; @@ -37,7 +37,7 @@ describe("RolesRoomSettingsTab", () => { let room: Room; const renderTab = (propRoom: Room = room): RenderResult => { - return render(); + return render(, withClientContextRenderOptions(cli)); }; const getVoiceBroadcastsSelect = (): HTMLElement => { diff --git a/test/components/views/settings/tabs/user/AppearanceUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/AppearanceUserSettingsTab-test.tsx index 310279ef018..0596652cf69 100644 --- a/test/components/views/settings/tabs/user/AppearanceUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/AppearanceUserSettingsTab-test.tsx @@ -16,9 +16,10 @@ limitations under the License. import { render } from "@testing-library/react"; import React from "react"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; import AppearanceUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/AppearanceUserSettingsTab"; -import { stubClient } from "../../../../../test-utils"; +import { withClientContextRenderOptions, stubClient } from "../../../../../test-utils"; // Fake random strings to give a predictable snapshot jest.mock("matrix-js-sdk/src/randomstring", () => ({ @@ -26,12 +27,14 @@ jest.mock("matrix-js-sdk/src/randomstring", () => ({ })); describe("AppearanceUserSettingsTab", () => { + let client: MatrixClient; + beforeEach(() => { - stubClient(); + client = stubClient(); }); it("should render", () => { - const { asFragment } = render(); + const { asFragment } = render(, withClientContextRenderOptions(client)); expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/test/components/views/user-onboarding/UserOnboardingPage-test.tsx b/test/components/views/user-onboarding/UserOnboardingPage-test.tsx index af94db64610..1bb24ed3467 100644 --- a/test/components/views/user-onboarding/UserOnboardingPage-test.tsx +++ b/test/components/views/user-onboarding/UserOnboardingPage-test.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; import { act, render, RenderResult } from "@testing-library/react"; -import { filterConsole, stubClient } from "../../../test-utils"; +import { filterConsole, withClientContextRenderOptions, stubClient } from "../../../test-utils"; import { UserOnboardingPage } from "../../../../src/components/views/user-onboarding/UserOnboardingPage"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import SdkConfig from "../../../../src/SdkConfig"; @@ -34,7 +34,7 @@ jest.mock("../../../../src/components/structures/HomePage", () => ({ describe("UserOnboardingPage", () => { const renderComponent = async (): Promise => { - const renderResult = render(); + const renderResult = render(, withClientContextRenderOptions(MatrixClientPeg.safeGet())); await act(async () => { jest.runAllTimers(); }); diff --git a/test/test-utils/wrappers.tsx b/test/test-utils/wrappers.tsx index cc9b6f35707..c138f133351 100644 --- a/test/test-utils/wrappers.tsx +++ b/test/test-utils/wrappers.tsx @@ -16,6 +16,7 @@ limitations under the License. import React, { ComponentType, Ref } from "react"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; +import { RenderOptions } from "@testing-library/react"; import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg"; import MatrixClientContext from "../../src/contexts/MatrixClientContext"; @@ -57,3 +58,15 @@ export function wrapInSdkContext( } }; } + +/** + * Test helper to generate React testing library render options for wrapping with a MatrixClientContext.Provider + * @param client the MatrixClient instance to expose via the provider + */ +export function withClientContextRenderOptions(client: MatrixClient): RenderOptions { + return { + wrapper: ({ children }) => ( + {children} + ), + }; +} diff --git a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx index 571d8a661eb..70e53788947 100644 --- a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx +++ b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx @@ -28,7 +28,7 @@ import { VoiceBroadcastPlayback, VoiceBroadcastRecordingsStore, } from "../../../src/voice-broadcast"; -import { stubClient, wrapInSdkContext } from "../../test-utils"; +import { withClientContextRenderOptions, stubClient, wrapInSdkContext } from "../../test-utils"; import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils"; import { MediaEventHelper } from "../../../src/utils/MediaEventHelper"; import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks"; @@ -66,6 +66,7 @@ describe("VoiceBroadcastBody", () => { onMessageAllowed={() => {}} permalinkCreator={new RoomPermalinkCreator(room)} />, + withClientContextRenderOptions(client), ); testRecording = SdkContextClass.instance.voiceBroadcastRecordingsStore.getByInfoEvent(infoEvent, client); }; From 6486255f54af7d6913bfddfdec57ebc7f7bbba36 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 14 Jun 2023 13:49:18 +0100 Subject: [PATCH 10/68] Fix slash commands not being enabled in certain cases (#11090) * Fix slash commands not being enabled in certain cases * Fix import cycle --- src/SlashCommands.tsx | 13 +++++++------ src/autocomplete/CommandProvider.tsx | 7 +++++-- .../views/dialogs/SlashCommandHelpDialog.tsx | 3 ++- src/components/views/rooms/BasicMessageComposer.tsx | 3 ++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index f8a3885ab66..524a22a21a5 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -69,6 +69,7 @@ import { htmlSerializeFromMdIfNeeded } from "./editor/serialize"; import { leaveRoomBehaviour } from "./utils/leave-behaviour"; import { isLocalRoom } from "./utils/localRoom/isLocalRoom"; import { SdkContextClass } from "./contexts/SDKContext"; +import { MatrixClientPeg } from "./MatrixClientPeg"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -122,7 +123,7 @@ interface ICommandOpts { runFn?: RunFn; category: string; hideCompletionAfterSpace?: boolean; - isEnabled?(matrixClient?: MatrixClient): boolean; + isEnabled?(matrixClient: MatrixClient | null): boolean; renderingTypes?: TimelineRenderingType[]; } @@ -136,7 +137,7 @@ export class Command { public readonly hideCompletionAfterSpace: boolean; public readonly renderingTypes?: TimelineRenderingType[]; public readonly analyticsName?: SlashCommandEvent["command"]; - private readonly _isEnabled?: (matrixClient?: MatrixClient) => boolean; + private readonly _isEnabled?: (matrixClient: MatrixClient | null) => boolean; public constructor(opts: ICommandOpts) { this.command = opts.command; @@ -189,7 +190,7 @@ export class Command { return _t("Usage") + ": " + this.getCommandWithArgs(); } - public isEnabled(cli?: MatrixClient): boolean { + public isEnabled(cli: MatrixClient | null): boolean { return this._isEnabled?.(cli) ?? true; } } @@ -206,7 +207,7 @@ function successSync(value: any): RunResult { return success(Promise.resolve(value)); } -const isCurrentLocalRoom = (cli?: MatrixClient): boolean => { +const isCurrentLocalRoom = (cli: MatrixClient | null): boolean => { const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); if (!roomId) return false; const room = cli?.getRoom(roomId); @@ -214,7 +215,7 @@ const isCurrentLocalRoom = (cli?: MatrixClient): boolean => { return isLocalRoom(room); }; -const canAffectPowerlevels = (cli?: MatrixClient): boolean => { +const canAffectPowerlevels = (cli: MatrixClient | null): boolean => { const roomId = SdkContextClass.instance.roomViewStore.getRoomId(); if (!cli || !roomId) return false; const room = cli?.getRoom(roomId); @@ -1425,7 +1426,7 @@ interface ICmd { export function getCommand(input: string): ICmd { const { cmd, args } = parseCommandString(input); - if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled()) { + if (cmd && CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled(MatrixClientPeg.get())) { return { cmd: CommandMap.get(cmd), args, diff --git a/src/autocomplete/CommandProvider.tsx b/src/autocomplete/CommandProvider.tsx index 995760f4b33..2cac8177628 100644 --- a/src/autocomplete/CommandProvider.tsx +++ b/src/autocomplete/CommandProvider.tsx @@ -27,6 +27,7 @@ import { TextualCompletion } from "./Components"; import { ICompletion, ISelectionRange } from "./Autocompleter"; import { Command, Commands, CommandMap } from "../SlashCommands"; import { TimelineRenderingType } from "../contexts/RoomContext"; +import { MatrixClientPeg } from "../MatrixClientPeg"; const COMMAND_RE = /(^\/\w*)(?: .*)?/g; @@ -51,12 +52,14 @@ export default class CommandProvider extends AutocompleteProvider { const { command, range } = this.getCurrentCommand(query, selection); if (!command) return []; + const cli = MatrixClientPeg.get(); + let matches: Command[] = []; // check if the full match differs from the first word (i.e. returns false if the command has args) if (command[0] !== command[1]) { // The input looks like a command with arguments, perform exact match const name = command[1].slice(1); // strip leading `/` - if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled()) { + if (CommandMap.has(name) && CommandMap.get(name)!.isEnabled(cli)) { // some commands, namely `me` don't suit having the usage shown whilst typing their arguments if (CommandMap.get(name)!.hideCompletionAfterSpace) return []; matches = [CommandMap.get(name)!]; @@ -75,7 +78,7 @@ export default class CommandProvider extends AutocompleteProvider { return matches .filter((cmd) => { const display = !cmd.renderingTypes || cmd.renderingTypes.includes(this.renderingType); - return cmd.isEnabled() && display; + return cmd.isEnabled(cli) && display; }) .map((result) => { let completion = result.getCommand() + " "; diff --git a/src/components/views/dialogs/SlashCommandHelpDialog.tsx b/src/components/views/dialogs/SlashCommandHelpDialog.tsx index c650e78f41f..e59c3178a53 100644 --- a/src/components/views/dialogs/SlashCommandHelpDialog.tsx +++ b/src/components/views/dialogs/SlashCommandHelpDialog.tsx @@ -19,6 +19,7 @@ import React from "react"; import { _t } from "../../../languageHandler"; import { Command, CommandCategories, Commands } from "../../../SlashCommands"; import InfoDialog from "./InfoDialog"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; interface IProps { onFinished(): void; @@ -27,7 +28,7 @@ interface IProps { const SlashCommandHelpDialog: React.FC = ({ onFinished }) => { const categories: Record = {}; Commands.forEach((cmd) => { - if (!cmd.isEnabled()) return; + if (!cmd.isEnabled(MatrixClientPeg.get())) return; if (!categories[cmd.category]) { categories[cmd.category] = []; } diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index 484c58756af..7bf01fa3afb 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -51,6 +51,7 @@ import { ALTERNATE_KEY_NAME, KeyBindingAction } from "../../../accessibility/Key import { _t } from "../../../languageHandler"; import { linkify } from "../../../linkify-matrix"; import { SdkContextClass } from "../../../contexts/SDKContext"; +import { MatrixClientPeg } from "../../../MatrixClientPeg"; // matches emoticons which follow the start of a line or whitespace const REGEX_EMOTICON_WHITESPACE = new RegExp("(?:^|\\s)(" + EMOTICON_REGEX.source + ")\\s|:^$"); @@ -268,7 +269,7 @@ export default class BasicMessageEditor extends React.Component if (isTyping && this.props.model.parts[0].type === "command") { const { cmd } = parseCommandString(this.props.model.parts[0].text); const command = CommandMap.get(cmd!); - if (!command?.isEnabled() || command.category !== CommandCategories.messages) { + if (!command?.isEnabled(MatrixClientPeg.get()) || command.category !== CommandCategories.messages) { isTyping = false; } } From b9b93264b63c45019d48414497240b5090ef04ab Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Wed, 14 Jun 2023 15:35:32 +0100 Subject: [PATCH 11/68] Remove references to `VerificationRequest` (#11050) * Update references to `VerificationRequestEvent` * Update references to `Phase` * update references to legacy `PHASE_*` constants * Replace `request.canAccept` with `canAcceptVerificationRequest` * Replace `VerificationRequest` with the interface throughout * Minor strict type fixes * Add a couple of tests --- cypress/e2e/crypto/complete-security.spec.ts | 2 +- cypress/e2e/crypto/crypto.spec.ts | 2 +- cypress/e2e/crypto/utils.ts | 2 +- .../structures/auth/SetupEncryptionBody.tsx | 2 +- .../dialogs/VerificationRequestDialog.tsx | 2 +- .../dialogs/devtools/VerificationExplorer.tsx | 7 ++----- .../messages/MKeyVerificationConclusion.tsx | 6 +----- .../views/messages/MKeyVerificationRequest.tsx | 9 +++++---- .../views/right_panel/EncryptionPanel.tsx | 17 ++++++++--------- src/components/views/right_panel/UserInfo.tsx | 2 +- .../views/right_panel/VerificationPanel.tsx | 6 +++--- .../views/settings/devices/useOwnDevices.ts | 2 +- .../views/toasts/VerificationRequestToast.tsx | 8 +++++--- src/stores/SetupEncryptionStore.ts | 9 ++------- .../right-panel/RightPanelStoreIPanelState.ts | 2 +- src/verification.ts | 3 +-- .../messages/MKeyVerificationRequest-test.tsx | 18 ++++++++++++++---- .../right_panel/VerificationPanel-test.tsx | 8 +++----- .../toasts/VerificationRequestToast-test.tsx | 18 ++++++++++++++++++ .../VerificationRequestToast-test.tsx.snap | 4 ++-- 20 files changed, 72 insertions(+), 57 deletions(-) diff --git a/cypress/e2e/crypto/complete-security.spec.ts b/cypress/e2e/crypto/complete-security.spec.ts index b598829b86a..00a57e7350d 100644 --- a/cypress/e2e/crypto/complete-security.spec.ts +++ b/cypress/e2e/crypto/complete-security.spec.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { HomeserverInstance } from "../../plugins/utils/homeserver"; import { handleVerificationRequest, logIntoElement, waitForVerificationRequest } from "./utils"; import { CypressBot } from "../../support/bot"; diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index 0a79c8f0e1a..920980e1715 100644 --- a/cypress/e2e/crypto/crypto.spec.ts +++ b/cypress/e2e/crypto/crypto.spec.ts @@ -15,7 +15,7 @@ limitations under the License. */ import type { ISendEventResponse, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; -import type { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import type { CypressBot } from "../../support/bot"; import { HomeserverInstance } from "../../plugins/utils/homeserver"; import { UserCredentials } from "../../support/login"; diff --git a/cypress/e2e/crypto/utils.ts b/cypress/e2e/crypto/utils.ts index 3e91d1e93db..e5f517a99e8 100644 --- a/cypress/e2e/crypto/utils.ts +++ b/cypress/e2e/crypto/utils.ts @@ -16,7 +16,7 @@ limitations under the License. import type { ISasEvent } from "matrix-js-sdk/src/crypto/verification/SAS"; import type { MatrixClient } from "matrix-js-sdk/src/matrix"; -import type { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; export type EmojiMapping = [emoji: string, name: string]; diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 4be5efa5c92..15d058b4da0 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api"; import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../languageHandler"; diff --git a/src/components/views/dialogs/VerificationRequestDialog.tsx b/src/components/views/dialogs/VerificationRequestDialog.tsx index 540cb3190eb..ebd54165ccc 100644 --- a/src/components/views/dialogs/VerificationRequestDialog.tsx +++ b/src/components/views/dialogs/VerificationRequestDialog.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { User } from "matrix-js-sdk/src/models/user"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; diff --git a/src/components/views/dialogs/devtools/VerificationExplorer.tsx b/src/components/views/dialogs/devtools/VerificationExplorer.tsx index 4a2523d7f0e..1ae3030eae1 100644 --- a/src/components/views/dialogs/devtools/VerificationExplorer.tsx +++ b/src/components/views/dialogs/devtools/VerificationExplorer.tsx @@ -16,11 +16,8 @@ limitations under the License. */ import React, { useContext, useEffect, useState } from "react"; -import { - Phase, - VerificationRequest, - VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationPhase as Phase, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import { useTypedEventEmitter, useTypedEventEmitterState } from "../../../../hooks/useEventEmitter"; diff --git a/src/components/views/messages/MKeyVerificationConclusion.tsx b/src/components/views/messages/MKeyVerificationConclusion.tsx index 5c51d5c2ce8..84dec016902 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.tsx +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -17,11 +17,7 @@ limitations under the License. import React from "react"; import classNames from "classnames"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { - Phase as VerificationPhase, - VerificationRequest, - VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationPhase, VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; diff --git a/src/components/views/messages/MKeyVerificationRequest.tsx b/src/components/views/messages/MKeyVerificationRequest.tsx index 60fc2963c96..cf5273a861c 100644 --- a/src/components/views/messages/MKeyVerificationRequest.tsx +++ b/src/components/views/messages/MKeyVerificationRequest.tsx @@ -18,9 +18,10 @@ import React from "react"; import { MatrixEvent, User } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { - Phase as VerificationPhase, + canAcceptVerificationRequest, + VerificationPhase, VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +} from "matrix-js-sdk/src/crypto-api"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from "../../../languageHandler"; @@ -139,7 +140,7 @@ export default class MKeyVerificationRequest extends React.Component { let subtitle: string; let stateNode: JSX.Element | undefined; - if (!request.canAccept) { + if (!canAcceptVerificationRequest(request)) { let stateLabel; const accepted = request.phase === VerificationPhase.Ready || @@ -165,7 +166,7 @@ export default class MKeyVerificationRequest extends React.Component { const name = getNameForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!); title = _t("%(name)s wants to verify", { name }); subtitle = userLabelForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!); - if (request.canAccept) { + if (canAcceptVerificationRequest(request)) { stateNode = (
diff --git a/src/components/views/right_panel/EncryptionPanel.tsx b/src/components/views/right_panel/EncryptionPanel.tsx index 6a036e695b6..ede0e96c99b 100644 --- a/src/components/views/right_panel/EncryptionPanel.tsx +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -15,13 +15,7 @@ limitations under the License. */ import React, { useCallback, useEffect, useState } from "react"; -import { - PHASE_REQUESTED, - PHASE_UNSENT, - Phase as VerificationPhase, - VerificationRequest, - VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationPhase, VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; @@ -78,7 +72,11 @@ const EncryptionPanel: React.FC = (props: IProps) => { }, [verificationRequestPromise]); const changeHandler = useCallback(() => { // handle transitions -> cancelled for mismatches which fire a modal instead of showing a card - if (request && request.phase === VerificationPhase.Cancelled && MISMATCHES.includes(request.cancellationCode)) { + if ( + request && + request.phase === VerificationPhase.Cancelled && + MISMATCHES.includes(request.cancellationCode ?? "") + ) { Modal.createDialog(ErrorDialog, { headerImage: require("../../../../res/img/e2e/warning-deprecated.svg").default, title: _t("Your messages are not secure"), @@ -139,7 +137,8 @@ const EncryptionPanel: React.FC = (props: IProps) => { const requested: boolean = (!request && isRequesting) || - (!!request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined)); + (!!request && + (phase === VerificationPhase.Requested || phase === VerificationPhase.Unsent || phase === undefined)); const isSelfVerification = request ? request.isSelfVerification : member.userId === cli.getUserId(); if (!request || requested) { diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index caa1692b206..aa06ef3cc70 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -24,7 +24,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { logger } from "matrix-js-sdk/src/logger"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index d594f7c75cd..6782c967a21 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -18,10 +18,10 @@ import React from "react"; import { verificationMethods } from "matrix-js-sdk/src/crypto"; import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode"; import { - Phase, VerificationRequest, + VerificationPhase as Phase, VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +} from "matrix-js-sdk/src/crypto-api"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; import { logger } from "matrix-js-sdk/src/logger"; @@ -202,7 +202,7 @@ export default class VerificationPanel extends React.PureComponent { @@ -93,7 +95,7 @@ export default class VerificationRequestToast extends React.PureComponent { const { request } = this.props; - if (!request.canAccept) { + if (!canAcceptVerificationRequest(request)) { ToastStore.sharedInstance().dismissToast(this.props.toastKey); } }; diff --git a/src/stores/SetupEncryptionStore.ts b/src/stores/SetupEncryptionStore.ts index d9300c6dc5d..31910e46bea 100644 --- a/src/stores/SetupEncryptionStore.ts +++ b/src/stores/SetupEncryptionStore.ts @@ -15,12 +15,7 @@ limitations under the License. */ import EventEmitter from "events"; -import { - PHASE_DONE as VERIF_PHASE_DONE, - Phase as VerificationPhase, - VerificationRequest, - VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationPhase, VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api"; import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api"; import { logger } from "matrix-js-sdk/src/logger"; @@ -183,7 +178,7 @@ export class SetupEncryptionStore extends EventEmitter { this.verificationRequest.off(VerificationRequestEvent.Change, this.onVerificationRequestChange); this.verificationRequest = null; this.emit("update"); - } else if (this.verificationRequest?.phase === VERIF_PHASE_DONE) { + } else if (this.verificationRequest?.phase === VerificationPhase.Done) { this.verificationRequest.off(VerificationRequestEvent.Change, this.onVerificationRequestChange); this.verificationRequest = null; // At this point, the verification has finished, we just need to wait for diff --git a/src/stores/right-panel/RightPanelStoreIPanelState.ts b/src/stores/right-panel/RightPanelStoreIPanelState.ts index 3599730e4f4..ddac46a0bc4 100644 --- a/src/stores/right-panel/RightPanelStoreIPanelState.ts +++ b/src/stores/right-panel/RightPanelStoreIPanelState.ts @@ -18,7 +18,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { User } from "matrix-js-sdk/src/models/user"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { RightPanelPhases } from "./RightPanelStorePhases"; diff --git a/src/verification.ts b/src/verification.ts index 6c6fc8780dc..4736b4693b3 100644 --- a/src/verification.ts +++ b/src/verification.ts @@ -17,8 +17,7 @@ limitations under the License. import { User } from "matrix-js-sdk/src/models/user"; import { verificationMethods as VerificationMethods } from "matrix-js-sdk/src/crypto"; import { MatrixClient, RoomMember } from "matrix-js-sdk/src/matrix"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; -import { CrossSigningKey } from "matrix-js-sdk/src/crypto-api"; +import { CrossSigningKey, VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import dis from "./dispatcher/dispatcher"; import Modal from "./Modal"; diff --git a/test/components/views/messages/MKeyVerificationRequest-test.tsx b/test/components/views/messages/MKeyVerificationRequest-test.tsx index 2fcfc1b5952..4c0e25f1f1f 100644 --- a/test/components/views/messages/MKeyVerificationRequest-test.tsx +++ b/test/components/views/messages/MKeyVerificationRequest-test.tsx @@ -18,10 +18,8 @@ import React from "react"; import { render, within } from "@testing-library/react"; import { EventEmitter } from "events"; import { MatrixEvent } from "matrix-js-sdk/src/matrix"; -import { - Phase as VerificationPhase, - VerificationRequest, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationPhase } from "matrix-js-sdk/src/crypto-api/verification"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils"; @@ -85,6 +83,18 @@ describe("MKeyVerificationRequest", () => { expect(within(container).getByRole("button")).toHaveTextContent("@other:user accepted"); }); + it("should render appropriately when the request was initiated by the other user and has not yet been accepted", () => { + const event = new MatrixEvent({ type: "m.key.verification.request" }); + event.verificationRequest = getMockVerificationRequest({ + phase: VerificationPhase.Requested, + initiatedByMe: false, + otherUserId: "@other:user", + }); + const result = render(); + expect(result.container).toHaveTextContent("@other:user wants to verify"); + result.getByRole("button", { name: "Accept" }); + }); + it("should render appropriately when the request was initiated by the other user and has been accepted", () => { const event = new MatrixEvent({ type: "m.key.verification.request" }); event.verificationRequest = getMockVerificationRequest({ diff --git a/test/components/views/right_panel/VerificationPanel-test.tsx b/test/components/views/right_panel/VerificationPanel-test.tsx index 9b43e418b5c..99a6404acb4 100644 --- a/test/components/views/right_panel/VerificationPanel-test.tsx +++ b/test/components/views/right_panel/VerificationPanel-test.tsx @@ -16,11 +16,6 @@ limitations under the License. import { act, render, waitFor } from "@testing-library/react"; import React, { ComponentProps } from "react"; -import { - Phase, - VerificationRequest, - VerificationRequestEvent, -} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter"; import { User } from "matrix-js-sdk/src/models/user"; import { Mocked } from "jest-mock"; @@ -30,6 +25,9 @@ import { Verifier, VerifierEvent, VerifierEventHandlerMap, + VerificationPhase as Phase, + VerificationRequest, + VerificationRequestEvent, } from "matrix-js-sdk/src/crypto-api/verification"; import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel"; diff --git a/test/components/views/toasts/VerificationRequestToast-test.tsx b/test/components/views/toasts/VerificationRequestToast-test.tsx index 6a73f445e92..6929e992224 100644 --- a/test/components/views/toasts/VerificationRequestToast-test.tsx +++ b/test/components/views/toasts/VerificationRequestToast-test.tsx @@ -27,6 +27,7 @@ import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter" import VerificationRequestToast from "../../../../src/components/views/toasts/VerificationRequestToast"; import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils"; +import ToastStore from "../../../../src/stores/ToastStore"; function renderComponent( props: Partial> & { request: VerificationRequest }, @@ -82,6 +83,23 @@ describe("VerificationRequestToast", () => { }); expect(result.container).toMatchSnapshot(); }); + + it("dismisses itself once the request can no longer be accepted", async () => { + const otherUserId = "@other:user"; + const request = makeMockVerificationRequest({ + isSelfVerification: false, + otherUserId, + }); + renderComponent({ request, toastKey: "testKey" }); + await act(async () => { + await flushPromises(); + }); + + const dismiss = jest.spyOn(ToastStore.sharedInstance(), "dismissToast"); + Object.defineProperty(request, "accepting", { value: true }); + request.emit(VerificationRequestEvent.Change); + expect(dismiss).toHaveBeenCalledWith("testKey"); + }); }); function makeMockVerificationRequest(props: Partial = {}): Mocked { diff --git a/test/components/views/toasts/__snapshots__/VerificationRequestToast-test.tsx.snap b/test/components/views/toasts/__snapshots__/VerificationRequestToast-test.tsx.snap index e34f816de17..ab302309fc1 100644 --- a/test/components/views/toasts/__snapshots__/VerificationRequestToast-test.tsx.snap +++ b/test/components/views/toasts/__snapshots__/VerificationRequestToast-test.tsx.snap @@ -17,7 +17,7 @@ exports[`VerificationRequestToast should render a cross-user verification 1`] = role="button" tabindex="0" > - Ignore (NaN) + Ignore
- Ignore (NaN) + Ignore
Date: Wed, 14 Jun 2023 16:49:14 +0100 Subject: [PATCH 12/68] Integrate compound design tokens (#11091) * Integrate compound design tokens The icons should not be included in this repo, and should live in the compound design token repo, but for simplicity sake at this phase of the integration they will be added here * lintfix * Use correct SpyInstance import * Using npm build of design tokens --- package.json | 1 + res/css/_common.pcss | 1 + src/theme.ts | 18 ++++ test/theme-test.ts | 14 ++++ yarn.lock | 191 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 222 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ffdc149531d..e41e0f8b18a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@sentry/browser": "^7.0.0", "@sentry/tracing": "^7.0.0", "@testing-library/react-hooks": "^8.0.1", + "@vector-im/compound-design-tokens": "^0.0.3", "await-lock": "^2.1.0", "blurhash": "^1.1.3", "classnames": "^2.2.6", diff --git a/res/css/_common.pcss b/res/css/_common.pcss index 6a3d9f03d41..0c9a44474cd 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -17,6 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css"); @import "./_font-sizes.pcss"; @import "./_font-weights.pcss"; @import "./_animations.pcss"; diff --git a/src/theme.ts b/src/theme.ts index 5fc7a6e50cb..afd31057d8f 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -269,6 +269,24 @@ export async function setTheme(theme?: string): Promise { const styleSheet = styleElements.get(stylesheetName)!; styleSheet.disabled = false; + /** + * Adds the Compound theme class to the top-most element in the document + * This will automatically refresh the colour scales based on the OS or user + * preferences + * + * Note: Theming through Compound is not yet established. Brand theming should + * be done in a similar manner as it used to be done. + */ + document.body.classList.remove("cpd-theme-light", "cpd-theme-dark", "cpd-theme-light-hc", "cpd-theme-dark-hc"); + + let compoundThemeClassName = `cpd-theme-` + (stylesheetName.includes("light") ? "light" : "dark"); + // Always respect user OS preference! + if (isHighContrastTheme(theme) || window.matchMedia("(prefers-contrast: more)").matches) { + compoundThemeClassName += "-hc"; + } + + document.body.classList.add(compoundThemeClassName); + return new Promise((resolve, reject) => { const switchTheme = function (): void { // we re-enable our theme here just in case we raced with another diff --git a/test/theme-test.ts b/test/theme-test.ts index 44a860f7ac1..b7d5f5060d2 100644 --- a/test/theme-test.ts +++ b/test/theme-test.ts @@ -22,6 +22,7 @@ describe("theme", () => { let darkTheme: HTMLStyleElement; let spyQuerySelectorAll: jest.MockInstance, [selectors: string]>; + let spyClassList: jest.SpyInstance; beforeEach(() => { const styles = [ @@ -47,6 +48,7 @@ describe("theme", () => { jest.spyOn(document.body, "style", "get").mockReturnValue([] as any); spyQuerySelectorAll = jest.spyOn(document, "querySelectorAll").mockReturnValue(styles as any); + spyClassList = jest.spyOn(document.body.classList, "add"); }); afterEach(() => { @@ -66,6 +68,18 @@ describe("theme", () => { expect(spyQuerySelectorAll).toHaveBeenCalledTimes(1); expect(lightTheme.disabled).toBe(false); expect(darkTheme.disabled).toBe(true); + expect(spyClassList).toHaveBeenCalledWith("cpd-theme-light"); + }); + + it("should switch to dark", async () => { + // When + await new Promise((resolve) => { + setTheme("dark").then(resolve); + darkTheme.onload!({} as Event); + }); + + // Then + expect(spyClassList).toHaveBeenCalledWith("cpd-theme-dark"); }); it("should reject promise on onerror call", () => { diff --git a/yarn.lock b/yarn.lock index f1c7b44dcca..ba97fdec20e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2059,6 +2059,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -2323,6 +2328,11 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/q@^1.5.1": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" + integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== + "@types/qrcode@^1.3.5": version "1.5.0" resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.5.0.tgz#6a98fe9a9a7b2a9a3167b6dde17eff999eabe40b" @@ -2585,11 +2595,23 @@ "@typescript-eslint/types" "5.59.6" eslint-visitor-keys "^3.3.0" +"@vector-im/compound-design-tokens@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-0.0.3.tgz#89214c69108a14f5d3e4a73ddc44852862531f2b" + integrity sha512-XxmySUvfjD6EuAM7f6lsGIhuv94TFfoEpKxYh+HKn1hPBFcMEKKImu/jK5tnpOv2xuZOSrK0Pm6qMLnxLwOXOw== + dependencies: + svg2vectordrawable "^2.9.1" + abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abs-svg-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf" + integrity sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA== + acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -3049,6 +3071,11 @@ blurhash@^1.1.3: resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.5.tgz#3034104cd5dce5a3e5caa871ae2f0f1f2d0ab566" integrity sha512-a+LO3A2DfxTaTztsmkbLYmUzUeApi0LZuKalwbNmqAHR6HhJGMt1qSV/R3wc+w4DL28holjqO3Bg74aUGavGjg== +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3165,7 +3192,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3314,6 +3341,15 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -3370,6 +3406,11 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -3511,6 +3552,25 @@ css-functions-list@^3.1.0: resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.1.0.tgz#cf5b09f835ad91a00e5959bcfc627cd498e1321b" integrity sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w== +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + css-tree@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" @@ -3519,6 +3579,11 @@ css-tree@^2.3.1: mdn-data "2.0.30" source-map-js "^1.0.1" +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + css.escape@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" @@ -3539,6 +3604,13 @@ cssfontparser@^1.2.1: resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3" integrity sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg== +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + cssom@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" @@ -3828,6 +3900,15 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -3837,7 +3918,7 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" -domelementtype@^2.3.0: +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -3849,6 +3930,13 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" +domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -3856,6 +3944,15 @@ domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" +domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" @@ -3942,6 +4039,11 @@ enquirer@^2.3.6: dependencies: ansi-colors "^4.1.1" +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + entities@^4.2.0, entities@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" @@ -5429,6 +5531,11 @@ is-subset@^0.1.1: resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" integrity sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw== +is-svg-path@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-svg-path/-/is-svg-path-1.0.2.tgz#77ab590c12b3d20348e5c7a13d0040c87784dda0" + integrity sha512-Lj4vePmqpPR1ZnRctHv8ltSh1OrSxHkhUkd7wi+VQdcdP15/KvQFyk7LhNuM7ZW0EVbJz8kZLVmL9quLrfq4Kg== + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" @@ -6514,6 +6621,11 @@ md5@^2.3.0: crypt "0.0.2" is-buffer "~1.1.6" +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + mdn-data@2.0.30: version "2.0.30" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" @@ -6639,7 +6751,7 @@ minimist@>=1.2.2, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1 resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81" integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w== -mkdirp@1.0.4, mkdirp@~1.0.4: +mkdirp@1.0.4, mkdirp@^1.0.4, mkdirp@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -6739,6 +6851,13 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-svg-path@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz#0e614eca23c39f0cffe821d6be6cd17e569a766c" + integrity sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg== + dependencies: + svg-arc-to-cubic-bezier "^3.0.0" + npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -6746,6 +6865,13 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + nwsapi@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.3.tgz#00e04dfd5a4a751e5ec2fecdc75dfd2f0db820fa" @@ -6954,6 +7080,11 @@ parse-srcset@^1.0.2: resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q== +parse-svg-path@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb" + integrity sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ== + parse5@^7.0.0, parse5@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" @@ -7277,6 +7408,11 @@ pvutils@^1.1.3: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== + qrcode@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170" @@ -7951,6 +8087,11 @@ sshpk@^1.14.1: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -8201,11 +8342,55 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-arc-to-cubic-bezier@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz#390c450035ae1c4a0104d90650304c3bc814abe6" + integrity sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g== + +svg-path-bounds@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/svg-path-bounds/-/svg-path-bounds-1.0.2.tgz#00312f672b08afc432a66ddfbd06db40cec8d0d0" + integrity sha512-H4/uAgLWrppIC0kHsb2/dWUYSmb4GE5UqH06uqWBcg6LBjX2fu0A8+JrO2/FJPZiSsNOKZAhyFFgsLTdYUvSqQ== + dependencies: + abs-svg-path "^0.1.1" + is-svg-path "^1.0.1" + normalize-svg-path "^1.0.0" + parse-svg-path "^0.1.2" + svg-tags@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== +svg2vectordrawable@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/svg2vectordrawable/-/svg2vectordrawable-2.9.1.tgz#23186ff7ace7038d09c031176dbca04063a97e5d" + integrity sha512-7WJIh4SzZLyEJtn45y+f8rREkgBiQMWfb0FoYkXuioywESjDWfbSuP0FQEmIiHP2zOi0oOO8pTG4VkeWJyidWw== + dependencies: + coa "^2.0.2" + mkdirp "^1.0.4" + svg-path-bounds "^1.0.1" + svgo "^2.8.0" + svgpath "^2.5.0" + +svgo@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +svgpath@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/svgpath/-/svgpath-2.6.0.tgz#5b160ef3d742b7dfd2d721bf90588d3450d7a90d" + integrity sha512-OIWR6bKzXvdXYyO4DK/UWa1VA1JeKq8E+0ug2DG98Y/vOmMpfZNj+TIG988HjfYSqtcy/hFOtZq/n/j5GSESNg== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" From 280f6a9d93fdbabfbf8491a5a5a6680a6d2c86e7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jun 2023 08:46:19 +0100 Subject: [PATCH 13/68] Use MatrixClientPeg::safeGet in src/components/views/* (#10987) --- .../views/avatars/DecoratedRoomAvatar.tsx | 2 +- src/components/views/avatars/RoomAvatar.tsx | 2 +- .../context_menus/MessageContextMenu.tsx | 20 +++++------ .../context_menus/ThreadListContextMenu.tsx | 2 +- .../views/dialogs/CreateRoomDialog.tsx | 6 ++-- .../views/dialogs/DeactivateAccountDialog.tsx | 6 ++-- .../views/dialogs/IncomingSasDialog.tsx | 6 ++-- src/components/views/dialogs/InviteDialog.tsx | 28 +++++++-------- src/components/views/dialogs/LogoutDialog.tsx | 6 ++-- .../dialogs/MessageEditHistoryDialog.tsx | 4 +-- .../views/dialogs/ModalWidgetDialog.tsx | 4 +-- .../views/dialogs/ReportEventDialog.tsx | 6 ++-- .../views/dialogs/RoomSettingsDialog.tsx | 6 ++-- .../dialogs/RoomUpgradeWarningDialog.tsx | 2 +- .../views/dialogs/SetEmailDialog.tsx | 2 +- .../dialogs/SlidingSyncOptionsDialog.tsx | 4 +-- .../views/dialogs/UntrustedDeviceDialog.tsx | 2 +- .../dialogs/VerificationRequestDialog.tsx | 2 +- .../security/AccessSecretStorageDialog.tsx | 4 +-- .../security/CreateCrossSigningDialog.tsx | 11 +++--- .../security/RestoreKeyBackupDialog.tsx | 16 ++++----- .../dialogs/spotlight/SpotlightDialog.tsx | 4 +-- .../views/directory/NetworkDropdown.tsx | 2 +- .../views/elements/AppPermission.tsx | 2 +- .../views/elements/ErrorBoundary.tsx | 4 +-- .../views/elements/PersistedElement.tsx | 2 +- src/components/views/elements/Pill.tsx | 4 +-- src/components/views/elements/ReplyChain.tsx | 2 +- .../views/emojipicker/ReactionPicker.tsx | 6 ++-- .../views/messages/DateSeparator.tsx | 2 +- .../views/messages/EncryptionEvent.tsx | 2 +- src/components/views/messages/MImageBody.tsx | 6 ++-- .../views/messages/MJitsiWidgetEvent.tsx | 2 +- .../messages/MKeyVerificationConclusion.tsx | 6 ++-- .../messages/MKeyVerificationRequest.tsx | 8 ++--- src/components/views/messages/MPollBody.tsx | 2 +- .../views/messages/MessageActionBar.tsx | 10 +++--- .../views/messages/RoomAvatarEvent.tsx | 4 +-- .../views/messages/RoomPredecessorTile.tsx | 2 +- src/components/views/messages/TextualBody.tsx | 2 +- .../views/messages/TextualEvent.tsx | 2 +- .../views/messages/ViewSourceEvent.tsx | 2 +- .../room_settings/RoomProfileSettings.tsx | 4 +-- .../room_settings/RoomPublishSetting.tsx | 6 ++-- .../room_settings/UrlPreviewSettings.tsx | 2 +- src/components/views/rooms/AuxPanel.tsx | 2 +- .../views/rooms/EditMessageComposer.tsx | 6 ++-- src/components/views/rooms/EventTile.tsx | 36 +++++++++---------- src/components/views/rooms/MemberList.tsx | 10 +++--- src/components/views/rooms/MemberTile.tsx | 6 ++-- .../views/rooms/MessageComposer.tsx | 10 +++--- .../views/rooms/MessageComposerButtons.tsx | 2 +- src/components/views/rooms/NewRoomIntro.tsx | 7 ++-- src/components/views/rooms/ReplyTile.tsx | 2 +- src/components/views/rooms/RoomList.tsx | 2 +- src/components/views/rooms/RoomPreviewBar.tsx | 18 +++++----- src/components/views/rooms/RoomTile.tsx | 2 +- .../views/rooms/SendMessageComposer.tsx | 8 ++--- src/components/views/rooms/Stickerpicker.tsx | 8 +++-- .../views/rooms/ThirdPartyMemberInfo.tsx | 8 ++--- .../views/rooms/VoiceRecordComposerTile.tsx | 4 +-- .../views/rooms/WhoIsTypingTile.tsx | 4 +-- .../views/settings/ChangeDisplayName.tsx | 4 +-- .../views/settings/ChangePassword.tsx | 4 +-- .../views/settings/CryptographyPanel.tsx | 8 ++--- .../views/settings/FontScalingPanel.tsx | 4 +-- .../views/settings/Notifications.tsx | 28 +++++++-------- .../views/settings/ProfileSettings.tsx | 4 +-- .../views/settings/SecureBackupPanel.tsx | 29 +++++++-------- src/components/views/settings/SetIdServer.tsx | 18 +++++----- .../views/settings/account/EmailAddresses.tsx | 4 +-- .../views/settings/account/PhoneNumbers.tsx | 4 +-- .../settings/discovery/EmailAddresses.tsx | 10 +++--- .../views/settings/discovery/PhoneNumbers.tsx | 10 +++--- .../views/spaces/SpacePublicShare.tsx | 3 +- src/components/views/voip/LegacyCallView.tsx | 6 ++-- src/components/views/voip/VideoFeed.tsx | 2 +- .../views/messages/EncryptionEvent-test.tsx | 1 + .../views/right_panel/UserInfo-test.tsx | 1 + .../views/rooms/RoomPreviewBar-test.tsx | 10 ++++-- .../views/spaces/SpacePanel-test.tsx | 1 + 81 files changed, 265 insertions(+), 250 deletions(-) diff --git a/src/components/views/avatars/DecoratedRoomAvatar.tsx b/src/components/views/avatars/DecoratedRoomAvatar.tsx index 508285d07ef..711bacda870 100644 --- a/src/components/views/avatars/DecoratedRoomAvatar.tsx +++ b/src/components/views/avatars/DecoratedRoomAvatar.tsx @@ -166,7 +166,7 @@ export default class DecoratedRoomAvatar extends React.PureComponent { } public componentDidMount(): void { - MatrixClientPeg.get().on(RoomStateEvent.Events, this.onRoomStateEvents); + MatrixClientPeg.safeGet().on(RoomStateEvent.Events, this.onRoomStateEvents); } public componentWillUnmount(): void { diff --git a/src/components/views/context_menus/MessageContextMenu.tsx b/src/components/views/context_menus/MessageContextMenu.tsx index 9fc7ca81106..ea5170c38a2 100644 --- a/src/components/views/context_menus/MessageContextMenu.tsx +++ b/src/components/views/context_menus/MessageContextMenu.tsx @@ -144,7 +144,7 @@ export default class MessageContextMenu extends React.Component } public componentDidMount(): void { - MatrixClientPeg.get().on(RoomMemberEvent.PowerLevel, this.checkPermissions); + MatrixClientPeg.safeGet().on(RoomMemberEvent.PowerLevel, this.checkPermissions); // re-check the permissions on send progress (`maySendRedactionForEvent` only returns true for events that have // been fully sent and echoed back, and we want to ensure the "Remove" option is added once that happens.) @@ -162,7 +162,7 @@ export default class MessageContextMenu extends React.Component } private checkPermissions = (): void => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); // We explicitly decline to show the redact option on ACL events as it has a potential @@ -184,7 +184,7 @@ export default class MessageContextMenu extends React.Component }; private isPinned(): boolean { - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); const pinnedEvent = room?.currentState.getStateEvents(EventType.RoomPinnedEvents, ""); if (!pinnedEvent) return false; const content = pinnedEvent.getContent(); @@ -195,13 +195,13 @@ export default class MessageContextMenu extends React.Component return ( M_POLL_START.matches(mxEvent.getType()) && this.state.canRedact && - !isPollEnded(mxEvent, MatrixClientPeg.get()) + !isPollEnded(mxEvent, MatrixClientPeg.safeGet()) ); } private onResendReactionsClick = (): void => { for (const reaction of this.getUnsentReactions()) { - Resend.resend(MatrixClientPeg.get(), reaction); + Resend.resend(MatrixClientPeg.safeGet(), reaction); } this.closeMenu(); }; @@ -253,7 +253,7 @@ export default class MessageContextMenu extends React.Component }; private onPinClick = (): void => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); if (!room) return; const eventId = this.props.mxEvent.getId(); @@ -318,7 +318,7 @@ export default class MessageContextMenu extends React.Component private onEditClick = (): void => { editEvent( - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), this.props.mxEvent, this.context.timelineRenderingType, this.props.getRelationsForEvent, @@ -345,7 +345,7 @@ export default class MessageContextMenu extends React.Component }; private onEndPollClick = (): void => { - const matrixClient = MatrixClientPeg.get(); + const matrixClient = MatrixClientPeg.safeGet(); Modal.createDialog( EndPollDialog, { @@ -359,7 +359,7 @@ export default class MessageContextMenu extends React.Component }; private getReactions(filter: (e: MatrixEvent) => boolean): MatrixEvent[] { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); const eventId = this.props.mxEvent.getId(); return ( @@ -386,7 +386,7 @@ export default class MessageContextMenu extends React.Component }; public render(): React.ReactNode { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const me = cli.getUserId(); const { mxEvent, rightClick, link, eventTileOps, reactions, collapseReplyChain, ...other } = this.props; delete other.getRelationsForEvent; diff --git a/src/components/views/context_menus/ThreadListContextMenu.tsx b/src/components/views/context_menus/ThreadListContextMenu.tsx index 543f2b9d969..c4464a6edd0 100644 --- a/src/components/views/context_menus/ThreadListContextMenu.tsx +++ b/src/components/views/context_menus/ThreadListContextMenu.tsx @@ -84,7 +84,7 @@ const ThreadListContextMenu: React.FC = ({ onMenuToggle?.(menuDisplayed); }, [menuDisplayed, onMenuToggle]); - const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(mxEvent.getRoomId()); const isMainSplitTimelineShown = !!room && !WidgetLayoutStore.instance.hasMaximisedWidget(room); return ( diff --git a/src/components/views/dialogs/CreateRoomDialog.tsx b/src/components/views/dialogs/CreateRoomDialog.tsx index 2484d5fd7e6..3ef2ff5c967 100644 --- a/src/components/views/dialogs/CreateRoomDialog.tsx +++ b/src/components/views/dialogs/CreateRoomDialog.tsx @@ -75,7 +75,7 @@ export default class CreateRoomDialog extends React.Component { joinRule = JoinRule.Restricted; } - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); this.state = { isPublic: this.props.defaultPublic || false, isEncrypted: this.props.defaultEncrypted ?? privateShouldBeEncrypted(cli), @@ -222,7 +222,7 @@ export default class CreateRoomDialog extends React.Component { let aliasField: JSX.Element | undefined; if (this.state.joinRule === JoinRule.Public) { - const domain = MatrixClientPeg.get().getDomain()!; + const domain = MatrixClientPeg.safeGet().getDomain()!; aliasField = (
{ let e2eeSection: JSX.Element | undefined; if (this.state.joinRule !== JoinRule.Public) { let microcopy: string; - if (privateShouldBeEncrypted(MatrixClientPeg.get())) { + if (privateShouldBeEncrypted(MatrixClientPeg.safeGet())) { if (this.state.canChangeEncryption) { microcopy = isVideoRoom ? _t("You can't disable this later. The room will be encrypted but the embedded call will not.") diff --git a/src/components/views/dialogs/DeactivateAccountDialog.tsx b/src/components/views/dialogs/DeactivateAccountDialog.tsx index 8e8f0be7703..0c3a9c85657 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.tsx +++ b/src/components/views/dialogs/DeactivateAccountDialog.tsx @@ -125,7 +125,7 @@ export default class DeactivateAccountDialog extends React.Component { // Deactivation worked - logout & close this dialog @@ -158,7 +158,7 @@ export default class DeactivateAccountDialog extends React.Component { // If we got here, oops. The server didn't require any auth. @@ -190,7 +190,7 @@ export default class DeactivateAccountDialog extends React.Component {this.state.bodyText} { private async fetchOpponentProfile(): Promise { try { - const prof = await MatrixClientPeg.get().getProfileInfo(this.props.verifier.userId); + const prof = await MatrixClientPeg.safeGet().getProfileInfo(this.props.verifier.userId); this.setState({ opponentProfile: prof, }); @@ -143,7 +143,7 @@ export default class IncomingSasDialog extends React.Component { }; private renderPhaseStart(): ReactNode { - const isSelf = this.props.verifier.userId === MatrixClientPeg.get().getUserId(); + const isSelf = this.props.verifier.userId === MatrixClientPeg.safeGet().getUserId(); let profile; const oppProfile = this.state.opponentProfile; @@ -233,7 +233,7 @@ export default class IncomingSasDialog extends React.Component { sas={this.showSasEvent.sas} onCancel={this.onCancelClick} onDone={this.onSasMatchesClick} - isSelf={this.props.verifier.userId === MatrixClientPeg.get().getUserId()} + isSelf={this.props.verifier.userId === MatrixClientPeg.safeGet().getUserId()} inDialog={true} /> ); diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 30dc15e5cc8..b5e96a8010b 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -373,12 +373,12 @@ export default class InviteDialog extends React.PureComponent alreadyInvited.add(m.userId)); room.getMembersWithMembership("join").forEach((m) => alreadyInvited.add(m.userId)); @@ -395,7 +395,7 @@ export default class InviteDialog extends React.PureComponent u.userId !== myUserId); for (const member of otherMembers) { @@ -491,7 +491,7 @@ export default class InviteDialog extends React.PureComponent): { userId: string; user: Member }[] { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const activityScores = buildActivityScores(cli); const memberScores = buildMemberScores(cli); @@ -560,7 +560,7 @@ export default class InviteDialog extends React.PureComponent t.userId); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.roomId); if (!room) { logger.error("Failed to find the room to invite users to"); @@ -694,7 +694,7 @@ export default class InviteDialog extends React.PureComponent => { - MatrixClientPeg.get() + MatrixClientPeg.safeGet() .searchUserDirectory({ term }) .then(async (r): Promise => { if (term !== this.state.filterText) { @@ -774,7 +774,7 @@ export default class InviteDialog extends React.PureComponent 0 || (this.state.filterText && this.state.filterText.includes("@")); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const userId = cli.getUserId()!; if (this.props.kind === InviteKind.Dm) { title = _t("Direct Messages"); @@ -1332,11 +1332,11 @@ export default class InviteDialog extends React.PureComponent{_t("If you can't see who you're looking for, send them your invite link below.")}

); - const link = makeUserPermalink(MatrixClientPeg.get().getUserId()!); + const link = makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId()); footer = (

{_t("Or send invite link")}

- makeUserPermalink(MatrixClientPeg.get().getUserId()!)}> + makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId())}> {link} diff --git a/src/components/views/dialogs/LogoutDialog.tsx b/src/components/views/dialogs/LogoutDialog.tsx index daf81ced65d..bf92719c195 100644 --- a/src/components/views/dialogs/LogoutDialog.tsx +++ b/src/components/views/dialogs/LogoutDialog.tsx @@ -50,7 +50,7 @@ export default class LogoutDialog extends React.Component { public constructor(props: IProps) { super(props); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const shouldLoadBackupStatus = cli.isCryptoEnabled() && !cli.getKeyBackupEnabled(); this.state = { @@ -66,7 +66,7 @@ export default class LogoutDialog extends React.Component { private async loadBackupStatus(): Promise { try { - const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); + const backupInfo = await MatrixClientPeg.safeGet().getKeyBackupVersion(); this.setState({ loading: false, backupInfo, @@ -86,7 +86,7 @@ export default class LogoutDialog extends React.Component { typeof ExportE2eKeysDialog >, { - matrixClient: MatrixClientPeg.get(), + matrixClient: MatrixClientPeg.safeGet(), }, ); }; diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.tsx b/src/components/views/dialogs/MessageEditHistoryDialog.tsx index a8b446df09a..58e7ba9b188 100644 --- a/src/components/views/dialogs/MessageEditHistoryDialog.tsx +++ b/src/components/views/dialogs/MessageEditHistoryDialog.tsx @@ -67,7 +67,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent(); let result: Awaited>; @@ -102,7 +102,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent b.id); @@ -130,7 +130,7 @@ export default class ModalWidgetDialog extends React.PureComponent { // Does the room support it, too? // Extract state events to determine whether we should display - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const room = client.getRoom(props.mxEvent.getRoomId()); for (const stateEventType of MODERATED_BY_STATE_EVENT_TYPE) { @@ -237,7 +237,7 @@ export default class ReportEventDialog extends React.Component { }); try { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const ev = this.props.mxEvent; if (this.moderation && this.state.nature !== NonStandardValue.Admin) { const nature = this.state.nature; @@ -312,7 +312,7 @@ export default class ReportEventDialog extends React.Component { if (this.moderation) { // Display report-to-moderator dialog. // We let the user pick a nature. - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const homeServerName = SdkConfig.get("validated_server_config")!.hsName; let subtitle: string; switch (this.state.nature) { diff --git a/src/components/views/dialogs/RoomSettingsDialog.tsx b/src/components/views/dialogs/RoomSettingsDialog.tsx index 55e49843b2a..86d748730b6 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.tsx +++ b/src/components/views/dialogs/RoomSettingsDialog.tsx @@ -73,7 +73,7 @@ class RoomSettingsDialog extends React.Component { public componentDidMount(): void { this.dispatcherRef = dis.register(this.onAction); - MatrixClientPeg.get().on(RoomEvent.Name, this.onRoomName); + MatrixClientPeg.safeGet().on(RoomEvent.Name, this.onRoomName); this.onRoomName(); } @@ -89,7 +89,7 @@ class RoomSettingsDialog extends React.Component { dis.unregister(this.dispatcherRef); } - MatrixClientPeg.get().removeListener(RoomEvent.Name, this.onRoomName); + MatrixClientPeg.get()?.removeListener(RoomEvent.Name, this.onRoomName); } /** @@ -98,7 +98,7 @@ class RoomSettingsDialog extends React.Component { * @throws when room is not found */ private getRoom(): Room { - const room = MatrixClientPeg.get().getRoom(this.props.roomId)!; + const room = MatrixClientPeg.safeGet().getRoom(this.props.roomId)!; // something is really wrong if we encounter this if (!room) { diff --git a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx index 4ae49a327fa..be59a3e0117 100644 --- a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx +++ b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx @@ -60,7 +60,7 @@ export default class RoomUpgradeWarningDialog extends React.Component { }); return; } - this.addThreepid = new AddThreepid(MatrixClientPeg.get()); + this.addThreepid = new AddThreepid(MatrixClientPeg.safeGet()); this.addThreepid.addEmailAddress(emailAddress).then( () => { Modal.createDialog(QuestionDialog, { diff --git a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx index 9ef3e83edea..37457000911 100644 --- a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx +++ b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx @@ -63,7 +63,7 @@ async function proxyHealthCheck(endpoint: string, hsUrl?: string): Promise } export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean): void }> = ({ onFinished }) => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const currentProxy = SettingsStore.getValue("feature_sliding_sync_proxy_url"); const hasNativeSupport = useAsyncMemo( () => @@ -87,7 +87,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean): const validProxy = withValidation({ async deriveData({ value }): Promise<{ error?: Error }> { try { - await proxyHealthCheck(value!, MatrixClientPeg.get().baseUrl); + await proxyHealthCheck(value!, MatrixClientPeg.safeGet().baseUrl); return {}; } catch (error) { return { error }; diff --git a/src/components/views/dialogs/UntrustedDeviceDialog.tsx b/src/components/views/dialogs/UntrustedDeviceDialog.tsx index 393590ff9d9..296a8f06102 100644 --- a/src/components/views/dialogs/UntrustedDeviceDialog.tsx +++ b/src/components/views/dialogs/UntrustedDeviceDialog.tsx @@ -34,7 +34,7 @@ const UntrustedDeviceDialog: React.FC = ({ device, user, onFinished }) = let askToVerifyText: string; let newSessionText: string; - if (MatrixClientPeg.get().getUserId() === user.userId) { + if (MatrixClientPeg.safeGet().getUserId() === user.userId) { newSessionText = _t("You signed in to a new session without verifying it:"); askToVerifyText = _t("Verify your other session using one of the options below."); } else { diff --git a/src/components/views/dialogs/VerificationRequestDialog.tsx b/src/components/views/dialogs/VerificationRequestDialog.tsx index ebd54165ccc..f087969c1b9 100644 --- a/src/components/views/dialogs/VerificationRequestDialog.tsx +++ b/src/components/views/dialogs/VerificationRequestDialog.tsx @@ -48,7 +48,7 @@ export default class VerificationRequestDialog extends React.Component => { // Now reset cross-signing so everything Just Works™ again. - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); await cli.bootstrapCrossSigning({ authUploadDeviceSigningKeys: async (makeRequest): Promise => { const { finished } = Modal.createDialog(InteractiveAuthDialog, { diff --git a/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx b/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx index 2bac1f028c9..7a6412c8ed3 100644 --- a/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx +++ b/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx @@ -73,7 +73,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent { try { - await MatrixClientPeg.get().uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys); + await MatrixClientPeg.safeGet().uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys); // We should never get here: the server should always require // UI auth to upload device signing keys. If we do, we upload // no keys which would be a no-op. @@ -98,11 +98,11 @@ export default class CreateCrossSigningDialog extends React.PureComponent): void => { this.setState({ recoveryKey: e.target.value, - recoveryKeyValid: MatrixClientPeg.get().isValidRecoveryKey(e.target.value), + recoveryKeyValid: MatrixClientPeg.safeGet().isValidRecoveryKey(e.target.value), }); }; @@ -145,7 +145,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent => { if (!this.state.backupInfo) return; - await MatrixClientPeg.get().restoreKeyBackupWithSecretStorage( + await MatrixClientPeg.safeGet().restoreKeyBackupWithSecretStorage( this.state.backupInfo, undefined, undefined, @@ -252,7 +252,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { if (!backupInfo) return false; try { - const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache( + const recoverInfo = await MatrixClientPeg.safeGet().restoreKeyBackupWithCache( undefined /* targetRoomId */, undefined /* targetSessionId */, backupInfo, @@ -274,7 +274,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { - const myUserId = MatrixClientPeg.get().getUserId(); + const myUserId = MatrixClientPeg.safeGet().getUserId(); const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); if (otherUserId) { @@ -281,7 +281,7 @@ interface IDirectoryOpts { const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = null, onFinished }) => { const inputRef = useRef(null); const scrollContainerRef = useRef(null); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const rovingContext = useContext(RovingTabIndexContext); const [query, _setQuery] = useState(initialText); const [recentSearches, clearRecentSearches] = useRecentSearches(); diff --git a/src/components/views/directory/NetworkDropdown.tsx b/src/components/views/directory/NetworkDropdown.tsx index b4ed2e10aea..081d7e2a7c8 100644 --- a/src/components/views/directory/NetworkDropdown.tsx +++ b/src/components/views/directory/NetworkDropdown.tsx @@ -42,7 +42,7 @@ const validServer = withValidation({ deriveData: async ({ value }): Promise<{ error?: MatrixError }> => { try { // check if we can successfully load this server's room directory - await MatrixClientPeg.get().publicRooms({ + await MatrixClientPeg.safeGet().publicRooms({ limit: 1, server: value ?? undefined, }); diff --git a/src/components/views/elements/AppPermission.tsx b/src/components/views/elements/AppPermission.tsx index f57a5e4a29b..463db9f2e3b 100644 --- a/src/components/views/elements/AppPermission.tsx +++ b/src/components/views/elements/AppPermission.tsx @@ -57,7 +57,7 @@ export default class AppPermission extends React.Component { const urlInfo = this.parseWidgetUrl(); // The second step is to find the user's profile so we can show it on the prompt - const room = MatrixClientPeg.get().getRoom(this.props.roomId); + const room = MatrixClientPeg.safeGet().getRoom(this.props.roomId); let roomMember: RoomMember | null = null; if (room) roomMember = room.getMember(this.props.creatorUserId); diff --git a/src/components/views/elements/ErrorBoundary.tsx b/src/components/views/elements/ErrorBoundary.tsx index a230ab84c5a..dca7d4562f6 100644 --- a/src/components/views/elements/ErrorBoundary.tsx +++ b/src/components/views/elements/ErrorBoundary.tsx @@ -60,8 +60,8 @@ export default class ErrorBoundary extends React.PureComponent { private onClearCacheAndReload = (): void => { if (!PlatformPeg.get()) return; - MatrixClientPeg.get().stopClient(); - MatrixClientPeg.get() + MatrixClientPeg.safeGet().stopClient(); + MatrixClientPeg.safeGet() .store.deleteAllData() .then(() => { PlatformPeg.get()?.reload(); diff --git a/src/components/views/elements/PersistedElement.tsx b/src/components/views/elements/PersistedElement.tsx index b1d53247db7..2d4c763eb20 100644 --- a/src/components/views/elements/PersistedElement.tsx +++ b/src/components/views/elements/PersistedElement.tsx @@ -162,7 +162,7 @@ export default class PersistedElement extends React.Component { private renderApp(): void { const content = ( - +
{this.props.children}
diff --git a/src/components/views/elements/Pill.tsx b/src/components/views/elements/Pill.tsx index 49c6c1bfd2d..e5e9d383eaf 100644 --- a/src/components/views/elements/Pill.tsx +++ b/src/components/views/elements/Pill.tsx @@ -106,7 +106,7 @@ export const Pill: React.FC = ({ type: propType, url, inMessage, room mx_RoomPill: type === PillType.RoomMention, mx_SpacePill: type === "space", mx_UserPill: type === PillType.UserMention, - mx_UserPill_me: resourceId === MatrixClientPeg.get().getUserId(), + mx_UserPill_me: resourceId === MatrixClientPeg.safeGet().getUserId(), mx_EventPill: type === PillType.EventInOtherRoom || type === PillType.EventInSameRoom, }); @@ -158,7 +158,7 @@ export const Pill: React.FC = ({ type: propType, url, inMessage, room return ( - + {inMessage && url ? ( { } private get matrixClient(): MatrixClient { - return MatrixClientPeg.get(); + return MatrixClientPeg.safeGet(); } public componentDidMount(): void { diff --git a/src/components/views/emojipicker/ReactionPicker.tsx b/src/components/views/emojipicker/ReactionPicker.tsx index 97222740f88..b479b446e27 100644 --- a/src/components/views/emojipicker/ReactionPicker.tsx +++ b/src/components/views/emojipicker/ReactionPicker.tsx @@ -77,7 +77,7 @@ class ReactionPicker extends React.Component { if (!this.props.reactions) { return {}; } - const userId = MatrixClientPeg.get().getUserId()!; + const userId = MatrixClientPeg.safeGet().getSafeUserId(); const myAnnotations = this.props.reactions.getAnnotationsBySender()?.[userId] ?? new Set(); return Object.fromEntries( [...myAnnotations] @@ -99,7 +99,7 @@ class ReactionPicker extends React.Component { if (myReactions.hasOwnProperty(reaction)) { if (this.props.mxEvent.isRedacted() || !this.context.canSelfRedact) return false; - MatrixClientPeg.get().redactEvent(this.props.mxEvent.getRoomId()!, myReactions[reaction]); + MatrixClientPeg.safeGet().redactEvent(this.props.mxEvent.getRoomId()!, myReactions[reaction]); dis.dispatch({ action: Action.FocusAComposer, context: this.context.timelineRenderingType, @@ -107,7 +107,7 @@ class ReactionPicker extends React.Component { // Tell the emoji picker not to bump this in the more frequently used list. return false; } else { - MatrixClientPeg.get().sendEvent(this.props.mxEvent.getRoomId()!, EventType.Reaction, { + MatrixClientPeg.safeGet().sendEvent(this.props.mxEvent.getRoomId()!, EventType.Reaction, { "m.relates_to": { rel_type: RelationType.Annotation, event_id: this.props.mxEvent.getId(), diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index 4eed475d5cc..87969ac4b7f 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -125,7 +125,7 @@ export default class DateSeparator extends React.Component { const roomIdForJumpRequest = this.props.roomId; try { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const { event_id: eventId, origin_server_ts: originServerTs } = await cli.timestampToEvent( roomIdForJumpRequest, unixTimestamp, diff --git a/src/components/views/messages/EncryptionEvent.tsx b/src/components/views/messages/EncryptionEvent.tsx index fbc1da18d34..963afd415e7 100644 --- a/src/components/views/messages/EncryptionEvent.tsx +++ b/src/components/views/messages/EncryptionEvent.tsx @@ -36,7 +36,7 @@ const ALGORITHM = "m.megolm.v1.aes-sha2"; const EncryptionEvent = forwardRef(({ mxEvent, timestamp }, ref) => { const cli = useContext(MatrixClientContext); const roomId = mxEvent.getRoomId()!; - const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId); + const isRoomEncrypted = MatrixClientPeg.safeGet().isRoomEncrypted(roomId); const prevContent = mxEvent.getPrevContent() as IRoomEncryption; const content = mxEvent.getContent(); diff --git a/src/components/views/messages/MImageBody.tsx b/src/components/views/messages/MImageBody.tsx index acfe2e27abb..3f62c72fae6 100644 --- a/src/components/views/messages/MImageBody.tsx +++ b/src/components/views/messages/MImageBody.tsx @@ -160,7 +160,7 @@ export default class MImageBody extends React.Component { }; private clearError = (): void => { - MatrixClientPeg.get().off(ClientEvent.Sync, this.reconnectedListener); + MatrixClientPeg.get()?.off(ClientEvent.Sync, this.reconnectedListener); this.setState({ imgError: false }); }; @@ -177,7 +177,7 @@ export default class MImageBody extends React.Component { this.setState({ imgError: true, }); - MatrixClientPeg.get().on(ClientEvent.Sync, this.reconnectedListener); + MatrixClientPeg.safeGet().on(ClientEvent.Sync, this.reconnectedListener); }; private onImageLoad = (): void => { @@ -373,7 +373,7 @@ export default class MImageBody extends React.Component { public componentWillUnmount(): void { this.unmounted = true; - MatrixClientPeg.get().off(ClientEvent.Sync, this.reconnectedListener); + MatrixClientPeg.get()?.off(ClientEvent.Sync, this.reconnectedListener); this.clearBlurhashTimeout(); if (this.sizeWatcher) SettingsStore.unwatchSetting(this.sizeWatcher); if (this.state.isAnimated && this.state.thumbUrl) { diff --git a/src/components/views/messages/MJitsiWidgetEvent.tsx b/src/components/views/messages/MJitsiWidgetEvent.tsx index b299d96f6dd..e71a4681437 100644 --- a/src/components/views/messages/MJitsiWidgetEvent.tsx +++ b/src/components/views/messages/MJitsiWidgetEvent.tsx @@ -37,7 +37,7 @@ export default class MJitsiWidgetEvent extends React.PureComponent { const url = this.props.mxEvent.getContent()["url"]; const prevUrl = this.props.mxEvent.getPrevContent()["url"]; const senderName = this.props.mxEvent.sender?.name || this.props.mxEvent.getSender(); - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); if (!room) return null; const widgetId = this.props.mxEvent.getStateKey(); const widget = WidgetStore.instance.getRoom(room.roomId, true).widgets.find((w) => w.id === widgetId); diff --git a/src/components/views/messages/MKeyVerificationConclusion.tsx b/src/components/views/messages/MKeyVerificationConclusion.tsx index 84dec016902..4ec24231dcb 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.tsx +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -42,7 +42,7 @@ export default class MKeyVerificationConclusion extends React.Component if (request) { request.on(VerificationRequestEvent.Change, this.onRequestChanged); } - MatrixClientPeg.get().on(CryptoEvent.UserTrustStatusChanged, this.onTrustChanged); + MatrixClientPeg.safeGet().on(CryptoEvent.UserTrustStatusChanged, this.onTrustChanged); } public componentWillUnmount(): void { @@ -89,7 +89,7 @@ export default class MKeyVerificationConclusion extends React.Component } // User isn't actually verified - if (!MatrixClientPeg.get().checkUserTrust(request.otherUserId).isCrossSigningVerified()) { + if (!MatrixClientPeg.safeGet().checkUserTrust(request.otherUserId).isCrossSigningVerified()) { return false; } @@ -104,7 +104,7 @@ export default class MKeyVerificationConclusion extends React.Component return null; } - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const myUserId = client.getUserId(); let title: string | undefined; diff --git a/src/components/views/messages/MKeyVerificationRequest.tsx b/src/components/views/messages/MKeyVerificationRequest.tsx index cf5273a861c..5ae432b90cb 100644 --- a/src/components/views/messages/MKeyVerificationRequest.tsx +++ b/src/components/views/messages/MKeyVerificationRequest.tsx @@ -55,7 +55,7 @@ export default class MKeyVerificationRequest extends React.Component { let member: User | undefined; const { verificationRequest } = this.props.mxEvent; if (verificationRequest) { - member = MatrixClientPeg.get().getUser(verificationRequest.otherUserId) ?? undefined; + member = MatrixClientPeg.safeGet().getUser(verificationRequest.otherUserId) ?? undefined; } RightPanelStore.instance.setCards([ { phase: RightPanelPhases.RoomSummary }, @@ -92,7 +92,7 @@ export default class MKeyVerificationRequest extends React.Component { }; private acceptedLabel(userId: string): string { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const myUserId = client.getUserId(); if (userId === myUserId) { return _t("You accepted"); @@ -104,7 +104,7 @@ export default class MKeyVerificationRequest extends React.Component { } private cancelledLabel(userId: string): string { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const myUserId = client.getUserId(); const cancellationCode = this.props.mxEvent.verificationRequest?.cancellationCode; const declined = cancellationCode === "m.user"; @@ -128,7 +128,7 @@ export default class MKeyVerificationRequest extends React.Component { } public render(): React.ReactNode { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const { mxEvent } = this.props; const request = mxEvent.verificationRequest; diff --git a/src/components/views/messages/MPollBody.tsx b/src/components/views/messages/MPollBody.tsx index 1029f7370d9..5a5c1b53157 100644 --- a/src/components/views/messages/MPollBody.tsx +++ b/src/components/views/messages/MPollBody.tsx @@ -118,7 +118,7 @@ export function pollAlreadyHasVotes(mxEvent: MatrixEvent, getRelationsForEvent?: } export function launchPollEditor(mxEvent: MatrixEvent, getRelationsForEvent?: GetRelationsForEvent): void { - const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(mxEvent.getRoomId()); if (pollAlreadyHasVotes(mxEvent, getRelationsForEvent)) { Modal.createDialog(ErrorDialog, { title: _t("Can't edit poll"), diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index 22f13178b3f..a0b730b65f9 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -311,7 +311,7 @@ export default class MessageActionBar extends React.PureComponent Resend.resend(MatrixClientPeg.get(), tarEv)); + this.runActionOnFailedEv((tarEv) => Resend.resend(MatrixClientPeg.safeGet(), tarEv)); }; private onCancelClick = (ev: ButtonEvent): void => { this.runActionOnFailedEv( - (tarEv) => Resend.removeFromQueue(MatrixClientPeg.get(), tarEv), + (tarEv) => Resend.removeFromQueue(MatrixClientPeg.safeGet(), tarEv), (testEv) => canCancel(testEv.status), ); }; public render(): React.ReactNode { const toolbarOpts: JSX.Element[] = []; - if (canEditContent(MatrixClientPeg.get(), this.props.mxEvent)) { + if (canEditContent(MatrixClientPeg.safeGet(), this.props.mxEvent)) { toolbarOpts.push( { private onAvatarClick = (): void => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const ev = this.props.mxEvent; const httpUrl = mediaFromMxc(ev.getContent().url).srcHttp; if (!httpUrl) return; @@ -63,7 +63,7 @@ export default class RoomAvatarEvent extends React.Component { ); } - const room = MatrixClientPeg.get().getRoom(ev.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(ev.getRoomId()); // Provide all arguments to RoomAvatar via oobData because the avatar is historic const oobData = { avatarUrl: ev.getContent().url, diff --git a/src/components/views/messages/RoomPredecessorTile.tsx b/src/components/views/messages/RoomPredecessorTile.tsx index bee0849fa41..67be715634c 100644 --- a/src/components/views/messages/RoomPredecessorTile.tsx +++ b/src/components/views/messages/RoomPredecessorTile.tsx @@ -87,7 +87,7 @@ export const RoomPredecessorTile: React.FC = ({ mxEvent, timestamp }) => return
; } - const prevRoom = MatrixClientPeg.get().getRoom(predecessor.roomId); + const prevRoom = MatrixClientPeg.safeGet().getRoom(predecessor.roomId); // We need either the previous room, or some servers to find it with. // Otherwise, we must bail out here diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index b02c52a64a3..3c4e98fb588 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -93,7 +93,7 @@ export default class TextualBody extends React.Component { this.activateSpoilers([content]); HtmlUtils.linkifyElement(content); - pillifyLinks(MatrixClientPeg.get(), [content], this.props.mxEvent, this.pills); + pillifyLinks(MatrixClientPeg.safeGet(), [content], this.props.mxEvent, this.pills); this.calculateUrlPreview(); diff --git a/src/components/views/messages/TextualEvent.tsx b/src/components/views/messages/TextualEvent.tsx index 93501bac671..fa82c3bf314 100644 --- a/src/components/views/messages/TextualEvent.tsx +++ b/src/components/views/messages/TextualEvent.tsx @@ -31,7 +31,7 @@ export default class TextualEvent extends React.Component { public render(): React.ReactNode { const text = TextForEvent.textForEvent( this.props.mxEvent, - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), true, this.context?.showHiddenEvents, ); diff --git a/src/components/views/messages/ViewSourceEvent.tsx b/src/components/views/messages/ViewSourceEvent.tsx index 1d33074a7a0..5bd85507961 100644 --- a/src/components/views/messages/ViewSourceEvent.tsx +++ b/src/components/views/messages/ViewSourceEvent.tsx @@ -42,7 +42,7 @@ export default class ViewSourceEvent extends React.PureComponent public componentDidMount(): void { const { mxEvent } = this.props; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); client.decryptEventIfNeeded(mxEvent); if (mxEvent.isBeingDecrypted()) { diff --git a/src/components/views/room_settings/RoomProfileSettings.tsx b/src/components/views/room_settings/RoomProfileSettings.tsx index 7117608f396..21768cd16ae 100644 --- a/src/components/views/room_settings/RoomProfileSettings.tsx +++ b/src/components/views/room_settings/RoomProfileSettings.tsx @@ -52,7 +52,7 @@ export default class RoomProfileSettings extends React.Component public constructor(props: IProps) { super(props); - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const room = client.getRoom(props.roomId); if (!room) throw new Error(`Expected a room for ID: ${props.roomId}`); @@ -124,7 +124,7 @@ export default class RoomProfileSettings extends React.Component if (!this.isSaveEnabled()) return; this.setState({ profileFieldsTouched: {} }); - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const newState: Partial = {}; // TODO: What do we do about errors? diff --git a/src/components/views/room_settings/RoomPublishSetting.tsx b/src/components/views/room_settings/RoomPublishSetting.tsx index 1aeeb1876d8..a2f93e4cc50 100644 --- a/src/components/views/room_settings/RoomPublishSetting.tsx +++ b/src/components/views/room_settings/RoomPublishSetting.tsx @@ -45,7 +45,7 @@ export default class RoomPublishSetting extends React.PureComponent { this.setState({ isRoomPublished: result.visibility === "public" }); }); } public render(): React.ReactNode { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const room = client.getRoom(this.props.roomId); const isRoomPublishable = room && room.getJoinRule() !== JoinRule.Invite; diff --git a/src/components/views/room_settings/UrlPreviewSettings.tsx b/src/components/views/room_settings/UrlPreviewSettings.tsx index e859f3adc4c..15d70132e24 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.tsx +++ b/src/components/views/room_settings/UrlPreviewSettings.tsx @@ -43,7 +43,7 @@ export default class UrlPreviewSettings extends React.Component { public render(): ReactNode { const roomId = this.props.room.roomId; - const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId); + const isEncrypted = MatrixClientPeg.safeGet().isRoomEncrypted(roomId); let previewsForAccount: ReactNode | undefined; let previewsForRoom: ReactNode | undefined; diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index e7f9141629e..73002f484b1 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -65,7 +65,7 @@ export default class AuxPanel extends React.Component { } public componentDidMount(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); if (SettingsStore.getValue("feature_state_counters")) { cli.on(RoomStateEvent.Events, this.onRoomStateEvents); } diff --git a/src/components/views/rooms/EditMessageComposer.tsx b/src/components/views/rooms/EditMessageComposer.tsx index 1b403e31fbc..08e307b5dd3 100644 --- a/src/components/views/rooms/EditMessageComposer.tsx +++ b/src/components/views/rooms/EditMessageComposer.tsx @@ -182,7 +182,7 @@ class EditMessageComposer extends React.Component if (!this.props.mxEvent) return false; // Sanity check (should never happen, but we shouldn't explode if it does) - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); if (!room) return false; // Quickly check to see if the event was sent by us. If it wasn't, it won't qualify for // special read receipts. - const myUserId = MatrixClientPeg.get().getUserId(); + const myUserId = MatrixClientPeg.safeGet().getUserId(); if (this.props.mxEvent.getSender() !== myUserId) return false; // Finally, determine if the type is relevant to the user. This notably excludes state @@ -344,7 +344,7 @@ export class UnwrappedEventTile extends React.Component // If anyone has read the event besides us, we don't want to show a sent receipt. const receipts = this.props.readReceipts || []; - const myUserId = MatrixClientPeg.get().getUserId(); + const myUserId = MatrixClientPeg.safeGet().getUserId(); if (receipts.some((r) => r.userId !== myUserId)) return false; // Finally, we should show a receipt. @@ -366,7 +366,7 @@ export class UnwrappedEventTile extends React.Component public componentDidMount(): void { this.suppressReadReceiptAnimation = false; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); if (!this.props.forExport) { client.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged); client.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged); @@ -431,7 +431,7 @@ export class UnwrappedEventTile extends React.Component } // If we're not listening for receipts and expect to be, register a listener. if (!this.isListeningForReceipts && (this.shouldShowSentReceipt || this.shouldShowSendingReceipt)) { - MatrixClientPeg.get().on(RoomEvent.Receipt, this.onRoomReceipt); + MatrixClientPeg.safeGet().on(RoomEvent.Receipt, this.onRoomReceipt); this.isListeningForReceipts = true; } // re-check the sender verification as outgoing events progress through the send process. @@ -443,7 +443,7 @@ export class UnwrappedEventTile extends React.Component private onNewThread = (thread: Thread): void => { if (thread.id === this.props.mxEvent.getId()) { this.updateThread(thread); - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); room?.off(ThreadEvent.New, this.onNewThread); } }; @@ -457,7 +457,7 @@ export class UnwrappedEventTile extends React.Component * when we are at the sync stage */ if (!thread) { - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); thread = room?.findThreadForEvent(this.props.mxEvent) ?? undefined; } return thread ?? null; @@ -519,7 +519,7 @@ export class UnwrappedEventTile extends React.Component private onRoomReceipt = (ev: MatrixEvent, room: Room): void => { // ignore events for other rooms - const tileRoom = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const tileRoom = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); if (room !== tileRoom) return; if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt && !this.isListeningForReceipts) { @@ -531,7 +531,7 @@ export class UnwrappedEventTile extends React.Component this.forceUpdate(() => { // Per elsewhere in this file, we can remove the listener once we will have no further purpose for it. if (!this.shouldShowSentReceipt && !this.shouldShowSendingReceipt) { - MatrixClientPeg.get().removeListener(RoomEvent.Receipt, this.onRoomReceipt); + MatrixClientPeg.safeGet().removeListener(RoomEvent.Receipt, this.onRoomReceipt); this.isListeningForReceipts = false; } }); @@ -574,7 +574,7 @@ export class UnwrappedEventTile extends React.Component return; } - const encryptionInfo = MatrixClientPeg.get().getEventEncryptionInfo(mxEvent); + const encryptionInfo = MatrixClientPeg.safeGet().getEventEncryptionInfo(mxEvent); const senderId = mxEvent.getSender(); if (!senderId) { // something definitely wrong is going on here @@ -582,7 +582,7 @@ export class UnwrappedEventTile extends React.Component return; } - const userTrust = MatrixClientPeg.get().checkUserTrust(senderId); + const userTrust = MatrixClientPeg.safeGet().checkUserTrust(senderId); if (encryptionInfo.mismatchedSender) { // something definitely wrong is going on here @@ -601,7 +601,7 @@ export class UnwrappedEventTile extends React.Component const eventSenderTrust = senderId && encryptionInfo.sender && - (await MatrixClientPeg.get() + (await MatrixClientPeg.safeGet() .getCrypto() ?.getDeviceVerificationStatus(senderId, encryptionInfo.sender.deviceId)); @@ -686,7 +686,7 @@ export class UnwrappedEventTile extends React.Component if (this.context.timelineRenderingType === TimelineRenderingType.Notification) return false; if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) return false; - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const actions = cli.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent); // get the actions for the previous version of the event too if it is an edit const previousActions = this.props.mxEvent.replacingEvent() @@ -697,7 +697,7 @@ export class UnwrappedEventTile extends React.Component } // don't show self-highlights from another of our clients - if (this.props.mxEvent.getSender() === MatrixClientPeg.get().credentials.userId) { + if (this.props.mxEvent.getSender() === cli.credentials.userId) { return false; } @@ -754,7 +754,7 @@ export class UnwrappedEventTile extends React.Component } } - if (MatrixClientPeg.get().isRoomEncrypted(ev.getRoomId()!)) { + if (MatrixClientPeg.safeGet().isRoomEncrypted(ev.getRoomId()!)) { // else if room is encrypted // and event is being encrypted or is not_sent (Unknown Devices/Network Error) if (ev.status === EventStatus.ENCRYPTING) { @@ -904,7 +904,7 @@ export class UnwrappedEventTile extends React.Component noBubbleEvent, isSeeingThroughMessageHiddenForModeration, } = getEventDisplayInfo( - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), this.props.mxEvent, this.context.showHiddenEvents, this.shouldHideEvent(), @@ -1186,7 +1186,7 @@ export class UnwrappedEventTile extends React.Component } // Use `getSender()` because searched events might not have a proper `sender`. - const isOwnEvent = this.props.mxEvent?.getSender() === MatrixClientPeg.get().getUserId(); + const isOwnEvent = this.props.mxEvent?.getSender() === MatrixClientPeg.safeGet().getUserId(); switch (this.context.timelineRenderingType) { case TimelineRenderingType.Thread: { @@ -1242,7 +1242,7 @@ export class UnwrappedEventTile extends React.Component } case TimelineRenderingType.Notification: case TimelineRenderingType.ThreadsList: { - const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const room = MatrixClientPeg.safeGet().getRoom(this.props.mxEvent.getRoomId()); // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return React.createElement( this.props.as || "li", diff --git a/src/components/views/rooms/MemberList.tsx b/src/components/views/rooms/MemberList.tsx index d064bdfe3db..e8e0403fa08 100644 --- a/src/components/views/rooms/MemberList.tsx +++ b/src/components/views/rooms/MemberList.tsx @@ -84,7 +84,7 @@ export default class MemberList extends React.Component { } private listenForMembersChanges(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); cli.on(RoomStateEvent.Update, this.onRoomStateUpdate); cli.on(RoomMemberEvent.Name, this.onRoomMemberName); cli.on(RoomStateEvent.Events, this.onRoomStateEvent); @@ -121,7 +121,7 @@ export default class MemberList extends React.Component { } private get canInvite(): boolean { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.roomId); return ( @@ -284,7 +284,7 @@ export default class MemberList extends React.Component { // The HS may have already converted these into m.room.member invites so // we shouldn't add them if the 3pid invite state key (token) is in the // member invite (content.third_party_invite.signed.token) - const room = MatrixClientPeg.get().getRoom(this.props.roomId); + const room = MatrixClientPeg.safeGet().getRoom(this.props.roomId); if (room) { return room.currentState.getStateEvents("m.room.third_party_invite").filter(function (e) { @@ -348,7 +348,7 @@ export default class MemberList extends React.Component { ); } - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(this.props.roomId); let inviteButton: JSX.Element | undefined; @@ -442,7 +442,7 @@ export default class MemberList extends React.Component { private onInviteButtonClick = (ev: ButtonEvent): void => { PosthogTrackers.trackInteraction("WebRightPanelMemberListInviteButton", ev); - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { dis.dispatch({ action: "require_registration" }); return; } diff --git a/src/components/views/rooms/MemberTile.tsx b/src/components/views/rooms/MemberTile.tsx index f8f891a4867..e5dd30a3025 100644 --- a/src/components/views/rooms/MemberTile.tsx +++ b/src/components/views/rooms/MemberTile.tsx @@ -62,7 +62,7 @@ export default class MemberTile extends React.Component { } public componentDidMount(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const { roomId } = this.props.member; if (roomId) { @@ -97,7 +97,7 @@ export default class MemberTile extends React.Component { if (ev.getRoomId() !== roomId) return; // The room is encrypted now. - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); this.setState({ isRoomEncrypted: true, @@ -116,7 +116,7 @@ export default class MemberTile extends React.Component { }; private async updateE2EStatus(): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const { userId } = this.props.member; const isMe = userId === cli.getUserId(); const userTrust = cli.checkUserTrust(userId); diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 55742d8c7ec..1c3167214f2 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -243,7 +243,7 @@ export class MessageComposer extends React.Component { private waitForOwnMember(): void { // If we have the member already, do that - const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()!); + const me = this.props.room.getMember(MatrixClientPeg.safeGet().getUserId()!); if (me) { this.setState({ me }); return; @@ -252,7 +252,7 @@ export class MessageComposer extends React.Component { // The members should already be loading, and loadMembersIfNeeded // will return the promise for the existing operation this.props.room.loadMembersIfNeeded().then(() => { - const me = this.props.room.getMember(MatrixClientPeg.get().getSafeUserId()) ?? undefined; + const me = this.props.room.getMember(MatrixClientPeg.safeGet().getSafeUserId()) ?? undefined; this.setState({ me }); }); } @@ -271,7 +271,7 @@ export class MessageComposer extends React.Component { ev.preventDefault(); const replacementRoomId = this.context.tombstone?.getContent()["replacement_room"]; - const replacementRoom = MatrixClientPeg.get().getRoom(replacementRoomId); + const replacementRoom = MatrixClientPeg.safeGet().getRoom(replacementRoomId); let createEventId: string | undefined; if (replacementRoom) { const createEvent = replacementRoom.currentState.getStateEvents(EventType.RoomCreate, ""); @@ -527,7 +527,7 @@ export class MessageComposer extends React.Component { const continuesLink = replacementRoomId ? ( @@ -636,7 +636,7 @@ export class MessageComposer extends React.Component { onStartVoiceBroadcastClick={() => { setUpVoiceBroadcastPreRecording( this.props.room, - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), SdkContextClass.instance.voiceBroadcastPlaybacksStore, SdkContextClass.instance.voiceBroadcastRecordingsStore, SdkContextClass.instance.voiceBroadcastPreRecordingStore, diff --git a/src/components/views/rooms/MessageComposerButtons.tsx b/src/components/views/rooms/MessageComposerButtons.tsx index dc95b5044e5..864809b5418 100644 --- a/src/components/views/rooms/MessageComposerButtons.tsx +++ b/src/components/views/rooms/MessageComposerButtons.tsx @@ -309,7 +309,7 @@ class PollButton extends React.PureComponent { this.context?.(); // close overflow menu const canSend = this.props.room.currentState.maySendEvent( M_POLL_START.name, - MatrixClientPeg.get().getUserId()!, + MatrixClientPeg.safeGet().getSafeUserId(), ); if (!canSend) { Modal.createDialog(ErrorDialog, { diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index b1186a6d457..84cb408c8ab 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -278,8 +278,11 @@ const NewRoomIntro: React.FC = () => { "like email invites.", ); - let subButton; - if (room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.get()) && !isLocalRoom) { + let subButton: JSX.Element | undefined; + if ( + room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.safeGet()) && + !isLocalRoom + ) { subButton = ( {_t("Enable encryption in settings.")} diff --git a/src/components/views/rooms/ReplyTile.tsx b/src/components/views/rooms/ReplyTile.tsx index 05997eb78ba..7dacf2bfed1 100644 --- a/src/components/views/rooms/ReplyTile.tsx +++ b/src/components/views/rooms/ReplyTile.tsx @@ -111,7 +111,7 @@ export default class ReplyTile extends React.PureComponent { const evType = mxEvent.getType(); const { hasRenderer, isInfoMessage, isSeeingThroughMessageHiddenForModeration } = getEventDisplayInfo( - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), mxEvent, false /* Replies are never hidden, so this should be fine */, ); diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 06396de03c9..8c0d797df6d 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -222,7 +222,7 @@ const UntaggedAuxButton: React.FC = ({ tabIndex }) => { if (menuDisplayed && activeSpace) { const canAddRooms = activeSpace.currentState.maySendStateEvent( EventType.SpaceChild, - MatrixClientPeg.get().getUserId()!, + MatrixClientPeg.safeGet().getSafeUserId(), ); contextMenuContent = ( diff --git a/src/components/views/rooms/RoomPreviewBar.tsx b/src/components/views/rooms/RoomPreviewBar.tsx index a4b419949e3..96cbaa99173 100644 --- a/src/components/views/rooms/RoomPreviewBar.tsx +++ b/src/components/views/rooms/RoomPreviewBar.tsx @@ -136,19 +136,19 @@ export default class RoomPreviewBar extends React.Component { this.setState({ busy: true }); try { // Gather the account 3PIDs - const account3pids = await MatrixClientPeg.get().getThreePids(); + const account3pids = await MatrixClientPeg.safeGet().getThreePids(); this.setState({ accountEmails: account3pids.threepids.filter((b) => b.medium === "email").map((b) => b.address), }); // If we have an IS connected, use that to lookup the email and // check the bound MXID. - if (!MatrixClientPeg.get().getIdentityServerUrl()) { + if (!MatrixClientPeg.safeGet().getIdentityServerUrl()) { this.setState({ busy: false }); return; } const authClient = new IdentityAuthClient(); const identityAccessToken = await authClient.getAccessToken(); - const result = await MatrixClientPeg.get().lookupThreePid( + const result = await MatrixClientPeg.safeGet().lookupThreePid( "email", this.props.invitedEmail, identityAccessToken!, @@ -162,7 +162,7 @@ export default class RoomPreviewBar extends React.Component { } private getMessageCase(): MessageCase { - const isGuest = MatrixClientPeg.get().isGuest(); + const isGuest = MatrixClientPeg.safeGet().isGuest(); if (isGuest) { return MessageCase.NotLoggedIn; @@ -192,9 +192,9 @@ export default class RoomPreviewBar extends React.Component { return MessageCase.OtherThreePIDError; } else if (this.state.accountEmails && !this.state.accountEmails.includes(this.props.invitedEmail)) { return MessageCase.InvitedEmailNotFoundInAccount; - } else if (!MatrixClientPeg.get().getIdentityServerUrl()) { + } else if (!MatrixClientPeg.safeGet().getIdentityServerUrl()) { return MessageCase.InvitedEmailNoIdentityServer; - } else if (this.state.invitedEmailMxid != MatrixClientPeg.get().getUserId()) { + } else if (this.state.invitedEmailMxid != MatrixClientPeg.safeGet().getUserId()) { return MessageCase.InvitedEmailMismatch; } } @@ -232,7 +232,7 @@ export default class RoomPreviewBar extends React.Component { } private getMyMember(): RoomMember | null { - return this.props.room?.getMember(MatrixClientPeg.get().getUserId()!) ?? null; + return this.props.room?.getMember(MatrixClientPeg.safeGet().getSafeUserId()) ?? null; } private getInviteMember(): RoomMember | null { @@ -240,7 +240,7 @@ export default class RoomPreviewBar extends React.Component { if (!room) { return null; } - const myUserId = MatrixClientPeg.get().getUserId()!; + const myUserId = MatrixClientPeg.safeGet().getSafeUserId(); const inviteEvent = room.currentState.getMember(myUserId); if (!inviteEvent) { return null; @@ -504,7 +504,7 @@ export default class RoomPreviewBar extends React.Component { primaryActionLabel = _t("Accept"); } - const myUserId = MatrixClientPeg.get().getUserId()!; + const myUserId = MatrixClientPeg.safeGet().getSafeUserId(); const member = this.props.room?.currentState.getMember(myUserId); const memberEventContent = member?.events.member?.getContent(); diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index d070c2a59d0..76f8154c7c5 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -285,7 +285,7 @@ export class RoomTile extends React.PureComponent { private renderNotificationsMenu(isActive: boolean): React.ReactElement | null { if ( - MatrixClientPeg.get().isGuest() || + MatrixClientPeg.safeGet().isGuest() || this.props.tag === DefaultTagID.Archived || !this.showContextMenu || this.props.isMinimized diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 263e7d13293..ed2e022b38f 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -336,7 +336,7 @@ export class SendMessageComposer extends React.Component { this.dispatcherRef = dis.register(this.onAction); // Track updates to widget state in account data - MatrixClientPeg.get().on(ClientEvent.AccountData, this.updateWidget); + MatrixClientPeg.safeGet().on(ClientEvent.AccountData, this.updateWidget); RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate); // Initialise widget state from current account data @@ -291,8 +291,10 @@ export default class Stickerpicker extends React.PureComponent { room={this.props.room} threadId={this.props.threadId} fullWidth={true} - userId={MatrixClientPeg.get().credentials.userId!} - creatorUserId={stickerpickerWidget.sender || MatrixClientPeg.get().credentials.userId!} + userId={MatrixClientPeg.safeGet().credentials.userId!} + creatorUserId={ + stickerpickerWidget.sender || MatrixClientPeg.safeGet().credentials.userId! + } waitForIframeLoad={true} showMenubar={true} onEditClick={this.launchManageIntegrations} diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.tsx b/src/components/views/rooms/ThirdPartyMemberInfo.tsx index 3a17f62eee1..9f3fb1e5d3d 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.tsx +++ b/src/components/views/rooms/ThirdPartyMemberInfo.tsx @@ -50,8 +50,8 @@ export default class ThirdPartyMemberInfo extends React.Component { - MatrixClientPeg.get() + MatrixClientPeg.safeGet() .sendStateEvent(this.state.roomId, "m.room.third_party_invite", {}, this.state.stateKey) .catch((err) => { logger.error(err); diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index fb90e17609e..6e0589868a8 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -130,7 +130,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent MatrixClientPeg.get().sendMessage(actualRoomId, content), + (actualRoomId: string) => MatrixClientPeg.safeGet().sendMessage(actualRoomId, content), this.props.room.client, ); } catch (e) { diff --git a/src/components/views/rooms/WhoIsTypingTile.tsx b/src/components/views/rooms/WhoIsTypingTile.tsx index 5552fc5c691..a478204edae 100644 --- a/src/components/views/rooms/WhoIsTypingTile.tsx +++ b/src/components/views/rooms/WhoIsTypingTile.tsx @@ -57,8 +57,8 @@ export default class WhoIsTypingTile extends React.Component { }; public componentDidMount(): void { - MatrixClientPeg.get().on(RoomMemberEvent.Typing, this.onRoomMemberTyping); - MatrixClientPeg.get().on(RoomEvent.Timeline, this.onRoomTimeline); + MatrixClientPeg.safeGet().on(RoomMemberEvent.Typing, this.onRoomMemberTyping); + MatrixClientPeg.safeGet().on(RoomEvent.Timeline, this.onRoomTimeline); } public componentDidUpdate(prevProps: IProps, prevState: IState): void { diff --git a/src/components/views/settings/ChangeDisplayName.tsx b/src/components/views/settings/ChangeDisplayName.tsx index 696384ef673..3add2d2b4c2 100644 --- a/src/components/views/settings/ChangeDisplayName.tsx +++ b/src/components/views/settings/ChangeDisplayName.tsx @@ -22,7 +22,7 @@ import EditableTextContainer from "../elements/EditableTextContainer"; export default class ChangeDisplayName extends React.Component { private getDisplayName = async (): Promise => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); try { const res = await cli.getProfileInfo(cli.getUserId()!); return res.displayname ?? ""; @@ -32,7 +32,7 @@ export default class ChangeDisplayName extends React.Component { }; private changeDisplayName = (newDisplayname: string): Promise<{}> => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); return cli.setDisplayName(newDisplayname).catch(function () { throw new Error("Failed to set display name"); }); diff --git a/src/components/views/settings/ChangePassword.tsx b/src/components/views/settings/ChangePassword.tsx index 69e30948b4a..4c3f51eda89 100644 --- a/src/components/views/settings/ChangePassword.tsx +++ b/src/components/views/settings/ChangePassword.tsx @@ -93,7 +93,7 @@ export default class ChangePassword extends React.Component { } private async onChangePassword(oldPassword: string, newPassword: string): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); // if the server supports it then don't sign user out of all devices const serverSupportsControlOfDevicesLogout = await cli.doesServerSupportLogoutDevices(); @@ -235,7 +235,7 @@ export default class ChangePassword extends React.Component { typeof ExportE2eKeysDialog >, { - matrixClient: MatrixClientPeg.get(), + matrixClient: MatrixClientPeg.safeGet(), }, ); }; diff --git a/src/components/views/settings/CryptographyPanel.tsx b/src/components/views/settings/CryptographyPanel.tsx index d947946b755..5a5e424abec 100644 --- a/src/components/views/settings/CryptographyPanel.tsx +++ b/src/components/views/settings/CryptographyPanel.tsx @@ -38,7 +38,7 @@ export default class CryptographyPanel extends React.Component { } public render(): React.ReactNode { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const deviceId = client.deviceId; let identityKey = client.getDeviceEd25519Key(); if (!identityKey) { @@ -103,7 +103,7 @@ export default class CryptographyPanel extends React.Component { import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise< typeof ExportE2eKeysDialog >, - { matrixClient: MatrixClientPeg.get() }, + { matrixClient: MatrixClientPeg.safeGet() }, ); }; @@ -112,11 +112,11 @@ export default class CryptographyPanel extends React.Component { import("../../../async-components/views/dialogs/security/ImportE2eKeysDialog") as unknown as Promise< typeof ImportE2eKeysDialog >, - { matrixClient: MatrixClientPeg.get() }, + { matrixClient: MatrixClientPeg.safeGet() }, ); }; private updateBlacklistDevicesFlag = (checked: boolean): void => { - MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked); + MatrixClientPeg.safeGet().setGlobalBlacklistUnverifiedDevices(checked); }; } diff --git a/src/components/views/settings/FontScalingPanel.tsx b/src/components/views/settings/FontScalingPanel.tsx index cfede4d14c0..04a511f82f4 100644 --- a/src/components/views/settings/FontScalingPanel.tsx +++ b/src/components/views/settings/FontScalingPanel.tsx @@ -62,8 +62,8 @@ export default class FontScalingPanel extends React.Component { public async componentDidMount(): Promise { // Fetch the current user profile for the message preview - const client = MatrixClientPeg.get(); - const userId = client.getUserId()!; + const client = MatrixClientPeg.safeGet(); + const userId = client.getSafeUserId(); const profileInfo = await client.getProfileInfo(userId); if (this.unmounted) return; diff --git a/src/components/views/settings/Notifications.tsx b/src/components/views/settings/Notifications.tsx index 18d5c34c10d..61ff474fa06 100644 --- a/src/components/views/settings/Notifications.tsx +++ b/src/components/views/settings/Notifications.tsx @@ -270,7 +270,7 @@ export default class Notifications extends React.PureComponent { } private async refreshFromAccountData(): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const settingsEvent = cli.getAccountData(getLocalNotificationAccountDataEventType(cli.deviceId)); if (settingsEvent) { const notificationsEnabled = !(settingsEvent.getContent() as LocalNotificationSettings).is_silenced; @@ -279,14 +279,14 @@ export default class Notifications extends React.PureComponent { } private persistLocalNotificationSettings(enabled: boolean): Promise<{}> { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); return cli.setAccountData(getLocalNotificationAccountDataEventType(cli.deviceId), { is_silenced: !enabled, }); } private async refreshRules(): Promise> { - const ruleSets = await MatrixClientPeg.get().getPushRules()!; + const ruleSets = await MatrixClientPeg.safeGet().getPushRules()!; const categories: Record = { [RuleId.Master]: RuleClass.Master, @@ -384,11 +384,11 @@ export default class Notifications extends React.PureComponent { } private refreshPushers(): Promise> { - return MatrixClientPeg.get().getPushers(); + return MatrixClientPeg.safeGet().getPushers(); } private refreshThreepids(): Promise> { - return MatrixClientPeg.get().getThreePids(); + return MatrixClientPeg.safeGet().getThreePids(); } private showSaveError(): void { @@ -403,7 +403,7 @@ export default class Notifications extends React.PureComponent { const masterRule = this.state.masterPushRule!; try { - await MatrixClientPeg.get().setPushRuleEnabled("global", masterRule.kind, masterRule.rule_id, !checked); + await MatrixClientPeg.safeGet().setPushRuleEnabled("global", masterRule.kind, masterRule.rule_id, !checked); await this.refreshFromServer(); } catch (e) { this.setState({ phase: Phase.Error }); @@ -428,7 +428,7 @@ export default class Notifications extends React.PureComponent { try { if (checked) { - await MatrixClientPeg.get().setPusher({ + await MatrixClientPeg.safeGet().setPusher({ kind: "email", app_id: "m.email", pushkey: email, @@ -446,7 +446,7 @@ export default class Notifications extends React.PureComponent { } else { const pusher = this.state.pushers?.find((p) => p.kind === "email" && p.pushkey === email); if (pusher) { - await MatrixClientPeg.get().removePusher(pusher.pushkey, pusher.app_id); + await MatrixClientPeg.safeGet().removePusher(pusher.pushkey, pusher.app_id); } } @@ -477,7 +477,7 @@ export default class Notifications extends React.PureComponent { })); try { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); if (rule.ruleId === KEYWORD_RULE_ID) { // should not encounter this if (!this.state.vectorKeywordRuleInfo) { @@ -536,7 +536,7 @@ export default class Notifications extends React.PureComponent { private onClearNotificationsClicked = async (): Promise => { try { this.setState({ clearingNotifications: true }); - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); await clearAllNotifications(client); } finally { this.setState({ clearingNotifications: false }); @@ -560,7 +560,7 @@ export default class Notifications extends React.PureComponent { for (const word of diff.removed) { for (const rule of originalRules.filter((r) => r.pattern === word)) { - await MatrixClientPeg.get().deletePushRule("global", rule.kind, rule.rule_id); + await MatrixClientPeg.safeGet().deletePushRule("global", rule.kind, rule.rule_id); } } @@ -577,12 +577,12 @@ export default class Notifications extends React.PureComponent { } const kind = PushRuleKind.ContentSpecific; for (const word of diff.added) { - await MatrixClientPeg.get().addPushRule("global", kind, word, { + await MatrixClientPeg.safeGet().addPushRule("global", kind, word, { actions: PushRuleVectorState.actionsFor(ruleVectorState), pattern: word, }); if (ruleVectorState === VectorState.Off) { - await MatrixClientPeg.get().setPushRuleEnabled("global", kind, word, false); + await MatrixClientPeg.safeGet().setPushRuleEnabled("global", kind, word, false); } } @@ -730,7 +730,7 @@ export default class Notifications extends React.PureComponent { let clearNotifsButton: JSX.Element | undefined; if ( category === RuleClass.VectorOther && - MatrixClientPeg.get() + MatrixClientPeg.safeGet() .getRooms() .some((r) => r.getUnreadNotificationCount() > 0) ) { diff --git a/src/components/views/settings/ProfileSettings.tsx b/src/components/views/settings/ProfileSettings.tsx index 529bb86a78e..f277872c868 100644 --- a/src/components/views/settings/ProfileSettings.tsx +++ b/src/components/views/settings/ProfileSettings.tsx @@ -47,7 +47,7 @@ export default class ProfileSettings extends React.Component<{}, IState> { public constructor(props: {}) { super(props); - this.userId = MatrixClientPeg.get().getSafeUserId(); + this.userId = MatrixClientPeg.safeGet().getSafeUserId(); let avatarUrl = OwnProfileStore.instance.avatarMxc; if (avatarUrl) avatarUrl = mediaFromMxc(avatarUrl).getSquareThumbnailHttp(96); this.state = { @@ -96,11 +96,11 @@ export default class ProfileSettings extends React.Component<{}, IState> { if (!this.state.enableProfileSave) return; this.setState({ enableProfileSave: false }); - const client = MatrixClientPeg.get(); const newState: Partial = {}; const displayName = this.state.displayName.trim(); try { + const client = MatrixClientPeg.safeGet(); if (this.state.originalDisplayName !== this.state.displayName) { await client.setDisplayName(displayName); newState.originalDisplayName = displayName; diff --git a/src/components/views/settings/SecureBackupPanel.tsx b/src/components/views/settings/SecureBackupPanel.tsx index 7f988d8d418..aabb1cb90c0 100644 --- a/src/components/views/settings/SecureBackupPanel.tsx +++ b/src/components/views/settings/SecureBackupPanel.tsx @@ -69,16 +69,16 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { public componentDidMount(): void { this.checkKeyBackupStatus(); - MatrixClientPeg.get().on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus); - MatrixClientPeg.get().on(CryptoEvent.KeyBackupSessionsRemaining, this.onKeyBackupSessionsRemaining); + MatrixClientPeg.safeGet().on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus); + MatrixClientPeg.safeGet().on(CryptoEvent.KeyBackupSessionsRemaining, this.onKeyBackupSessionsRemaining); } public componentWillUnmount(): void { this.unmounted = true; if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus); - MatrixClientPeg.get().removeListener( + MatrixClientPeg.get()!.removeListener(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus); + MatrixClientPeg.get()!.removeListener( CryptoEvent.KeyBackupSessionsRemaining, this.onKeyBackupSessionsRemaining, ); @@ -100,7 +100,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { private async checkKeyBackupStatus(): Promise { this.getUpdatedDiagnostics(); try { - const keyBackupResult = await MatrixClientPeg.get().checkKeyBackup(); + const keyBackupResult = await MatrixClientPeg.safeGet().checkKeyBackup(); this.setState({ loading: false, error: null, @@ -123,8 +123,8 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { this.setState({ loading: true }); this.getUpdatedDiagnostics(); try { - const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion(); - const backupSigStatus = backupInfo ? await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo) : null; + const backupInfo = await MatrixClientPeg.safeGet().getKeyBackupVersion(); + const backupSigStatus = backupInfo ? await MatrixClientPeg.safeGet().isKeyBackupTrusted(backupInfo) : null; if (this.unmounted) return; this.setState({ loading: false, @@ -145,7 +145,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { } private async getUpdatedDiagnostics(): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const crypto = cli.crypto; if (!crypto) return; @@ -195,7 +195,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { onFinished: (proceed) => { if (!proceed) return; this.setState({ loading: true }); - MatrixClientPeg.get() + MatrixClientPeg.safeGet() .deleteKeyBackupVersion(this.state.backupInfo!.version!) .then(() => { this.loadBackupStatus(); @@ -246,7 +246,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { } else if (backupInfo) { let restoreButtonCaption = _t("Restore from Backup"); - if (MatrixClientPeg.get().getKeyBackupEnabled()) { + if (MatrixClientPeg.safeGet().getKeyBackupEnabled()) { statusDescription =

✅ {_t("This session is backing up your keys.")}

; } else { statusDescription = ( @@ -272,7 +272,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { } let uploadStatus: ReactNode; - if (!MatrixClientPeg.get().getKeyBackupEnabled()) { + if (!MatrixClientPeg.safeGet().getKeyBackupEnabled()) { // No upload status to show when backup disabled. uploadStatus = ""; } else if (sessionsRemaining > 0) { @@ -311,8 +311,9 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { {deviceName} ); const fromThisDevice = - sig.device && sig.device.getFingerprint() === MatrixClientPeg.get().getDeviceEd25519Key(); - const fromThisUser = sig.crossSigningId && sig.deviceId === MatrixClientPeg.get().getCrossSigningId(); + sig.device && sig.device.getFingerprint() === MatrixClientPeg.safeGet().getDeviceEd25519Key(); + const fromThisUser = + sig.crossSigningId && sig.deviceId === MatrixClientPeg.safeGet().getCrossSigningId(); let sigStatus; if (sig.valid && fromThisUser) { sigStatus = _t( @@ -419,7 +420,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
, ); - if (!isSecureBackupRequired(MatrixClientPeg.get())) { + if (!isSecureBackupRequired(MatrixClientPeg.safeGet())) { actions.push( {_t("Delete Backup")} diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index df2f9926014..2256556b582 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -88,7 +88,7 @@ export default class SetIdServer extends React.Component { super(props); let defaultIdServer = ""; - if (!MatrixClientPeg.get().getIdentityServerUrl() && getDefaultIdentityServerUrl()) { + if (!MatrixClientPeg.safeGet().getIdentityServerUrl() && getDefaultIdentityServerUrl()) { // If no identity server is configured but there's one in the config, prepopulate // the field to help the user. defaultIdServer = abbreviateUrl(getDefaultIdentityServerUrl()); @@ -96,7 +96,7 @@ export default class SetIdServer extends React.Component { this.state = { defaultIdServer, - currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(), + currentClientIdServer: MatrixClientPeg.safeGet().getIdentityServerUrl(), idServer: "", busy: false, disconnectBusy: false, @@ -118,7 +118,7 @@ export default class SetIdServer extends React.Component { if (payload.action !== "id_server_changed") return; this.setState({ - currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(), + currentClientIdServer: MatrixClientPeg.safeGet().getIdentityServerUrl(), }); }; @@ -149,7 +149,7 @@ export default class SetIdServer extends React.Component { private saveIdServer = (fullUrl: string): void => { // Account data change will update localstorage, client, etc through dispatcher - MatrixClientPeg.get().setAccountData("m.identity_server", { + MatrixClientPeg.safeGet().setAccountData("m.identity_server", { base_url: fullUrl, }); this.setState({ @@ -181,7 +181,7 @@ export default class SetIdServer extends React.Component { let save = true; // Double check that the identity server even has terms of service. - const hasTerms = await doesIdentityServerHaveTerms(MatrixClientPeg.get(), fullUrl); + const hasTerms = await doesIdentityServerHaveTerms(MatrixClientPeg.safeGet(), fullUrl); if (!hasTerms) { const [confirmed] = await this.showNoTermsWarning(fullUrl); save = !!confirmed; @@ -217,7 +217,7 @@ export default class SetIdServer extends React.Component { busy: false, checking: false, error: errStr ?? undefined, - currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(), + currentClientIdServer: MatrixClientPeg.safeGet().getIdentityServerUrl(), }); }; @@ -272,7 +272,7 @@ export default class SetIdServer extends React.Component { let currentServerReachable = true; try { threepids = await timeout( - getThreepidsWithBindStatus(MatrixClientPeg.get()), + getThreepidsWithBindStatus(MatrixClientPeg.safeGet()), Promise.reject(new Error("Timeout attempting to reach identity server")), REACHABILITY_TIMEOUT, ); @@ -362,7 +362,7 @@ export default class SetIdServer extends React.Component { private disconnectIdServer = (): void => { // Account data change will update localstorage, client, etc through dispatcher - MatrixClientPeg.get().setAccountData("m.identity_server", { + MatrixClientPeg.safeGet().setAccountData("m.identity_server", { base_url: null, // clear }); @@ -376,7 +376,7 @@ export default class SetIdServer extends React.Component { this.setState({ busy: false, error: undefined, - currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(), + currentClientIdServer: MatrixClientPeg.safeGet().getIdentityServerUrl(), idServer: newFieldVal, }); }; diff --git a/src/components/views/settings/account/EmailAddresses.tsx b/src/components/views/settings/account/EmailAddresses.tsx index f698b0b331a..3d7fe2380e7 100644 --- a/src/components/views/settings/account/EmailAddresses.tsx +++ b/src/components/views/settings/account/EmailAddresses.tsx @@ -77,7 +77,7 @@ export class ExistingEmailAddress extends React.Component { return this.props.onRemoved(this.props.email); @@ -181,7 +181,7 @@ export default class EmailAddresses extends React.Component { return; } - const task = new AddThreepid(MatrixClientPeg.get()); + const task = new AddThreepid(MatrixClientPeg.safeGet()); this.setState({ verifying: true, continueDisabled: true, addTask: task }); task.addEmailAddress(email) diff --git a/src/components/views/settings/account/PhoneNumbers.tsx b/src/components/views/settings/account/PhoneNumbers.tsx index ece9549d949..f7a4109ae70 100644 --- a/src/components/views/settings/account/PhoneNumbers.tsx +++ b/src/components/views/settings/account/PhoneNumbers.tsx @@ -72,7 +72,7 @@ export class ExistingPhoneNumber extends React.Component { return this.props.onRemoved(this.props.msisdn); @@ -182,7 +182,7 @@ export default class PhoneNumbers extends React.Component { const phoneNumber = this.state.newPhoneNumber; const phoneCountry = this.state.phoneCountry; - const task = new AddThreepid(MatrixClientPeg.get()); + const task = new AddThreepid(MatrixClientPeg.safeGet()); this.setState({ verifying: true, continueDisabled: true, addTask: task }); task.addMsisdn(phoneCountry, phoneNumber) diff --git a/src/components/views/settings/discovery/EmailAddresses.tsx b/src/components/views/settings/discovery/EmailAddresses.tsx index 1d1cac2d67a..01b6480d738 100644 --- a/src/components/views/settings/discovery/EmailAddresses.tsx +++ b/src/components/views/settings/discovery/EmailAddresses.tsx @@ -78,7 +78,7 @@ export class EmailAddress extends React.Component { - if (!(await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind())) { + if (!(await MatrixClientPeg.safeGet().doesServerSupportSeparateAddAndBind())) { return this.changeBindingTangledAddBind({ bind, label, errorTitle }); } @@ -86,7 +86,7 @@ export class EmailAddress extends React.Component { const { medium, address } = this.props.email; - const task = new AddThreepid(MatrixClientPeg.get()); + const task = new AddThreepid(MatrixClientPeg.safeGet()); this.setState({ verifying: true, continueDisabled: true, @@ -125,7 +125,7 @@ export class EmailAddress extends React.Component { - if (!(await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind())) { + if (!(await MatrixClientPeg.safeGet().doesServerSupportSeparateAddAndBind())) { return this.changeBindingTangledAddBind({ bind, label, errorTitle }); } @@ -82,7 +82,7 @@ export class PhoneNumber extends React.Component { const { medium, address } = this.props.msisdn; - const task = new AddThreepid(MatrixClientPeg.get()); + const task = new AddThreepid(MatrixClientPeg.safeGet()); this.setState({ verifying: true, continueDisabled: true, @@ -126,7 +126,7 @@ export class PhoneNumber extends React.Component = ({ space, onFinished }) => { {_t("Share invite link")}
{copiedText}
- {space.canInvite(MatrixClientPeg.get()?.getSafeUserId()) && shouldShowComponent(UIComponent.InviteUsers) ? ( + {space.canInvite(MatrixClientPeg.safeGet().getSafeUserId()) && + shouldShowComponent(UIComponent.InviteUsers) ? ( { diff --git a/src/components/views/voip/LegacyCallView.tsx b/src/components/views/voip/LegacyCallView.tsx index aa0e24e7352..4d90539708c 100644 --- a/src/components/views/voip/LegacyCallView.tsx +++ b/src/components/views/voip/LegacyCallView.tsx @@ -432,7 +432,7 @@ export default class LegacyCallView extends React.Component { const { isLocalOnHold, isRemoteOnHold, sidebarShown, primaryFeed, secondaryFeed, sidebarFeeds } = this.state; const callRoomId = LegacyCallHandler.instance.roomIdForCall(call); - const callRoom = (callRoomId ? MatrixClientPeg.get().getRoom(callRoomId) : undefined) ?? undefined; + const callRoom = (callRoomId ? MatrixClientPeg.safeGet().getRoom(callRoomId) : undefined) ?? undefined; const avatarSize = pipMode ? 76 : 160; const transfereeCall = LegacyCallHandler.instance.getTransfereeForCallId(call.callId); const isOnHold = isLocalOnHold || isRemoteOnHold; @@ -452,7 +452,7 @@ export default class LegacyCallView extends React.Component { let holdTransferContent: React.ReactNode; if (transfereeCall) { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const callRoomId = LegacyCallHandler.instance.roomIdForCall(call); const transferTargetRoom = callRoomId ? cli.getRoom(callRoomId) : null; const transferTargetName = transferTargetRoom ? transferTargetRoom.name : _t("unknown person"); @@ -575,7 +575,7 @@ export default class LegacyCallView extends React.Component { const { call, secondaryCall, pipMode, showApps, onMouseDownOnHeader } = this.props; const { sidebarShown, sidebarFeeds } = this.state; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const callRoomId = LegacyCallHandler.instance.roomIdForCall(call); const secondaryCallRoomId = LegacyCallHandler.instance.roomIdForCall(secondaryCall); const callRoom = callRoomId ? client.getRoom(callRoomId) : null; diff --git a/src/components/views/voip/VideoFeed.tsx b/src/components/views/voip/VideoFeed.tsx index 87c0eeb7f0c..9c4d25190e6 100644 --- a/src/components/views/voip/VideoFeed.tsx +++ b/src/components/views/voip/VideoFeed.tsx @@ -200,7 +200,7 @@ export default class VideoFeed extends React.PureComponent { let content; if (this.state.videoMuted) { const callRoomId = LegacyCallHandler.instance.roomIdForCall(this.props.call); - const callRoom = (callRoomId ? MatrixClientPeg.get().getRoom(callRoomId) : undefined) ?? undefined; + const callRoom = (callRoomId ? MatrixClientPeg.safeGet().getRoom(callRoomId) : undefined) ?? undefined; let avatarSize; if (pipMode && primary) avatarSize = 76; diff --git a/test/components/views/messages/EncryptionEvent-test.tsx b/test/components/views/messages/EncryptionEvent-test.tsx index ebf084cdbc9..75d11bdc9ef 100644 --- a/test/components/views/messages/EncryptionEvent-test.tsx +++ b/test/components/views/messages/EncryptionEvent-test.tsx @@ -49,6 +49,7 @@ describe("EncryptionEvent", () => { jest.clearAllMocks(); client = createTestClient(); jest.spyOn(MatrixClientPeg, "get").mockReturnValue(client); + jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client); event = mkMessage({ event: true, room: roomId, diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index 95c0fff74c4..66e75e4ab58 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -166,6 +166,7 @@ beforeEach(() => { } as unknown as MatrixClient); jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); + jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(mockClient); }); describe("", () => { diff --git a/test/components/views/rooms/RoomPreviewBar-test.tsx b/test/components/views/rooms/RoomPreviewBar-test.tsx index 0638ed31b6e..e39c5fc6f67 100644 --- a/test/components/views/rooms/RoomPreviewBar-test.tsx +++ b/test/components/views/rooms/RoomPreviewBar-test.tsx @@ -18,7 +18,7 @@ import React, { ComponentProps } from "react"; import { render, fireEvent, RenderResult, waitFor } from "@testing-library/react"; import { Room, RoomMember, MatrixError, IContent } from "matrix-js-sdk/src/matrix"; -import { stubClient } from "../../../test-utils"; +import { withClientContextRenderOptions, stubClient } from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import DMRoomMap from "../../../../src/utils/DMRoomMap"; import RoomPreviewBar from "../../../../src/components/views/rooms/RoomPreviewBar"; @@ -77,7 +77,10 @@ describe("", () => { roomId, room: createRoom(roomId, userId), }; - return render(); + return render( + , + withClientContextRenderOptions(MatrixClientPeg.safeGet()), + ); }; const isSpinnerRendered = (wrapper: RenderResult) => !!wrapper.container.querySelector(".mx_Spinner"); @@ -92,7 +95,10 @@ describe("", () => { beforeEach(() => { stubClient(); + MatrixClientPeg.get().getUserId = jest.fn().mockReturnValue(userId); + MatrixClientPeg.get().getSafeUserId = jest.fn().mockReturnValue(userId); MatrixClientPeg.safeGet().getUserId = jest.fn().mockReturnValue(userId); + MatrixClientPeg.safeGet().getSafeUserId = jest.fn().mockReturnValue(userId); }); afterEach(() => { diff --git a/test/components/views/spaces/SpacePanel-test.tsx b/test/components/views/spaces/SpacePanel-test.tsx index fc798abbb29..e474c3f0556 100644 --- a/test/components/views/spaces/SpacePanel-test.tsx +++ b/test/components/views/spaces/SpacePanel-test.tsx @@ -127,6 +127,7 @@ describe("", () => { beforeAll(() => { jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); + jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(mockClient); }); beforeEach(() => { From d381fa3f2c7572d8a28814955e8f74efd4b13e3e Mon Sep 17 00:00:00 2001 From: JMoVS Date: Thu, 15 Jun 2023 09:35:33 +0200 Subject: [PATCH 14/68] Reduce and normalise volume of notification sounds to LUFS 20 at TP of -9 (#9143) The values are the same values that Signal Desktop uses for their notification sounds and currently the sounds are 5-10 LUFS too loud, which makes them incredibly loud in many instances (especially ring.mp3) Co-authored-by: Janne Mareike Koschinski --- res/media/callend.mp3 | Bin 12971 -> 17373 bytes res/media/callend.ogg | Bin 13932 -> 25434 bytes res/media/message.mp3 | Bin 12627 -> 9836 bytes res/media/message.ogg | Bin 23440 -> 13455 bytes res/media/ring.mp3 | Bin 36119 -> 31089 bytes res/media/ring.ogg | Bin 66715 -> 26077 bytes res/media/ringback.mp3 | Bin 18398 -> 24676 bytes res/media/ringback.ogg | Bin 8352 -> 22236 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/res/media/callend.mp3 b/res/media/callend.mp3 index 50c34e56401d25f33db5fdf41c1fb1d223ff6cdd..ae7286fb1dca127c89f3aa9b6bd3b276e072bbc4 100644 GIT binary patch literal 17373 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 LM6ir(Ko0y5xc4|N diff --git a/res/media/message.mp3 b/res/media/message.mp3 index b87eeda7c2d720ea6d59bd1743642fba0cced0d2..0e2f3207a73add3af1322c97cb9c722ec8938ae2 100644 GIT binary patch literal 9836 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 literal 12627 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 RyzU1B0G`*Y@c$<=^j{Ryx)=Ze diff --git a/res/media/message.ogg b/res/media/message.ogg index adc74437d023de4da296d17ab83985a9a21a8f3d..23e5a89f1ca8bab1f16104d3532fb7bdd8ab6085 100644 GIT binary patch literal 13455 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 B=9~Zk diff --git a/res/media/ring.mp3 b/res/media/ring.mp3 index 36200cd89d5969b57cf75965e7b7701a7c001e14..9b08ec7b36683985fd25121675b92e4f555a294f 100644 GIT binary patch literal 31089 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 diff --git a/res/media/ring.ogg b/res/media/ring.ogg index 708213bfaca2f520c2c3b10bc6b7a53574a909ba..41a22160b007422d08b277c1c21ccc8224f442a0 100644 GIT binary patch literal 26077 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 S3*TPO>aN|npu__j2mK%Q&J-2^ literal 66715 zcmb?@c|4Tg|LB<+jNOneTSF8=_N_3aLP97Knh>%?$Qm9?sDuj15*3v~C}l}T*$N@q zk}X0hwAh!q=Na|+e!jnZU-$lV->-4z?8|#U@8=n&E-n@T3;w`28*m)W4kauyVni_h zUOo<|e9;PwegXOgfP6lV_FaxKMMwSv=tvAKHOG~PaavIS8@g~CGs(gZ)~CE(`=z8L;^U2d^U0hE3Nue`x!N0vGdrbBBnrp!^zNej> zPkEfg&~Hq+LvPO!3}A$P6pV@WNPE&@00;n(#iiJ+va%s?vuOj zr{79RFtQz`2M}Q-1JU!`=4j#&CmO>KlUO02CeeGDKFrehb0V1)n}|`yieGZCnP`5w zey~?~IHUx{f#&RWsO~^L?u^k@+WKqMd^X_oBzJ*@>XXh)y3TbvMli zPPK6?B0%6V!Tu}Uh9%t1B|@zo;-)`k4sBBDP}*x@dGIiVvvyYgCwu(Q_V|Y$O*?V_~XC7qz51%cD@-(uXuy0H@M z5O-^gnBJcLt5t(N{c}z}cSdLJ175QlyGzxmW-8>3uDfQ+?ea?;cbl_Jjin10;T8AwUTlsq)y?(a;*?GI(vqL?hLp_&|^Tqs6 z#`;Hc02G>NnoK4;#49vrMw#d_Z4mgcB(cyLXSTEBQP6cGt_hTUy_50h?qvgsKHgb z|42@bWWp!)gsp~G#gAUy7L@98AwB=b%`f@4ng566xL&-McJW@^#fi8C$qT8cE~G!G zxb5+#XsYu6`T0k3_WOv#50sp}KH~q99EyU_9w<$vG6#QcoKdR-BGhj$-@gd}040g_ z08Nh^GMDW#SLrgBJ#4A^ z%w0Dal9#5N;4^!}!AL{0?A}|2ytpg2$97y1t2@E2dDukizV1Y~A)_oFsy#Yz2#T9t zoI)MHL%ePiQ9oXRadUK>0)uE&yDp=|`D8^#(Uc_fI(}GuO^i;~Gu)h|G&2sS8Ah&#PCWRl3` zHZshQN-~$s%QG`_lgi__Fp~8rm^0_WhEq~y+{cV$8!jp^-taY7oXCwfw}B0*8r$>W z2>h+&tLE-`x!thAuifwqG1=VwRDSY%8%c>W*dUS8ZR4(Rx7&f0HdSHndj~hEr`sSzi+)ylBc>T`4#M?&aiLbgFl{l6krBYz*_t%#035 z7#YH;Yz*_u-rE!wmhtamlsb>@>Pf22MTIMi>wrb>YJGx0Vytj#$eJbQwpb|hkIWOE*8ElYkfGDBuN*0bVF02Tca9a6YDh4(MS4e{`}#i^c>I(jYp?JYHi$DB9foBEkHk!bE12nUPV7xv|1HTIZH; zZlusatIIzbkJb_3%w^E@XGRh9q@>Ea;aESoT3%kSCL9Zwg!s0B^TB?3dEE|Ew0;tK z?` z1?z0wp%;T^hd$K4cEf!sdwX49p2z3VSe6@Niq(NtAtuw8pvZW|msJlh>H`@e*U4P( zlxUu@k%2@muLk5Zb496Jd1mI@ByufnqyYE^(ff^}C#mct0g~|0dgXFMrzR#DqM<=( zj}gSOV?5+(ST~Vu4v(DX3lkw|FQDH8u_xr%0@3B00C5598qtP{Ovept!UTY{H#EM* zhn##PujD}S#DaWiB@~QkcojI$4|_A_DH|w^!%Z9IZxoZwH;PgcXsals4%(^*#SSe( zq#0$Q#aqeAdW_IaK+K(o2+vElw2{t(ogu1@Y~aJ0k{A zpe*Y$h&rO4Q8Y~8KKapo%HLw;7AWNc2GA>Gh3CgZ>7wzLk)agI6)2~mtcDW*PthIj z1ugRLqP4m!#B3BDS<#KVD|Fk?THRa9{uJGfpv1Wss!Sw9(eAeKeM=9B%IW|S?k(1Q zY1m;+z9qT?gX{*tK#JyALg_-0j#A^VpQ&*7pWJ`yXu0t}4J{~f;x)#F(6O1mCXoFj zaDaqd7R%TaPsf9vkr9$Q{sfKvlZ@^p(-53;L?ACSiVNjvsGubBAm__IZPAdDN`Xg_ z_2+^`$fN}&P;l?yc}%3wv+QAtMbg`fEyi!~-b=t@eJ^*Y3>^w@;3=RbiHl+D|^w`#;e4X@BG z;M=OQ@bLhgIo-WT1)uFpxSQAOc3EX2GlZbI_}=Dn#kh!HyY7UDpHU&Sf^W6(qUUBS z%BuvBt!s-~-aWYd>8{Do z+Z@>JcFv3|qSwt8u5Hpyd~bFsg&cdtJBkmX88mpyA;dw9_$CmNyB)q)SQ=@#l}sdy zMrJ<5~z8;_8VCw*(}m@8Xbp`j~T| zslC^rsvMES*|yi);q@5x2$9x8Bn*?f`bf<7!P#iR%p42Lujo4Hy?Pj8p$~}NzHwu8 za&{3gac&lsQqa&fJOtG-1R_|3O7I90=juGJuDM&Aq^G~nz{uFd%-r&y5CeiBw7w*m zBFIJ%fg(+-0%#Bc?I@RUu0_;tAJt6D$fSmvL>;A`rj}{x=xG_6R8xcXN&9y1IdHJj z?`JUn>ivNyt1H`AS8nxJKMve=lh-R;s_R?NdcdKDhN6!(!X+{q^Ha}LV-tqHRjtlX zQ|>c?#_UE@*WZgDTRhu>j#=M&_Qkc&uP;AD_0WLbk@K-rO+Ukx?&(o-#rj!G3$|!R zKYTGGaqrvh8LbKS@3#Ka6&5Vk1%f-a=vM5zx=Ra3d3f-Ck{L!J4PX(~rdxp`(Yl)$ zC}co+1sK2>Fu&e%^3f4(cdhEE@KrN02jz7P*tc(AjHuG4hGML>rzZw1CfySPB5#Mn z%LXvusK0yO1k-NN5R`)ji^94M_A9W z5XIg6u|m>S$brMf9PHs}1*M)Ax0y_ci;D}m@8oj3X5N?T37Vt8Re2R^Q(&{2u-%A9&mR zZ*R~m_P2UA?r#nNfxlUj2Q6pw#Cqw}N152EPqv;9PL{Pm27kUQEiz~`&o@r4&lm(Z@`}dEm9|J-G`*u3@)+I5J zZmD_W(iy93h@2+`iII<9>#Boj2DM7n=N{vdil$%b1?+d$td_-uy%z0oS>jGD<@vhH zm#p*cI12~|NT<@luU&!wvHFzw2}AKJU*zv))yG0lAvzJuLfi&p7dTLvI>y>`@5o7(l_RS| zsTlBjGil`KmRUOS?aynlAd(e;L9ZS;5NW%H1$C?_bL3!*4nHf_nb0khNfG3@`%*F~ zWB7DQuy6moe^m~uISIy%o!-4GAmOy{A+2-g zRN{U$C+gnIzng#Ro@;h^-AKr(;8XjbZ2fTNBlqI+*$7@)8{H0{_ibTAKR6H$fC%RJ zQ>$IlA&VeX6}92tlS^tk1wyMeSysImK*rgggNGgFP#C%-NW2e#N0?W(HE}QG-Cj@( z*_a|*`u#^JPzMw(xGlbRm>`Q1vd%}(PMhw0_{Jk-{9xI=p}aR*&0|!JeVgb${oHo| zA%Q`Omld2cMi)$MJa2buezt$Qcs1|y0P^H*TTXXbiruOQ_1P;~!H&Mod8~u-yVq@F zVN4O+G8KPHeIS}JQ+{}9bS4ipo#vQxE(K?8iGCD`C%qA z6hB{mzlKp)yKuldlw%8mWTCOY2J2h4SICG4=?LWQ2uZ) z(LE#VJRY*0^sdj6h=`YThU!RO8yI3(xs$$12_O#7=Q~vb^Tn z%vY~a`Optou)1uL<#T24uaJC>tkdgTv{{0(^dInkJn&-A!4Ac_`t<`XeUb{_2k)h2 z|HzxQ5VzjRVz{&vo_~#Dus8R-6ieVS(3qInIN6evqj##gO6pALyS3`+tq+4_uk2SV z*mf!WHG5ZF=-3%+^=YpCf%99p6nA`nu6aSBLWMgjroHIf{aP`vj{)bN#Voh(ov@00 zS${ged#9%O?w#xD*0Sbu$sf(|zvO*7zvC6-mYwWI;uxSJYIFE4Fe!%Zuwb20xa4Sy}1rkbAydC|}{Qjjt<&xSKIMFeZh%osPAEr@gwiqyAI9 z%;()0NzLlR^s%+q=U@1`y~n&fTJ-zJaTA@Ww|RJfdVuH-rt`kNB)O?Y>9F=@@~C?K zi5QylT)!(GMq0=KtUZAwCNXv_^v;0xEGq<>`d{EJq{mp+;CuEQL2v!%5R9m+_@|q=sq$Ql?S9~CeN3Xs(#EB zOUm*&x_Za7%qN(tb&u8W)U@*(Gq@TJz-`9nOA5q`^Fy1!YUdCqn5l*e5`-zF$51v` zGtrz8v+IUAQ-A!3ygN5{$MUnGl!MTc(Xt~j&n*vXS>o!;rDN@f2N`HhuIX}dL9`*W zVGkN=gZ$m5yq4<%UA+>m%Cn@7$A*8H!H^h5>#V3XZy*z&ZP&$s7O_Sr8y=zFnWCBU zugX`p$b4gHjM0K#!gp4!IR-6gRmw!JG)49wsKv=!1EFmmfI&4U5{Is$4wA(N#LPr@ z`ajxCQCH>F_L;FRCYOkKEWCYh;U)iC3z!M8;eaTKz=%Xfu-qqLurJB482F3Anzz=> za4zDK#lACWp5QI0>e;@>V5QHxw|;+Y*f9KUSCIi=$kO>ysJ%xs&ckhsF1>)!*9cT1G}{{zjoc^Lnl=)g&INS-MeW zU-Lx?&(SwDG(??Kw9QJQBVVnN0ZEydI3u;B%-NG$0HY!RfmA#oCH)Fy01h$G4?tdG zL_lCmf6T~Q+p`BJDnsbGHyMY|H=W^!Yi|I;N}7E0;BN`;O+E#hULtg<{{sk|8Aw9c z>>q%hBMSceSk=Co-};y(`n702Tw-a40}LnaTpOBQ8_Blc^5VNr=+9^Kzve1f`a?4M zr#-bPvEdYvu)rz1%rUEDVQ)@~c!Xccrc``(ng8N4@m9z?8`vLVgY!}XgrIE?9UfIv zrL1|v`x)pQWJOv{t_r>QzyUxK1M1-{Q3gR2JJA5MzdL+wC=8w!-lX6-=(2TuS`R(> z*?fHUv8NneT18o-VhEW!tNlJaQTjbs@1^}f|bztQ?fcF_Xdy+_qXAL7Or zEk*v-1*ls5({lfx9CYw#`GKT)McctQ&&!oRu7o~wx#k;r0#_$C<#T>MY}PVga60do zey~K@*@9kgj#O%bzTLPpya5fbS2IT6U|4KteRgJ8Cv6{z2pE|WU7(8xV%``sI&to{ z0fxI4yp^BRLc^=$Zus5uyXReyf#aPTZx4vR8H|f!0rDP)^s+dQGRxBe{7Z12fV{^h zA(t?W@X`Rs3ykVl`Sl%0V_7O{RA9fjtDIrJH1@J%eJYD;ck_`R$4RD}7!daTgo49M zj$y#h%1Fn6(aYf}2a|vVi3ITpXUW_)1rWaZs#reFXWyBs?M!-SUzleP72e*?y007$ z4pYU{8JR{@JzQD0(e)bF?gWLZ2IK^C6>OtM zLZ@e;FOyXt_%U-R6c0sf+!Awq*Pa_Lu;qGIIj8B(S6`{V&Q|Yrm~*Jq%cLqUFpov{ z_S7E-IN%vbfc#Q5#~un0S9l%SjlqHY=zX67q~9HrD8{y4?^x-pYitt;+U;!?ELHgX z)Q9?3j-7ebhV`|i?H`}dD*JT&x^m?N|FQ~J9C8c7Hs%ODV5my3ZhC4CH34q+wKhUj z(}qtOUP1Fn68nfnR@Szp3HR(hx zuy*{Rm0Wm{0Dtbw@&ghUUhFd=h=i&-BfUK0S&xy&e6bCRv%^`UN)^>52%2mFvY|ob zQKhy48#hbJ2Rh)nX`_aSlOIU>mo^|?``Z_O8~>{T7N7n>Hhenr57_^cgG=)fnl`is zt@7_J_4%JJ#i#mi@|`nj-jcoJ-qgy6D;!C+lZl+UBAs-Z%^C9kJ&l@uPO(wvLsXw% zNtx$4tM%saYb7u{8>}C6<-InZy_S@72yEUBb>AI8CgEA-837NVpzFctL(buR_2Mr@ ze2uofs@iy#DoTlv>Njlj;2~YRWoJl~VYXl53)}5y>9+w2AXn7_(lC}Gf?X33NE__* zWZQl`!s0x&EOO?W;Jj~fadwnr?aPSEt^J33EWC>A=t22!#n?6ftv9r*U_H2Xc_l-~?mc$Rrc4S#uQ z`M#0b2r`Kvq|N`D!96Du18G8%C!;t@7W=Gg+vO>jV5fxaT*RHmH>URYBHurD{8kp^ z|A+%8eTe{QgD5;3f{})6V{74{hVNAGlTR0MyFkIZnqL zBUv*c#$`b-aA4T>`P{2E9sBk6jE6hzHMJJLGY{yxwugE?a=)X-#ei+5EUu4d=184e z_k~g{q%+D<{8J84rpl&jNs!dZnex|R$d{d{WzRKyp<`7Y7?5#*IMTK=xbSywCEcMp z(Xg8R!vzArGW!Mw?5J(A791Ha9VPpVhVH~P%UR8xshpr!q9;G|%Q*gwyt8P3$(ZhO zYWWEiNs!NHk=5XXpEe2ryf>J_Bh}iar(b9WL+uFh>7mz3G$0TGDIVelxhV- z9n>Cz*+F=%g7CJ&(HjJMp;>?bCoxNPH{u8LZLMlHH|iktm2GW1cW>G36H-1|#mD|= zE^L=w{g1+DMT3<;gn~n<%w2n?-d110t+pq&HJ!)Y3mP5&+5B*)O!p%}3eK!Ld@9og zP%eJng*mszsU~zZc&et+R8W8(Olw+XLj*k7j2jkzUHjVZ00Xe$jj*02g7MEo&z#6O z(k#?+G7eBVFpg>S|GK;t6rB5vl>i`3w#^rw#o^`jTk1#&GiPSC+6mtuZ@8+(Gj{I{ zns$40LNGkaGjO)?6Zhi#D`*4RriqVRT>Y-ep zKC|>4htKBb;e!Us6fY!RFrPWu9q|q%JvUV&DcQ!Xo_U{Kb*`8fD{eV^h$$d%&yl@{ z=x#T$6h1Eq*el%X#<~296G>m}aC+JFa1R|*#cp{+ESRl%H~VW-;9_o2#7;tu%_)Z_3jl^OUXs6w*KiFaxT#(KMrNw@v=-{YS!<1E_8DYeH82l`>O5tp)@x%YxSerw2cd z)+%!Waym^VUKQni45NJsJ4SBD+-@x?Ww|s*{>5|l{T|=Pe31UHY&XU5Sk^}}G6J&q zb_2l7Zh}FO2s(^(8kPd719HViSu=;v8$J5r_blCPv!-WbfApj7&cRDn~=cdLlBVziXIMdZzI$%N?uG$uS?whP8YH4V5==VoteoQ?_+t-A_4Q<(#JaTIsFWQ2J16x#;)M^ULX*H;q4oAyqIN zQ#*9MiW50?^MTw?AwUYry39z_cpgd0p&_uIb;6 ziHeoiw;XDC`0DZ0hv(HRm%|nfsa2M!l~VFx?Pb*hPp~ zN`a*O3#r@iSO4b!eCw0{yMi`U1V3i!25e6~d}`Sd?0-)u{J=6^sbV>A-VXX#qw`aR zkB?n2t!jM3f1;F1!5c?aRR8{bq9<#m!>8qJY^dL6Srd+`>5mC2Q|qlWYd2wV)*F-i z<9DHJeJMtR%iwBtP-!?9v>;8;Zd7Emu=_-QjT%QRb3~YaAd)(qqtVljQ_!p?CWaglWe5 zfss$be=EdEfALb>7ydU3hc%;5=>9htHh0?4iD(P*FTV-ebl~{6`?|_hz&qairrWEn zqwam*Z;v%Ha$|n3tCDLSRiN)gaEMRZ#pNpooi;=k4oOEMhyc3U5EzLtL7?pgBp5N9 zY_y6-C5pt5g6skm;Z??2?4)TYAa;G!&D~@EL8T#NQ{5(h0WsuVuk=xD|*wPQi{{Z-X-9~~Er`xvetnb+-mtew2s zSFKDaE#-5j+0e<82`0byQkOI7N9L~MJ$(#bU+8&XrZ%k;oK<^}FzlFiohYW#&Nr<3BAVZB;@*9AgfDfSqN#dZK zau67J^WiDb5d^O=?0}++^=_%h72N6etpUD52&gCE5Da|3mN91!9S__Od{^$L5j_EeK}_%<2?zYZBq0tUSJ*Kg z;7|BdFBXo5I{_9y?_$9pV+y@ftzS6yvGk~YI@_I)n?gO0=eOR~SP*=8QU^J?wMIK@>1no0-{Vlcy0?07RV{O| z2D!wBy+Erss>@Bw!&&Of^bK&Kd z*9*6F-&0(md{CQSl=B*F)eT-Qp7UTPr#FlRf@>7N?Ycoz-yVGa{hEKWIMMvktpI3h z7odX+fm{oUJz_n7?0e@j`uw6O!vAAHbri}H+wf_Ba%>fKWHwM_6$5&UqgV(KA;c31 zGlM>mm%x#*+mUnUo}=hMuC+3Q47SZlm>aB3h<^HM1M=B2z59TSIY`1VVDuS?P+<%^ zpal65C`1=Usow;O%UOzHn9*B-)mK*tcN$JZoc?5u<&rsLN1%LS0{mEJ6u;hY?Qv-2 zfCvxg(OEY;8L|w;FZ4 zyQhl=s5Te|A}Mp;MgNm?ioQ*?ZQ^V4FERFjP8jyG!7;F^3VQ1eCRrJfH3yPFnu%cs z8xbQ<-^oUd;6a_$yW;PdgOq3%q8FZtJ4p?)8c}5j)Gm|23=?gl!H)q1j4QTX6c%9) zY(=awdjKgGFd~f7!tJ84N*|>2ASXaMQ$kfa3+WRUcC-WrTp#I~CJ7hFbP%_7KG6Rp zT-YOqVvcmu03fVTpSNRpKpaA60PD~{2#6?}a)FORP*~8g3W7jwZH89T0D+@v8SJ1^ zhS605YrAg3B)o8!+}d)YLD_TpMFx-i!aG&F{i*EEYHRxY$hdDGyq$IAJKGMZIwvX| zU4KSBu%o%O+e<&=VQhNgd_+d^SH*?^qeFtyES1wqm(otu&l*<^rJCNe|6u!OF>9V- zNINa}f_9w9TK!Reyq5(t-vxyx+@lMF7JmBx`BltcRZrWM$jYl69X?Aa*+IoHT1O-u zN8jeytU_ii2V=8zVxr}v(0+5*)06&+mFT?}ujvV+3 zW+d}1Y@9yo&5=OPb8I@i+-lsy0#~j$gb@H96475eN0II`qD(Icj@(1^i#*Usrsy>i z-cs6v1UXj`KxADohI|b`1OQBaj5!t?5g1Y)Rue7(cp#minE}IS7kG_f52KZ0v9{Mv}gKc@=KK4}dy7)EV}BHzZuBHZ0yxbM=)mn8kpScX^GbFCPLA?v&KZwL0mS$NjHEmSLS9YV zow$OGwq1?~?V^^9!a!PIY!cywVO@QIJ|!`r6BfI#i~)7bKv;}u8HYeR!n+|9G6Epr zBk0OukcZ5~&Pc4a1-5*k04EIzM)77Xhdcvi4zCVb0XjoXqp%l%H0Pvko1hy@0x2;6 zCz#d_1p=kcKY;wraRc;{h(?k?w?+T-G#+z*cmxyn>G5tIo%N#Z14E_9PWDWk^>4Dz z{}z5S;HFnz(X;AT_OFyyXYo4ZM_TjiLma&;RBB_%O4uS}Z%}=#&WEz**pgquTS{2` zgXVtR8Q{?dTSi6$wyZNEdrFo1DQEr92G*{zOjoqVuw;bD6<0i~pV#QW6oT|mCthaj z#RApdkH>o>3kzFWhKt?#+!J|J0iSgV)`R`XH0DyhL;ahGkYak7BdS7Rx`NVhk%uzw z9nJy@1k2$e_J2s85qd~GjrIR znG-+>0Qf;w968<;^t#uM0X7&=hwmD?ogT{28= zTRVNJzVtO_$Nl|^T$}8HfzG zs{=XYDb5zE7dmKO3BaSh*uhP#P!G4t^Yh)znR$GZ(;Jwy2v_|uj2b3$9PQ%AOtVJr5*>`(Y@z*#=$EDNUpWUA@qYr)Q;m` zYoR0GSJzZ`TDxmF{$xKE+VEy`jd0C)d#ia3?+)@#fb?BH9UA+#w~bpe&2~TNh+r=f zF;Lv+&d9C*kOB_`9}qBK#{f0fh2>`l^Bh91R_T0OTYNJu8~CFo)FOzaz@(eRZw0j6eKopQIbY(XWr=VZ>=;rzK}Gfwt^;9;7zr@^FL# ziN>ElQ=dF`e^FtqxPHqsd~y-*O_>WG7|0q4-aN1aJVgQYhWT$UEijWz2G7knLZCTc zsW+Yl>e*W!@ql5+Tukrk>?1fAh5iKqE-X;*46hsk-|}&*+)xDI^&I(;EDmv?-rIcQ zx)v{rjGO4vl=10lpKT{XL#vkVF5Nvz4`#azPC?ZfM}SERCR+E^-$#Yy0Qf-o50Hkp zezN{sC?3rp_|J0%a{QgY4S@84hILwb_dm4&WTZ&_tMpIS*z}()087#Ti0Z#;0iwSR z)deV!_(?v0^N?d9AR=jo-SVTg4@1~%hK5eIrR=aOVujC&f3*}CuTj2Imr{80k|mvu zq^|6=FiPD~%u?{}o+_{Z84d{mUTYD~0FHKR1P8f}gyE+*D+Y`pGKa#jL89b!4=XcW zwf!|b^Ro#Kb-!eT)=#g_JK~EL)Vu~?!~|!Tt+bIxSV1p6c*vMFRw*bYW{`k`OhXa7 zerJv@dB;|0=8mjQlDug+p<~XnUU?}0S+dmNXKqYD(Zr0(#WTt$z7H`p90QA1O7g*g z%mEDS%2vN)0demW1$h5{ya^sr7zdEMBseY9uP_4g;jXw;iCoM{+jW25?DfKdAHtWm znA$Iw3xu?3nf|OvWFHN$id`OU-8A@P-?m3zsIs2e*C?uvWjQhfk`Kc$J@q%8xFJOZ zT4D4FlL$h*E-Z~l#F@Z3lmH0BOHtqZ4;XE0Tn~nJ`-h#EMlkzuf+btfNl-v==Y1SD zvS>PNxD)DK)E@i?P$E&NpZ-&^@g5}ae#_V;SH7El(~;qtr}{AqP!+IK^A@c>CuUn6 zTOZ&Me(@~4D3zsu!sGPH;)s>M4tBPPMg2Q${88cULLauM6Y@Qb4hea>T16ciX#()x z*B7XPF7x~oL+`l->Mo&=L(Y(C<^4^OO?53CDx{>y12MHYGk%i&QC1<^Gg)uiwjOIt zv`)R4Jlbw;ksD@=+W9!Wir1>Y$^{sqV_w$|5GI512M0g0!&ha%CA6J!mXHP&9L>0+ zQ&5~m&@U9Az(FAKkM%&Nf60gzAb4ZA6*qFUl!hQAaQA;C2UZ^xBW@rLwIF|i{P)2R zT9Fg^<*U*^Z_mNE6))K0M&gr0V6l(Zf{#MhUAY0u!usqF&p+ zRaRR%d3f^SSss;p%UG9_9lsnqnD{vazG4t|#el2+)HGmM#UUgzfyii(gGDg#n)V#h z7$s@JwkbNm*U}wWx!>0F{^b{m0flU-6-smOGvXmjho$fJ+4*iFjzaL0C*Xzk@^DSb zEWpxC6iXMqCyD_ii@>)%`uE&V8+dgijwAIf$G|s$WCd3%1#UkT*gjSLcw`qS9A|Rt;ivz)T$3)?)wckv`t1f19eOOyhBX17V z|8hrX-u`0ts803GmhAdAEQ~~|F7!f%*@#-E+55vs%Synj=a7Cvuf+sC3`^KMQ0ILU&gYyAHqg>@7EWiUPQ#7=x*8u z&o8L|N1P~{uA%m(}H-7 z*!q&qd-qPyh&%fZw&HTi&ql+qMMJp_^=@R0P?77f0^<7ns#iNCv< z&F?(SJzar{Ww=&qSQMR*2K;P(bbuI1M^is$!HFGz8(`c}07X6u@F%UmeHBuFZImDa zXfV9q;S$CHLo&=_Cw9MmaLIaW!}Mi^eRTRpSLm$mtt{09uHF4rv@bVY=>r3G!M)w1 zn`$cjq{3e=$g!yPbF9~ELHFigm#!SD+~mhOsQtulWvfM_tBhNVXZ&xv#wv}&zLz!X z(|Qum$^|{n@=bFu2M&wW)+|kq6Qy5qjBBMjq^_XUQDM95e(h-yPj!I?&Sk(r&J;_wqBNOlAR#&xlpq>B^90&YsYgWk>(%M;+w}i-j!WQB9TiWwq30 z^wu`z+owSSdtmZwY_~7)?qoA+3qIElEwN1>r^5~d6F+wldtBQEE{KQ^n zJ>TO0+30Sy=ICCb%AnlNFDs32QVaKx8RFFr^|gH%zSQ!Vd0-{#iP!Ypt>->pKC_v} z#o(;oy?U73x)S7<^1EiTEPU-rE$4tV*L!1^Bnii-o2Hzc)Ksaw0t3k2T+@_$dL^Ze z1>{(TcUa)wKA;c!RM z(Tw4V?5(@DPmnzazQnD|t*O;iXp3oRoUco++?g`D?br09rsMZi0e>Tg9+Ou1LS}A2 zoSm|C@BH|WhrRm|A_?ATAh04t^}5v`zbTZj!z_n)>Ti~Wl{v*%u#tKKm@LTXW~@Wm z*F$?|4q97Z9peH79-d(mJJh&8NBfv^KHbKW%}YM|jVL7dhi(!R*`w}dx`fk`W4lV$ zc=|Y0&CE%fCKB)(CzwcndQ9s>`;iZH9sqrIw5bp^n_5S8qJG|`zt70vIrTENhiXbijvP5orApnj5?7Ni z3Gs2+?JG;nSG4WDW$zH8cC+BZK3b$B`G43)K^s^rQ>@F~z@35K|ZkL0%i1 z>nyoZbD`C+fm=KKYhc%mIsb0N&IRt2{Nn15?oL4<&j+-il^{H#N2a%shx_(CesR{q z>&H>*VCB_soGnwgxIR!_r72r?>KKvMa@XNQW4~ph21YU+Pn-XuK6xjgy8w8?E;x-Pnv^)3I zUsntK7TOwj7gdaiU*Y^C=Fp6n?X&>~)$4(t=VSZiPQow-yj&&>x71a}c%b4qFF}tsAUSt|7-cn9S?IN~K^JhPKf8$w4 zgKgu0*K_acgtF|yJC|xl=T5pWoXKYAU^n^NF5_TSGHL5pYA9Scq@k~{howzntgMs6 zOfevrE+Cix>=T9bov|sAjf{-ueIxDbnB36C%c)M@rYDX@^MXBsNJA%&8sRy^&oiHbepYj$LW-OVnVAIq3?Vv#=_^z_EfXKbyw5|8^4YC6AVZIxm zQfmCO=Cj`ZFe)fHUXvMLn16UmLmjV$7pk#Wb!*Y_Db@Yp=Hc(PzARC6LFJHo#%0H? zPL|jAkmDao_-cRJ?qztvZum;H8dcA&rt8bA$18!Ag-Cz4o+qQu8X2?I;GH#R zm{6evwh_`6J|ajJSKH@k?sI!0{&wq*NF9QB<5QIsatQg{8dX>~AYefiP=4sPYw-)m zv+G3>)=hfK_TlIZHYs;SA7ywKv?;#Xff|beimOkT|IjjYK_Fz}*ri32dtaSF%cEjJMT2VU}9g;0po#u=fC+kMtb_S{q zz~ z=b`7>YhI>(H|%Wd`mU-c3FH~qrAtp}bAv+@zjnW{pvFDBX>S+0 zut%+-W^kxlq;l@iz|Xa{b=%^bR;yp$^}V&IGf@k*Xw(6eHO1qzVx5`$r!tQ%?7r(dRL`>y8%9nH`B?)-IrM!b5Gc)hZUo!1W!+6> z7&=8(=n+Id@>=dee>v5qyu$i*G|?H9S0nI|>f2sg@PTtKI6!~okUO{1ruA`~saomx ztDjO71?ft7gta?g-1u}MVB|?rH8iJIP19eN;1gbzklzotB{_%cT9)VlrLV0jwEW#rWEBN2K5J^UFi{81yEWfqvFy>rhbYmcSPdr!i#q5 zj9&J25RxND&pK&^2O${b!mZd=0~|02b3YBhy-iTD<(b26RNw(V3FdpAKq1+3-(w4dWbJJb?vO`f8F1JT4EcM z!PVlDM#w4f2T$kHF^oQ=q4e-2VMw(mZpz+0M-7|bR9u!0ix%YACPj=X!IT)Ne%y&- z0|g4es`;Z_wgHG~?+YmJ-haz~zvV^K|7Owe4RsV|RVT>v`eV7$6<1Tw7mxP+HsO;! zFqZZ~d2hzH71M7w?k&6Re{$-gUHa~qjmIw*Y>Tn|eqwNKa7^;&i{C>BRx-2gzBY3N zQB{W-lB$m%L{-e99ejm%Q7eSHsI!aWs3C~3dHZ$Vg=jmN{dqzFJU2vQx4a}{X|I5* zdlbY{zdWAH()2tN&@|fmiJ% zL8B&irpKm8Zb{kJdwlyVJAQxJzUAUq9jOP~8*Bw%r#%b`S%@epwli0v zd&VynGyeORY@a-qB;2YY$$xzvlrA*ei@A5lZmHUCiZ3Ob6-Fo@yR3*`$d9|AY*Kr@ zH2G^4hd!2}4lrDQ=H=$f%9@)D8)YM{5yws`-6C@>K0LkifAFz4Ce57FdTxIh_Kbz; z9hWOEeCW~np0WO6nv^ZZfHM24sff#ED2aOLuRz7eX&aToIr@hDf>XSW$-?a}ajp9lO+R+%DRGOelu9Cw_bZ)^R zcqhJ#eI|_SXOpp7Hg}D7K=n!Hl7IkLdB=aegD|XJ*m#`HwZRx6Qc6ho378yvv2|iWa@oZh5sr&-&A;nm#Js1^~dy_tj$04 z>!aMAzdFw@^Cld2|DnjPO>uZci%WMKH4Sv9tjxq9K-^Nt-p3dh24VWx22NXrve5}{ zReVCotxSiP_e&Un6LL6bwtzb71@7$=%tb|KP}pUJQ*sb84JiR&4}hgSPg>nwcM}Ul zGF38hC-kV$SF)B)|1N9rvey5E^Y(6HR7eEfxm2l#zxskYeboFb%S84}uy8<|jNHz|u|c-idLTfbn4v2D?2=<4^&|3>*{01du(2~Q?adx0qKH}``U zoLUf9{DhaIaN#%M$h$nyxAN-Mltqme@t zpLj{%96tUvj$2XE5ob@T&rs6z^V@LTH@x9ChciJiDPvHOV8cp}uZphij=|9a9 zM|sj2KmNceC7lo0dBJxk?4SM%FaNz0=#MG?7xP~}8}k{ivU9&5`d?QEomcw*w`KnO zItj~9@}Q<&pUeL~0=qC?g4o9vJoTo`UFm{6UTl%$zFm}S_og(!0<0dU=U9tWuv}Xp zpc_^B3%>JSGdut@_(G;AR8fZ5F1}1{`{B@hIVhW#9ObrTS@M^32Gm=u^K(&wqrYHc zwFu|&e$mwa8NTH9x9$7gxD?ezdnhA=t(MS|wU?utbeI>}fUtn1|D`l#yu}-U!>&>5 zbt&-KNl`m&^OedhO$*?(&`<1$#rQkk*mB>(WIYPa_Zk0}W}(VbX5L1-hM8)-&f3^yCsQ z`BVVxyS2u~7#zU#qg~xBj`FYrPon>_RI-}4fdUi(T(&3AN@kySH`%#zZB5bhYrfK2gVNd_CX5cBNt6crJW&_ul<*@`Hk_ z|7OKRs$)$(cUlLxYb(oiEUrkCPxc_LC|?qFkmk0i=ntR8#pwvws(=s!6lE4r&1|fdZVa2v!dm0I)C8RH4WC6((u5i1 zd7x9hNQ{HHJkeVo8nJBk$gQ**GriVWupi$!d%see$TK%FoQ^`w`C){fG0qUM#mAcIq|xZAz_r zsXvsDY%}M23WQjv+U{fqqqCoL%aVbA`_MEO$uwc#E3oC8^S>VjOM{xNrmr>}Z|fdb zoABRka$opoD>*j>@TFojkjF>qA`r%-`Q^7<~hUdD%1{(SvR{x zaF5_D6*l;0!-_Wbc$;YN9TgH$At2xOSm%EUAClzExT{&1%4MM|tk`Vppt^E=-EVd$ z$Ow0t|B-!W{&S-jvJbq;4)7~8$0T^3dR};5hQFqt`{A#t=V>Z#T^%eE8l+w^_B;Ti zBPf9<1^0A!V~HZNvto33A?%x{jz9~@D>>n*#>m+gf!k0^T!w%VGT~x!-|bjoT`Q@nMz_O+(1icR=gk-x(Br6DN`U3+l{!e+ zaNw`PD=YJL@wnU}Gc@1+y)>h-ken~jklJ79Wp2@^Dh-KOvVOMC#YyQ$iG>64U`$;t zM4Lopuvs~s$xCN4PYVAg^C;(*D_7GkAuT@N@bv_hltz+#g!Ee6j4vWTFNg7rNhmq< z+tMaw2+#R3(w>0h5L1sbKoL3WdZ=)*P(cyAtR@&lAGKGApy|7Sc*2+$R~JB zTg01=-Dvih&~Jf4HWRVw6vcZB+L_Hkwr5Y(Jjngry6tb3sKztk6FCnOLQS|Z1>D`v zEDYnH6j~b}^R(7gB;D&4peonK!Nc_B8?{WaF7Ez1fA+RZ=ayHIUlnY=gGHs;)h4({A+!>$V3BKU`+LRD zw;`HXP&^eCF&ZO^Q}AMM1<(8CPyEWu((NFdmRK0WIE)`^+rGW7^0UKqM6j4m-4|n8 z$jat!-MDP5CY!K!spHJB8Rm6wvOE0&tMD@P&_?vz54?Na!xT?79u@Qx&B~q$!`noi z*EgAWEQ{>yiPXMTO1I`A+VwrZO&-x+jICDd+%NW&oS`X=c+7V?YGR7B6TF?^rBwXA zS3kft_@VPh1UaQAaO;hSujZ0S*iQQEZ79$rfKl~`;0r&LAaXS^1PN;uIEsqNP&4Sr zk>qh?K>>V@Xn%v|H{bv%98Q@}004eEglJL;!IP4%j5q%|^V8P}C>or-3~*qI{@7FU zgEqI$;Led0iA?3*7p)hI&vd_Ixi+N4hFQ_3@KG(%#;r%iRx6xhp>#ge6O=62dqdXD3Y8kkSAhS)+N{i_?%yX8Ct3Q@Uc6~Q_%%niU! zYXPz(RAuI4Iu11k>lxpOT(8T*+(s3XsKkO zqfoMM6+|k>oW%uH$|H!6q~X2v16TOhKg5opx|WADPSfmJb`ol=77}^$IZtA!$RAACDxTOnQDICW%rIc2<}4^WQI;h+Tg-%`JeOk{{h)j zKsm60z0=~zew)N=0~g-!9NyTV{ayY;q{Ea+5ZD-ypic$L0f?jV+<^$cg|DxMU;u1l z!ARUI@ENdkGJtxpn+Vp|ZwO$EsK0C8!X6Z05gczG0ARHr-a{?Ycwx=Ld6vGv-|(XV z_zKdxoj>#Z2x(7>*bK*_T^c=S@V%S%z>W{<4OY|}kaK+BJZ^Jss|a#v;O8D5uIscZ$!G2?*4V?BRZP!Iw+-w%3MEYDa_}wmew~;j zMuDB~FGF$Od*;R6orS@cdO;ICEQb(;O`yN zi?;S>j@>xGg@uLXbkjj@LLn+jFPH%#lXkH(V3j~Y#}G;&A4|uS$vcj%p?er2w}qM) z+dxbX9YFyZ5d)sRhOa}Dv)$|t!_$y}7n2AZ;fORAYHt7HYd2+zxaIr1*xMpnVh!#` z-RGEF&ySM_x<{XXtSV!!N|VtGYY@4*GciXBE`O!TQeTZXz{E6&B5*FA7ra8tXWmG8 z@?v*uik&M)>rF9w416tf|D;`UFYs4i_o|U~dS=;m|63Zrs*nUocXo0d`t*1wZ$zfF znsa1Wt(eLsYMC+aXW|^V*7tTUeL=OU09|a%@I8-Pv^@^Ocnz|+UOD5l{*@B8B%M2; zoXvx-YXuny*5X`%9XJBO20vBLNdRfX4xeZMCnRj`WC$lcd;kC=eCJIip~m``LA#DX z07Vc#5IG$-5-obj>+f0n0K!uwvgLFhBGF+XDXoI(Ufa8Xetp{8=p_oD&1;f#w^#B? zJOes8Ig3?_`EvZ|I&m;%V)^Sfd)kX%q8p{t`UzJEXTfpfa6ev`IK}Fk9+HJ4I;W5a zkwp_bNLHGllYq82wh6_f$h#}%331ud2-|Ap&Il)FgMJRIvYp`sybDcC{h<{!$@%yY z>P!ks**BB3Kr{|>Y}d9X=!9gpI*@oajsJk6BjfTL$)Lh^3!R3=+SH1H8;Bn5^nM)E zE!6>s75!hE0Du{_%`PkFO8?)FeFL>e%eeI`e_mYoyKD5`&qh+)^)*VV?pSY_TBO<& zNnCBaA!T}9%bSX!!Qy2{>Gh44=i_B-f2`g$ObR_G7BPg-oL-H>l zANvc|F6O)zb%8~0E6Q8fKEpi-A2WTK%55}CP{>zYw@#Eobm)cq7+d4v5)D&G%BMDE z_Hh*R?b{@H001{n2`wDxvcdv@rFN#%dD6FSr{S=uFkF5Hj6eNj76a$SsK37}09F7r ze08KD=d`KFXy=xWzy3*>pWR!Jot6WdS~GHh6DK!3E&EFX@uA~nAv)xPR+6^LZtui? zAX6l30B|)!`w{e|YFt=4CwvhQ$R%SbaA42D3NydND z{SCF7qhi^iHnbnVTswRWrg+WLC}E2Xwv*9XN*d_yRZ1)x1=AWeYo#r?RA1hn4 zJwM?7agUFxZhJ^frf=B(*~yD?#G8|BPn7EYuz@a`l~L0xE9UktMJwMcH_hmo7cX~8 zm8?}a-(O?=)#NHu^u%m3nEB-S`c|(5O zPRny(df~i_Za~A4N?OVOMuv=)_gJN;g(cRGO^bASG2nys<%4QEu~V#;C{m2}=I@00C%p7K6=z>f_sV71 zXzfDz@b>!3=&cx}(`1YvShAt)&%b!`}=A^3VZU*nh z;ui4;8?7ANL|LV*+J;9{(NgLZvH3i+kYstsOkm50D|94c$A2aDEKQNy$xFcpT%iMC zHc_dS!SkzXWz%xX1-F``P(OY;iwh|vxTOK#ZD`7)V~*lF3KX+JFzrr*@;5HxHxJlt zqLd^*F|STWh>21$UH@sZ?bdaMrc!B+(WQSxSPC5cDTfoY@TIpPimdXQaAuoh z(F=*J0Pw0XJjlu|BLQCjIUTvS_GX=XhOY?joMF#Whyb*L?I12Ur6{AN@yC13+nlAe zlD;#euH^N<(~Qay4Ji=_R_Pw)lURX^>eB|LFytT~X8YPeWai3m!dM`uZ}qpGB2Rcr zC5KY|!K1$=@EA*NE}|U-6zy@W_GiHkOUR&D^quSc{D&vE%~|JBpPA0xjksSx&L2%i z$wz66+{{ktNX)hYgw|P_q7=TeNf{q5+xIqgADi56e%|!2*3e$QKID#CTkKL8ObW)| zL1n0DI1a%F_vgxikGv|h1MS)h`t5``5o3KuH`IjW+V%)p-QGt(pXd+#OA6{MBPQEY znsTe#j#>dOUfvnGgZ`SWRZ&Qmttsg?$JTI53p@Wc%!%*t4O6R63(bKW{c z9VziG4o8VNOD~+a0qVwp0*vQ)-F)d@Yzq1fZ)EZV`i6hA_0-B@OC=jkm~-4a^M>Un zjzCU%dy`e0hWq8_voRDow>C|eJ|8m7*0{KH@#tEZ%pav}jVW9wxE2zAxXSmnb$V5f zr<5E#p(vZEc%66F_viG)!+suhs~#}uB9~mxFDf_VCuRnj97(ik&!ffGDv{{8`yrqp z>Hbe@6JrAJ#L=(Z_G;DqqfFIQs`w5@he))}iq0O%c&%OxOf5v{y#BQnb!jMW($NXJ z0wex{(g_8#qRw)Zq?RLO zX=x;re3}uE?pr@z-AgI_{Tv^S)$`a7a@SX08l^uvEVe5ajX;bUd~3DsyIgJ@DNw&J zhR4c6JP*?YrP90Ao)hE+e9==?j%YvmAPh?CU&MOQj8;J#91_xi=&qTQ6pFJM$Mj5r z=u%?)WLRw%i1sN{5P$@T7DR{7pLKnN*D2+>H#ogA4he@}&&}fg7*V8+Ij9Dqe(4!E zDetZ~hT@VS0RmQH5io?3-&NaCz4;$nasRSHcuND0R=}GxfU^ql4Z-vL^X~H=T;X;N z{=W6R^t`sjr5h8ARfIMkfCLzyMI(; z!*V-Ig(sOqvFTEb{KZSAxvFRBCQ#1zp)kUD_;&sq&^ztrry6Nti z!wrYvu(%5>LRX8@F>D(}elUJK7`qmV4jaRcEhyx#dQO6GS0g2xge5FjNPs7ASnx}n zhAQhfiItuXFSX&bRrp`ZlsYkP?>ADjRIL<&UtQ-{dRGS-`t#jiPPsK3Cab33F6c-L zB9nPKx+$soKKKmUlI_=b9@$DhhS87-HR<3kN9_XV2Mliq`I|qWcK^jEnkI{XHsF~+ zIhy^sP5Drmje4KL{@M1CuN}w!6)?v+&Q0`?g!}`5i5&bCLtan&TZVrsNi~jMUOTWt z4>2<|XruKko@aLKNURTC7!5#^X+FkxY?FaXu!BfUPcv1F44madf)8qeEh90imv4;W zE3yZ@pi@h#3tP#am{+ya)yx6 z&2;+%IwWH*GHZHEEz&mjoM5zZX89*lxy}MFq}`|`2%4~j9?(|Tla=;1_w?BV?HaGtZKp%j58S7Z=5}?_i~H-#HvCV-7GvyU zi`wx*+ms(Z{aMnkK^LNKx>RCpPR}rQL)~a?Jr5Yk0>>1{BJ9(5ncT>z#h(Jp|6&f#xl(z4jvyLJI6#OLZT!p*BFj%7~Od zO+Yvi;Rgs+Biz81dEkp$U3)18;1@jCvVI6?LRf)oy+VQ~oGX(;WQOi1De8>RSbu?U z=_ArH)?zPKAV>UjZX~;{hG)SQxx+4Zt4F>1p0MQUvRyKZ`!eUJL@KInBdjVdt$ZnO zT{=d$UiH<^vBtRzqGCrv_Ifvx_O0+98M=v!qx2Kg`WAni8E)hfw>HB<3pZH@t^$?YcA|&ZDU1<5zgGX zu8uV=HeWZBHhqq63Nyrda1vp6QeQztQcx@1{W&h0t=+o8 z?T#>PA~|f3qRjD1mOjOM5I_QGGk6p>ysGy${%)u^&FQJ}#lpCMOq(>Nk&%VxQSq!! z{PN)vp3CvU#|!SVuBer?;U9L`$7)!xF45&%9X4td<1m%H+sjeSY4K+1C8I*FDQDJ@ zu`cu#(aksr6)qQ(%SPBWcPIhL#z^K2>+Ux6`fJ>3wk^M- z4#`)bLJh6&+KYZ73e~?oH4%5eel+sE#HTzK9p2aoh|I+=pX$%OD9v4H%(t48zA>%O ziKQELtZYuu09ZR=Bz`p#OV}Tv0`|h!n_exezo2$kLCp{;%ftMrYssCq z*n!j@4MpPmw<;z1*MkFV+|Vl-f1lXe$k(O88@8LK%DHzmcN{ZYv|CpnW|(H`N(;9j zrVmbAXkvt-IZSMQ{p7;c6KZ+xnQa7{8?^!gHy9|!#+G7i#5 z2yP}ziFI}hmSkN?w1D(3jVXkPPY({YVYcKy9mV&>W2TbphL_y9O-CU0qGf+)MA^LR zybMc?g@T74CjbVTsf@}p5-5Nm6RJ*Riuz3q@FBU1gON(45WMlq!h0|UjKW7~g@^2W z33!GLy%t`$jA%yZ;||spaD9%k51$-MJ=As>dZ6#^bez`~bm=`hQk2u0u&1O3o@uwD zXCUM1%6#7qyW3#9a=+)%nXub8n(6y=zdd5Do!U99SUyP>5;ZZseofu2;m)sHN<@L0 zBJIxqdVYHP(`d;X#r@us=_yB}wvZN{uI4a!)9*bDfPKyoLqQe3$wj~2 zU;7J80#0LanO%nN7?VJ}8i4QkW9jIsaj*+BM1arpHI=_j_ze%J42fl5Q4rC^%45?p z$E?re+t7YIKZ=?Cz`jG37WMdC#7Hc{`+H zwR~b5-GhQ<JUnAy!Q^%>;t^t z;T*@9^|T44EVOIjK8@WXm=>Rhc#TKe-8FBQ?ZZaH4kw@Ddk&8sGmOwE>PCt>bOjp6 zf-5TBTwY`7ocd&=d919GKpiQONJ8np{H=i}%fz*yWK*ygQRYQ%gFPiDd!UprHQF-T4-|8Vac7P-u358Oc0hdUa_H zS?umewXhEZ1n^_EyHu)?wBlWLc@4WjAyEpgynF=JApQMBO777vcZ+s4D>WHYQz8y8 zorL>rx=X5c)`QaUP3BenBWjZWJZ*YMsZGHr6$u$pb7NO~=W-x1CuB!2&woUShp z4jr@N#FBn2?+FVpaDg&C_02*6GY6-D6Btvb(ddU9u)nEL)i6L#OJ_W4O>IvNl*yBl z<;lSCaxZos)b~yoI?s96rc`frutjRgC}z47v<8aa>0^$1Hkz&mVLjEpmR)d`%LIJ=N2uANR@9;qRMo$njX zD%4`*k!H05^wEy&to@xy=p$-llvo%qU2;Us_a{Ckmx}43Koqymq8YgDfEa;V(-BrJ zsaUv;otI;_{VAQ@3sX}TUILK1`1o1s1f0Wo^N%b!>leScnyUA64uYxAS7w`0f~dj?z*%GxhKf!{0eF2zQYW-8|TU>w?HI$VJVL zr`3{*q%p9xtK4yxFg{ziqd-w*G_)2F=)@P8W(w3q-IOkoe1bJQ=uaqPMG@Ud5cs(o zI<<-niXRIB(D}y$M4url8lVnt>V{3(l~?ES#LqMjp`B>O;c(}iG?Cd8iY?-_Ochg=lJ;@Y-NhAdw+i0rV~?d z*XjRd{H|#Z4x^TZvxGCt_1td|?m3!My3)v?``cudh3Px204%M8AVLn>74DtdT#va8SNDY4vf;CZ3gdhv$42i&?SC_3kqz0hT+dX7Y!L#0XRz zc#fUCzB*|z9J>465Csi{r{&aTP01bfh0}D_o-zyL>UYGqOsaDS5(9779ns#^%RADz zRg+r9R0_9ntP;nBIP<@ag4<=z%B|bYa@VKFJQ;diIGyaFW8B#BN8i+y>zLbHlE&S*@xb6;@FxIu0UwMnGfP#2>0^o{bmE91uHn+Orm)9KF^giM z!C!^7cK8F8$d#`F@UK|1i0r}qC_rVHvWTC zQf-e-%pnmt)cC zY2^Vf^f{qHUyWH<-0f$n6{5!JI^|;XekeyyM+vOYw@(zE7m82@Srw2T*waw0B?;IF zkX>lyPXnJFjIT)pnY+WV&CD!oBEg&NP}x8fh?NO;#&nM$ie+w-RFjOBb%{K)jVypc zR>@{0O|AiGh1@9HxLM?Tn*?E1j9jcTKF9dfR-}5319Mr|WR!;uE@8VMxgE9&t{<%R z4e*x0D%vzgptuY7?gd=Jk94J*SO7F?-<}DxDJu0x1y!S{3I@iVJ%N%avL z`|rhcOKpSNP^2Y{&9hUD-7f2Exqd-OUMO&~ z?gicia9m^;&g7VXo`4SstvruE@4%l1crlFuScK~9#TI!^M_3KK0 z$5R!g7I{?+GtTs`Se)##y*KzDx|34YgB^W~r>rRtB9mk_WibY!fsw^CbP{3>0x=3* zg5z4MGf0DXQ`5~!GEAyWo)co_eY?P#@Fz!s*D|#%j;4=BpCWS%Qm9rG+H88#Fil8{ z@!z__utJlIMR^~MTQ_Vf=K>k}KJIr(561BQTFMkkLfFZ=2fOyWiG3W0t@d-lR&%Ao z>baL3>XG#(wLeQ;M|(f~VUA@-)En~^Oj@cyg1N@K^(eWH8%Fez_$=;*96dTzwul|O zk3a(|@^MPyxL(yPnuJfeL{vKQ#Rch!f1m?&uWcw}hrxM1}GO^X-aUSwYyWezj$h_5SpAKpC@bRbB8rcp8EYk z^+LrW@3x%o3sA>&761{VoZ$ErOwgpmbqJIj4PIF3+`WQx0K|Q_@0Hgf{FT#pnKB|A z6Bd!p_vI*QPIn)(z4!k@9?xh$VKZlq%rQ6`@dbob9xJvbD)+HB_V^_edwVizDV2TJ zhtO($oMlEQM_Lb7KrPFosAZ-)U)(O`K&SM9nEe&ZS@)}&n_l#lb^_{yhPl~Ke3W~h zMrk}98Jx9Q7&L1qZpTdJ>qSID7llV0VHSvu_KF>S1a#rdww(js3Y?s~?1*$V zj79_(mxYM2t*T{DZI=Tx2mlgqI42;Q>xT72ADn*hl9_>rync~VjgmQEmJoR;`*wjy zH<;R0kIJ&X^;|Tu@=-&ZSOuTokxM0OfvK)X5Xy;ckzsk&epp39xxN2@rZ8lHFuptVBS=)J`VApwtlj-`@J`sb`4p*h*7%oFZ+vgI6r!TT_w6yVBv$S52BF$O zD|dG+$qnjP{80AhdVUTi44R8oU>4-)&BU+vyz$|Xeg!W=I$MkR&qY?r((;|Ka_$(? zIh)4Su@(V2`>2u(5!LbvMfWJZk8tIq+GX8@6Z%eknJqQQLvggmsKa4H*td_Uiilw|RSijTWD9G}J(Wf0dKM7_u*;W+Z zNBE~-T5$4~UM1-_Xbo_{$N_le%P=a}NCuS|zL4CD?AeoyTBawQgg@DC#TaAe#yi3` zycguam6LnO2;RfG$jDo`M%h?x@gg4|NFUR(F}(vh+YyzQRXzsDzR8gY&%tQ=^$R*px)@ErHVa4tN0tfc5~>Ud##)%qqXj(J?+@;{d=g z5x42BU@{Ey6!Uil7lb`YY_iPL-d@Y$jqm2B8b>&i@#t1-zRt*m@pR|g@z3Za()G;u zpD%=3;%a?=RqwQW$)k(xyK_Kx`#X^1_OTrVlrG`Q&PbUl8&T7PMdnaT3 z+1xhC8s+@86an#@%&OnzJ#*|Q#}SO=zXNwmRpUE_7YU(8^z|uHT9U}d!*mFADJLDF zket_nNBp~Vg6ZYR^6Yct{VD!6jiACQ?1eH(g^rE!m?&ne>lk~8-|$n-=_c6+;t_4g zWYLI@8v@`Vvr;~w1_jVs4BwOCLKu=Qf2U3l5Ay4WSRTrA(Yg*kr`@X7_&Wdjy1|A$ zukvBuYF_3|o=5EIT9BltZGypEck;znsv^qZt?#(uR{MTQS!!GQcK&{$wij0QV%@Xm zQbO?7`RBpIfi91S=vR{oXZQY~?ukpYpJWuqnvAMhDUH=-*?3r@;0g~DPq#&sMZwdA z0M~Yxt8{a(IM-*Q+yd75DA=fAuN*n9?$)9Fw1A8bN)5uVdjmE(Kt3 zC@FSYT=hrUA$54Xt&J0#ex6BrrGSuU*DQ*s6%;8ntfD_1?aqT=M4hd;r2>&K@BLo=B z0Cei#EB?X5_tS++O9dieF2CnJG^)HjP#`B_i@$-*Arh;wbb&1ZoX;@<_1xXSgjfyV zL3~f#OZL9KtEjx_Rt&SIuVwKXqc)5yC7xSmF zt@D_KIjf)Yshvpi3GWsktJEH|Zf~M}*Zb!A3g^1U-FBkc8?;;M+9KfLr0^P?fHT6Gu5So9`ssP{a8 z*(5-z@6r+j5Up;luatWALTF4JKxR`n7qSAqvOcs!1S2ECg$k`>zHk6YV9-;gg}mCd z3ClmE$it5Lx&soP&S8 z$m_NFAwCqq16&2NEk)8{1_k&W4HF+ZiSB1#;;E45Q33Iu0l(zAyRMF)-XA8tRApnRTVWqloWWNo%*03U{i*ex`+pz zA%Z(6yu*b3BQV}S$g>b87D`aZbeOSdGYHnbX=%w4e7atKj>b{%iLlEzA8i@BUS63> zusUDVK@ajjnutYt5W&XhRkqAsUDdq!&G843TqLO#o@WY z7u?{QZiM=UA<=BS{U-SIHaZqR_tX*-HL#mLSRV%gnvRVC55S*)<6)1zT>*fP(bhh} zwMG?*?eT4?yne*46Txn|*_5^^oCjHKt69u?@~nz&(NgTw?3jjE4}wdYP_rI?^lOeD z`rSZthWm1AJPkdD*E2}xm~mN7u@X?}vifuTrQGWoAU;WW4AtdnKV9FqlQGAKcM@Yy zqgbF;;9vx^VB4T-c9H1P!N~dJzwzjtS9?~0UIUr$U4WFqI=VzzV!}WpCs0RN$ZrMifo$E7xQ8 zagk|7J}AMqOTR2!o_g;2uWaIPi;RT*H-HgRQ)2eJH;ht;ZNEmr=_1)HO|M=a0B8iH z+%b3HtWq<)aOqxN&2hjlRNrm}k@z0jIEuMxs}A-g*-q}$eVR_EA@lWEBg!^_bW>h=}|iFgor zdgHK=7jW*cRgWzW9@`?;Kcr5sfNTHZVOVA5iK8?SD-#d0B;^dxOrm}@Lu?_ zcs`jUSY%H1gu=Iu{DnP_UlCI=%btnEoTM|*9xEF#wtvKdujD?2J9A)!|EX{4wST2PwAww6EL&6Z1+VfLM!2}`m zE9M2{@KI@#D|&%%m&a?yRUj4sWbcjm`vrJ zjzNf)xm_VoWH&+V`qLP64-7H%Lk5U6+`puJj0s6?Pt!K496#DZH=rtffc`Qa5$rKP z_ZIfi0Ck)d+bMvNPaWE!b6pr3&LNivoO;LZ#03EM|1V+EeUN%NRmC&wPw4EPsIbM8 zQ=Tov6+W96E>z(XI#Mc8{wXSlfodAZJ_V}+2%He9BvKiUGu5SBg_@qIyR&buU3QMB zTb?hnr37Y|DalTA&P84-FhT@dj9!a+l+Th@)H38&DcyZ@J!Eet1h(DxeTtr^z?qiz z3Qis&%ET~M>U2q{IX9w*L_OO6eZE^t%##Y0uuP}I&5^jy7gHTR-n?02TF`_ z*Tz~{{UuAXk?C-8cUQ1;4`pl$X#YM;A3M!;2>I1Ho@48N-4>ITVy8_8KUUApb-oQ* zp29`!An-4xNL4eY7ABx_e~5J|cC)HVeg?JajP+ZMx&?(ml`O)T?!P!*N1a8Dn0ckrB_vcdmCmPm>IAJp<6cVu@DV(UWJLUh$^c>)nnVU z*nHGcgz5vm6uv8g^6pvZ=Eq)x?Xf*Lu;Hg}qCmWeyiCMl*>c&`nU;$H2!4F^f9QtP ze;LIKy7AHqAC7TUBzWG2V;WO%Y-15VwKDPydmdO)O##K=t!BcLt&Q5lqdLYU(B_T< z&*@UnqI+lt9(LWU1%FhLZ{y;D1|H)r)O>CviUuiRF# z<$37Ij={)kHN{#8!e@&mnxv>y(a*f8XxR?5Ww%oAdprYjLf8^!YVO<>>qcOdY2Ld=I@TFA9nIc(jr(*c-}6oR=`6R_OZ0|>f4wmDSa9Jb zVE1=pur~5~w$Zqk&7I&UR}KGN98jsgy{mIO@m2G~t@NMgc1@g1ppfixU79maFESF$ zj^lCl%(V9@h_AYWk)TYWtj^e3)As?8ip~unLN6cYf{(1OWDuu)WjA0m(+H-c! z3Xsu>_6{a?$&$B6fo^5kmW2S2+j}p_19+^1c$?qGVTkX1F2;KIZzy{qgk&eshX;Je zrvRMHPzsQYScHb1j0sP6JC5nuYgJ-3DgT2eb+{ft3k&k*Z^k%YuGOC-6n(wg+7x+6 z!|hq!f>CFuTjGZU0e|xiv9PW{rP$tE=rs{SQdOhrk!JVsQ0t^>zFYk z`U_?w;%XP+M4O0@E2XYt7O!IIK*aKpuw*y+>S1WkrF6)a{?9;KmR=KzyKkH#tE6-raEvEA!s$nY9D%2I4gJ%9p9@3p|njm%ehQ zvu@`r4f``Anj%H1*;X7MY=;Tr3YRkPY6uzbacEYjKW~xUaV&3G7is%0)UA8Z@%ib? z9(Qcq7(mr?^ZjO2+W;4{K568^Wwpm-2Cu0_TxNC~6YW=58vLs65k_>{Gith(+8o}B z4*vo)5WN+{U)j2eYcj?YAa4p~c9Q@*bqghNgYVy9NCKp$^F#$?VLJ2q z+E2zXM>hQ2OzAiy)bTwZIcodrTTZj*=o-fhV8uaY*J~4x0fhbb<;jMcuw2i<7#!U$ zK~5*q&z*fn?q^xupQ`anJ5Qq{f&PLb!L;P1B}h0NxYkBiI-npWN2?|F`oQLjNQGp@ ziE3UeocmRkr(U&r9VB#YB=ixt*;+fQO!Bn~Gm;{Wg}SxxZ_ zNqX~CS>*A+uB&iKIN!dNvBtV3l5%4xNe(Vj2~WSxh?@Z5n!ec|NPrh&0PvXK&)r6P zv=pRnEcEtBC;*=nNGPa3q%S4He=Eu2UKj4Tp0OS-@)OQL+is z)CN43Z?HlaWpfM)`KDhV(h4T z=5DBpa*gZ=5|;Emzr#`lVDAu1zefG*ptFshwYPL|v!;?H)WM@T94djkZ1I0Z@B?q7 zt*&oM@3~;f46nNg`mvKU)#74ux&$61)I7*_u!jP(HeHupfU>;2Mt}Tw&gIpfg%!`O z-TYfe2Mz)oMI8nymnuu0F$F4SP67&m99*P8(Xa+J1=9a<@CFG}%I8o7`WFbKT%Ch; zWj-Y$$YcfpnD1v8ArfAPn@>9j9j@2%bZF>#R{U6O**w%$-$=)zt->j&-4$^gXkULF z4E)v4m5)LsIcmd2=K|XC`|*w;%7!qp+Qq%MY6Ax2t>ISTg_?R*d&m6!Wk(YEo~aCo z#NMDW0-(G6%O;w)rX~QbimlgxgB`9t*V&*KsyzI1T&rkz=_gv4AbbFF)P5aQu=&~~ zEXv)ICqAtC=9B%yh#N&zSk&WUQ5AIhVboDVIOr?I1Tn%vtudgsLReFIPOJ4flh8BO zxu7|_1KDlp=WnPoXPx?Hy(-%NqT!Hu8jKL|mAQ(j3{8TpsZyu{EcMf)ZAQsJzl)LF zVqt=bMAe~QjF+4DOX26+Id3GhOC<*Ao{jw}-YZyR5Kc<(nRlaoxPU&%5wo|?a{@$U^2hh^1|eYVh;g|3g>3kOS9!(Y=^=rU05|E6#Gy=}FBzqz zaRwNSJQHOlXcbPpr}XZJ&aXv>QZ#+Zm`v91CKNMjtwEFA#0zdrWuMcOE6oJa8Rca6 zXQDJm%M7x4N5|#3=2|dx72*YP=;HYuO$0Xj2t-+hXN`7|Lv!S)AySfhF7DNqmX^FO zp+P;L6_gU|hCkqTuU6AiVh8k3C|4QB%~~<-D)vdbQ}Tw{=_G)d7-w!CUGiWSHl!g( zzCWg=%Wcw}mi1+YVDb(N8{HVad9Fr6PJ#{9+;AETdN@0Vyv-o1P`-^rBT~}=A_;7C zxj#wY8V%Nx-Dn7Kfh?2w?q%MelfJM};c+;|YDu+)GV8)@{yA?;r+bZS$gT4K5%rc~ zQN2<3@S#CqDCty6rMp9satIaaknV0A8Wfchk)c7Pd+6?NWavh^yX!stp6C5PXTHqE z#fNjwp8MYSUVE>#s&`l_n7kZ{M#jRveb~eOxHGVQr}mbh)+RWXiryhu^J;mB5cvGo z`&D2Q%x=E3!8$@BlSbGzvslDlb2*_-XsHc@;B^6m*0SyKSuB#|!ojRXe=ka;CnF{6 z=jyw1#ujBa;fSM->FW63u#*?hGi-9mps@MBvvtQ?6KZs9)Z}eC_lMD0ABP2TjVGRV zN`w9j9>c1Y`=1_vq-tJvvXW96rg2Cv2%PAc zfRnU&vFMmUIvUXZXq<**-u`2qi%B&?pE8X6XX;TtJk_%kYk+06@>gX3OcoS>Uka}< z=zz(o2b*?|M`A$1zr;&s#F33cA(Q^Nd)mfAr=0VZtcP;S!5u&H3DRizmZiUi>-|0w ztv36*V~b2NCFQ!D+z`j)Qi(-*ukv-lvTi{eoTVUHTn1r0<)A&?^eaezG11bDgk4-q z%frgtMB?zE<-0(f4ogVrz@be!{R_$3f;lIi()R8T1p`K1a&rCark^IQgES-r8Ka|> zoSpZjg`$T!fasa)mOUJyPb`J;+7S8x{m+rfPW~STQujLyfSg-MW95Hw1^nty{z9*p zu1;(;&L3sW2;CNKX1`GLOOlm6de+7>as4)0#5;E+3O9~eeAl-+L zu#f>4E%X=qe~tdo4)IB*h>W0NVTWzHze$p1t^e{tYL|zNijo8qw`pztaFY)ejU5@H zdy04|&qXIA%4{vp+K2AcC8=pJmdm~#@m!c6|4X3kqo~~Q7T&%3%)eKc6{fA8ia3fm5MhUf*QKld`QUK5^!Os$ ztjO5;sfXJ?qellViy0``7VW~1G3VVeb5GQ6e`l^!-X#4QU!g{wZeEu5Pc{1JQ=iiN z5AfuuiBn{iLV72J)oBrO^ELw;c$BV0i>WBhS0uoh%};8e64MfZ@q>OTGY!!>IY0)u z1#^o8P!?SmIccXGxjZh&oIZJ?-!{fFrxnN|5pVPG^;aP-O?vkRV$_Zxyfcz zULvsgC#Y6K^jMVRD3P-?UsWs1bdOf`v54epgWazvsCWZ%NRefdlwNBFz0!&OWP#Y( zIKy-bgJDx)}Psup`y&2=0oLL$OML5o3KnLg-UVjCI)p9eN)<{ z88l`cRB@Tig}>(>w|-0y`b*4aqxgqSMieap>jCD3&Yq#g<7eM?n0Z;f9O5c?kARarulI}Q5h4?NXY*L_ddpX zaNPHrdn{sR{o8>0heg4zT(-I@P1=|*0 zt_A{{II8g{E$Fzq8*9G2h54gwV^AMr(ES)^LsBVlu4mUU7(5a-gZvdq(d%sYs|!VS z{6cIuQLvIR<= zkgc^}lSsk83=+Bm!(bN3&$I{KF+f@zlNBBP|A#Lu{tv!*Kvt)n>ja3wk&GE|G~*u# zW2}J?#?XC#BRHIK{mLKP5ojv(?D(^4US_6K2cwIGkA%j^l9!eYcsd4cz`D+IvbN{H(mFJG+K%oN|N@ACAU6RD_>K8~gTXkm7rX zh7G3iO}wi))G6k&LN7z{dl{xxy~x~-HgEf@sPshwdS2WJrcI<;Se~U#XCD!dKh_@ zDmCdNJT|Smxt>vH_t(;6nsPvaj0w+@r0^&D zC;I5}ZYc6CZ z^dI~PJ|viQ)qXKKQUMTP!&?Y+apKt2K2(b|z*pTri z_mAq=YY6dKN6P{+apjL3M9meI%zqkMq~xzP#mwo^UKNHI{`8;hMGskhvU!m9MCQlY zwp!k9-BplHUiFS6c{NsEJ`6l94a#?49W95Tu`%r!lmR}LHCBhV#9Py3v? zurT(&_}r+je8zlUT8+VqDsT@sE|Dz@UmMRO%7p-Elj%^W~aut?;>Dt;EHq48bMS z7rPh>zhtQphYArnl8@|@0J}&zM2f%q)T?pNU=M%bwv@{;iRt?DwFt_;)%M>i;N&F8 z6Bd>}UcO|(6M3j=Lt_bNQNRSeXfx9}&{ExKWIF4}Pd^kCnoyRSQeVO^;`B*;XOa-q zrOV=NbUGU#8&DW-85Z@cII#|Ee>_=D;(&-NfsiO7$u6+1S+2lN z)w#^3cjRZsgbHu6{Gg7ANwTy5c*S$v)=;BKcc~!7Z>)xP^n2C(U&lk0-E_5Agj)D- zuwGc`EgwkW(A0WxL~-c5RDHIQMk9JM+-$H>&BIqdVVYRSny1 z4hnkS{yb^a1_nCE<&i>_3Zr{?b2RAv%M0Y(kTz7@=O>2$L5$ zb55nO=MA{y1Y}xZ@Ae+=I}xH)Rw&KNqx*>EF_yJ9dx*6NfAW+Tt<#76(#?fe!;M>khKDa}m8lklI?n>K}W$8Dy8(Wm^CNc-Bl*e-`~bmTCT zt*(}JIQzSED+HzXQQKz_X~KF>swGK(-)bGV$i!f-oI~wd{X+C+bW8{D?^Ws-JcwC+i^{;1-gh*w!;ji` z+%E9fpo3>zlJ=i|d!(@MD~qcOJr#N8pMLoE3Z-t1xJq=?&0yMqQD4L{nEs_g2fVekXYnqPmfwvl*EgZr1t!pozk2^!(Mko)Z#wC{&LZq7NU zm<|otB>n)qgjXL2gJk@3L%QBkIkYd^Pi3_(Oh5tKRPA>1S0e*~A(gO_tY?4z;w@nv0#AP8Tk7(q`ul^6M+{(xkyWbq%9arv{c>u7*Sz z=bF2%19m0CltyTcorQ_>6rIo4f~~Z4-hdPcg$;zFgW!OyGF?Th1@+7=+2GyD*N)!z zd%pSGD`_2Rl<}okUzwE?>b09iGO*Hm5#_EF)0NpS=TFyrdN2w@!^0g|YWfS!N5~R7 z+fRv%lG%fe*)ZA%gJ7vjGWwup+@pN#tj$O-g*)IKL6XDBW z7|ZA9eR(lk*+E;c=-8AT$DGoK3v;#aOIl>ORZ?hNb`=nu=ShhRyc1N-d+X0>dPZq> zw?|lvCh*=oz3{X}(6UE_%ZUlmB@~)8T;7Bir_ItAVhW76G z0OrHp%)P`yjx!}4@_}*UE=epaIvg?%s3qSNm1A_2qN}k71%s~2!3lSql29Q^;_amC zin*v2C5zCv*wDK1AP2d18k3p$o6# zBiJmWK6;NeDZA@IVZr(UX8g9na6RYzkGmk9R(e04d^N%z?H1GZF132slk2RAcq-@e z9j5Zdwddg3Sk=7krP?qox#osV?tWjp)(thYALevkX=PbS3;Uhs$j-+fm>n&9DnFmf zdOvl(UzJvLTy^w1UynXHYzm@%*YAb3m-)-h`u+2{yhB9gzrw3A=3k$;RdC+_-5LB- zDo<)zM=CMM>afVErTh|OE03jAsCikIp7WVYS9P6i0EU#d+TV?dkC{{-f$z|lR1;qi(GOj_sR*6)k(+M+Ku2kcc~wb^T_jD zST$vEE!WJ^CvleSx>!rG!KI!MG_nGrS`%S{LdH4`dPSbwKVuD!p%rygAP(hS5SnBZ z%>gAddXJ5iD7ZdR_|Z72HTE=7X+rsHWsl7EaDqvJvw|S|Lj$$@Dw`|X0d|ROtMSPZ?|V-A>`#9*)C-e95`&=^>rD8Zze5uh z$C=FT`QKYC{|aD73OrD{HjTzt|Cfdlq>QPGN{8>pho0_QUFlQK?oH)wl|M7sToibr zC}!iiz{Y0p$EE7D;2_m7kpOk3*W@iw+D#|z$a!zlu>}*)E1D^0gTXpGQygxyH&!_Z+AAQb;F10ch=5JpUYGGurTN_1u(`&M~07`!31Xc{1+32`~egI8(!ZbMgy+jH=q|UxJwW74Y|av z1S^JHm`Y3v)JXmou^*=&6p!B%p;Hv)pRwSdY-*w`LHIrQI!&^#aKHNGEQo?tD};1w z4JnFV8_mW`D``~EwHoLAQn#A;x7xK@HeamNe|nbyH~mQFmgbA51p`T2&8B^z=~?1j zPfaF#@1O!sSBv~kKOYy_E5I^v;ChH~Pe5uguESSfJ8P;po_9i`WG^G&^AtAuE>=z~gpY^k1Q9QiAFVMi~Wbx42 zwh7>)_qq6Urg@(ZHo(C-B|@uOJFTm*Cemi#ceaMh7E=LTQScN6O9z=f%&|h34{d@3 z!jN9(gOR)X22Z-CTLDX&!*xyps#}-(W0uk2%#gE#3mM(PzKVItoWWc755Ew%QUf_Q zyOWt=Qd2KN>7PS5798B`S2n%!QbGm=1gJJ&eLjdutC#Y1zHg9f%dai4w-5+nJExH? za<}F3p)R=bsat@!46q3rzyW_dG&nseAS~GkcIhe?GM}4*w`}+a&3hA>REqxlwCdi^ z4=wZx{9~jE|62wTcd;hUGgT!OJ@f~X+<&_<2tGdK+-|@2<~Y5_A;C`CthXJBQ7i&j za%Cdh1-56A1v^_LwK#i;%!%Y!46_Zdy-%XvZtsv%{%!klqMBxP{&b=Dxg<(R@Sc#G zR>F!F7UNuKXzqSO#AB(w_a7B<^v0I9#WhUjk3pw8yxDmp3l#nweK3 z=?PTf``RKSHwIZq=Iy38V;MI6;*X&}PU97%1d@#-4H;;OP8klE&BiOaVM+v`R1P-O z3T__+7P--TiaJ#-*uku^5dUdzYp93NEFTVN&?!oeC=bYF-kpz3jv`Xgc0OvT9)$o0 zQvQ6SzOni`e!>rt1%-Ln<9vN=6aLZG=Fd08eoM$scK+;J)f;i@C}6ER)@rCcc|5Pt zTlZnfIW1K18Q#%2q~{ry=qW+cp?g<}2jjGNwKH|Hua7TbZ!yK=w2hPYl zt?>m3Fd3vD9_D|AIYR#r)p)=>`d)WZJ)jz6Aeu1+e&#?q(UJR6a9SODNr?w`iu~t4 zWg3OrPPc|yS*pgF!)%--W|)-eN8I6Hsw>OkeETo zWmituHDaMRL26Z^D$JI?PT(UWTSrtZ7*pafl;=o^*wG@AA_(PO?F=a~gCRq-5ME;u z#2KDH^T~9nucaG7zP64v6^kme!3r*i7{;5YV{UgW!yw$64~8IclCPC)s%?6iHY+~r z>0lvyV6iyz8;~i)Xds#^7yO67Z6#4dhv}BAa%nH}_l~W^ z&eEKR&3f3^F67@}i(9Whw>9jVnrC(O$FD49MY zhK477-D*)Wp8dn%@CdZ#z`mvd2}Oo2=$38dpxd$$m()YpA4;T~AaFzfZNR=CyoF=_ zJ6{54_mOo)%;{@*X97bStYYURH1bOgFE$-c2mCC%GcG-Xe#_o9`prc^PvXbXReVu>?OKEn;b)( zQt_uD?_om~i|<5rQ@!NQWqWB!K3y!Gdw9ewPqX<=`~H(ketguDj&M`A$Uywv&hxEl zy)c@wJYUX-avsKY!pS1HkC1Vfn$r|J&^RjG`I4>{l z=im2fKJxs=h?Wptz^8S9s-p9(IB@6kxso*?6#8g*WuJ~{Q7fwTg$01xexdiXR(f|9 zXr3Yx1=YXp-;z8$5(v;aQ%Smjy5N@$7(P3xV2mGD>Qz7xsw1!^0^f0Waju`THoaHi zyW|(2qG$ez7^9_0hdF{}0%0Yg68WLts~-?r+i+JrhmXI4mzO51Im-DZ^l03qX!fO| zL!h137B?zp$b<;2(4>fS=Ns5=CNa?FDDEO2Kf_wct}NkuUsN*cA(9gW+>MAA+njZz?)O(!u7|7!A5jnNzxPz3an2}Sat=sUhK`0V+6y7eAW%4)5Mk59?V?=#p2~=_^BSN z-}?RM-W7|ak)Gx-Yc&`@(<4|s^w>bfVBk65Du|J-9LN0n?YyxA}mf1%28B@}+egLD#NOSKb!$8Jeu4 zMSYUQl6KZElRCM7@rvJFxMf<4lxeAo{*836fJVr7ZQTqT@G{dUpF8q!mKH5Rl{Pd% zWRFVY9eZ&Vyt&nAb2GlYK$6*$;t!np8uzR$fjiKlH)|?M5yDF|6-=T1J~R_qE8xAL z$IXlF+?ACDrJ>G`(T7rYClU^HJKAo|RiIxlrwJh1=g3k7%PWp33zohqzq+8h^^4dZ zYL6%ISI3%b-hwi+C&Vz!H89Op@u0HHL87e|JDJGCluFx)C#7qzAG4r9*k%mLL2jhG zR^V+*Zc4#w|07%A0y0OigO%j?OF?4UHmrbEoE+>kd>jSznWfJ|E7t@)_zJ4$b|l$tW2i8_q}58ey@Y=lbq;8@H|m z$o|WnNbK${tzd~J(%WT64el5nni;)Q_E@|dH1RoecUqIdSJ{`)R3*icdm*oUyZ$xp z`X$4&7iMLKqldM{>qXUuNF2k#dze^tt4`_e6BZ|bj~8G6^)N8>wsnz#B%yj&&us(0 zmr{`>;Mwr-W~A94EPFvs|I!f@09D#kuD+wl0$b;qb&XX>y!Q`~D%*v!(6D_p#yBuf>WTI&sg2S+#_d+ZVg zgx0LjlFztKhmW|m^d4{dmwg}b3Q9&z2>}YAaPwI@P%I}P8p|~6SO>vdQNXp>ukg7{ zRuF1ssh^aIVhi6R!2e<7Y-;-Rz;yd*dtnDLE!4&NOXS2@o`p`+XwD9@OEK=G0%$g^ zxRO$r9Ba?C#NJr2y)H!tOhq^n^^N% zY-PW23%Y`;DMHTmF?#KJ3md4(HTHqVrBhc;p1Sjwon-OdH+XPClE2vNgGa z%dukQcGdG>dp@_OAT;tDU)Q%Ld=NTZyMhtW_014WCgYUU>b&mwy}5yT!lTtG6J9d7 zZXu=k+%4Dl^Xn1aa#H>$kRMwvN!_wIkbq6F|g(|*j`HX zTqUgQJno^hAfp~x{b17t;OiRopg*kF&I{142qN9!_>6_#AWXcfVcYomsiotd>AH`d zhO(`ca$#6H%xSb#Z}gT)Lp$kS$%ew{KHkezw0pfdtzrsK$caZ18dSYNO4MXrAxhdSc&}dWi2|^!~?i7Gb8sHin$rjJiVP9!AI#j%sE_ z{49-jtX3fNKBQs8fIpg1o_{GSv!`c(Ny&hdrzb-V3ccr>I4GMom=~bHSa=R!6noqB zAs6~CQwIguQZn>Vbpe0yptGpm4{c4mQpakqabxHy*YdTXLE5*H zLwIz#D6DI8m_XA$)eL&BPqe*RcpARUu$)X+_#yFPFYI15znD7f#{Sm8a42G2Yfp4y z`S|Mmmgs}ydoW*mJ775-PC!V|DMP4_2E2Ao_6@m8pG2(tixk6* zCo(mDR?@DR-aJl-$HBC_sLL+@v(@n{|aH&eI}#;6Q6v-&~!_ zkA*yMe(9gFN0@>?603#d68kh|v5#}228nD4*^70TSv+~Qs2S0;K4yx1>37ME*)m%9 z(c69cGoto>d~f{=chATp*D7{uz&=IAuAKLPcZTKoOOKhb{*=MBjy#I`eKa5RQcV*L zux{E9ylsTXf$w41*@5{jL=AeFN6j@6T=Lo#C9hWa)7bkayIc47V*j#*s){X>WOIAA zRdhVH)<1ZK{UIBv;l{>%cEWM6XEDMtAH{mT`+`weOnA8x(qfxq)zhng@mypiWs@)0 zWMX+DE3RzB#A$4bOV2$tyR~a6sfx7#5Z8#ho&RpoeXEN?@UmN~JTh-GCla?n?fmK} z>h_0y*Vrz9adEo;a=4`!GrB-k3IJ>sS+ZAsv&RD1^^%L`@TK!p_|`a0C7Q{Aur?Hi zl^hgyMV5yF^q)lJ0fUONOX&|w*7CsL;nyVt_zk?kgE8zT2nFONG{~~zY(ON|QfQW6 z8m}Cf8r(}KN7a{FE~F9+7&<#;!JLG{v|7x0c#Sg~E!i1E(4bcI4$dy^rY?*++WbLd zwb^dJYp~;1{KG)ak;Cp0!wbyu`59N;Kt&BP_7TQmT1i`LlxS)=Vp!QS$416AJ5ER5 zmV^8Xp5%z|uWBXS*CnfML;RXhp(7@Kz866#8N@%_P?CJb!dt4sQr;fW!ZgQC>9!xc zWd4m=yG%^!+~uuX-iN!v8qL}gKM_G3fbrAEnhU=ny=yuE+fRinlDkI-@YnSI#aH`r zlFcUV&e5<5t-0s^)qiv6Ck}=8U9HHub~YHszmt^#3Evl_uR0U+Q%0$M_09TyzV%Kj zx9B$AM0)S`)||iOS^aun%Z{S8S{SE;Q9OTHa+ZwW>D2gRcFL6fFrTP*R(*QRNBSSl zehD)7O!CB06v4UK?e;~kVOw!IGWO4)tYve`e6;R3RL!ie6_qBPg@9}dW69;@@bZ@@ zDqxc2W1;waTmH+!w1)+#ZN?I^^$m;o#|A_0hgz*a0_Z@cquM=|B$!Z9{QF4R8CQE1%_`EckdAzVg3MbgGM7;eM3Hj?# zr{$=&Pt zRvNv+344#ubgdN9od=Iqr98+)vl?;!{Sm0KM6n4Sx4F&`9KhBKhR4OP&gcs!g?oFn zruwuCg0mn-IMg%N!}hjXTPj)9ow;|J@7vC2`EJGf>K>hykqIw@S#&{9_mbeDLqce| z1HT<~AVu)Z{Pk*mCDdiMjI`_!iny&xpYJdUoeZ2~$Py4*QaC+S7fUY@xYM*CJ!2W5O5MP|qMz1cJ5q6Hfxzisb~q?B^6$(fT({^9Z}Rx+&T^>6HKkmo`9}Hm_P-I^ z&t%iW!~Bi-?To{+Np$_vye~Gm&B>cEH%Zk0#V?A%Eg8HXNF@&%`y*ytkspC?OjF||xGg+j;pEN*OHh$$g8 z;0j$bWdWN-QT1$(W3#|?aE(^w#FHGH`P^8HVoE&rF1LheW-pXPmpNu3tkRQBGJkk~<~{Eu&l01>ysXIP}5d zgC<$6aa)_5z$2!GExP>^Ha@Oub(7|_!%hq;R9nFR7|R4E4{pzi(4S0>=s!@gfUTAJ zVoPH`{(7&tdc>IFbN-(OjZl}h^n*NWdYmfE#Ba{#&1<`xU&zi9Te7O&gq|!W_}16q z-TH<{MJ*Cz+6YDCUCDCC7bR<68=Vf(M643pl7&a2pGRxSWW?_0UP&xZ3~_|BqddvN zPo$}Zak__dZp=%Y%Ydcp4>}#Al`)@JRb#a$YwY;7X5Subl@#)^y9Mc(PR})%&Y?Fi z;subi8}%`9i}04B50#ueZ*WD4p3Hh7<#Cy6wL4E~R0`dr*@Oycmk>D^LV8CEp}EMsgS7US#vrTEk&e$p)P* zx+)c)#&|VtFU#voyd7^&2a1J$)@2o5WCfnxLHBun(dwss*;e&E-du0OxRK=d{R+_( z`lD0GvGOG~1d0{%rb1U)iq}^|NE32_>eC@r7eY{$=i@_VIlghg_R?hWfXM-X$MD1gZcLg*Fe9Ac*BUgjUik59V4m|nmFP+i6>dgCw;jH zmxo)g{<0Jon33`LSBc?$gI74lag2p6ND*Qft~Q=|@JQi`6ZB=9dq54Si|aKa&U6*J#HjYj19zDu^M^{P|m!0L+^ z?;*93k0lOX{0-l0F&{CGT2B0Zy~{17R5g;Q+D61i%cv+Hz4;~W^ExDCk3@&cbt@ut z-2ddj`B7p|;!;IHGG5PzV|y2uR%oH((G@2?!dPG2$dm(4MkSh1h`d;>54mjao>s6G zRDI&$3-i60?0Vr7!#s!2sMJdA?{4m^Kd!=UhxkmS6&RIoU%mEbZhtHfZFJ-(>WsUo zsjZoXl9?Hv)~4j4CU2#i6iDH{ca6>apbENc0zmLwg+*i=u4X;&3)cnT>35$RUvVVO zj@Rc!a$I+DLVkIdDKQfTg$TY6;oMuq6(w2V&E#BAqqtrV@}K#0pZHXE-?Y*3%;tVp z^SX+Y#&^RfUz_eD7ObGg9TUqrNL7PuJ$FH8(;%Me*1dpL>Ox`Sds=R?-(wbvFrAP; z6DC_57;j;#t^)J@kE2jsj(+`14kkvGzIdLIP9=cP$%5JpVJ2ZiwP+XM>8O-6bE3kc zc=;lN5j4%VOymRZSZ#oA3N9ZQ{EcP2mhb-pMq$HH&NdpeAS@fJL)ZmuN85AN*yjKA?Q;uVp~1XeBwm(fXv z*?QWTeBRSf%iLQU837F6+hewrMMbIQD$a5D7?#4?R+u>8-XveFKAFno&>;)1cAZ%o z+lT~1$>xAcZZD=;RIM#YGF?6OqJ>tYGASHzuL@%?K_{2KX1&lDTCrGH69KM0P$s7c zj^0hQa%!>FxyS5LU!ny2}Jc?{raSYb&Z`O z{B}Jt+z==k-z_6;7{AXJ@frWJh#xV4j5|1@5PE-)+fJ0yK8wNLfYR?M6=>O@F2u00 z*m>=MRT-YOaF0EF1Cfk+lFuK2rY{-bk-wOb_yh4Kg(6IM%b5m9>I4|5mI2%!9YgMv z&_N0Q`8eaZ+HVd>3uqn(3%oj8eq~wtgsusU`Ty_>u+7o9MS(@_*nR9MZp(-X)+%cs zzMU%NpR17(u8Y?tGEEbD`LlDzW&FwII~Hj+v7#LIxx4${)e~%x{l{Z1qP$b3-51!E zlkCa_*LCqaEV_(j>~qG{m6Vb;6%@Idx21i~4mk8SnY!?CL*A6X_2cGzHEiO%`kq2* zd~D<;XCA5gYEbV-wzcwEu5C=4d)QKhA8KLU(|mZp$L!<9kz0~fO#x&rrdP&RO#Z_e z9xhr5;%&spp0Vr@w`=fML3VMrS4Ga{7AcM~$TSx{>3%KS(#_ONJR9ll>i?3J((l7i zwYTaimtU7h%pUoD!`>PJiZ_h^-Y-}}^}ln6^rCW6Dw^^<&CE=a<2;|<^3^f&aA@mU zS*50Y3N_SW-C?|Z({P+J2ErqBh4V2RL3F=n#VloOyH091$m(4rK>IA5r}I58S()Fe zPxg$44Q&biwgG#hV)FA0GGuJw?Y(Eyoq(3xbb)HRC&r6CjIlyXR3D$}fQo&eUcrtR zQnYK!bAy||gOY6NwZyX_wM%6?I!kF)`!6(S#z};S&BMk6FbDR(H2eF-Z~!?2DAJVJ z;Nh^UML3)|qccE(@0$}5Izvy5p^V9*fxIC3%bjvbdVc1IlN6X_qhlX$0pR>pwfca* zaz2+trTfq~XpA95u2zj^0#YMF)=YAT+R$WJ_w{`+b7Y2qyhahUD6-~_8~(C`7T+b08$K69$dw*$Xr=lO#S ztqeCKYzPc9(@aQc7EykV^q@q(jXzDhY#X>I40=m{swA{BOyEyELpFur9t~>4o`r(4MpL{L-S60 zya;7hmF6`rV6+Tw!SiZjd9nXVLTV$)UC>g_&KmiKOCqHJeSz_hnevi?bE6Nb`SU8qi zUd}+tLIG<=#yLjD7%)%PDh{=G4Hm0Y1lZ5m8(P5%uFupUD&{zF!+T%2mNCx3f>(O8 z4(OnUIT@KWrOE-yL|Qj3+dRA;!0v8o+OF+Ba8`WV1ps@G?mAyhW+Z&IJ}}yynOwh)xLmqlke2YrYLx>2 zce1SGi-so0Zb1Y9?AlHaKjc$z!0~|yv{e%mm95 zH3u?)65ana1j%^X%tDJC)*m-wtM_Zms)H~hnG&L&FZCd^1U4X2r+XMHMkHFg(b3Ue zwXT!e)W_S=pymy3axf^(OVvn$jfN?hb`t2PoC(W?UHZO<$I*!Rn0j3UAYcV4u#Jr1 z?&RH=t|-chz)ns&@+6hu1B|!VTgu+b`KIZ|0ME0x%gSRZCvo`V0-d)<$6h!7j>;<2FHxrRj-M^5eU(1d7 z_jiJPfBuIn_`XwDzcNb-umg*6JZrEx~(d_1Xf7&9fXU6L=5Dr{7AA0YL zi1go6bI%hXa8gnDo0jK5Bk1G5Q`soZ5z51z_KhIUc>nT|v^CiCiMBga9&%D4MAnas zQx@$z{B)DpbgUN?dSy;nNDV>Xtade;HoxVQVr&{J-^Jm(GdB_|<$Vr6%-Eg%I*`H* zg*vV^k05F!C87@v@pTajo54w$~Q~DW%!!lBw z1mie@e0h=y`s=h-45j!d;1F~sF72KRV@5uBMfu$a)FRh2L`82c7nqf9+g-+F6 zYWdqU{urF5xTZRfa^D$A_HhSgSvTP;(^{EDk3P=ovGxM@9}Ri^S(iNRm8l}uB{8?Q zCuyW~7p!G=)lWI-UM|Mi4AKxPhD?+6w}D#Q-xmKpatyegiBZ8bA}^{_v>X0)gW0UL z@OooP-`_;-NlRc55jmiFrQf3A1U}JkVx&9wQuw5tr48f%A{fa3Logo5DbE({Lx{o2 zi&+rESOS?PtDu$Z%KbxWl=wfT(UtF%WR$DPu4H2hztupkZ~5aB^L%~dqs`eVV@mB^ zZmi8jHF(x$hoSS)-aypAO75=H z;{;!q?sum*WfpnwDWfa-E)k#Ruy&CF7$TKV8CMMN1KQrFFwOCH@)?=f1S}fBUs>4W zvI}l7(NF}yfH?OyV#lFdY$$gHqb*Admvt_+957gZrs<*leu0Gathkx8gH9OS^#B0< z{Mk%$(3`ZXqG@5hfh~Owwe$UjF2ie+()jI8c^^rUCF>V7KF`!sZE8v{0*4q?S?|{i zGGI^CLlsSAIllT@WwSTEdD7Rm%F{2;f1mWdVp5qx3u8HaGy}HuM0pv{N_p1$Pb%v3 z(A&Rv5vhYIkfoK)P14b1SUm@EiFPb0{)Frb3Ni#?%qj=iY@oIYgyNzolrw~* z$g#5mfT)BB6cZhb8s}jbfB$pX=`8^9mt+-7yB)~M;CwOa#WnNWXH_YsQ^dvNh#gKm zF{KyEn4YYw6j^(XX2Yuc@|Qj%`9$H4NVJ!8a4?!ig?o0=LMdb?mGa5!9Li03q&RwZ zL>wn`ge!6k74Au)Q&j>P?~F{Opb?clD^edH;MWmQpzoJG5Y~Lbrc$U_oO|`6#MkvR z;NF34S|s7(5>nwqVPu5^ovxLr$>B(HV%e(T6LACD1z-Mw2u5#WRI15k&7mbmDFSE2 z%}lPyTasw#oQ0%%KKVi_tznP+Bqf{wer4ApUbNFL1d%EVj&L*q*QzZOQ zTPKKANgGlw%$-pjZXrtDoNDOlM+!Hqac`Ct*_f`OHau^1F*jlQR12D+T_>B422;8` zDCeJl)lhwku%H^!n6cpLHP~gEnaEy~qlqETcX_5SnIHL?T5VNv9V-u+OhXGK+|oxf zp1$S&H#w1;SKU>WF4u*#A)p3(uF;CI#py(L>`w5FesFFhZsb)=mN{jj6`*&!6=&`% z=B0$9mJnxw4fbnoZSbwv{a_t6Z~6Sca0j!YR8fSVMZUA94?g%}-oEBntVicvk|hKj zbUdkEqj69DjMn_c%+};u3>6IRj6BzKBRnue`_re;*s>N5xZ05a^yHfrf8#iu&TRo6 zr0I!G|MI&r@K>-)l`9;I)>F+v8Kc2%SoOgDfad)0US1)xzGJf?n2cl2b*7x>NKr?s zC^hrY-KX`@b)Dt}*m-E}AYRr-P=o~+cvUa5rnmaw9@vFa~;FAl~UT& zq8h?7Yz=Jv=5w_I-&kJe7y&O4Ip5Z-@C$IM{uvpXF=>g5!lR<9%a^nvotkng+RHi= z)7C=K(cG=rzXci3@5m&QtX=<%5x@skDv(j9X|!1F;%~Hb_x_pRD}NZ zd9PqIrN6#^gP(8H%^0|R35QLUPpiBH36`lZY75>_ehpRm7;P^%8oQrX3>^E|YD?VJ%? z_T+P2RtE+1NZM4f(`TAf_LU-uU*qdr8jp0}#{7Lpp|$bqdCisQvJd;nZuP| zt-&k){kaBEB2HBS^>;Y|*VdA19I>pGX*@###q{XkoJkgI86d$KBbKdXA(14~7m0Q$ zAmJL#s0W2#@C(}T)Ktq>_qUnL+=n%*6E$pX2?qAENTkZoi;2dS|2#K){v=>-YgN(H zMR@I3CQg>$9u0&#tMD*EFXTxjt4;PUyvICAKuOfE?rhC<&_>GI4noboX{6d+d;j$7 zOkOAvgr8&kClwjgQEL}AH?SUS5#(SkeNJ5GPP8`luTzJ;FVRqn@wE=pWU~nLCc6$R zPF6{%1WNgnR}_UR$?|oxx_&-(@kn6S4aLu}L-i*417k`qcvCH~$n9qR?I2sxYsnQ2 zkr6!yJr^S%tpMJ88=p*-8-yWOswpb zk{e1n%LQDySVgwRmkAl26g=$A*jvfkNDYJ4>WH~EF`5RA?X&)L$qe$2;JS}U@%UEl zpMh-Xp1uyJNRzvK&t-2Tj%>eVCR7YzEc!A^v0NW0*@6|3Lz5}{Y@Q9LX&d&ntglF*4 zfDFoR`(L&y;+#)uFwkeH_VEB?yiUXQqoPL(*bfUAh_h9vU~bAu<7$f14T0y-1tzm2&oK~V%K0TuWB3fV!5 z8wGP8KCNsTSv9e1+XoBU91~lE6X@dN@!}LElt!C`jptF^z_3IqI&`#w@0C#V;3nAa z^*4k6zD@@$cA9<9_C=%qzoNc6EUGW+`Vvx7g3_fRsI+v02q>ZWz3q>fuD@svd zABH+e>)hC3d|uYiSC@LDwuY4R!-(_b@3}fl;hWYVBU$hKQDW6bbjYR;6R3~KfR;T+JL3=m> zUY-A&H+W&!iMa&sPoCw)EG)AWbwMblscW7Y0o;+)lBl^nTa(=$N2r&5hb%MVvz6YQ zx$e$xc$bj3jk$mC-cwVK!EoNOi9rqf^ExV1YN)LVJPAwA{PgVZIEpN?EOFtqU~O5A z%g*Q1i*b!1Br^?u@tAyNSB^9cn|mA+bQqnyG3DpRm(>!1jY;^NjS!*ij%VB9e8{1$ z`Nyd>KPqb95w&NYPT+1tbUv$BvyqZ?C$}|u?!sfB58I26%u##utyZecd@grl6gYj*`-2sO zcxin$jnA8j8MXe7#gwM3W@qfuU7{Ng#p+Bq(uZ>FI*c1Ux6H#+i(Z-C<91u7 zEME+Fb)GYl$s8Y`$_8r1!`!X{ZG#rC2Kyj5wxvyFvUFS8${vFY<)tP~f0^-RoXJk3 zc;KJQ$D4J-ZU(-sr&F90OpZ$NnD1oi#mz05)xF?IU85ANul8VRkc#p7r^{Q zYWwz!Hj3k$w+f-a^g6Y3J@^N?ucU;9JQ$wG0x0#sc5w7fm9{bZ4i6WLyzcO0hnX;budZxSWE9QxyB`x2LcO5~NO1{<4F`k0Q$_(ohF3n|5R#iJAk zxhx42OJmeKzDdTBp1Ey<;1ym4MH6NX3j&qv!6Zd@mTv5A$7$ku1Yz>QtpYkTk($3r z#f*vlv<|y5kJ*R@xrcn?twf|#0qDj4n#QN!fHEmCCW6h&Z+CPly>;~l%Mu-`D^Xz}8yeT@K1Y^1#EVky`Y$u80OQK>5{T*nc9Q#JE2kT%<*<=Ujf1f|`fL zFnH${t@RG^=e;`**PgMvmU*cU`jyA}Pv@dZWylmSq&UCuQZl;AMq*rHn*2sOxC)}94f z6nMD=k^{H)rRrw*aG-5Hp7Yre6UXBe;X1nYy^6WD?BNAsLdi6=DBVpXhmSB6Ei!cv z9HPu?y*2T&8!X-7&_`$(Y)&6VPdN-aiivGc)TyoR)#%qzXpph5UZ-h-;WbVp3gxDW^xSiuHfPB9f{g6@W}M=!kTl9&d(>^+Cx~lR}nZsW2l_!e_iM zm8aP>nW_Yf*o(%@p?Kk|Q|D|+8}CyR`f^26Sc+@<&xa`aV7c4_JYsOYCxEyxR=r?S z2dYg7Eb*+Q3!twg_2rzgaje=^mms@_YZNfUGs?rob*2V+U1)n zBLU-PtBznJoJf*0r@KVY??D$r)O+*0WDif5j~^Dn@sT(2qL4)H5fSE4HeH7z0-B0)w1OQ8uD+Dw@L)nsSEO79BcBzwXioTrTh9~6RtEiu|7vemq6ScoHo9Gobn8Wk+CLt|is`l^YA*)V+JHh!o zX4`w?KL}4|B9CfX+v5+(;xpqSoOs52yUYoc|M7<8I+k{lfXX<$V&Q6wpmwFiJUUcv zCgkS3G%I(hB{u4t)5LDWYg<{H8^z2r8oo3{FIu}K`#{IC?|b+T`opkF=Wi8)bip^M z7&KT)?*lddZJfzDoa(v+$gmZuqJ{RMJz15t2x2ON00@WkZdQr8Sy$eQ!Jxm>%^SUC zffkocXb=J#hCpvBY?&&Do;(IxHofz}+1xZch`QA7e+*{d7v1CIYd`khHb2o&z-oHj z?Pfv1&>Wo;9}}RcmSM0=pAo8rl4fXDqAuMM5bnqnCT-^Fb> z%)=zKh(X|M3TSyZx8u-MZa0MH%gt8`)hFRTAIVRHWe z@r`@9-GI~#i{!2w)Xf+NF^(k=;g|xM8ngEdjoAM|GgJHD31-ajaKxp0o4H+?xK`Se z(tM?>IyyX4Bkrb)jTeM`r>$Hko(dRoc?$B0oSfy-EX%`|1h1o$kRy7-#tvVwrZ zFnbj_3v9s))bI_TM(0QNPe)IFysMKvd<2STe1)@i-u=b=c~)v`t^MDBMbcxcVdci6 zG+52~%3293XUu|`78KelwNyd1K=Dxl+#J7nX(%7d7v9yK!n_`Ooflyn2FTf+{eAc>27jKmRT>)6n(=H9yfwsLOlL;Htby5Y^xFQ4i;&d@wb zphYdBdw=jt4O)cX<^|Iy3q!?rFolke2{_!TxzsoeF#sr(0RR!t zx7z&vt7MBSh5M89IDHeZzOO!A(3|hMGq*ZW57LD1_Daf130O26*8EN`MOLpvz>C)Q z-`-&hg812xRL8%w4HoLnf?2J3?OdoI*K1Cpy~E4iyV*JBgSk&wv@x9hZC_Ys!_*0i z5evfA@dJr`R%?F{70zO{#c{-y`@zz5TJh@<*33^E^@-tTR*}y;xI{-r@y|Hp*R3JN z_@z8>wxiWdTtXVc>|(T#Ng2Izqcvbz&Gn_2xkw_-0;i^0%5yEoOphWoX~)?273!(g z!Bu`r0`|=vOfW{c*yzno#>Zk$MLscD^XFZn2LBOKU!Im(MgR)kz;E#eJ&9%<{K0O+ z{aQ8o%S1fHBi1AN#BjD%*x%>l<%umKt5Kq!WiCF~nmFQDYCDygWKtNVxfD2K7`B{%a#S&MP z;+W})qxR4aUYd~NFWBL*#k65-9gA6VWDL9i?Nhsrfv>r!;XWvvhX1&7*n*!PqG+f) zVI$q9sTGmu%}lS&D?b!Mcc*FUyu4GHI36L8lF)%plDIJFw@^X z$n__is6A|u5%jV^DrjzP2cDX8%`{?qT>q+pqxt4MvV64|RdD;Blbl}StMAQ4iM8wy z2uU5wxf7DBelSg)-ET>n`|lhDIFB;>XIi)AKN73M+01>XjzvK7N`HD1l>$l-Af$X|-3Qk2rwH|)EvKH!5^@?zxp z^xNs_Q%rc~=Ca1^k>w-$xDF~fQi@YMyv3lNyiLTW3LriB*ZdrM#qhhNsb;OcF|9{T z!!=G>4Bql0Y*(;`mT%k(ZVR*ZUU?5g3!h)92gxh1MAo-cEzYclVEIiK+byiYEZ`xv z)_DQ10AMQWp{v0fniB3`@XH&p+uK9Wyv6h~!NDodNp7_Ad8!c_;{^6!w%Y ze8Z*>Y$?5xSuh%_qri=~&!-%RSOtaBf+X8s;twb4sBuYFBU~4?Zk_nF?Igbj?d+PICz52%u=t3 zzL(%>8JmM3eT8&1G&O$^ya=V2>ukQ__7*&sXrZhEZwQVOKua%3RR$BKD{(aadMqq>{$$vAAf7#ngs6wp?`;SMSByTQBFtsemUlnJyn0C2u4Dy6#6<-W(mWmI+Q2aV5Pm^ z&B}_gXA~WU0Wfeol4^*7?RgzxeFlBLK%XzZTRZ2fjUwIBU@XMezn_BSzezzbwIaU~ zHklXX(AJ#9aFk^6D!gv?~?!2Il`cIX$@cWq>Ny6+6qyWL!ciM96 zYNrzI%~Q#b^$Z6WnJp!lQhP#v_c^D351Kr(Wxd)g@=$s6@8ya4ZZZewmLOw}8SCQQ zfv&h{{%r1>7A)N>bR9!CuTj%SiKI4@UF!R#)Y=IP$s7TLCq_;>*>|locHbN_@Kff@ z#Y`BwpCruhudIyhc?ro)`Fgi?HrnU=%6!63KM(D(ZV?VsyO@6LLRIPg)O=FSLE`zr zbn=le#<4E3uYYUTU83$2bl|U(S(@D~WhKNjJkiJn1S*=|0Ah&CrhXm>tbMO7=KhTZ z^%F4uKR{*cJT?BBfL6BXycTg`+~_gd?O>R&Z0FHPm-iHU0IxzgzN4pFOwQ#-T33~r z#Al4A*^CNB?UtmCnho(NxpG_5YA2&wiy^v2*{mVh+L9vvEUEclVKk4?G5Hp z2pRL#5;dn4R-q=8PKzy&W>wI!MDPDKv7iz4;Ni@x$*-V%v(@fEChntip76jyQR1?l z1Cwy2VP%q5$8t@Y$l6KDJf4KvnI}}`ntu<`DP{s8BUJi=`fV~1;h@RTDjgy{edYlG z$IP=ptREGtbWSDRLbp`;N@g^*KV0A7&s{%QR80kYXFX@K_nS1_%Xfa?HhiHma+S?` zXqj~k!5KWT!>(My5bx@7{dcIks(j1t5FU$o!OHH@^FSf|a+mTp~35X&N<8F<|-me=8#R`a2qca*I!L}5hB1A`OIJh*U%GLXlgexLNzs|xX(_RGxA*+a$W6&K{#Q*F zP-9OK%vsIy*~{TkC`(lI#qr2s;jj~}*inBwgQJCc@UpaE=Z*Qs!l>7q^ANHSQAKVQ zt1jBoG(v&p>0fRl_>(^gQC0|(;7MQJvB_eszY@A07#VUddE1>^Xz@JMgBoS4f&-LT zPbnt!0O_c63|nTfN|T)c!TVwb?za(utVzdl-)bYlsw=IRSgbIEiHuGFNRy za&B;4&7|a`!<9M*N9vc<%kUNy$-(bL%>6w~M+{)c)VSG?r-kD#6S#OIbKn02-h`l# zUXMZXE`0j2AvCC5SHu|RL+LBk+sbMaa=VaOXlQ-6GK~)rYGw5WeP2w}lltN(rVA2l z@5t0l$kTHTEkq!i{0Mi|G*>%LxDjgHVYi$Vza1 zh$Zavr0KKvuRG})v?Wlvrw_I~Qt*SS(QdCZHb=H97)c_uPNR}6%5{}Duybg4*9k3I z`x*fLaHL6jeRzvstFS9M5u5aGx1&AtiMm=zI=-RNRBEKDdr$3{HkvfAm36#-y&4=# znw4vQTvU*|9o6%kDZ|mqnN5kfy~yet?HE%%J$Jz6RZ|6G>rUhR*l>S(C{J@skZW49 z11A~Myz`cjAVr_JCCi4eJtq5uyWmXpo^G;Y6#gVfGC^SgJ0qn24Di(p>|#S79sqlO zto-QVmP;MtT%XuR1~imZ8~0Mpcej0Vw+8d=A4;D0Q-WcG-E7IzXe!ogj`~T5Hl2O@ z*8PO0&7`esos^yhJC@_$CS8a6KYpiJFUtJpx*=~`Z)>(tUeqLSw^yI9E|_1};$BM{ z9?@+J33X7I!&qiO^n0(lEBSEZv3KRynp`arv28=o?}qkX2D0n1<#-(KXGC?MW(QAN zF9tE+y2fbp5?UkNSN=*cqOb2N*HZ=B>@ejS~Txrj=Mn^jq}k+g>IJU+suM~&-uxFK~!#RYNJ@#O={ zZ_Lar^NM*HVZICWnp9k0rNM@7wxRj=n5!S6_H}<>AJK5V`Mvi^>^LJW*~sHDNTjd# z8zY$Orw6KLS~*%!l%Bhr-e)2^sY32p8rmv%w$uBz4)vGK6|GvpOwN6BiJy=2>QJ8@p}I(AL*ZdtsnlwZ$)elw);&iScN zeE#Nf?2Dh1YSkDwEbm-Ik~g@f)r0iWVV#InK8-82p9H4xD)?t43xVaKE4ogLjzI6_ zKiOKTl79E9%vKIB!;BQ0TyeyaI1A9iec^oPZ)JHZHW9vvOAFb+B3rvW(`Te`!T&$l z;(vXW?*~hWz*uGQ)eT9&fs%VUj3tnofdb(R(6!|DvqHf?DzQR}C!b?H>XP*JH~$ zl`fp|Kc6V7oSXDP?9sw7U?DL%&-Y**XSVzhTwekhyb{wf>IcEUdA`&^og1(H$Mi9A zR=S{2;h^B1&+YeqeF->y{1kd!)!@9&!<{!%|8sAuLH?T`-d;*u7<6Ju?H3&# zS3MWvpw8aDwW>X;blAXq@4+3riZ-QqXsh9=(7T&j!VQfc2r;d}6HhN3REczx$izKA zsj?N@8)0PClBpBcNRV5VZwxX0WXd9%xaVD!Nlu+PH9+-vHk_3 zlg@G0q^slgVhJYA&ONOqe8}3%H_JNO{syQT5oc+ai=BZMlU1Xa6qJmdFOhksqnz=v za^hU^qpjsBs|c3!2n3(`>>e)jIrd7M;!v>lw@P@Cl2-#on4NI#kC4FbAfk}C)|@gT z0VZW0SX7G1KhK+%L#WO%-+8-F(^d-yq`7vQSQufidR<@NK0+dIZ8eC`-yv;CVEV8a z(vXQwJ-Q5Y%?#g!Vdp22-R7cY4iEBf1be0 zX~i*zbp-!-X@We3YJ+;_*fS ze~~!P^m|qB-E24qX^28xLYMvQ>z(wzPr83QdnUZM>2lwpIcm@G#GK}knwEGLhHYFW zJ?q85iD4PDG0wlGRCxxCvNLfh#LRjCwyH46U zUoJ}+vt@;Jgswp-iy~|VZKb~TN((T85*u44V_Uj?WE>g>B!fmS0`OE(EZY?4_SmSv zG9=cf$!k@iisa7`h&HC_2l+g3wCU%jgI@C=fkg?#eZ@*L0o~pr%EMddK#kJuN2+mu z-1;x_v`*Y4aM~XtGS`;OG=<7i1iF>#`A>{YC?mdAC!p0Y;Pd^r9Gu#&3s$Hzp8A{uHCLM}l5u z3rMC1*&yipd0)$J*9>SB(em~&uvllrDc$8s?Kuz!z+O%g(Q_ZfYX;bB^>EwG<=$(4U% zTM*aay!(rodvrB}Hkn_$6CCLV2Q{e}q=E z5{);MHExP~Tg`J0njru3-}lI`{GHPg5)m?fBFdUXShy(F1%W0u7S#~?vcl>K>if_tbD2qpZm1?v-=Sx4lP!q zeC^MRr_co3;Ybr;V7H9FtQYOawr30gY@U2=!%4^g%8<&9rPw$p2nu6B)CAB0X~O^2 zKneJUMgu_p7{jj&_!GEpjvc+2n=?f?4IGs1|FzO{P3B75aVrvL;BqQ)NF}S_W5XRs z2Z;1R5NqI8NDJF3mlDf-tj<_75WBvcnNG`8@*7c9MOv;v$R0yuFOs@!{j1cuk)jPl z$Kkt&(AhqN(ci5G9sXjOowvDV99WFY&!;~N*A?q4K>ywi(tP}5h>C*u)i)brcj-^5UhKW%o7H@MDz$i)pJ!e)$|835T-4m&$VjdL2XFuBnY`X~#K4g?U^#TlC zc4qnSD(Nu+av*wBm5nMJErEw#jj(~B3x?66>*R?A$^+hyGX|PI1FYRbxX5(hgNdDk z-3dkF;k6Q+@p8&C4QSw9l4ee5dDK~aiPXw;;I8G)t6*VU3=MlE3#90!9i+1D*jpRD zXBj$vIZ3y@`srGtwI>WUSp4Ct>LFzr)R2}^G5FQZWDn)Ycra`zjY_*;riRko3c)Fh zZdnvYHVJ{?rah=Hh7uw5`8Ax5T3WSX=8J*tA&=gmtoaMs*Ys`SRAGb2Ws1vP`x^&m z&7@f(W4ANY--}yv>dhr6Ll!X-tzmZk{11KE=!I-Rx$d6Sc%l zQGe;>a?8z}$m^#H6I?5L6K|Gta~>WFydA5!_7?LAG&7KR)1Ou0Ew(8fHKBEJU_%#s zNNh6^7pFh=C(1EL2$CPna~`H-EES&DIpdj8(0=kY-DV80T9nmmv6jjwF5u>>>R5d2 zwl{-xaKm{LF`s#t_XvvJzRdlmKW{!Ddb{wcq^9wml+1;F@ipX{IfHhU$nE_5lyOJh zE17s)%#IK50iF+kEevFP(0H&VtHkCi+=%(#Qg6Q`^wh(G7qlu%HK=_>fiXQ5Jkq{@ zf6Zzs*_KW3qrBIr(hj|&M91~TSgJ1LnM9VwZMIrwEyH&&YD;rK=ELwi!LT!mdc(w} z{G8dvczY?NwvNf0n{hww;9}mA@&dHSXy8w~roHPAF+> zySlpynwX^#)~~Ey)f#Rm!JpISnKsIZCJ4Zva>r?fmy&Rd1F+u8bH}qFqlAv$V4mz+(B(?%RQ6i>rKLI(- zvd3EaTDi`t_RR+C?~Honh=0CP>2qX8L+h<*!(NsHYW$LEFO#&gT-X=x5VIYSKfhpNhME4?CzKrd*BOp%cFicLi@;@mn|Q>EN1SR%XINLPEIgva?wHwK+%>Ct5;)3Jth=IpeGzvzsU`bH@wM2Nn!<~>?h$ue z7B1H|$;p4>v+OpXidC-f(I$h_A`gm7U~tXhnL9;^+rz%N*rqAv=HfcgiSOX2NZ-YK zB;(UVpXYaZJ_u&+qYXZGr9XMv91xQD^Z<|_vQ|avn`3q1Od=9qiidDWPnzxRM2mNR z3D_pv_tS+S&_TWb`xt$JgQUs#cTbN#dvh(Lv?%E$LcJ87>4jxV)Lz?tOe%5R)H-PO z9@}?SBKp%I7BF?HzPMBx&bew~?vX68q>F+5lDDP}ga?daT5hHp;yd*F%OR}9E` zV+O`83xMnB6o>}Ix<+#iS@SGu(ZILhQSynM1fB)jNksf1|5?-% zYpnPI6TvrT=CAO_Zwih5*^t{PKA{A(au_xPC=}jd! zZF`{PM%lYhzmY+q5+TxU9Bc&GLp;3eS^L=C-Y0WIpi}nlMcKqwgLGfL{Ux$mt2Q@- z@bbbXF4M_;_7~Jehl&zVFGwP{dgbfccg6%tc!%LDz7}#5zEqol7TguAm!A3cA^R!kxbG&;Y}ErS%&C3?NPLd{iZC~Zr8tbwpt%h)7TNcNu_y%ibZxijsZ z3q*ZHQY0Z@|9EJ;A@9@arFZf9%b-H+L&y(B=a1_BgxVo}l&f`3&iNsSd6x%1ZdF1% zx{-N4OIy9CXXjjXXv-J`Fra zGx(lfZ0~HG`LGc+O_MRnvz6UukpmGM{xJvZO$cZ1RKhbCn--HEl1bMxO15;(A$rDm zYlrVAu{kLxB`C-{oh#lH{a?8(M5C#L3L%5R!LtcxE}Z<>v>m-q!_4VMtr~5V_{Bpe zCDKYHPJAL%5sgIGf6Pnhv8b(;_y>Fs9@j}f6xL$YIPO^`-eOCRiOG3SuNcgH<79>g zYlmNPDxh<<^LXsU||B=~R&&B9O8w zjYyRVj;k)h9(|<2mL{5Xyb7_`r={G}!b?6Yxna_vBE-Pr5=Ye#onuQ}t1ZtH)Tw8z zuBQV_Yy{SJP)ugOhjo;G)(}itz>?4q3BZb`A2G}6lO$q_muGw=mME})gJG<6pI6_v zFU07!0r|}NC2DUma(Q*`@&VQ3$@C8I#*ppbS}uzC*Ixw48of{dE+=YBWB+^pkJ-2B zkAz4}=l&QT8TZSP+P~ZJDA%5kG@Y@>ttZ!>N}k#E@8_*9k_Fu`-Ov zkLoDH@-!{Dafz?$UaKc%J@HUz)xDOoq7j9f^g z*kOwoa&y{9vtxn1wts99uWvf_EnO%3;-Hg9ZZ2KmC+$d8@tJu%^XTgIpMhKJl2|XJ z>?Bdm1u7_Wjzy}<-6p^bb30lU(l>JFE@6H^H_Iyai;;jOz%Z>tZrp_=gF^)i?+29l zjEF5rt6vf>my5o}SED3^qH8DDc^|Q?%>$k`($t1`$dxPfKxss(PnnBjzKr*A1JPCP z)N}DRN<^jU6K|75Ix#-I6%mXmDBRc)PYBhND3$(m%Ad>6w?J-Iq5aZ2`?CQZqKc&; zCT_`-D`c=XU+WTMzAsQoHO{KU?Y&kgm6+}G=ziy;aWjrLw|rgDFK9=Vvm9j)Plz!< zdT2l^%004Wf@b-3DbKZMlPrs0ayfeLmbad5y*i)JRKAnSqWXyb{ttD%S{aXt*9~Pa zQy;ZoK~+_Iwc~sa68L{Ul$XtWQamP{*7($5>cT{leMLd} diff --git a/res/media/ringback.mp3 b/res/media/ringback.mp3 index 6ee34bf3953c5ec94dfecce2fa5bfc6756ed4732..32f93307fc75e80fc44da17539cdc2b887ff4fea 100644 GIT binary patch literal 24676 zcmaI72UHVX)HXUvNC5(bYUqK4jxvErlakOukg6bph9ZKB0-_?KB!n(VS45Cd?Jrn( zRV*ZQ5S3!DK|sM86f`0*cf$LB_ujScx9(YMW)jMt^UN-1?`NO!bg_^C#6veIFwhmg z$^rn&JK{i$CAh|74HX1ee)-=C`b+2izrOzW(Q{ACUigz7eC!7RrZs@UN#SJ`l~<{% zYw1$-4NX>4EiA2V?dZ;~?w;%Y0)jS$gl>z7+P-7wo`j_2gNKf!XJlsQ7ZjD0Rh+6m z!>w;TfAMn5m1{TK@7&{e^$pyAI6V6F`KveY#;2w~efcJw|NZChlK9T>9HUm|DE{(4 zgMumf?<3Rtryp>2LymSVI;5<6?*A$gGIETKC?jWEEXw$ zZ+BcCqVUAf)$OOv)Lbte(0|4g&@ilFHA#ssRM-56m>r*=wcI6!T!|b+0wM=FjzB=d zAvLtq-zOY_*2v{Rb1XpNsRR-MaQ)zihIB_V9gN9N+gO=b>RaDlOM8?H))lueNPNqo zsJufAYH%+eRjGRk)^Azfpw*SzoeGWbr{uKOu3F!*yl%s)oGYU<2caKzU%;NI%!nvF zIEX-Cm}>wJW-atLpj(17*oMVu&H@N;?mPZ+_45oaGa*wDd8+p4krP9dgm$cXq%3+3 zlK~7db%APTK0Ab|%Hc8%fO4ifD}{+*=>g~K!vW~Op}le`a9mskK{JqN#*p*?2Prq3 zNl=T+9<{nsaw~CjFEe5W68g*(DiC$9XWnAXP#Yf@U4rFGvJPS?cbJN07_KWx={-)iad_}(P{WG|N>%!PT{{hi*uD7-?RABqmfya6 z3&jvbIuKt@nBD*_cg$DfNG3!q-Jw!SaG0pT2&|;;Dkbi<_dGQeF;uC;BB_VGIC=6^ z#7Nup>Hc%Ww1OQmhg;KO84$DtZ95B%Eq7$>{d4DJMh66ahQdKpUL@!RD1wLRX5dF) ztN96MZ%5bd6Sm*G?^0usfX(XDG`+p%t(Vs1jZFfV5wkMNed6qP+Z8vFNKP?~?% zuPBTJexElo?yt&@fa$0fwb;ba2m$5lG?|8A+=FGjf{*{6wJEiub1D5ZWHL8(w8ETO zK3wN~0OPw{O1*^N=8%3`pTb_Qg zfO6&VP!g`?_@u0%OrWzA-ljzI!St!ZJ!5=+{9VgeGXhcngMPBB&c*>~s*k46;$m9t6{!wtzNl2hf~V z4BGM1LHi7f3i`Ph|chp1*ca*)g$SRiuz zAFXt&yyj#JTy_MG1BwAzat3M?CFEHCY-5suy<9yUNzEqgQ}tMVYCRQM4)~QmWW|b~ z@L%{4k3YUb4rB&(KxdbX!BF;TFx-g-rlCr}=kD3&zN;_WeZO~j9p<~w*00{3ppw$G5_(#_hh zpav@tG@-kJZh#F~KzFA0NjW)0t$RYf)w>Q1<#Ry;KodmKH-K6!U(kZ50|GoZ`cp9= z56=Avt!25nX1*$y%NI(eWa*c^729rb@wfa)4j4oA3_UbWLX7Eg1k{c)@tPeK)ov$0VH(X0WE%Psh+PS z3S5W=!0Jjz(kfyyPx+L$pt&}0x?S^);2+`z)&`BYb*GMBsoZ^oXq0uF7)2`0<^_Z@ za>TQ_0jq0896ybyEdtG!pe5~TY2XA?6=BF^WE>C!+Q=D1qhTL%<2!O;4dp&(7GYc> zHebINh5zFh@0(4F2mmaZEY>npkL|{kMv$0#D08M4kV*tHzOp*R;uj;JiNonDW{e?8 z^XNA!4e6>xf39iepRS*^lA5no8@il`VS<-uA}e<_U%3(-^Lh`Sh+tUGcHH$<7soMJ zVX&pbBLm{4L(oFUV-Uf+1XB3npl`-;u*vlnmDea?P3JD!e$4BodN>!^F1T1C$}Q2Z zYwNpdcBRxXEW_;}Q4A=G(+~R)Y;t^kSVPOW=Y?6Y;3fDN-v*zcMH5J7J{=B1We9H! z2azIWfsB?%p+UnkrTuzQ6F&YHMJzHI7bN==$%B92ia{a5u!=yx4lhBS!II_?G#_;w zhUM@u5GzIOXEUHYs}4YVFx@hBnVph^$l-sT4$qDl@7X1<%QVYMj<E#L`KOIDuJ^SJ^I!ZIeD#b>CvGil08vQS*$dCXH zGgOt4-blb{=E(WIZ*M`%Ruk?52x7@WD4L)5HV%N3Q{ZiMlkl3>$L8eq0wISx0Sxa+ zJ)Ud$T)9(9h_5qUU;6Cwg}}d#OWT6yZ!Co_b^KaxgD0iu_~SQx30|lGbpdC6Y#P4+ zLFhsu()wR2<9~(TWQhS)bNW5V7}@j;hSh&QBq&u$={CX9-fk~`Byscp_1*UkL!>;3 z7}N!i*OAru*mrPjc7zXDGVJgC4E1HJ<2b}QUJh!AuY~1xn#eEq+)L{ptqzm;svs}w zo{-d-=w*$#f)B434BlrxFei2j*B+oJmkY$t-Y#c zY^CKFBO1w>dK2mXi6UW4|3~X?IxJ>Me1gZHZOfa#LeREnEuTRL1Q`Swx}XCx3OwXm zP2H!Bc6_@=#@fKE0_1Q~ERUK_83$gLKK^bB_Zlcdvm*CZ?$&?JbCsCfoZI3q>+j#Y z#bPgGoDtC>+6#To&W7z>e2Zmu2oJpx1M1@pgv(*XQO>rWDUNc`$ggP7z;9(V1HV#-r<*@7cgjk+PHj?=+Q{8Ae&yyU6l&T}@G z2#r)(!$dyPyZoWEi$%0&Wmx9@pg~gh7j|*m0gryQi~7+8g(YiaHuS4;Q?+xDv1wY^ z80Q4xI&+s8W@MpihH5F9MsbUc^(d*-X>f_jF5t^ZD$oog3!^QRER_6){P`9tkFPnc zy-_F~urFsdqBSWh0v&eBIY$iWIj6r4yAtI*#6EQWc|ev|7%PjEr9zNsH68z%UT-!r z^l+=;ogf!2H`6>RUngD9=>WHn%@sS9YSrfR^go_`o8>UCAXQ3p(KGdg=1KLBuLH$V z?=Y5KMe0RGLjS&nR5*HI(ITioCaeUlh;Z?o;#w7ofaEL2{uoAK#n_deS!lfw11`AYM4|#)bBts!AMA@~e z;Pgk#*&|`&l(M7+jR#({-Q`=G7yn{v z-EnIa#CocS__;=V?tZ910R0T@PrbV#Km9Iy{<_R%^YOXh_&+C4FKJF4aro`Mced^8;b+Q^4yG}JGn`NI*N!FP@r2Xt! zpNX&Y@Z4JNzPfX%qR%oRJ8yfb*`3u^GJnH8OSXBlZe$l;Xtx z>MA9B#oD)%Z?#DzW2tg5cu^VRcVx8gfQ?>cE+e9;NeP}Cf;BN^H9iru})7Zg)?C1#b2gpN!r6%e}MBBX{Iw2Iwe6s(YMSe07*o^E<8J)aMCT zD;7XP0uq^gOuBFfl+iJUDVoWA?_}?DH@?5UbM(|Bd3Ymu~nl`;*f^@@c84L1DW=9 zX+^QlRqi3v8o3F-G#V-rO>w7TBlwO${VO}1NQ82LjZdwzV#mN03D?zUTNm_*E` z6-9$dG~*kO>|a4&;u3E0pXLBlU7XfMIw}(HC@b4dOkO8C2rZLxpz&G!K)#V%j$y3R z?gk60lami^6uq_7VY0fwc9JzWqF?C1)=o7*%>1rHjgZItgy9btL$wzf&=0D*SVKYp zpQ!b<0oG7_V+|$2p;8>x|0wEulv;O|OeSXQ_P@WS;!cdz(Q+gDXX}>W#;>}O^BZuk zx_HAqNW-p-WEY@QrX-UXk)8cQDFT&jnN7@hmzfQ+LM24>B)rls06Tk$5xqpKTy{G+ zA?sR_o4Hd=Xk>^_P|Ayue`y0kN9~0ZSOm7^U;XEr+hz|EBqE8SO7C%6s2PIf+8>iZ zA^Xm5AjrWGL6;Hi-Yt{FvS45gda(I;KW20xqKZfbdKFL(yT=$PA?Sz)u1|-eUpw}^4Ox34DMpHAp?57{>JiER9A)y-W;e=}lcY{Q zFX;W?hr@5f6maEr6{KP>?w__hTRuD?{{^aYSs)&0|O#Q|}V66SP#7(lAX z;>;%%q+i~#y!`s`Essx@TSn8N5giFOoamgFN3}tJH?MgD0Qh*cG=MNpWCQ235HCve zB!89q_cT!R$63R)ToOsbP~zWKv!T)MjPM(>p4TUvcNm|&Vp0ul@XXt+v2$H|i{tN8 zE|aG>tQT9v-*MfO`beUDFA%HSERo=lEgh26Y}93n-Ja#&n|4Un4V@D70mn7##qdeuEFvaMnw^=+RtO<- zp382?I)1EpfAvZDpWsa9;K~L1CI{8kcF}VU%RWBZ&-86`ib-* zM@Z70n%>z-ZN~;mhyk5P4tn3F-W>yUlt^&Js$%W0L^UK{Qz<7^vh0-pXSVgXiX1@@OSZ5uKMe)HyEPq z^d1!6nl=2W)hgXkio)XY5_+82y6;+GIsY?$^g(lQ`LWyv%FzQrIhdbP;sc-o@(Rcd z_6B=WFIzNseTPY)0#KIj4x;H)5C=GcYP@Hl7EhV}@r;qLC;TMMRy1@QHShqgSmE+P zjU?bddhEf5CgA(bLzj4Mh!nmd5XAooWOh{|iU+sI5Es9nxb%5I{!WhvhBa{_b6END zeRS@?mLMFn5|A`HeMK=BF1;7Ep`{L325h2Hkl+px&ZAlB-=S%k17L%O2of03=?FGw zB~Z^#`_k%uTRW_B;!qD}sM*TgU+Y?Au{oC7QAD%83XFWzy*;2rHHWPb`WsPvEzOHIlB^Iv^^E;046)sV{89uHneDcQs zZGd>*Lr{g04mu&5!K$7a>K!t$j&(C-t^cu9P~hTeHA=Je-(Xq(&T=M{78HphPSD9# zNo1z>N`>wVKmTZ00g$jo6cT(5TH3a$y!I@c;WX>aMD{ng+C#w#8;9K@CK1A-aO(9Zvj^svWmo0eKxs=s$K_+hoqn` zM+ov=*!aJq0i}Q)AO`3Kbr}1=FwQ3G_D#PXn_L5JvJ23}<&Q-mJeoQ7~xz=Serl}KpUhl3wI5vLm zAK}X~Mbq_U&q}d|zM=-#VgYdpuC>DYSSzmUM;spc2UmvkG=Aktxp(4zE;_br2vz_Bj_3$uyU^FPnD{r>>^9|K$|KR2*8gGYEI z@GG#{oXg+qsN!$9uAh&gZU@%b6slUFOxU|yLi;o~xNc4Rn zI~~477RGv;P!6VgA^EiIEz|#BKo>56D)gP;Dxemo*JQzfj6^Cuz}vy-@qHT%+Y-#+ z&6~$oQd2^x$=kqD63#LMaU&x*X?h~2Ot5sXDj8lKnjtUa>7%w=O^WP6 zN5T#lsx1W>*Gj954TjwZW(6Qmp~ z+A=PH8<4lbV%LXMuFhY3Rol;?!Tr;qy>m8gIp}ReS%=)C1G?4>l1-`$X6JAk)8v`> zrtqCbZ&e}TBQafux%Up5UrJrRrjBt^{cuN5>%i40l7`^ODyBpHV6GU@FV4Up8H{)x ziX*}Oq4MWa%u+--DpL?-jB$`h)cznvfuwd7bH-YuS7PySXn?|R%@{{skgV^BaJL00%;R1*SHi9^qbV2~XK`EzpQ0}B6 zxc1I8xW3yP#PHg!IkEg6XQd+VRzM+H4&|rySr$;c>SdWv`>-N%Ml2U*?+c%WqTnCE zKI0)>hFBytNQKCe@dfywhFnJ3{7>huGQo}p6%9qji)T2-k|^JiK#BoMrf-74xWe_M zmWly==L}NeC?{;@8|bcN3d2kkJCCifR0v!OxrNtTGbUeQb?old)gH1)PW4SCH3=zp zBKPqg+M19(ezBWE&PPs3om4#J`)4marKv-Er`D(6PapXYr3-=uUSx!VS?nL+J=Y%U zBf|rhdS_NU)Mv|5uXc`s_mC=}K}IB4$?E|(akT8s3Zr8MR&Aoqe;8OgWux`E{do0G^BD)<(=Eip2S+4gXn))g9gy&s7|;Ue{1=3Dz0S}BxAr~!@=2HTz*(1kTI#nF?LIGWw{6G{3wTvw;O;mzU=B>BcH#bGkN%ERBLe^|d%X+tVava!m* z9_iV5`t@(J59RGgt`DW`+u<@#$EFMIPi*WYSD)(XjUE_ux5zFQ1A;h%HgKjOJd0Qm z1F9BzM01hD2X&*Z*ovV6K^HhD-n`EKGjM?WJTn~W(S43HL*56uRC&D4lX>#^XJlxZ z=eNTp`c=N4C5CNcW^!-*%8)dQ^=#6u*kPue_PXfNGqb7X^z}TykMB4b&1%pvSCW%8 z7Uf(x{xVDONtsJjm$|A-?_xRU7Qa`>?5b&ATmH{CV@L1#iKDOJj13ySv+6ib1ydT^ zzQJiyl9Z}6Ha(o^m`-Q8km!?p4vZf1RFux9i8 zke*GIPI7gYnuMm5M9n3x)W&@$56OMpl6=Aa{$9MaWYh{r-Myp4wUvfVxm1(*#)AKMkGK)V`+dE)FRDtPJ?Vg9|2%4zTB zDR=#M1fwy}`mjiSNiQ3%I7y4&McN(S_bIt!UL>HYXeHb30z8Lvv}7X4{qs6o8x>CT za~-syzw6!O;+$WustWa+glQk^4`0dWHk97*T-(WtgIcA(S}dPrJ-600kv@Oe@1f)^*6?;m-II>N8O!Qb;N2OImF$6{dkZ~oy<1!@RRrUP57gqsi`aE=26*e8n-IR z>xr!KT|6Kd0?c417cx!I%yY(490YfmX$a6Uc$40#>?X{F2Z0@{q>UZ1_RaLoN}76~ zIx=@y3w!H0@u`~~%KuZOwtL-p((s{#<(mx({Fy{XT1kuk(K!1fWY^J4*N>T0JDM2xzuvki1_W~oe{^TaFiDa?5tAf_x$e3p zc14TVpv8?){z!=8xqnRF-%w^)XC#~C&xUPM&hihtzFK1V+G>~Q*A(eK;mu&AIzBW}7$1*m~+l0t~;3z_e zIK8tsUrS@L(6Th=<>McBFSQmMt@ld)^r>OtTD}kR z2}j!(<_4p)w~p9=_hP75?0TH8$K)$t*i!WrKnnf?gukUbOKw|~4{y|rMqRZAdQYnU zW%v$%xoO7V`SjP$caU%^E=ACbKx!Pk>#$sCb!%GD6S+oXUkR4LS>yRFiT)C_Q#qKR zjU1UvV`+!_7x-%HVy4g+`=;v^PssJ)-uFDcPy08#k;2m;$#o+{DF+GT&kyKybwOPM zi)9^Xk?|gnr*wMpmGy_jbLk^ClH1l-`G~Hlpj5pWv#X zv(9y2myeW?_F6BEHXm+_^G74MC;EASnJXpu*9@s6q`zeL+{ksx7S_{_+=y8LH>XHL zBO#4Q<=%6gU|xY!4;6fx1dhohP^ZuQzBMC^K+-%)kkj-DWu@HmP9KlT8e+jI^Ldp& zDoYFTRJ-ZM^&b~|$qy`iz+W47`_hWu-Er|T{X{}{9DcoT?$+bD<>cSrlFwaDoBVxd zs^j4wXtA|r*}a5}b0z;F7D(G@B)Mr>^i520M^|D$)p)AR)G zF@eAou%r5VF|v+mGzOP=M@w3y4T(tF9d_4##T{66H>@_Y=`!}}d{5eNr%UD2Owzv5%gYOVV%pmO>A3`oG0pAh%SIE7g4dZ$vex20h= zBc_?Y`NVF)9=l7KM*t!T!CVjpW9qImpQmZ}@h8>fbd&h=DLWmz4KCrZQ{!sF-43Rh z`jlmdB95q9uGcZINBJtjbTfM6#`R*86SDAp-R})eXAp^v;YySao*!W=t^A~s;Wi^e zq#Pkh^faWU9Nw5vaiYB3bPF#A<)R}TM^BeB#?ZG*1*1%~8pVF;+d%OP7&GJ1PnR8@=d?}hgD&=C5h>DpOYO(NDHhB{^Uyk+Bm1)HHxU{L5WhY5i)ODLx z3xDm#mUAlgOn!U7y#D`asEzWiPOn;iKcyx*xpo+Ae9PkSzgP>SX0tPjCVB-o%_aoU zVxoY++=lNI03pm+)Xdotn}-nh8+s(+6Ad>^@&x^--wI=`3)PfL0eTXR)Vi<11sJ7E z3WhwFhq#`ib6s5k`uQETcGNCxNH!2zLm3>66e^F^+qf*) zETzN_Vh1;CI-SmpOdiQ7a?7&OiFm8j^0fmsLx|KZd zo$LC?{k2elz_8spI>QmuBP5rP_1`>kTE+V*o@`!cI*SmEKzSgz8wp@8z@iypfjK)l zrOxH%#l_Cf3bbKv0>N3>vxH>6`dj9PwM$*1{dph1unumqm{Vu#C`EYz zyHEBPxDqV}*DJpO<&p2>up=HQ5A$&6l1X>7&dl3JX1QL6nwq>UN*r`1;yt^D1Z?tmLOvM?VPV{^Q6&D6_~DUkJVs5 z`AHo#etVd7vc{&oCLNme(a36VX(@kAdEr?zeEEat%mQ zAff@CIz~habGMarS4zyPY!O;|y!BC5Ru-DB1*DHx(+p|I&hcXIzc=E2!b7(Dv?aCP zR<}QQ_hj*hV3WSaS7v+ekKVj(QuVTrdw12@sK;C1sIPW=c3|e*ogYFmpsy_NM9L&W zZxOMpQEYLdr+wYIelqk zdM{QB{JpMTFt|5;i9)(KIz6oaTNl+%@7(KFMe$sB>!DK~Y5PM4zp)IXn5u-6C#OQPRtazV#RxU(j`J|3QP(Rl|297q(tSx=8dN>vsb@N|IwNU#w^dkoroQ2C@qI z<&-nm_cbl#)zoXAs`>Y++w&w9TdR(Z z>|ZnWuH9^B&!K~xE-F8)xX>ebVmA9NyKdsmk;cOZjt}3n{4;sUX^-^0nMk2y zwf01tQCNL?HXG1DGM8pPkXwmIB3gx*)sxQsY1$utOr4q9*^$3J6ObTi5^|X|?6@c; zAw{lRXS@@a!pRZICrvl|$g?<6=L&xRA3W&OiPnIYcf_d!SiURxrkInxAO znWFA~6kLi4?c)0TCGteBqs^zg}TEx`t7mHxH+&Tf;}zZ%E&H1Ql> zLmM?V>@K9SvDT=yC#^*(4MiDrkDvsr%xx7P5F+h=ox2@$pmC8H5S;n2bp=ScdSEm% zuj{C`=XAp*3*5cUO@Ij9Bs**lFuC?fQyqS5oHOAutw_^k`k*u6NnGv?0~44w%Px)8hvYTNfIb zdS?*_8uRtb{F~xDweR)EDI4gm8nZ#IQ`wZ7PirhSQpZts``K_G`jnDxid&{{r=>hX zTep8QiK3fc0N0T?zG9|_TV|SW1W^}h@Y3(CE$&+mt#0CljPtXc8(U59`zhV5nzdZb z(7qoqbj`GTxJ@z9N|6reUG&v>BZD5$u+RNa(m$-$BnC7MNFLEc)B#T!rzzpl`|gS+ z?GFE9_q)qp2a|L@cg4mRd8~WdzRR`wrrG8)mu*KJAqo7d#@cU+_HX2-MOkYK4fG4V zvruQgQ3!ckD{lZZWK3~`ykw?f_c@WSuDmtjJ*}sNJ?)Qk(HS@Mwj&dcHOO(3HfsAf zxIaC4N4xi@?nTSN@0-#gamJX~;HHK0p?Q=ZE_0gW3Ab>aCUMSpc~=d{F6{B{5w@V6 z-ilzn{>_r0CxC24cEYrl2Ko$}a5|*8Hg|SS6yZWhr;~w z^?1{ZH&Ztg9NMYEl+4t8!<_zSmn!v%lWAK_PF#2uzURb1q{|g;Ep@4Ebaq5U@js3^ zZYf_(dS{;pL$f!v1>D|V3G?tbZ)kD)c~&-R5+!zW03AI&oN+Gj;5R`Mwz(9<2(}(tE{pZ5KQ>=P{zeUVIw7+1*dWvszAogXyaAV zSrK8e-?P!8?~uqZWG^1h5G06*Y#<~oQ0+Onpfx~Y@bkElO6T_0)wx5WABxtI&T^5; zXO8E(=QxcA{8VIrQE+pR)hCb2S1D1=v}OAOyPfYy7x`U?zxgWuh8l${lgoTg(7Z zzf%$6t8BMoALb1C`T3<Bdu4aCc31wN z$SggtL)(5p(2pKbj!=|MA|#AUIH2e$nbx)%8sBo}Vuy7xqx;xfb5)!sOB&FABp+}( zLDiFnQ^4?+^C-8waT(ZR(kTM!n+6IdEBU&O-W@QUMkQA*}m)#r_qt7^>L0;wd;_#Y5{=fh1vOLC5*%jQ@zUE<9<{kAl zH70M^e(gnp0awDFZq+nV{?z$py4_@bXKVbbtuyDZn4V7c+zjvE;P%TGZ|9e*(!XTx zR%Mjo`rXptODCwRqlz0Y>@>&UbVHHM)0g4D!uVz~%?00fRVspP*{<*V-f{t{~MC0)mUVkezv$#47BGhDaZ zJP}(&R9Xe7S%YB-&Ws5riPQ(7u^BKQ2WSwVm31tAoPJh%H*Hp6a&LxlB=EEJ zYn$M~FBgq$oU;}^hs|W+yDQS*uds_?fIUL5{+r`VA zdQ3plUe9U&dLM-}q7(k?M7~YI(M(!4xqBD*KqG^FZ5YWEKtYmU7$w8&A*6C*dH|5sj&a8#IJA0Vk)=f@U>V|2bP;=VRZ)rLsDV-p({Qz-pkj8&en4#{Q}_nJ$?1psnngxZ zV@)Nz4#mDja0=vl3#5hVdo5Q{X9@KDBJ`&^`rcWg5J(-=fN+ zA%YlJ6+0$Bd(5z}g7!z{_n=qFdG@E2cwb!#FMVYX?F_=JNf$2g&LEC8hVdxZSe?U$ z*F({T#-Jbb4pAGeCcUT>i%(47v)nXF2PE(i-pku#&@3^2YNGVxA$S|mj5fX0aszZjqE zhO~ee9whuSLo|m%1_n^4*h5YhIDSalC9M!drd|Z}gSQ?4QA4DkIBy*XR##^$5y_O~ zWCN8z$V?^%P|mzE+Dc4~kc`TTa9Gh0AQM3mAg?SVY$&xfvOzJxlQY5A(l;prQk|O= zI^+L+760$&&=5EHy(FH$h$qP515AK<2PHB?szZy+#12G(1PGG$ri|B2u&F#hg8^LH z@5j&Ul!xp?bEg0Fj z1ZSUdsz|H)Wd6&UYGttLgYi)D9_|ag9i>u-kKf&=@UmxL&F*Ej_A5CDJKhuQ@ z$&dA;U$8FAbPGc+(;vH2EG>8TjrWpbL;lqBIZ;zGJVQg7TJ?6Cq4j~th5)BefUZcg1BR{5VG|jjL2i^1d(4i)S@!e~lO!sP7t*82xZr0nsQHLpEj=*$s zti#q25m{Nz&ft(GAO`fECFM$%X<%0-hBtDBI(qknSZU#q71Sx@@o zM2A>;>Xc_mUOtWP;%z~d?P7^|HU}9mK3n8}of5_hxGU21nP#-8l?HCE=45;+J97Tujz3w7)9zgohu=^kJ2;MeSZss)Lf*=7@i;&QI8?#rWWp;A2BV~aBRs$cPZ`4 zoXSY>8iFbFdE@k4OV8)l{WBr(-=n_*W9W zjL?legq>2D{+Jl^JmOAiqOMCeeu!U6G`Bz(RD8=G`}eHd;FZy~ca~3%S5-_+ZH+gn zF;QFTcTE78pg$;X=9NKIV|D8$$J}pEPi@>zr}xLG%4lafO=Qb;a**R`X5+QTRf+43 zenFzG#8o^ELnOf2ekRVq=kC#CKlSZCRM}`8KTKPi82Ffy>ss3FwH+ohyw{s@PZkHti_J8I6efny5 z^h!taBXYeA9Jb*C&$}_a9B$wtn4*!K+o#oNDP6+DWwpdrH=xB;8ON=y?eK}e&=$mX z5x0-+ct0iS?^$z?7Swn_VY$aaK{;iEGG2*T$GEuK@bo(K_wSmRGg9aDiMhJ+v>1be z_j~2u6OWQa>58IiA4NP=026VucqjPP4-J%B=&E+nl=G@S@R!yD8I9zUY3ApxTgiR4 zqR-CQ+a5ETQ5AUSlR2$3&M$3=Imjuw#U!0KM^e_3%P~6r!zkP*nUGK0G~U!5dF97i z@gV>|17`|XmKh~*6C5@kRewK&ecV2htUqZY+2aM`69 z(a;sLbE0wW_}MP{E~wxgr0Jk`hlV+~QwH!Ji^+&;E>!dw^QlUU&ku;FSq36YjdWAu z60e`e+D2<+vl{gkqnN&9l69?>>PB^~v4xq#66Z5VnorGKf5An|dnuK9@2+x=76bYS zNIua0A}+ygDYb9|uiN(liXJZ)8A4)QVtTbE*hfu#=ZlE%)ZuL^6UdAS6dH*|%?&pf zzELA>Bv=+s^Mg5YIa<=(1RD0HDjGmqkz|FK$~A%@+d>26H#8vJ%hA;KQ7oxf-Y~&g)Rm6>_9&9<2sf-77ztD!gUuN{vC-Um`k&)1 zWfn?^JrZFQCf*|zgV*SDet># zzVp%Kx>kj%cW6v@7sz|7j_-u&Ms-$1-L{TfghZ=b3bBa1=#-_2KWFNFlv0f*CJ%Re z!3#$r#naHcGD_8)IhPdwBeq=M=W19{NzhzUvzp&r=Q*Cu1Eq+8;opNnyXHSWnslyc z1Ef}S>13C>Joe6{(!NNhc(Wopx!Psq|EnPr0;D`3rje zm`e5ao-D-(Brjj3QmQVl*ldh>y$I|urz#a6$ucWyKR`DR|B5jWs&oSsXbILT;2Jv(?Y@ixIqhD4$~$;BKB5nSallKU6_naNDYf3# z%UKRsHZN(f56#nh73%FA^CG)Ajueh01aoD%PKEzhFIOIwRNHS4BH)B0CeBRGrKn_T zq~L62iF2lgW`>Pw!)s;#4CYG!heefL}U z{&m;-?swPy=NuMr4s4!>XFb2Y_irl}qS{ zDtg~Fx;<|%QQ$cma|$^GqXh{?}Q%uQSYGz}o+k=k75(IhuQIdAkm)3jMbWT?fQ z%ACy5d3ovS%l+3H_2#{qihG|$SS3rHrAyP_Ol<0!u^(}5WR<%eCry7|aSyND(^Tu9 zU<==9sK2G{%hShN@(cG$y7mFOzOodee%ClUF;jX^Mvh5UjaN}c38Q)b6tlj3qrNk2 z0!}Z#AiGBO$V)m_(Wl<@TyOT9RfwoBG; zTh`{#?UXg0Z7uChY(L6r`&S1XwtG{Re@{6vpg<#hXK9`h#1JZ16g-TqAq*0d?Amn? zXMo9MxAI z1BQ@<*!?A)<+eM)l~hf|Lxgn2kwnW0Ov_;{A9d8Z^!v`-KfDQo;v4HAeW_W|I`RWS zF34avB;uM_xVCgMIg-0T`%N@{1dwZRA8>UQl*L+1n`+q)GbW_iHHUoEk!XR|PIsAY zX$aQVMz6~gJ4iRCjO_qi;{Xz%sF<_mq`6bFAL872EuVNeK(-Qv*s7;F>pesh&_ufD zG?{$bNN3S|Sn>}MW=a_biE92aM*CLN0*~`Oy!c{wXh)E4<&|sw5p_4hCN~Z2kbW#5 zOQS=m8YPD^bhOs9FQxCEY#SRHDaqMsTjXSL;fG6zSs9hK^F7jwRx{+JZME9*@WfA& zD@>t(*ibO$a?Zd+bbfqg%6$ir5RjK+ovE$^%ZERzhV>A++4tV|7lYd!(5}aXEAXMD z8@ucnbN#zW;9)7MK~H@YAR}&IzQ{pNBP1kosuQoamMSx3qckJda6x{dx$C z!A|g|)G`0~4=HxwK4Oi|Q8RhrbicXY zh+GBvQiKV|b;6qS5z;uGrg?Mwn!@Ki4Bv^QD$a$=#nCcgf}UAcoG)Qa4{HxHXwS$bcT-)~`=-OO&?U8K)#8i)oX6W(3ojbt0I z00QL{O@r^4bgTP!c-#4)obn0-Q0M`6IzS|gu4JpEnBl@+23%cbT4Z#A$S^zYl^_vC zq@<(RR+eCEMs#%{y2V6iWUG@%UVaMIXj~a`P^}bd=2D~~@21;l8Kskv;pO3#U#AXk z+o32AMt*^Lo4$ZwpSUL22Aay2<_W0#w?13IBwFd}D4{V{KH%mp6;lID1A#KK1w5ox zu3oM#e8`2&NgZ7#Tjd{c?zP|}qy3!a2LPp%tdo#@P$A`;@NXvrza_~JOeF`xw;IrQ zk;DrJK#~NG(ptj~tzVU&C`_E?(lg*H2(-aEwk%|}cL{ZHSy4wRg)fC@%cR`?lHxMQ z@TW{r`q#-!`PL)2KBB*1>hQ39Xq-VBBs0;R#ZMyTLxE?Wfkgk+ajql zsn`lzP~Eu8+yQbXpm_~!xqZ7RJ9PNoF9iloo?%q?Q53<%6w}wN*`d&VW{eaWRY(+- z`O6cqh2G`)uc7M>94agLhk_u2i$zM+yg-wFPsFD z^(8g!78r;u(SC?4i08KKKp5tswM2L9logllpc2wGAoe4@ASeS+~sN^90U{AX( zf+(#?qgcZn{NiX7y!M4KEyWZ*rZBn@Gm3>@3_@W}!-UYtsbkxLd8PQqpUaCMgLRW` z?MHk+b7gn9`KKi5vYAtA-SC$Lr>c$HZhCM!ANE>4+OjL-+ktgz@zkab2D@V{4TvY* z-=DYi^lZ7ld1kk*aUE@PsIxjT%tlkX5CbqFRp8EZWy&!(br7Z?AW0I<`@L-cYF_@- zd^6bMMfvOXho625{V;N6X6E_fh&=X8vt{buivu0kFCT5*6Pzlvf5P1!eCon=JD9jN zDcpT5>1c%F=yykDmznUA^r;)VGE?D*hSA9Z!`k50n)^V6#4i@CjVDAxvH+8k};+40}g|vjsQ0 z2U6bty8dj>b5r%Oh-ZzP#h1zTYxg}{%IBA_Wme@7$CwhgmmA(Zbw9uL>eR9JmwDhO z#u^4`H$}3!O`3ou5=Mjqrl1FR)B-Nz0ru;bP|^i}&Ey{>b#Ti`BQc!KQ6Dm-%W;mQ2I_ zCwP4Hr@(^K@#yW{4uur-29gfPI@Dc@`P=hBn%+6Ae%=y8R? zuK6GD-llw>Hn@A2cDhsZL3qHaN(<-n^qs})a;3|*JDoF{)`~WdHLQiV z2+wXdPUMwuu(3m;grf-JC@G_pKD@a;59@`%oU4cQ#g@YhqlY%fdyyayV~u4OWU8+- z^1#}`Og?q<){gp85)w+;w*m-U6fWRCfXg;;5s01xvfw)Mcgl5Rau~;bHf=Cb+2~d+iih(bg;tjhnY97?&yyV?$^G z+w0BTmJd87NS{Umia${}m6`=9UL@Uovi>7RKC)Jv%F#{DwdZGu6K9v9h9ED}ba-bsBE6_O2e~BfbiyYP{11r-;J<09r_w$XSb_I!?;UPDDxK zs1-*Np$NLoRK)RoD!-7Dr+z}0IY+%`M#w_@5QMkCY^sT~oRFhP==MmNDPUrY8;ckK z!A?QwfIeL9W_^}c0txc0J}59S8iJR}Ga%_DqfNVJm+jW+q78e*(xv?mrxthA9J}|! zwh-^;*;nv2+JvLvYoD8|kU+u2RT8oaiZbQ1lz|Hd%GG%&mv49i-l*%!kVMj#5@u=Z z5PA~KLIVN)%~a{ey>$i42VY+01m?V)W9W@F2X1aNx?<-hgJ$b1X95Bxd)F*7nabbY z)vl~M_9kS%g>-4X6e4mmWQrD2l(Q&tOtK5K>%7XL&dZ5k$jPF^Kf3s4U7e{PFlzGZ zt32PV>Fypf&TJ1SE$-kRZv;YQ$j8kItMY0tsXaRKmuu+$4QD|!pjagC)uTI=pCFx( z@k(+k8u6AvY#_;Z5T|1idMr6pC41f3UKFi4?WAGv6e26QIifXgpR=o8yyf;!bC?F0 zQo84V`u{3%(*kQy3nPRd1~;c&i3}-ntVLN@@?&XqQ?C&j1QZT3J5dNIfA+?{^8{dBVCEc`_j!o%iU17R+bT{*8A|l%MA;&5 z(pGOm!G^xVBvPCt*D2Qj?z5M<_EC)ua2We}<^!kptoe1i{1W}s!MX=}C~T;k>dF93 z14?RyPgtW0)*pm7q(k-gOH^g%EN~PW`3f-(L1ZG5snRZKmJ^h@TLr?Jl>-laA_dBf ztBA?!8@v~EkkB>CGrKmx{?qKJ#K}T1J!9Nb5g=F4PyjpOk3Ja%BvQR z4E-oCfL-Y!uDW<{(+)OpN zmAlTXDE7PEe&1nZxmh@iwQO)1k)k4%%s6M^R8&*IQW>KfDG`A*4H!tGiY~Q#&r4-J zq%gJNT^u(BDk{N*Bko&jjS@I%a;N)tMyPSdfWKQ#8m{8t2VJL@+wmdoTj1ipb1kx| zM?MD0YsvyJ*0#)E68dT57efF|Kl1C-2}9|(X3{~pJs+P3pO9{v=cTP*&_irM&#RAT zyT{ofV>~<4)HnmHIMAuDYT2`v5gk!O(~u8QeFE-Mn-0fy1<*UD{02Ovn(!uakXL#5 zsG)FrQpl;38|z&%1U*-?D@luBq!=H6S}qRK+H>;n-NU%aFI70>q|)S5Wi#(8n$^6n zMlO;^+#Y_nISr3&K$EI6n0^_OBc9F7AvT(wKM=JxuaJ4euwN)x{N` zB^7~ecx-u0VJt|9mvLi_eMm#~r5||x^kE+z@2fsGzX1V?8$ipcegz~0^aLNi$}jz8 z$B+kELNFz0#51}^#WLvii4)zAq|!$7dtBU_>uwTa=cP8rg^tZzyJJ;_eyH5ojO;>C zC@n2CxCGc);(clx}7D1F_)#d%x`I^~B3UJd+pa0NVCdcjgQtiZ53 zD+%o&oU=bX``AG^)bCj^mGBj<_++e4=ol(DM?TVq6~EG&N_>wOr0_vA7$ppnmLT&X zF`=4NlA5{ODS|Z+x2;AlaQ-kufXbqbv+!9BN@VrJU3r{!E0GE z9iNO1zz{2;k1x131ktzHoRc3696>6MQ?K|g?R7I$Lh(Y-09b~s&bEFg{k-Gj?YGCL z3a`}GUf7ZqZyw;7;=1oSRewvU4M~G*tahn1jSr~#1{FjzZs+j)>^pOo0z2_zYH6Oc z!Q+2PJD)Aw!Yx^%++~9m(-c<#%|IVe3F9)AOAs9XHDAM)pQaYS3QmjMm~dW!l+$pz zmeGI;%`@vXuW1R~V;AD<9>Bnj1!`W{S)gc$e*$jv@zZ&@yb=lRb|p2 zDYHJ*z>~Hn+D)AJFjuewNWw&F;wHFjzYLXt?lMp_CX6jFAJecUeBTCr9}F%#&u({& z`LJrYtBHp>tQ`u7}gxJ@kFG@Itg8$KZ%V0mX3?)g(V zs5)Y5gy@+rve0MBLShqIPH4#25f{$hTUJ@xoh!Dk8d$2{F&^)nDF+%i*;wVQ4NAXG z9B=#nuH^gd<6(#3Ouw%Nw}vyTFF~u35fKbmu+@xwLgGtK?bLXBSuS`@9(~cdhwS2x*6x3%yT3!%ok&uZP%;sST)BfVk!#}H)6TPZ zrJ@#Jv^7)WORwGWImlQ450UF^w&8{KP4E6%>#5fJZ5Q{T<*nMQEw|4&e|~qAQ}#;} z08Fp5qGPh4zpIA{V52p-Y=ca&J5Qg2x|+SPqw@~HCxeuxbb%54{*l6uk@s65?;k37 z?zDEKm6hT3sW;Th@;|S~o>uISav36-rd@82VEV6VR+qUpw8~>v>r{7Vh30LaZ3dUO zkuJh5LZ7?1srnDg{-;;>UwG+vJ7S-fi{z5b(DC5)K^ME9 zHzfsD4@PWiJ#H5`Jv$wIYG_K?=I?zRzuTctKfUMzQvr)HODTD5G%5wA$5?H6X$NRI zuDQXQJ!ZS-(9!YM3-`?*C;F7{Iy7tcrz2rMFTfZ~3Os`OoZ#+~OT2gHMt@^}Bg{3f zeV;ibV+GKArhJJJv|8JVmdT4~3%JVcl1Yu@z9L$pw3*nD-JMIImUj!HFgTdYuc&di zwV_AHO#R#&w0=+U*-Y&lmkrizc`!C{@S@ke$A_;oiD&z}Bj*kjT|3@NU-$aj+W9@= znTm?tg1IS?0etc1HQh|@zne6BJ^OFJXbrtgVNo%F(cxo025Gy!?j)aj`%ZKV4D8_K zDrp+J5Bm@b2zd$&jY9>?g|bn7w2nU4K7Su;bJaz!0vm+W62AbB$#Rie=4hN?YLjMRJ9cD9K2lq~AJG&f~j z$@y#gWZi%$)bc1U%9RnfXh5N@g=`x!+Bi7({$a+(V#{d%qB9jvzIOg4+NjO9`L`bB z)b9Fr&i-Vf-3vbl9XBG8J|v)4&sgeaO5UJXp|lhf#(eK+FU1M&ik)@r)_1mpGA@8# zb*RGmQ3dKyO>I?`qs2H^&)34yjC@!bq5?o@;_<8JADz=uum4k3-k{3op~`$#*jxeD zV+D{HkkdE#i3OgrUh%?sLC(yoj87RrHJPUSN>OmHk z_N~pPvY1lzL4GoHb|unOELd>7wIV~t}>)N5%q&CrcGG+%dDN+MUq7rlNt^k?@ ziGWL*qcIi&@< zm{)7kyD580AdNw4LGViDRJ0jQ_+!?rU0LswU-$Iotwn z0Sz8RH=!Glq3t?#bnxPfE0|+WzTy^Lw zz3|k%qBRMA_-6UW*??k(I^pr7JSgp@^b__yitJ?{(QOb!CUK>q;^Ot8hK8m0ygyfiN^ v%F394NVe)?2JwFf=->Gk|A*JE0IE*q0}vhn03eM(Vgi8d|Ka`r-*5FFE?|OO literal 18398 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 literal 8352 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 From 386a459b9f8629e0c3948a0cf412d2b8b0d768c7 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Thu, 15 Jun 2023 11:07:26 +0200 Subject: [PATCH 15/68] Remove unknown MXIDs from invite suggestions (#11055) --- src/components/views/dialogs/InviteDialog.tsx | 19 +------------------ .../views/dialogs/InviteDialog-test.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index b5e96a8010b..68a31a8bdb5 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -26,12 +26,7 @@ import { Icon as InfoIcon } from "../../../../res/img/element-icons/info.svg"; import { Icon as EmailPillAvatarIcon } from "../../../../res/img/icon-email-pill-avatar.svg"; import { _t, _td } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import { - getHostnameFromMatrixServerName, - getServerName, - makeRoomPermalink, - makeUserPermalink, -} from "../../../utils/permalinks/Permalinks"; +import { makeRoomPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks"; import DMRoomMap from "../../../utils/DMRoomMap"; import SdkConfig from "../../../SdkConfig"; import * as Email from "../../../email"; @@ -724,18 +719,6 @@ export default class InviteDialog extends React.PureComponent { expect(screen.getByText(`Invite to ${roomId}`)).toBeInTheDocument(); }); - it("should suggest valid MXIDs even if unknown", async () => { + it("should not suggest valid unknown MXIDs", async () => { render( { initialText="@localpart:server.tld" />, ); - - await screen.findAllByText("@localpart:server.tld"); // Using findAllByText as the MXID is used for name too + await flushPromises(); + expect(screen.queryByText("@localpart:server.tld")).not.toBeInTheDocument(); }); it("should not suggest invalid MXIDs", () => { From cb2b1718ff55c8d9ac2838bee60e214109853adf Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jun 2023 12:02:45 +0100 Subject: [PATCH 16/68] Remove `feature_favourite_messages` as it is has been abandoned for now (#11097) * Remove `feature_favourite_messages` as it is has been abandoned for now * i18n * Fix test * Remove unused css --- res/css/views/messages/_MessageActionBar.pcss | 5 -- .../views/messages/MessageActionBar.tsx | 42 ---------- src/components/views/rooms/RoomList.tsx | 36 -------- src/hooks/useFavouriteMessages.ts | 43 ---------- src/i18n/strings/en_EN.json | 5 +- src/settings/Settings.tsx | 8 -- .../views/messages/MessageActionBar-test.tsx | 83 +------------------ 7 files changed, 3 insertions(+), 219 deletions(-) delete mode 100644 src/hooks/useFavouriteMessages.ts diff --git a/res/css/views/messages/_MessageActionBar.pcss b/res/css/views/messages/_MessageActionBar.pcss index 04ef242f93a..040442b8af0 100644 --- a/res/css/views/messages/_MessageActionBar.pcss +++ b/res/css/views/messages/_MessageActionBar.pcss @@ -21,7 +21,6 @@ limitations under the License. --MessageActionBar-item-hover-background: $panel-actions; --MessageActionBar-item-hover-borderRadius: 6px; --MessageActionBar-item-hover-zIndex: 1; - --MessageActionBar-star-button-color: #ffa534; position: absolute; visibility: hidden; @@ -118,10 +117,6 @@ limitations under the License. color: $primary-content; } - &.mx_MessageActionBar_favouriteButton_fillstar { - color: var(--MessageActionBar-star-button-color); - } - &.mx_MessageActionBar_downloadButton { --MessageActionBar-icon-size: 14px; diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index a0b730b65f9..e792a47221b 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -28,7 +28,6 @@ import { Icon as EmojiIcon } from "../../../../res/img/element-icons/room/messag import { Icon as ResendIcon } from "../../../../res/img/element-icons/retry.svg"; import { Icon as ThreadIcon } from "../../../../res/img/element-icons/message/thread.svg"; import { Icon as TrashcanIcon } from "../../../../res/img/element-icons/trashcan.svg"; -import { Icon as StarIcon } from "../../../../res/img/element-icons/room/message-bar/star.svg"; import { Icon as ReplyIcon } from "../../../../res/img/element-icons/room/message-bar/reply.svg"; import { Icon as ExpandMessageIcon } from "../../../../res/img/element-icons/expand-message.svg"; import { Icon as CollapseMessageIcon } from "../../../../res/img/element-icons/collapse-message.svg"; @@ -45,7 +44,6 @@ import Resend from "../../../Resend"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import DownloadActionButton from "./DownloadActionButton"; -import SettingsStore from "../../../settings/SettingsStore"; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import ReplyChain from "../elements/ReplyChain"; import ReactionPicker from "../emojipicker/ReactionPicker"; @@ -55,7 +53,6 @@ import { Key } from "../../../Keyboard"; import { ALTERNATE_KEY_NAME } from "../../../accessibility/KeyboardShortcuts"; import { Action } from "../../../dispatcher/actions"; import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload"; -import useFavouriteMessages from "../../../hooks/useFavouriteMessages"; import { GetRelationsForEvent } from "../rooms/EventTile"; import { VoiceBroadcastInfoEventType } from "../../../voice-broadcast/types"; import { ButtonEvent } from "../elements/AccessibleButton"; @@ -254,42 +251,6 @@ const ReplyInThreadButton: React.FC = ({ mxEvent }) => { ); }; -interface IFavouriteButtonProp { - mxEvent: MatrixEvent; -} - -const FavouriteButton: React.FC = ({ mxEvent }) => { - const { isFavourite, toggleFavourite } = useFavouriteMessages(); - - const eventId = mxEvent.getId()!; - const classes = classNames("mx_MessageActionBar_iconButton mx_MessageActionBar_favouriteButton", { - mx_MessageActionBar_favouriteButton_fillstar: isFavourite(eventId), - }); - - const onClick = useCallback( - (e: ButtonEvent) => { - // Don't open the regular browser or our context menu on right-click - e.preventDefault(); - e.stopPropagation(); - - toggleFavourite(eventId); - }, - [toggleFavourite, eventId], - ); - - return ( - - - - ); -}; - interface IMessageActionBarProps { mxEvent: MatrixEvent; reactions?: Relations | null | undefined; @@ -518,9 +479,6 @@ export default class MessageActionBar extends React.PureComponent, ); } - if (SettingsStore.getValue("feature_favourite_messages")) { - toolbarOpts.splice(-1, 0, ); - } // XXX: Assuming that the underlying tile will be a media event if it is eligible media. if (MediaEventHelper.isEligible(this.props.mxEvent)) { diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 8c0d797df6d..0d905ccdd23 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -78,7 +78,6 @@ interface IState { sublists: ITagMap; currentRoomId?: string; suggestedRooms: ISuggestedRoom[]; - feature_favourite_messages: boolean; } export const TAG_ORDER: TagID[] = [ @@ -443,7 +442,6 @@ const TAG_AESTHETICS: TagAestheticsMap = { export default class RoomList extends React.PureComponent { private dispatcherRef?: string; private treeRef = createRef(); - private favouriteMessageWatcher?: string; public static contextType = MatrixClientContext; public context!: React.ContextType; @@ -454,7 +452,6 @@ export default class RoomList extends React.PureComponent { this.state = { sublists: {}, suggestedRooms: SpaceStore.instance.suggestedRooms, - feature_favourite_messages: SettingsStore.getValue("feature_favourite_messages"), }; } @@ -463,20 +460,12 @@ export default class RoomList extends React.PureComponent { SdkContextClass.instance.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate); SpaceStore.instance.on(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms); RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.updateLists); - this.favouriteMessageWatcher = SettingsStore.watchSetting( - "feature_favourite_messages", - null, - (...[, , , value]) => { - this.setState({ feature_favourite_messages: value }); - }, - ); this.updateLists(); // trigger the first update } public componentWillUnmount(): void { SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists); - if (this.favouriteMessageWatcher) SettingsStore.unwatchSetting(this.favouriteMessageWatcher); if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate); } @@ -607,29 +596,6 @@ export default class RoomList extends React.PureComponent { ); }); } - private renderFavoriteMessagesList(): ReactComponentElement[] { - const avatar = ( - - ); - - return [ - ""} - key="favMessagesTile_key" - />, - ]; - } private renderSublists(): React.ReactElement[] { // show a skeleton UI if the user is in no rooms and they are not filtering and have no suggested rooms @@ -641,8 +607,6 @@ export default class RoomList extends React.PureComponent { let extraTiles: ReactComponentElement[] | undefined; if (orderedTagId === DefaultTagID.Suggested) { extraTiles = this.renderSuggestedRooms(); - } else if (this.state.feature_favourite_messages && orderedTagId === DefaultTagID.SavedItems) { - extraTiles = this.renderFavoriteMessagesList(); } const aesthetics = TAG_AESTHETICS[orderedTagId]; diff --git a/src/hooks/useFavouriteMessages.ts b/src/hooks/useFavouriteMessages.ts deleted file mode 100644 index fe5b23dc9ed..00000000000 --- a/src/hooks/useFavouriteMessages.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 { useState } from "react"; - -const favouriteMessageIds = JSON.parse(localStorage?.getItem("io_element_favouriteMessages") ?? "[]") as string[]; - -export default function useFavouriteMessages(): { - toggleFavourite: (eventId: string) => void; - isFavourite: (eventId: string) => boolean; -} { - const [, setX] = useState(); - - //checks if an id already exist - const isFavourite = (eventId: string): boolean => favouriteMessageIds.includes(eventId); - - const toggleFavourite = (eventId: string): void => { - isFavourite(eventId) - ? favouriteMessageIds.splice(favouriteMessageIds.indexOf(eventId), 1) - : favouriteMessageIds.push(eventId); - - //update the local storage - localStorage.setItem("io_element_favouriteMessages", JSON.stringify(favouriteMessageIds)); - - // This forces a re-render to account for changes in appearance in real-time when the favourite button is toggled - setX([]); - }; - - return { isFavourite, toggleFavourite }; -} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4b5c15f333d..a7b25faaa83 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -985,11 +985,10 @@ "Temporary implementation. Locations persist in room history.": "Temporary implementation. Locations persist in room history.", "Dynamic room predecessors": "Dynamic room predecessors", "Enable MSC3946 (to support late-arriving room archives)": "Enable MSC3946 (to support late-arriving room archives)", - "Favourite Messages": "Favourite Messages", - "Under active development.": "Under active development.", "Force 15s voice broadcast chunk length": "Force 15s voice broadcast chunk length", "Enable new native OIDC flows (Under active development)": "Enable new native OIDC flows (Under active development)", "Rust cryptography implementation": "Rust cryptography implementation", + "Under active development.": "Under active development.", "Font size": "Font size", "Use custom size": "Use custom size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", @@ -2390,7 +2389,6 @@ "React": "React", "Reply in thread": "Reply in thread", "Can't create a thread from an event with an existing relation": "Can't create a thread from an event with an existing relation", - "Favourite": "Favourite", "Edit": "Edit", "Reply": "Reply", "Collapse quotes": "Collapse quotes", @@ -3246,6 +3244,7 @@ "Copy link": "Copy link", "Forget": "Forget", "Favourited": "Favourited", + "Favourite": "Favourite", "Mentions only": "Mentions only", "Copy room link": "Copy room link", "Low Priority": "Low Priority", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 6f24491c57c..5ff31b602fa 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -426,14 +426,6 @@ export const SETTINGS: { [setting: string]: ISetting } = { shouldWarn: true, default: false, }, - "feature_favourite_messages": { - isFeature: true, - labsGroup: LabGroup.Messaging, - supportedLevels: LEVELS_FEATURE, - displayName: _td("Favourite Messages"), - description: _td("Under active development."), - default: false, - }, [Features.VoiceBroadcast]: { isFeature: true, labsGroup: LabGroup.Messaging, diff --git a/test/components/views/messages/MessageActionBar-test.tsx b/test/components/views/messages/MessageActionBar-test.tsx index da475a58de2..634be112db1 100644 --- a/test/components/views/messages/MessageActionBar-test.tsx +++ b/test/components/views/messages/MessageActionBar-test.tsx @@ -433,88 +433,7 @@ describe("", () => { }); }); - describe("favourite button", () => { - //for multiple event usecase - const favButton = (evt: MatrixEvent) => { - return getComponent({ mxEvent: evt }).getByTestId(evt.getId()!); - }; - - describe("when favourite_messages feature is enabled", () => { - beforeEach(() => { - jest.spyOn(SettingsStore, "getValue").mockImplementation( - (setting) => setting === "feature_favourite_messages", - ); - localStorageMock.clear(); - }); - - it("renders favourite button on own actionable event", () => { - const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText("Favourite")).toBeTruthy(); - }); - - it("renders favourite button on other actionable events", () => { - const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent }); - expect(queryByLabelText("Favourite")).toBeTruthy(); - }); - - it("does not render Favourite button on non-actionable event", () => { - //redacted event is not actionable - const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }); - expect(queryByLabelText("Favourite")).toBeFalsy(); - }); - - it("remembers favourited state of multiple events, and handles the localStorage of the events accordingly", () => { - const alicesAction = favButton(alicesMessageEvent); - const bobsAction = favButton(bobsMessageEvent); - - //default state before being clicked - expect(alicesAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(localStorageMock.getItem("io_element_favouriteMessages")).toBeNull(); - - //if only alice's event is fired - fireEvent.click(alicesAction); - - expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(localStorageMock.setItem).toHaveBeenCalledWith( - "io_element_favouriteMessages", - '["$alices_message"]', - ); - - //when bob's event is fired,both should be styled and stored in localStorage - fireEvent.click(bobsAction); - - expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(bobsAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(localStorageMock.setItem).toHaveBeenCalledWith( - "io_element_favouriteMessages", - '["$alices_message","$bobs_message"]', - ); - - //finally, at this point the localStorage should contain the two eventids - expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual( - '["$alices_message","$bobs_message"]', - ); - - //if decided to unfavourite bob's event by clicking again - fireEvent.click(bobsAction); - expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); - expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual('["$alices_message"]'); - }); - }); - - describe("when favourite_messages feature is disabled", () => { - it("does not render", () => { - jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); - const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText("Favourite")).toBeFalsy(); - }); - }); - }); - - it.each([["React"], ["Reply"], ["Reply in thread"], ["Favourite"], ["Edit"]])( + it.each([["React"], ["Reply"], ["Reply in thread"], ["Edit"]])( "does not show context menu when right-clicking", (buttonLabel: string) => { // For favourite button From 7236e48765c1464d7a4ec864f6ff663ac9d78d7c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jun 2023 12:57:58 +0100 Subject: [PATCH 17/68] Allow maintaining a different right panel width for thread panels (#11064) * Move Room context threadId our of RoomView state * Allow maintaining a different right panel width for thread panels * Fix types * Fix types * Add tests * Increase coverage * Increase coverage * Add comments * Update src/components/structures/MainSplit.tsx Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- src/components/structures/MainSplit.tsx | 33 +++++++++- src/components/structures/RoomView.tsx | 29 ++++++++- src/contexts/RoomContext.ts | 7 ++- test/components/structures/MainSplit-test.tsx | 63 +++++++++++++++++++ test/components/structures/RoomView-test.tsx | 1 + .../__snapshots__/MainSplit-test.tsx.snap | 61 ++++++++++++++++++ .../views/rooms/SendMessageComposer-test.tsx | 1 + test/test-utils/room.ts | 1 + test/test-utils/test-utils.ts | 1 + 9 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 test/components/structures/MainSplit-test.tsx create mode 100644 test/components/structures/__snapshots__/MainSplit-test.tsx.snap diff --git a/src/components/structures/MainSplit.tsx b/src/components/structures/MainSplit.tsx index ab29cd2b397..64b2e5c04fd 100644 --- a/src/components/structures/MainSplit.tsx +++ b/src/components/structures/MainSplit.tsx @@ -26,9 +26,24 @@ interface IProps { collapsedRhs?: boolean; panel?: JSX.Element; children: ReactNode; + /** + * A unique identifier for this panel split. + * + * This is appended to the key used to store the panel size in localStorage, allowing the widths of different + * panels to be stored. + */ + sizeKey?: string; + /** + * The size to use for the panel component if one isn't persisted in storage. Defaults to 350. + */ + defaultSize: number; } export default class MainSplit extends React.Component { + public static defaultProps = { + defaultSize: 350, + }; + private onResizeStart = (): void => { this.props.resizeNotifier.startResizing(); }; @@ -37,6 +52,14 @@ export default class MainSplit extends React.Component { this.props.resizeNotifier.notifyRightHandleResized(); }; + private get sizeSettingStorageKey(): string { + let key = "mx_rhs_size"; + if (!!this.props.sizeKey) { + key += `_${this.props.sizeKey}`; + } + return key; + } + private onResizeStop = ( event: MouseEvent | TouchEvent, direction: Direction, @@ -44,14 +67,17 @@ export default class MainSplit extends React.Component { delta: NumberSize, ): void => { this.props.resizeNotifier.stopResizing(); - window.localStorage.setItem("mx_rhs_size", (this.loadSidePanelSize().width + delta.width).toString()); + window.localStorage.setItem( + this.sizeSettingStorageKey, + (this.loadSidePanelSize().width + delta.width).toString(), + ); }; private loadSidePanelSize(): { height: string | number; width: number } { - let rhsSize = parseInt(window.localStorage.getItem("mx_rhs_size")!, 10); + let rhsSize = parseInt(window.localStorage.getItem(this.sizeSettingStorageKey)!, 10); if (isNaN(rhsSize)) { - rhsSize = 350; + rhsSize = this.props.defaultSize; } return { @@ -70,6 +96,7 @@ export default class MainSplit extends React.Component { if (hasResizer) { children = ( { showApps: false, isPeeking: false, showRightPanel: false, + threadRightPanel: false, joining: false, showTopUnreadMessagesBar: false, statusBarVisible: false, @@ -623,6 +628,11 @@ export class RoomView extends React.Component { mainSplitContentType: room ? this.getMainSplitContentType(room) : undefined, initialEventId: undefined, // default to clearing this, will get set later in the method if needed showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false, + threadRightPanel: roomId + ? [RightPanelPhases.ThreadView, RightPanelPhases.ThreadPanel].includes( + this.context.rightPanelStore.currentCardForRoom(roomId).phase!, + ) + : false, activeCall: roomId ? CallStore.instance.getActiveCall(roomId) : null, }; @@ -1011,8 +1021,14 @@ export class RoomView extends React.Component { } private onRightPanelStoreUpdate = (): void => { + const { roomId } = this.state; this.setState({ - showRightPanel: this.state.roomId ? this.context.rightPanelStore.isOpenForRoom(this.state.roomId) : false, + showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false, + threadRightPanel: roomId + ? [RightPanelPhases.ThreadView, RightPanelPhases.ThreadPanel].includes( + this.context.rightPanelStore.currentCardForRoom(roomId).phase!, + ) + : false, }); }; @@ -2439,7 +2455,14 @@ export class RoomView extends React.Component { viewingCall={viewingCall} activeCall={this.state.activeCall} /> - +

({ +const RoomContext = createContext< + IRoomState & { + threadId?: string; + } +>({ roomLoading: true, peekLoading: false, shouldPeek: true, @@ -39,6 +43,7 @@ const RoomContext = createContext({ showApps: false, isPeeking: false, showRightPanel: true, + threadRightPanel: false, joining: false, showTopUnreadMessagesBar: false, statusBarVisible: false, diff --git a/test/components/structures/MainSplit-test.tsx b/test/components/structures/MainSplit-test.tsx new file mode 100644 index 00000000000..f32a0720701 --- /dev/null +++ b/test/components/structures/MainSplit-test.tsx @@ -0,0 +1,63 @@ +/* +Copyright 2023 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 MainSplit from "../../../src/components/structures/MainSplit"; +import ResizeNotifier from "../../../src/utils/ResizeNotifier"; + +describe("", () => { + const resizeNotifier = new ResizeNotifier(); + const children = ( +
+ ChildFooBar +
+ ); + const panel =
Right panel
; + + it("renders", () => { + const { asFragment, container } = render( + , + ); + expect(asFragment()).toMatchSnapshot(); + // Assert it matches the default width of 350 + expect(container.querySelector(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("350px"); + }); + + it("respects defaultSize prop", () => { + const { asFragment, container } = render( + , + ); + expect(asFragment()).toMatchSnapshot(); + // Assert it matches the default width of 350 + expect(container.querySelector(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("500px"); + }); + + it("prefers size stashed in LocalStorage to the defaultSize prop", () => { + localStorage.setItem("mx_rhs_size_thread", "333"); + const { container } = render( + , + ); + expect(container.querySelector(".mx_RightPanel_ResizeWrapper")!.style.width).toBe("333px"); + }); +}); diff --git a/test/components/structures/RoomView-test.tsx b/test/components/structures/RoomView-test.tsx index fadeb5c2c2e..61b987585b7 100644 --- a/test/components/structures/RoomView-test.tsx +++ b/test/components/structures/RoomView-test.tsx @@ -225,6 +225,7 @@ describe("RoomView", () => { }); it("updates url preview visibility on encryption state change", async () => { + room.getMyMembership = jest.fn().mockReturnValue("join"); // we should be starting unencrypted expect(cli.isCryptoEnabled()).toEqual(false); expect(cli.isRoomEncrypted(room.roomId)).toEqual(false); diff --git a/test/components/structures/__snapshots__/MainSplit-test.tsx.snap b/test/components/structures/__snapshots__/MainSplit-test.tsx.snap new file mode 100644 index 00000000000..d6cd2015941 --- /dev/null +++ b/test/components/structures/__snapshots__/MainSplit-test.tsx.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders 1`] = ` + +
+
+ Child + + Foo + + Bar +
+
+
+ Right panel +
+
+
+
+
+
+ +`; + +exports[` respects defaultSize prop 1`] = ` + +
+
+ Child + + Foo + + Bar +
+
+
+ Right panel +
+
+
+
+
+
+ +`; diff --git a/test/components/views/rooms/SendMessageComposer-test.tsx b/test/components/views/rooms/SendMessageComposer-test.tsx index 0d392c97c2a..bf9fb0e1851 100644 --- a/test/components/views/rooms/SendMessageComposer-test.tsx +++ b/test/components/views/rooms/SendMessageComposer-test.tsx @@ -57,6 +57,7 @@ describe("", () => { showApps: false, isPeeking: false, showRightPanel: true, + threadRightPanel: false, joining: false, atEndOfLiveTimeline: true, showTopUnreadMessagesBar: false, diff --git a/test/test-utils/room.ts b/test/test-utils/room.ts index 501afb947dc..2b5491dc789 100644 --- a/test/test-utils/room.ts +++ b/test/test-utils/room.ts @@ -61,6 +61,7 @@ export function getRoomContext(room: Room, override: Partial): IRoom showApps: false, isPeeking: false, showRightPanel: true, + threadRightPanel: false, joining: false, atEndOfLiveTimeline: true, showTopUnreadMessagesBar: false, diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 6eea4114d23..811290be17d 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -237,6 +237,7 @@ export function createTestClient(): MatrixClient { searchUserDirectory: jest.fn().mockResolvedValue({ limited: false, results: [] }), setDeviceVerified: jest.fn(), joinRoom: jest.fn(), + getSyncStateData: jest.fn(), } as unknown as MatrixClient; client.reEmitter = new ReEmitter(client); From 707fd9ccf05251a627a3bc2f2a32f45bcd63000c Mon Sep 17 00:00:00 2001 From: Suguru Hirahara Date: Thu, 15 Jun 2023 12:22:15 +0000 Subject: [PATCH 18/68] Move style rules for AppWarning.tsx and AppPermission.tsx from _AppsDrawer.pcss to _common.pcss (#11094) Because the style rules do not belong to AppsDrawer.tsx and are used by other multiple components, it is sensible to manage the rules on _common.pcss rather than _AppsDrapwer.pcss to improve maintainability. --- res/css/_common.pcss | 9 +++++++++ res/css/views/rooms/_AppsDrawer.pcss | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/res/css/_common.pcss b/res/css/_common.pcss index 0c9a44474cd..b0d2a1d2090 100644 --- a/res/css/_common.pcss +++ b/res/css/_common.pcss @@ -695,6 +695,15 @@ legend { color: $username-variant8-color; } +.mx_AppWarning, +.mx_AppPermission { + text-align: center; + display: flex; + height: 100%; + flex-direction: column; + align-items: center; +} + @define-mixin ProgressBarColour $colour { color: $colour; &::-moz-progress-bar { diff --git a/res/css/views/rooms/_AppsDrawer.pcss b/res/css/views/rooms/_AppsDrawer.pcss index 306e4c103c8..17095a4c54c 100644 --- a/res/css/views/rooms/_AppsDrawer.pcss +++ b/res/css/views/rooms/_AppsDrawer.pcss @@ -305,15 +305,6 @@ limitations under the License. flex: 1; } -.mx_AppWarning, -.mx_AppPermission { - text-align: center; - display: flex; - height: 100%; - flex-direction: column; - align-items: center; -} - .mx_AppTile_loading { display: flex; flex-direction: column; From dd46db4817abe2e51c66adf9ae3f9b09b0d18464 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jun 2023 15:11:49 +0100 Subject: [PATCH 19/68] Use MatrixClientPeg::safeGet in src/{stores,hooks,components/structures}/* (#10988) --- src/components/structures/FilePanel.tsx | 20 +++--- .../structures/LegacyCallEventGrouper.ts | 2 +- src/components/structures/MatrixChat.tsx | 63 ++++++++++--------- src/components/structures/MessagePanel.tsx | 12 ++-- .../structures/NotificationPanel.tsx | 4 +- src/components/structures/PipContainer.tsx | 8 +-- src/components/structures/ThreadView.tsx | 2 +- src/components/structures/TimelinePanel.tsx | 20 +++--- src/components/structures/UserMenu.tsx | 8 +-- src/components/structures/ViewSource.tsx | 4 +- .../structures/auth/Registration.tsx | 2 +- .../structures/auth/SetupEncryptionBody.tsx | 4 +- src/components/structures/auth/SoftLogout.tsx | 19 +++--- src/hooks/spotlight/useRecentSearches.ts | 2 +- src/hooks/usePermalinkEvent.ts | 2 +- src/hooks/usePermalinkTargetRoom.ts | 2 +- src/hooks/useProfileInfo.ts | 2 +- src/hooks/usePublicRoomDirectory.ts | 6 +- src/hooks/useSlidingSyncRoomSearch.ts | 2 +- src/hooks/useSpaceResults.ts | 2 +- src/hooks/useUserDirectory.ts | 2 +- src/stores/ActiveWidgetStore.ts | 2 +- src/stores/OwnProfileStore.ts | 2 +- src/stores/RoomViewStore.tsx | 12 ++-- src/stores/SetupEncryptionStore.ts | 30 ++++----- .../notifications/RoomNotificationState.ts | 2 +- src/stores/right-panel/RightPanelStore.ts | 4 +- .../algorithms/tag-sorting/RecentAlgorithm.ts | 2 +- src/stores/room-list/previews/utils.ts | 4 +- src/stores/widgets/StopGapWidget.ts | 2 +- src/stores/widgets/StopGapWidgetDriver.ts | 14 ++--- src/utils/PasswordScorer.ts | 2 +- .../previews/PollStartEventPreview-test.ts | 4 ++ .../widgets/StopGapWidgetDriver-test.ts | 1 + 34 files changed, 139 insertions(+), 130 deletions(-) diff --git a/src/components/structures/FilePanel.tsx b/src/components/structures/FilePanel.tsx index 34b517b99cb..be3d1e981f4 100644 --- a/src/components/structures/FilePanel.tsx +++ b/src/components/structures/FilePanel.tsx @@ -74,7 +74,7 @@ class FilePanel extends React.Component { if (room?.roomId !== this.props.roomId) return; if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return; - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); client.decryptEventIfNeeded(ev); if (ev.isBeingDecrypted()) { @@ -109,11 +109,11 @@ class FilePanel extends React.Component { } public async componentDidMount(): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); await this.updateTimelineSet(this.props.roomId); - if (!MatrixClientPeg.get().isRoomEncrypted(this.props.roomId)) return; + if (!client.isRoomEncrypted(this.props.roomId)) return; // The timelineSets filter makes sure that encrypted events that contain // URLs never get added to the timeline, even if they are live events. @@ -133,7 +133,7 @@ class FilePanel extends React.Component { const client = MatrixClientPeg.get(); if (client === null) return; - if (!MatrixClientPeg.get().isRoomEncrypted(this.props.roomId)) return; + if (!client.isRoomEncrypted(this.props.roomId)) return; if (EventIndexPeg.get() !== null) { client.removeListener(RoomEvent.Timeline, this.onRoomTimeline); @@ -142,9 +142,9 @@ class FilePanel extends React.Component { } public async fetchFileEventsServer(room: Room): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); - const filter = new Filter(client.credentials.userId); + const filter = new Filter(client.getSafeUserId()); filter.setDefinition({ room: { timeline: { @@ -163,7 +163,7 @@ class FilePanel extends React.Component { direction: Direction, limit: number, ): Promise => { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const eventIndex = EventIndexPeg.get(); const roomId = this.props.roomId; @@ -185,7 +185,7 @@ class FilePanel extends React.Component { }; public async updateTimelineSet(roomId: string): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const room = client.getRoom(roomId); const eventIndex = EventIndexPeg.get(); @@ -221,7 +221,7 @@ class FilePanel extends React.Component { } public render(): React.ReactNode { - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { return (
@@ -256,7 +256,7 @@ class FilePanel extends React.Component {
); - const isRoomEncrypted = this.noRoom ? false : MatrixClientPeg.get().isRoomEncrypted(this.props.roomId); + const isRoomEncrypted = this.noRoom ? false : MatrixClientPeg.safeGet().isRoomEncrypted(this.props.roomId); if (this.state.timelineSet) { return ( diff --git a/src/components/structures/LegacyCallEventGrouper.ts b/src/components/structures/LegacyCallEventGrouper.ts index c87f08bfffc..467b579410f 100644 --- a/src/components/structures/LegacyCallEventGrouper.ts +++ b/src/components/structures/LegacyCallEventGrouper.ts @@ -129,7 +129,7 @@ export default class LegacyCallEventGrouper extends EventEmitter { public get callWasMissed(): boolean { return ( this.state === CallState.Ended && - ![...this.events].some((event) => event.sender?.userId === MatrixClientPeg.get().getUserId()) + ![...this.events].some((event) => event.sender?.userId === MatrixClientPeg.safeGet().getUserId()) ); } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 6a03bc7aed3..829a337256a 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -32,6 +32,7 @@ import { throttle } from "lodash"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import { RoomType } from "matrix-js-sdk/src/@types/event"; import { DecryptionError } from "matrix-js-sdk/src/crypto/algorithms"; +import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; // focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by various components import "focus-visible"; @@ -355,7 +356,7 @@ export default class MatrixChat extends React.PureComponent { } private async postLoginSetup(): Promise { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const cryptoEnabled = cli.isCryptoEnabled(); if (!cryptoEnabled) { this.onLoggedIn(); @@ -574,11 +575,11 @@ export default class MatrixChat extends React.PureComponent { if (payload.event_type === "m.identity_server") { const fullUrl = payload.event_content ? payload.event_content["base_url"] : null; if (!fullUrl) { - MatrixClientPeg.get().setIdentityServerUrl(undefined); + MatrixClientPeg.safeGet().setIdentityServerUrl(undefined); localStorage.removeItem("mx_is_access_token"); localStorage.removeItem("mx_is_url"); } else { - MatrixClientPeg.get().setIdentityServerUrl(fullUrl); + MatrixClientPeg.safeGet().setIdentityServerUrl(fullUrl); localStorage.removeItem("mx_is_access_token"); // clear token localStorage.setItem("mx_is_url", fullUrl); // XXX: Do we still need this? } @@ -625,7 +626,7 @@ export default class MatrixChat extends React.PureComponent { this.notifyNewScreen("forgot_password"); break; case "start_chat": - createRoom(MatrixClientPeg.get(), { + createRoom(MatrixClientPeg.safeGet(), { dmUserId: payload.user_id, }); break; @@ -647,7 +648,7 @@ export default class MatrixChat extends React.PureComponent { // FIXME: controller shouldn't be loading a view :( const modal = Modal.createDialog(Spinner, undefined, "mx_Dialog_spinner"); - MatrixClientPeg.get() + MatrixClientPeg.safeGet() .leave(payload.room_id) .then( () => { @@ -755,7 +756,7 @@ export default class MatrixChat extends React.PureComponent { this.viewSomethingBehindModal(); break; case "view_invite": { - const room = MatrixClientPeg.get().getRoom(payload.roomId); + const room = MatrixClientPeg.safeGet().getRoom(payload.roomId); if (room?.isSpaceRoom()) { showSpaceInvite(room); } else { @@ -794,7 +795,7 @@ export default class MatrixChat extends React.PureComponent { Modal.createDialog(DialPadModal, {}, "mx_Dialog_dialPadWrapper"); break; case Action.OnLoggedIn: - this.stores.client = MatrixClientPeg.get(); + this.stores.client = MatrixClientPeg.safeGet(); if ( // Skip this handling for token login as that always calls onLoggedIn itself !this.tokenLogin && @@ -935,7 +936,7 @@ export default class MatrixChat extends React.PureComponent { } let presentedId = roomInfo.room_alias || roomInfo.room_id!; - const room = MatrixClientPeg.get().getRoom(roomInfo.room_id); + const room = MatrixClientPeg.safeGet().getRoom(roomInfo.room_id); if (room) { // Not all timeline events are decrypted ahead of time anymore // Only the critical ones for a typical UI are @@ -1062,14 +1063,14 @@ export default class MatrixChat extends React.PureComponent { const [shouldCreate, opts] = await modal.finished; if (shouldCreate) { - createRoom(MatrixClientPeg.get(), opts!); + createRoom(MatrixClientPeg.safeGet(), opts!); } } private chatCreateOrReuse(userId: string): void { const snakedConfig = new SnakedObject(this.props.config); // Use a deferred action to reshow the dialog once the user has registered - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { // No point in making 2 DMs with welcome bot. This assumes view_set_mxid will // result in a new DM with the welcome user. if (userId !== snakedConfig.get("welcome_user_id")) { @@ -1098,7 +1099,7 @@ export default class MatrixChat extends React.PureComponent { // TODO: Immutable DMs replaces this - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const dmRoom = findDMForUser(client, userId); if (dmRoom) { @@ -1116,7 +1117,7 @@ export default class MatrixChat extends React.PureComponent { } private leaveRoomWarnings(roomId: string): JSX.Element[] { - const roomToLeave = MatrixClientPeg.get().getRoom(roomId); + const roomToLeave = MatrixClientPeg.safeGet().getRoom(roomId); const isSpace = roomToLeave?.isSpaceRoom(); // Show a warning if there are additional complications. const warnings: JSX.Element[] = []; @@ -1154,7 +1155,7 @@ export default class MatrixChat extends React.PureComponent { } private leaveRoom(roomId: string): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const roomToLeave = cli.getRoom(roomId); const warnings = this.leaveRoomWarnings(roomId); @@ -1188,8 +1189,8 @@ export default class MatrixChat extends React.PureComponent { } private forgetRoom(roomId: string): void { - const room = MatrixClientPeg.get().getRoom(roomId); - MatrixClientPeg.get() + const room = MatrixClientPeg.safeGet().getRoom(roomId); + MatrixClientPeg.safeGet() .forget(roomId) .then(() => { // Switch to home page if we're currently viewing the forgotten room @@ -1212,7 +1213,7 @@ export default class MatrixChat extends React.PureComponent { } private async copyRoom(roomId: string): Promise { - const roomLink = makeRoomPermalink(MatrixClientPeg.get(), roomId); + const roomLink = makeRoomPermalink(MatrixClientPeg.safeGet(), roomId); const success = await copyPlaintext(roomLink); if (!success) { Modal.createDialog(ErrorDialog, { @@ -1246,7 +1247,7 @@ export default class MatrixChat extends React.PureComponent { const welcomeUserRooms = DMRoomMap.shared().getDMRoomsForUserId(welcomeUserId); if (welcomeUserRooms.length === 0) { - const roomId = await createRoom(MatrixClientPeg.get(), { + const roomId = await createRoom(MatrixClientPeg.safeGet(), { dmUserId: snakedConfig.get("welcome_user_id"), // Only view the welcome user if we're NOT looking at a room andView: !this.state.currentRoomId, @@ -1262,11 +1263,11 @@ export default class MatrixChat extends React.PureComponent { // the saved sync to be loaded). const saveWelcomeUser = (ev: MatrixEvent): void => { if (ev.getType() === EventType.Direct && ev.getContent()[welcomeUserId]) { - MatrixClientPeg.get().store.save(true); - MatrixClientPeg.get().removeListener(ClientEvent.AccountData, saveWelcomeUser); + MatrixClientPeg.safeGet().store.save(true); + MatrixClientPeg.safeGet().removeListener(ClientEvent.AccountData, saveWelcomeUser); } }; - MatrixClientPeg.get().on(ClientEvent.AccountData, saveWelcomeUser); + MatrixClientPeg.safeGet().on(ClientEvent.AccountData, saveWelcomeUser); return roomId; } @@ -1413,7 +1414,7 @@ export default class MatrixChat extends React.PureComponent { // Before defaulting to directory, show the last viewed room this.viewLastRoom(); } else { - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { dis.dispatch({ action: "view_welcome_page" }); } else { dis.dispatch({ action: Action.ViewHomePage }); @@ -1468,7 +1469,7 @@ export default class MatrixChat extends React.PureComponent { // to do the first sync this.firstSyncComplete = false; this.firstSyncPromise = defer(); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); // Allow the JS SDK to reap timeline events. This reduces the amount of // memory consumed as the JS SDK stores multiple distinct copies of room @@ -1588,7 +1589,7 @@ export default class MatrixChat extends React.PureComponent { cli.on(MatrixEventEvent.Decrypted, (e, err) => dft.eventDecrypted(e, err as DecryptionError)); cli.on(ClientEvent.Room, (room) => { - if (MatrixClientPeg.get().isCryptoEnabled()) { + if (cli.isCryptoEnabled()) { const blacklistEnabled = SettingsStore.getValueAt( SettingLevel.ROOM_DEVICE, "blacklistUnverifiedDevices", @@ -1618,15 +1619,15 @@ export default class MatrixChat extends React.PureComponent { } }); cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise => { - let haveNewVersion; - let newVersionInfo; + let haveNewVersion: boolean | undefined; + let newVersionInfo: IKeyBackupInfo | null = null; // if key backup is still enabled, there must be a new backup in place - if (MatrixClientPeg.get().getKeyBackupEnabled()) { + if (cli.getKeyBackupEnabled()) { haveNewVersion = true; } else { // otherwise check the server to see if there's a new one try { - newVersionInfo = await MatrixClientPeg.get().getKeyBackupVersion(); + newVersionInfo = await cli.getKeyBackupVersion(); if (newVersionInfo !== null) haveNewVersion = true; } catch (e) { logger.error("Saw key backup error but failed to check backup version!", e); @@ -1639,7 +1640,7 @@ export default class MatrixChat extends React.PureComponent { import( "../../async-components/views/dialogs/security/NewRecoveryMethodDialog" ) as unknown as Promise, - { newVersionInfo }, + { newVersionInfo: newVersionInfo! }, ); } else { Modal.createDialogAsync( @@ -1686,7 +1687,7 @@ export default class MatrixChat extends React.PureComponent { * @private */ private onClientStarted(): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); if (cli.isCryptoEnabled()) { const blacklistEnabled = SettingsStore.getValueAt(SettingLevel.DEVICE, "blacklistUnverifiedDevices"); @@ -1734,7 +1735,7 @@ export default class MatrixChat extends React.PureComponent { params: params, }); } else if (screen === "soft_logout") { - if (cli.getUserId() && !Lifecycle.isSoftLogout()) { + if (!!cli?.getUserId() && !Lifecycle.isSoftLogout()) { // Logged in - visit a room this.viewLastRoom(); } else { @@ -2057,7 +2058,7 @@ export default class MatrixChat extends React.PureComponent { {...this.props} {...this.state} ref={this.loggedInView} - matrixClient={MatrixClientPeg.get()} + matrixClient={MatrixClientPeg.safeGet()} onRegistered={this.onRegistered} currentRoomId={this.state.currentRoomId} /> diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index f73bea5aa32..687ab1f56a1 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -473,7 +473,7 @@ export default class MessagePanel extends React.Component { } } - if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender()!)) { + if (MatrixClientPeg.safeGet().isUserIgnored(mxEv.getSender()!)) { return false; // ignored = no show (only happens if the ignore happens after an event was received) } @@ -743,7 +743,7 @@ export default class MessagePanel extends React.Component { lastInSection = willWantDateSeparator || mxEv.getSender() !== nextEv.getSender() || - getEventDisplayInfo(MatrixClientPeg.get(), nextEv, this.showHiddenEvents).isInfoMessage || + getEventDisplayInfo(MatrixClientPeg.safeGet(), nextEv, this.showHiddenEvents).isInfoMessage || !shouldFormContinuation(mxEv, nextEv, this.showHiddenEvents, this.context.timelineRenderingType); } @@ -779,7 +779,7 @@ export default class MessagePanel extends React.Component { // We only want to consider "last successful" if the event is sent by us, otherwise of course // it's successful: we received it. - isLastSuccessful = isLastSuccessful && mxEv.getSender() === MatrixClientPeg.get().getUserId(); + isLastSuccessful = isLastSuccessful && mxEv.getSender() === MatrixClientPeg.safeGet().getUserId(); const callEventGrouper = this.props.callEventGroupers.get(mxEv.getContent().call_id); // use txnId as key if available so that we don't remount during sending @@ -833,7 +833,7 @@ export default class MessagePanel extends React.Component { // Get a list of read receipts that should be shown next to this event // Receipts are objects which have a 'userId', 'roomMember' and 'ts'. private getReadReceiptsForEvent(event: MatrixEvent): IReadReceiptProps[] | null { - const myUserId = MatrixClientPeg.get().credentials.userId; + const myUserId = MatrixClientPeg.safeGet().credentials.userId; // get list of read receipts, sorted most recent first const { room } = this.props; @@ -856,7 +856,7 @@ export default class MessagePanel extends React.Component { if (!r.userId || !isSupportedReceiptType(r.type) || r.userId === myUserId) { return; // ignore non-read receipts and receipts from self. } - if (MatrixClientPeg.get().isUserIgnored(r.userId)) { + if (MatrixClientPeg.safeGet().isUserIgnored(r.userId)) { return; // ignore ignored users } const member = room.getMember(r.userId); @@ -1301,7 +1301,7 @@ class MainGrouper extends BaseGrouper { public add({ event: ev, shouldShow }: EventAndShouldShow): void { if (ev.getType() === EventType.RoomMember) { // We can ignore any events that don't actually have a message to display - if (!hasText(ev, MatrixClientPeg.get(), this.panel.showHiddenEvents)) return; + if (!hasText(ev, MatrixClientPeg.safeGet(), this.panel.showHiddenEvents)) return; } this.readMarker = this.readMarker || this.panel.readMarkerForEvent(ev.getId()!, ev === this.lastShownEvent); if (!this.panel.showHiddenEvents && !shouldShow) { diff --git a/src/components/structures/NotificationPanel.tsx b/src/components/structures/NotificationPanel.tsx index cb580dd1346..03ca356df3c 100644 --- a/src/components/structures/NotificationPanel.tsx +++ b/src/components/structures/NotificationPanel.tsx @@ -63,8 +63,8 @@ export default class NotificationPanel extends React.PureComponent ); - let content; - const timelineSet = MatrixClientPeg.get().getNotifTimelineSet(); + let content: JSX.Element; + const timelineSet = MatrixClientPeg.safeGet().getNotifTimelineSet(); if (timelineSet) { // wrap a TimelinePanel with the jump-to-event bits turned off. content = ( diff --git a/src/components/structures/PipContainer.tsx b/src/components/structures/PipContainer.tsx index 6d761f9c793..6f249fd6779 100644 --- a/src/components/structures/PipContainer.tsx +++ b/src/components/structures/PipContainer.tsx @@ -139,8 +139,8 @@ class PipContainerInner extends React.Component { LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls); LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.updateCalls); SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdate); - MatrixClientPeg.get().on(CallEvent.RemoteHoldUnhold, this.onCallRemoteHold); - const room = MatrixClientPeg.get()?.getRoom(this.state.viewedRoomId); + MatrixClientPeg.safeGet().on(CallEvent.RemoteHoldUnhold, this.onCallRemoteHold); + const room = MatrixClientPeg.safeGet().getRoom(this.state.viewedRoomId); if (room) { WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(room), this.updateCalls); } @@ -239,7 +239,7 @@ class PipContainerInner extends React.Component { let notDocked = false; // Sanity check the room - the widget may have been destroyed between render cycles, and // thus no room is associated anymore. - if (persistentWidgetId && persistentRoomId && MatrixClientPeg.get().getRoom(persistentRoomId)) { + if (persistentWidgetId && persistentRoomId && MatrixClientPeg.safeGet().getRoom(persistentRoomId)) { notDocked = !ActiveWidgetStore.instance.isDocked(persistentWidgetId, persistentRoomId); fromAnotherRoom = this.state.viewedRoomId !== persistentRoomId; } @@ -318,7 +318,7 @@ class PipContainerInner extends React.Component { pipContent.push(({ onStartMoving }) => ( { Array.from(dataTransfer.files), roomId, this.threadRelation, - MatrixClientPeg.get(), + MatrixClientPeg.safeGet(), TimelineRenderingType.Thread, ); } else { diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 5b5e0365efc..103e6d844b6 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -291,7 +291,7 @@ class TimelinePanel extends React.Component { readMarkerEventId: this.initialReadMarkerId, backPaginating: false, forwardPaginating: false, - clientSyncState: MatrixClientPeg.get().getSyncState(), + clientSyncState: MatrixClientPeg.safeGet().getSyncState(), isTwelveHour: SettingsStore.getValue("showTwelveHourTimestamps"), alwaysShowTimestamps: SettingsStore.getValue("alwaysShowTimestamps"), readMarkerInViewThresholdMs: SettingsStore.getValue("readMarkerInViewThresholdMs"), @@ -299,7 +299,7 @@ class TimelinePanel extends React.Component { }; this.dispatcherRef = dis.register(this.onAction); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); cli.on(RoomEvent.Timeline, this.onRoomTimeline); cli.on(RoomEvent.TimelineReset, this.onRoomTimelineReset); cli.on(RoomEvent.Redaction, this.onRoomRedaction); @@ -788,7 +788,7 @@ class TimelinePanel extends React.Component { // read-marker when a remote echo of an event we have just sent takes // more than the timeout on userActiveRecently. // - const myUserId = MatrixClientPeg.get().credentials.userId; + const myUserId = MatrixClientPeg.safeGet().credentials.userId; callRMUpdated = false; if (ev.getSender() !== myUserId && !UserActivity.sharedInstance().userActiveRecently()) { updatedState.readMarkerVisible = true; @@ -881,7 +881,7 @@ class TimelinePanel extends React.Component { if (!this.hasTimelineSetFor(member.roomId)) return; // ignore events for other users - if (member.userId != MatrixClientPeg.get().credentials?.userId) return; + if (member.userId != MatrixClientPeg.safeGet().credentials?.userId) return; // We could skip an update if the power level change didn't cross the // threshold for `VISIBILITY_CHANGE_TYPE`. @@ -1276,7 +1276,7 @@ class TimelinePanel extends React.Component { } // now think about advancing it - const myUserId = MatrixClientPeg.get().credentials.userId; + const myUserId = MatrixClientPeg.safeGet().credentials.userId; for (i++; i < events.length; i++) { const ev = events[i]; if (ev.getSender() !== myUserId) { @@ -1557,7 +1557,7 @@ class TimelinePanel extends React.Component { * @param {boolean?} scrollIntoView whether to scroll the event into view. */ private loadTimeline(eventId?: string, pixelOffset?: number, offsetBase?: number, scrollIntoView = true): void { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); this.timelineWindow = new TimelineWindow(cli, this.props.timelineSet, { windowLimit: this.props.timelineCap }); this.overlayTimelineWindow = this.props.overlayTimelineSet ? new TimelineWindow(cli, this.props.overlayTimelineSet, { windowLimit: this.props.timelineCap }) @@ -1748,7 +1748,7 @@ class TimelinePanel extends React.Component { arrayFastClone(events) .reverse() .forEach((event) => { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); client.decryptEventIfNeeded(event); }); @@ -1796,7 +1796,7 @@ class TimelinePanel extends React.Component { * such events were found, then it returns 0. */ private checkForPreJoinUISI(events: MatrixEvent[]): number { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = this.props.timelineSet.room; const isThreadTimeline = [TimelineRenderingType.Thread, TimelineRenderingType.ThreadsList].includes( @@ -1921,7 +1921,7 @@ class TimelinePanel extends React.Component { const messagePanelNode = ReactDOM.findDOMNode(messagePanel) as Element; if (!messagePanelNode) return null; // sometimes this happens for fresh rooms/post-sync const wrapperRect = messagePanelNode.getBoundingClientRect(); - const myUserId = MatrixClientPeg.get().credentials.userId; + const myUserId = MatrixClientPeg.safeGet().credentials.userId; const isNodeInView = (node?: HTMLElement): boolean => { if (node) { @@ -2121,7 +2121,7 @@ class TimelinePanel extends React.Component { canBackPaginate={this.state.canBackPaginate && this.state.firstVisibleEventIndex === 0} showUrlPreview={this.props.showUrlPreview} showReadReceipts={this.props.showReadReceipts} - ourUserId={MatrixClientPeg.get().getSafeUserId()} + ourUserId={MatrixClientPeg.safeGet().getSafeUserId()} stickyBottom={stickyBottom} onScroll={this.onMessageListScroll} onFillRequest={this.onMessageListFillRequest} diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 99325da99bc..2968c9eff3f 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -291,7 +291,7 @@ export default class UserMenu extends React.Component { if (!this.state.contextMenuPosition) return null; let topSection: JSX.Element | undefined; - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { topSection = (
{_t( @@ -372,7 +372,7 @@ export default class UserMenu extends React.Component { ); - if (MatrixClientPeg.get().isGuest()) { + if (MatrixClientPeg.safeGet().isGuest()) { primaryOptionList = ( {homeButton} @@ -399,7 +399,7 @@ export default class UserMenu extends React.Component { {UserIdentifierCustomisations.getDisplayUserIdentifier( - MatrixClientPeg.get().getSafeUserId(), + MatrixClientPeg.safeGet().getSafeUserId(), { withDisplayName: true, }, @@ -428,7 +428,7 @@ export default class UserMenu extends React.Component { public render(): React.ReactNode { const avatarSize = 32; // should match border-radius of the avatar - const userId = MatrixClientPeg.get().getSafeUserId(); + const userId = MatrixClientPeg.safeGet().getSafeUserId(); const displayName = OwnProfileStore.instance.displayName || userId; const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); diff --git a/src/components/structures/ViewSource.tsx b/src/components/structures/ViewSource.tsx index a4e10e12e07..d1e60ad12f6 100644 --- a/src/components/structures/ViewSource.tsx +++ b/src/components/structures/ViewSource.tsx @@ -142,7 +142,7 @@ export default class ViewSource extends React.Component { } private canSendStateEvent(mxEvent: MatrixEvent): boolean { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(mxEvent.getRoomId()); return !!room?.currentState.mayClientSendStateEvent(mxEvent.getType(), cli); } @@ -155,7 +155,7 @@ export default class ViewSource extends React.Component { const eventId = mxEvent.getId()!; const canEdit = mxEvent.isState() ? this.canSendStateEvent(mxEvent) - : canEditContent(MatrixClientPeg.get(), this.props.mxEvent); + : canEditContent(MatrixClientPeg.safeGet(), this.props.mxEvent); return (
diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index d8fd848320b..75269301526 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -420,7 +420,7 @@ export default class Registration extends React.Component { if (!this.props.brand) { return Promise.resolve(); } - const matrixClient = MatrixClientPeg.get(); + const matrixClient = MatrixClientPeg.safeGet(); return matrixClient.getPushers().then( (resp) => { const pushers = resp.pushers; diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 15d058b4da0..7afa4f321ba 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -87,7 +87,7 @@ export default class SetupEncryptionBody extends React.Component }; private onVerifyClick = (): void => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const userId = cli.getSafeUserId(); const requestPromise = cli.requestVerification(userId); @@ -142,7 +142,7 @@ export default class SetupEncryptionBody extends React.Component }; public render(): React.ReactNode { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const { phase, lostKeys } = this.state; if (this.state.verificationRequest && cli.getUser(this.state.verificationRequest.otherUserId)) { diff --git a/src/components/structures/auth/SoftLogout.tsx b/src/components/structures/auth/SoftLogout.tsx index f8a058fc7b4..932cdef475c 100644 --- a/src/components/structures/auth/SoftLogout.tsx +++ b/src/components/structures/auth/SoftLogout.tsx @@ -94,7 +94,7 @@ export default class SoftLogout extends React.Component { this.initLogin(); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); if (cli.isCryptoEnabled()) { cli.countSessionsNeedingBackup().then((remaining) => { this.setState({ keyBackupNeeded: remaining > 0 }); @@ -124,7 +124,7 @@ export default class SoftLogout extends React.Component { // Note: we don't use the existing Login class because it is heavily flow-based. We don't // care about login flows here, unless it is the single flow we support. - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const flows = (await client.loginFlows()).flows; const loginViews = flows.map((f) => STATIC_FLOWS_TO_VIEWS[f.type]); @@ -148,16 +148,17 @@ export default class SoftLogout extends React.Component { this.setState({ busy: true }); - const hsUrl = MatrixClientPeg.get().getHomeserverUrl(); - const isUrl = MatrixClientPeg.get().getIdentityServerUrl(); + const cli = MatrixClientPeg.safeGet(); + const hsUrl = cli.getHomeserverUrl(); + const isUrl = cli.getIdentityServerUrl(); const loginType = "m.login.password"; const loginParams = { identifier: { type: "m.id.user", - user: MatrixClientPeg.get().getUserId(), + user: cli.getUserId(), }, password: this.state.password, - device_id: MatrixClientPeg.get().getDeviceId() ?? undefined, + device_id: cli.getDeviceId() ?? undefined, }; let credentials: IMatrixClientCreds; @@ -196,11 +197,11 @@ export default class SoftLogout extends React.Component { return; } - const isUrl = localStorage.getItem(SSO_ID_SERVER_URL_KEY) || MatrixClientPeg.get().getIdentityServerUrl(); + const isUrl = localStorage.getItem(SSO_ID_SERVER_URL_KEY) || MatrixClientPeg.safeGet().getIdentityServerUrl(); const loginType = "m.login.token"; const loginParams = { token: this.props.realQueryParams["loginToken"], - device_id: MatrixClientPeg.get().getDeviceId() ?? undefined, + device_id: MatrixClientPeg.safeGet().getDeviceId() ?? undefined, }; let credentials: IMatrixClientCreds; @@ -262,7 +263,7 @@ export default class SoftLogout extends React.Component {
{introText ?

{introText}

: null} void] => { const [rooms, setRooms] = useState(() => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const recents = SettingsStore.getValue("SpotlightSearch.recentSearches", null); return filterBoolean(recents.map((r) => cli.getRoom(r))); }); diff --git a/src/hooks/usePermalinkEvent.ts b/src/hooks/usePermalinkEvent.ts index 1cb2b05e3bc..e72592a6e5f 100644 --- a/src/hooks/usePermalinkEvent.ts +++ b/src/hooks/usePermalinkEvent.ts @@ -74,7 +74,7 @@ export const usePermalinkEvent = ( const fetchRoomEvent = async (): Promise => { try { - const eventData = await MatrixClientPeg.get().fetchRoomEvent( + const eventData = await MatrixClientPeg.safeGet().fetchRoomEvent( parseResult.roomIdOrAlias!, parseResult.eventId!, ); diff --git a/src/hooks/usePermalinkTargetRoom.ts b/src/hooks/usePermalinkTargetRoom.ts index 826f995bbe7..d6d1dd09591 100644 --- a/src/hooks/usePermalinkTargetRoom.ts +++ b/src/hooks/usePermalinkTargetRoom.ts @@ -59,7 +59,7 @@ const determineInitialRoom = ( * @returns Room if found, else null. */ const findRoom = (roomIdOrAlias: string): Room | null => { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); return roomIdOrAlias[0] === "#" ? client.getRooms().find((r) => { diff --git a/src/hooks/useProfileInfo.ts b/src/hooks/useProfileInfo.ts index 6110c6732cd..d85012b87e9 100644 --- a/src/hooks/useProfileInfo.ts +++ b/src/hooks/useProfileInfo.ts @@ -51,7 +51,7 @@ export const useProfileInfo = (): { setLoading(true); try { - const result = await MatrixClientPeg.get().getProfileInfo(term); + const result = await MatrixClientPeg.safeGet().getProfileInfo(term); updateResult(term, { user_id: term, avatar_url: result.avatar_url, diff --git a/src/hooks/usePublicRoomDirectory.ts b/src/hooks/usePublicRoomDirectory.ts index 9572862d007..d3c6b00c0b8 100644 --- a/src/hooks/usePublicRoomDirectory.ts +++ b/src/hooks/usePublicRoomDirectory.ts @@ -72,7 +72,7 @@ export const usePublicRoomDirectory = (): { } else if (thirdParty) { setProtocols(thirdParty); } else { - const response = await MatrixClientPeg.get().getThirdpartyProtocols(); + const response = await MatrixClientPeg.safeGet().getThirdpartyProtocols(); thirdParty = response; setProtocols(response); } @@ -105,7 +105,7 @@ export const usePublicRoomDirectory = (): { generic_search_term: query, room_types: roomTypes && - (await MatrixClientPeg.get().doesServerSupportUnstableFeature("org.matrix.msc3827.stable")) + (await MatrixClientPeg.safeGet().doesServerSupportUnstableFeature("org.matrix.msc3827.stable")) ? Array.from(roomTypes) : undefined, }; @@ -114,7 +114,7 @@ export const usePublicRoomDirectory = (): { updateQuery(opts); try { setLoading(true); - const { chunk } = await MatrixClientPeg.get().publicRooms(opts); + const { chunk } = await MatrixClientPeg.safeGet().publicRooms(opts); updateResult(opts, showNsfwPublicRooms ? chunk : chunk.filter(cheapNsfwFilter)); return true; } catch (e) { diff --git a/src/hooks/useSlidingSyncRoomSearch.ts b/src/hooks/useSlidingSyncRoomSearch.ts index c4dfbbf409a..5cf1f6bea83 100644 --- a/src/hooks/useSlidingSyncRoomSearch.ts +++ b/src/hooks/useSlidingSyncRoomSearch.ts @@ -62,7 +62,7 @@ export const useSlidingSyncRoomSearch = (): { let i = 0; while (roomIndexToRoomId[i]) { const roomId = roomIndexToRoomId[i]; - const room = MatrixClientPeg.get().getRoom(roomId); + const room = MatrixClientPeg.safeGet().getRoom(roomId); if (room) { rooms.push(room); } diff --git a/src/hooks/useSpaceResults.ts b/src/hooks/useSpaceResults.ts index 35a4627b058..ebeed762396 100644 --- a/src/hooks/useSpaceResults.ts +++ b/src/hooks/useSpaceResults.ts @@ -54,7 +54,7 @@ export const useSpaceResults = (space: Room | undefined, query: string): [IHiera const lcQuery = trimmedQuery.toLowerCase(); const normalizedQuery = normalize(trimmedQuery); - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); return rooms?.filter((r) => { return ( r.room_type !== RoomType.Space && diff --git a/src/hooks/useUserDirectory.ts b/src/hooks/useUserDirectory.ts index d775994e3a3..9dfcbc05ada 100644 --- a/src/hooks/useUserDirectory.ts +++ b/src/hooks/useUserDirectory.ts @@ -49,7 +49,7 @@ export const useUserDirectory = (): { try { setLoading(true); - const { results } = await MatrixClientPeg.get().searchUserDirectory(opts); + const { results } = await MatrixClientPeg.safeGet().searchUserDirectory(opts); updateResult( opts, results.map((user) => new DirectoryMember(user)), diff --git a/src/stores/ActiveWidgetStore.ts b/src/stores/ActiveWidgetStore.ts index 90e349e6686..edc46324860 100644 --- a/src/stores/ActiveWidgetStore.ts +++ b/src/stores/ActiveWidgetStore.ts @@ -51,7 +51,7 @@ export default class ActiveWidgetStore extends EventEmitter { } public start(): void { - MatrixClientPeg.get().on(RoomStateEvent.Events, this.onRoomStateEvents); + MatrixClientPeg.safeGet().on(RoomStateEvent.Events, this.onRoomStateEvents); } public stop(): void { diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index db8960307bf..bae9534846d 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -159,7 +159,7 @@ export class OwnProfileStore extends AsyncStoreWithClient { ); private onStateEvents = async (ev: MatrixEvent): Promise => { - const myUserId = MatrixClientPeg.get().getUserId(); + const myUserId = MatrixClientPeg.safeGet().getUserId(); if (ev.getType() === EventType.RoomMember && ev.getSender() === myUserId && ev.getStateKey() === myUserId) { await this.onProfileUpdate(); } diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 2f8c80023fd..1fa116094d8 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -306,7 +306,7 @@ export class RoomViewStore extends EventEmitter { this.setState({ shouldPeek: false }); } - awaitRoomDownSync(MatrixClientPeg.get(), payload.roomId).then((room) => { + awaitRoomDownSync(MatrixClientPeg.safeGet(), payload.roomId).then((room) => { const numMembers = room.getJoinedMemberCount(); const roomSize = numMembers > 1000 @@ -361,7 +361,7 @@ export class RoomViewStore extends EventEmitter { private async viewRoom(payload: ViewRoomPayload): Promise { if (payload.room_id) { - const room = MatrixClientPeg.get().getRoom(payload.room_id); + const room = MatrixClientPeg.safeGet().getRoom(payload.room_id); if (payload.metricsTrigger !== null && payload.room_id !== this.state.roomId) { let activeSpace: ViewRoomEvent["activeSpace"]; @@ -488,7 +488,7 @@ export class RoomViewStore extends EventEmitter { viewingCall: payload.view_call ?? false, }); try { - const result = await MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias); + const result = await MatrixClientPeg.safeGet().getRoomIdForAlias(payload.room_alias); storeRoomAliasInCache(payload.room_alias, result.room_id); roomId = result.room_id; } catch (err) { @@ -531,12 +531,12 @@ export class RoomViewStore extends EventEmitter { joining: true, }); - const cli = MatrixClientPeg.get(); // take a copy of roomAlias & roomId as they may change by the time the join is complete const { roomAlias, roomId = payload.roomId } = this.state; const address = roomAlias || roomId!; const viaServers = this.state.viaServers || []; try { + const cli = MatrixClientPeg.safeGet(); await retry( () => cli.joinRoom(address, { @@ -568,7 +568,7 @@ export class RoomViewStore extends EventEmitter { } private getInvitingUserId(roomId: string): string | undefined { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(roomId); if (room?.getMyMembership() === "invite") { const myMember = room.getMember(cli.getSafeUserId()); @@ -596,7 +596,7 @@ export class RoomViewStore extends EventEmitter { // provide a better error message for invites if (invitingUserId) { // if the inviting user is on the same HS, there can only be one cause: they left. - if (invitingUserId.endsWith(`:${MatrixClientPeg.get().getDomain()}`)) { + if (invitingUserId.endsWith(`:${MatrixClientPeg.safeGet().getDomain()}`)) { description = _t("The person who invited you has already left."); } else { description = _t("The person who invited you has already left, or their server is offline."); diff --git a/src/stores/SetupEncryptionStore.ts b/src/stores/SetupEncryptionStore.ts index 31910e46bea..3c47d6b2667 100644 --- a/src/stores/SetupEncryptionStore.ts +++ b/src/stores/SetupEncryptionStore.ts @@ -62,7 +62,7 @@ export class SetupEncryptionStore extends EventEmitter { this.started = true; this.phase = Phase.Loading; - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); cli.on(CryptoEvent.VerificationRequest, this.onVerificationRequest); cli.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged); @@ -83,15 +83,17 @@ export class SetupEncryptionStore extends EventEmitter { } this.started = false; this.verificationRequest?.off(VerificationRequestEvent.Change, this.onVerificationRequestChange); - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener(CryptoEvent.VerificationRequest, this.onVerificationRequest); - MatrixClientPeg.get().removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged); + + const cli = MatrixClientPeg.get(); + if (!!cli) { + cli.removeListener(CryptoEvent.VerificationRequest, this.onVerificationRequest); + cli.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged); } } public async fetchKeyInfo(): Promise { if (!this.started) return; // bail if we were stopped - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); const keys = await cli.isSecretStored("m.cross_signing.master"); if (keys === null || Object.keys(keys).length === 0) { this.keyId = null; @@ -120,8 +122,8 @@ export class SetupEncryptionStore extends EventEmitter { public async usePassPhrase(): Promise { this.phase = Phase.Busy; this.emit("update"); - const cli = MatrixClientPeg.get(); try { + const cli = MatrixClientPeg.safeGet(); const backupInfo = await cli.getKeyBackupVersion(); this.backupInfo = backupInfo; this.emit("update"); @@ -161,8 +163,8 @@ export class SetupEncryptionStore extends EventEmitter { } private onUserTrustStatusChanged = async (userId: string): Promise => { - if (userId !== MatrixClientPeg.get().getUserId()) return; - const publicKeysTrusted = await MatrixClientPeg.get().getCrypto()?.getCrossSigningKeyId(); + if (userId !== MatrixClientPeg.safeGet().getSafeUserId()) return; + const publicKeysTrusted = await MatrixClientPeg.safeGet().getCrypto()?.getCrossSigningKeyId(); if (publicKeysTrusted) { this.phase = Phase.Done; this.emit("update"); @@ -184,7 +186,7 @@ export class SetupEncryptionStore extends EventEmitter { // At this point, the verification has finished, we just need to wait for // cross signing to be ready to use, so wait for the user trust status to // change (or change to DONE if it's already ready). - const publicKeysTrusted = await MatrixClientPeg.get().getCrypto()?.getCrossSigningKeyId(); + const publicKeysTrusted = await MatrixClientPeg.safeGet().getCrypto()?.getCrossSigningKeyId(); this.phase = publicKeysTrusted ? Phase.Done : Phase.Busy; this.emit("update"); } @@ -217,7 +219,7 @@ export class SetupEncryptionStore extends EventEmitter { // secret storage and setting up a new recovery key, then // create new cross-signing keys once that succeeds. await accessSecretStorage(async (): Promise => { - const cli = MatrixClientPeg.get(); + const cli = MatrixClientPeg.safeGet(); await cli.bootstrapCrossSigning({ authUploadDeviceSigningKeys: async (makeRequest): Promise => { const cachedPassword = SdkContextClass.instance.accountPasswordStore.getPassword(); @@ -227,9 +229,9 @@ export class SetupEncryptionStore extends EventEmitter { type: "m.login.password", identifier: { type: "m.id.user", - user: cli.getUserId(), + user: cli.getSafeUserId(), }, - user: cli.getUserId(), + user: cli.getSafeUserId(), password: cachedPassword, }); return; @@ -265,12 +267,12 @@ export class SetupEncryptionStore extends EventEmitter { this.phase = Phase.Finished; this.emit("update"); // async - ask other clients for keys, if necessary - MatrixClientPeg.get().crypto?.cancelAndResendAllOutgoingKeyRequests(); + MatrixClientPeg.safeGet().crypto?.cancelAndResendAllOutgoingKeyRequests(); } private async setActiveVerificationRequest(request: VerificationRequest): Promise { if (!this.started) return; // bail if we were stopped - if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return; + if (request.otherUserId !== MatrixClientPeg.safeGet().getUserId()) return; if (this.verificationRequest) { this.verificationRequest.off(VerificationRequestEvent.Change, this.onVerificationRequestChange); diff --git a/src/stores/notifications/RoomNotificationState.ts b/src/stores/notifications/RoomNotificationState.ts index 3c0447a1434..d647f5cd1b8 100644 --- a/src/stores/notifications/RoomNotificationState.ts +++ b/src/stores/notifications/RoomNotificationState.ts @@ -59,7 +59,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy }; private handleReadReceipt = (event: MatrixEvent, room: Room): void => { - if (!readReceiptChangeIsFor(event, MatrixClientPeg.get())) return; // not our own - ignore + if (!readReceiptChangeIsFor(event, MatrixClientPeg.safeGet())) return; // not our own - ignore if (room.roomId !== this.room.roomId) return; // not for us - ignore this.updateNotificationState(); }; diff --git a/src/stores/right-panel/RightPanelStore.ts b/src/stores/right-panel/RightPanelStore.ts index 942645b15f4..6d827aa171a 100644 --- a/src/stores/right-panel/RightPanelStore.ts +++ b/src/stores/right-panel/RightPanelStore.ts @@ -310,7 +310,7 @@ export default class RightPanelStore extends ReadyWatchingStore { // RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request const { member } = card.state; const pendingRequest = member - ? pendingVerificationRequestForUser(MatrixClientPeg.get(), member) + ? pendingVerificationRequestForUser(MatrixClientPeg.safeGet(), member) : undefined; if (pendingRequest) { return { @@ -344,7 +344,7 @@ export default class RightPanelStore extends ReadyWatchingStore { if (!this.currentCard?.state) return; const { member } = this.currentCard.state; if (!member) return; - const pendingRequest = pendingVerificationRequestForUser(MatrixClientPeg.get(), member); + const pendingRequest = pendingVerificationRequestForUser(MatrixClientPeg.safeGet(), member); if (pendingRequest) { this.currentCard.state.verificationRequest = pendingRequest; this.emitAndUpdateSettings(); diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index e0b7d4bd206..15c579312ae 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -56,7 +56,7 @@ export const sortRooms = (rooms: Room[]): Room[] => { // See https://github.com/vector-im/element-web/issues/14458 let myUserId = ""; if (MatrixClientPeg.get()) { - myUserId = MatrixClientPeg.get().getUserId()!; + myUserId = MatrixClientPeg.get()!.getSafeUserId(); } const tsCache: { [roomId: string]: number } = {}; diff --git a/src/stores/room-list/previews/utils.ts b/src/stores/room-list/previews/utils.ts index ec8ddb28e3b..07a561a2199 100644 --- a/src/stores/room-list/previews/utils.ts +++ b/src/stores/room-list/previews/utils.ts @@ -20,7 +20,7 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { DefaultTagID, TagID } from "../models"; export function isSelf(event: MatrixEvent): boolean { - const selfUserId = MatrixClientPeg.get().getSafeUserId(); + const selfUserId = MatrixClientPeg.safeGet().getSafeUserId(); if (event.getType() === "m.room.member") { return event.getStateKey() === selfUserId; } @@ -31,7 +31,7 @@ export function shouldPrefixMessagesIn(roomId: string, tagId?: TagID): boolean { if (tagId !== DefaultTagID.DM) return true; // We don't prefix anything in 1:1s - const room = MatrixClientPeg.get().getRoom(roomId); + const room = MatrixClientPeg.safeGet().getRoom(roomId); if (!room) return true; return room.currentState.getJoinedMemberCount() !== 2; } diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 2fc176e6dc8..2781612a0c3 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -167,7 +167,7 @@ export class StopGapWidget extends EventEmitter { public constructor(private appTileProps: IAppTileProps) { super(); - this.client = MatrixClientPeg.get(); + this.client = MatrixClientPeg.safeGet(); let app = appTileProps.app; // Backwards compatibility: not all old widgets have a creatorUserId diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 9e24126b15d..b0fe61deb77 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -138,7 +138,7 @@ export class StopGapWidgetDriver extends WidgetDriver { WidgetEventCapability.forStateEvent( EventDirection.Send, "org.matrix.msc3401.call.member", - MatrixClientPeg.get().getUserId()!, + MatrixClientPeg.safeGet().getSafeUserId(), ).raw, ); this.allowedCapabilities.add( @@ -266,7 +266,7 @@ export class StopGapWidgetDriver extends WidgetDriver { encrypted: boolean, contentMap: { [userId: string]: { [deviceId: string]: object } }, ): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); if (encrypted) { const deviceInfoMap = await client.crypto!.deviceList.downloadKeys(Object.keys(contentMap), false); @@ -382,7 +382,7 @@ export class StopGapWidgetDriver extends WidgetDriver { if (opts.approved) { return observer.update({ state: OpenIDRequestState.Allowed, - token: await MatrixClientPeg.get().getOpenIdToken(), + token: await MatrixClientPeg.safeGet().getOpenIdToken(), }); } @@ -393,7 +393,7 @@ export class StopGapWidgetDriver extends WidgetDriver { ); const getToken = (): Promise => { - return MatrixClientPeg.get().getOpenIdToken(); + return MatrixClientPeg.safeGet().getOpenIdToken(); }; if (oidcState === OIDCState.Denied) { @@ -425,7 +425,7 @@ export class StopGapWidgetDriver extends WidgetDriver { } public async *getTurnServers(): AsyncGenerator { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); if (!client.pollingTurnServers || !client.getTurnServers().length) return; let setTurnServer: (server: ITurnServer) => void; @@ -468,7 +468,7 @@ export class StopGapWidgetDriver extends WidgetDriver { limit?: number, direction?: "f" | "b", ): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const dir = direction as Direction; roomId = roomId ?? SdkContextClass.instance.roomViewStore.getRoomId() ?? undefined; @@ -492,7 +492,7 @@ export class StopGapWidgetDriver extends WidgetDriver { } public async searchUserDirectory(searchTerm: string, limit?: number): Promise { - const client = MatrixClientPeg.get(); + const client = MatrixClientPeg.safeGet(); const { limited, results } = await client.searchUserDirectory({ term: searchTerm, limit }); diff --git a/src/utils/PasswordScorer.ts b/src/utils/PasswordScorer.ts index fa2ef920582..187962e8f33 100644 --- a/src/utils/PasswordScorer.ts +++ b/src/utils/PasswordScorer.ts @@ -61,7 +61,7 @@ _td("Short keyboard patterns are easy to guess"); * @param matrixClient the client of the logged in user, if any * @returns {object} Score result with `score` and `feedback` properties */ -export function scorePassword(matrixClient: MatrixClient | undefined, password: string): zxcvbn.ZXCVBNResult | null { +export function scorePassword(matrixClient: MatrixClient | null, password: string): zxcvbn.ZXCVBNResult | null { if (password.length === 0) return null; const userInputs = ZXCVBN_USER_INPUTS.slice(); diff --git a/test/stores/room-list/previews/PollStartEventPreview-test.ts b/test/stores/room-list/previews/PollStartEventPreview-test.ts index dc9bcabfe3f..fdb02ff1867 100644 --- a/test/stores/room-list/previews/PollStartEventPreview-test.ts +++ b/test/stores/room-list/previews/PollStartEventPreview-test.ts @@ -24,6 +24,10 @@ jest.spyOn(MatrixClientPeg, "get").mockReturnValue({ getUserId: () => "@me:example.com", getSafeUserId: () => "@me:example.com", } as unknown as MatrixClient); +jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue({ + getUserId: () => "@me:example.com", + getSafeUserId: () => "@me:example.com", +} as unknown as MatrixClient); describe("PollStartEventPreview", () => { it("shows the question for a poll I created", async () => { diff --git a/test/stores/widgets/StopGapWidgetDriver-test.ts b/test/stores/widgets/StopGapWidgetDriver-test.ts index b5bea5b692e..b3d11f4b9b2 100644 --- a/test/stores/widgets/StopGapWidgetDriver-test.ts +++ b/test/stores/widgets/StopGapWidgetDriver-test.ts @@ -63,6 +63,7 @@ describe("StopGapWidgetDriver", () => { stubClient(); client = mocked(MatrixClientPeg.safeGet()); client.getUserId.mockReturnValue("@alice:example.org"); + client.getSafeUserId.mockReturnValue("@alice:example.org"); }); it("auto-approves capabilities of virtual Element Call widgets", async () => { From 06fa49a9da044fc3114629a894e79c29a50c5f35 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Fri, 16 Jun 2023 11:27:56 +0100 Subject: [PATCH 20/68] Use new accessors for `VerificationRequest` (#11092) * Switch verification request accessors to go via CryptoApi part of https://github.com/vector-im/crypto-internal/issues/97 * Update references to `requestVerification` https://github.com/vector-im/crypto-internal/issues/98 --- src/components/structures/auth/SetupEncryptionBody.tsx | 2 +- src/components/views/settings/devices/useOwnDevices.ts | 2 +- src/stores/SetupEncryptionStore.ts | 2 +- src/verification.ts | 2 +- .../views/settings/tabs/user/SessionManagerTab-test.tsx | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 7afa4f321ba..1de7cc7d83f 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -89,7 +89,7 @@ export default class SetupEncryptionBody extends React.Component private onVerifyClick = (): void => { const cli = MatrixClientPeg.safeGet(); const userId = cli.getSafeUserId(); - const requestPromise = cli.requestVerification(userId); + const requestPromise = cli.getCrypto()!.requestOwnUserVerification(); // We need to call onFinished now to close this dialog, and // again later to signal that the verification is complete. diff --git a/src/components/views/settings/devices/useOwnDevices.ts b/src/components/views/settings/devices/useOwnDevices.ts index 34227200778..b7eff43f0c8 100644 --- a/src/components/views/settings/devices/useOwnDevices.ts +++ b/src/components/views/settings/devices/useOwnDevices.ts @@ -177,7 +177,7 @@ export const useOwnDevices = (): DevicesState => { const requestDeviceVerification = isCurrentDeviceVerified && userId ? async (deviceId: ExtendedDevice["device_id"]): Promise => { - return await matrixClient.requestVerification(userId, [deviceId]); + return await matrixClient.getCrypto()!.requestDeviceVerification(userId, deviceId); } : undefined; diff --git a/src/stores/SetupEncryptionStore.ts b/src/stores/SetupEncryptionStore.ts index 3c47d6b2667..2c1e64353b2 100644 --- a/src/stores/SetupEncryptionStore.ts +++ b/src/stores/SetupEncryptionStore.ts @@ -66,7 +66,7 @@ export class SetupEncryptionStore extends EventEmitter { cli.on(CryptoEvent.VerificationRequest, this.onVerificationRequest); cli.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged); - const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId()!); + const requestsInProgress = cli.getCrypto()!.getVerificationRequestsToDeviceInProgress(cli.getUserId()!); if (requestsInProgress.length) { // If there are multiple, we take the most recent. Equally if the user sends another request from // another device after this screen has been shown, we'll switch to the new one, so this diff --git a/src/verification.ts b/src/verification.ts index 4736b4693b3..aa20740b213 100644 --- a/src/verification.ts +++ b/src/verification.ts @@ -120,6 +120,6 @@ export function pendingVerificationRequestForUser( ): VerificationRequest | undefined { const dmRoom = findDMForUser(matrixClient, user.userId); if (dmRoom) { - return matrixClient.findVerificationRequestDMInProgress(dmRoom.roomId); + return matrixClient.getCrypto()!.findVerificationRequestDMInProgress(dmRoom.roomId); } } diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx index e9c6641b39d..f2e34d37e61 100644 --- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx +++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx @@ -18,7 +18,7 @@ import React from "react"; import { act, fireEvent, render, RenderResult } from "@testing-library/react"; import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import { logger } from "matrix-js-sdk/src/logger"; -import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { defer, sleep } from "matrix-js-sdk/src/utils"; import { ClientEvent, @@ -88,6 +88,7 @@ describe("", () => { const mockCrypto = mocked({ getDeviceVerificationStatus: jest.fn(), + requestDeviceVerification: jest.fn().mockResolvedValue(mockVerificationRequest), } as unknown as CryptoApi); const mockClient = getMockClientWithEventEmitter({ @@ -96,7 +97,6 @@ describe("", () => { getDevices: jest.fn(), getStoredDevice: jest.fn(), getDeviceId: jest.fn().mockReturnValue(deviceId), - requestVerification: jest.fn().mockResolvedValue(mockVerificationRequest), deleteMultipleDevices: jest.fn(), generateClientSecret: jest.fn(), setDeviceDetails: jest.fn(), @@ -531,7 +531,7 @@ describe("", () => { // click verify button from current session section fireEvent.click(getByTestId(`verification-status-button-${alicesMobileDevice.device_id}`)); - expect(mockClient.requestVerification).toHaveBeenCalledWith(aliceId, [alicesMobileDevice.device_id]); + expect(mockCrypto.requestDeviceVerification).toHaveBeenCalledWith(aliceId, alicesMobileDevice.device_id); expect(modalSpy).toHaveBeenCalled(); }); From 77da949fd443500c0e5cce11a72e5301be9a02e2 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 16 Jun 2023 14:25:24 +0200 Subject: [PATCH 21/68] `CreateKeyBackupDialog.tsx`: Remove logic for servers which are not supporting cross-signing (#11076) --- .../security/CreateKeyBackupDialog.tsx | 369 +----------------- src/i18n/strings/en_EN.json | 32 +- .../security/CreateKeyBackupDialog-test.tsx | 69 ++++ .../CreateKeyBackupDialog-test.tsx.snap | 168 ++++++++ test/test-utils/test-utils.ts | 1 + 5 files changed, 260 insertions(+), 379 deletions(-) create mode 100644 test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx create mode 100644 test/components/views/dialogs/security/__snapshots__/CreateKeyBackupDialog-test.tsx.snap diff --git a/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx b/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx index ec8cf31f30f..822e9fd46b7 100644 --- a/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx +++ b/src/async-components/views/dialogs/security/CreateKeyBackupDialog.tsx @@ -15,41 +15,27 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { createRef } from "react"; -import FileSaver from "file-saver"; -import { IPreparedKeyBackupVersion } from "matrix-js-sdk/src/crypto/backup"; +import React from "react"; import { logger } from "matrix-js-sdk/src/logger"; +import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; import { MatrixClientPeg } from "../../../../MatrixClientPeg"; -import { _t, _td } from "../../../../languageHandler"; +import { _t } from "../../../../languageHandler"; import { accessSecretStorage } from "../../../../SecurityManager"; -import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; -import { copyNode } from "../../../../utils/strings"; -import PassphraseField from "../../../../components/views/auth/PassphraseField"; -import Field from "../../../../components/views/elements/Field"; import Spinner from "../../../../components/views/elements/Spinner"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import DialogButtons from "../../../../components/views/elements/DialogButtons"; -import { IValidationResult } from "../../../../components/views/elements/Validation"; enum Phase { - Passphrase = "passphrase", - PassphraseConfirm = "passphrase_confirm", - ShowKey = "show_key", - KeepItSafe = "keep_it_safe", BackingUp = "backing_up", Done = "done", - OptOutConfirm = "opt_out_confirm", } -const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. - interface IProps { onFinished(done?: boolean): void; } interface IState { - secureSecretStorage: boolean | null; phase: Phase; passPhrase: string; passPhraseValid: boolean; @@ -64,16 +50,11 @@ interface IState { * on the server. */ export default class CreateKeyBackupDialog extends React.PureComponent { - private keyBackupInfo: Pick; - private recoveryKeyNode = createRef(); - private passphraseField = createRef(); - public constructor(props: IProps) { super(props); this.state = { - secureSecretStorage: null, - phase: Phase.Passphrase, + phase: Phase.BackingUp, passPhrase: "", passPhraseValid: false, passPhraseConfirm: "", @@ -82,59 +63,22 @@ export default class CreateKeyBackupDialog extends React.PureComponent { - const cli = MatrixClientPeg.get(); - const secureSecretStorage = await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"); - this.setState({ secureSecretStorage }); - - // If we're using secret storage, skip ahead to the backing up step, as - // `accessSecretStorage` will handle passphrases as needed. - if (secureSecretStorage) { - this.setState({ phase: Phase.BackingUp }); - this.createBackup(); - } + public componentDidMount(): void { + this.createBackup(); } - private onCopyClick = (): void => { - const successful = copyNode(this.recoveryKeyNode.current); - if (successful) { - this.setState({ - copied: true, - phase: Phase.KeepItSafe, - }); - } - }; - - private onDownloadClick = (): void => { - const blob = new Blob([this.keyBackupInfo.recovery_key], { - type: "text/plain;charset=us-ascii", - }); - FileSaver.saveAs(blob, "security-key.txt"); - - this.setState({ - downloaded: true, - phase: Phase.KeepItSafe, - }); - }; - private createBackup = async (): Promise => { - const { secureSecretStorage } = this.state; this.setState({ - phase: Phase.BackingUp, error: undefined, }); - let info; + let info: IKeyBackupInfo | undefined; try { - if (secureSecretStorage) { - await accessSecretStorage(async (): Promise => { - info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, { - secureSecretStorage: true, - }); - info = await MatrixClientPeg.get().createKeyBackupVersion(info); + await accessSecretStorage(async (): Promise => { + info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, { + secureSecretStorage: true, }); - } else { - info = await MatrixClientPeg.get().createKeyBackupVersion(this.keyBackupInfo); - } + info = await MatrixClientPeg.get().createKeyBackupVersion(info); + }); await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup(); this.setState({ phase: Phase.Done, @@ -145,7 +89,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { - this.setState({ phase: Phase.Passphrase }); - }; - - private onSkipPassPhraseClick = async (): Promise => { - this.keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion(); - this.setState({ - copied: false, - downloaded: false, - phase: Phase.ShowKey, - }); - }; - - private onPassPhraseNextClick = async (e: React.FormEvent): Promise => { - e.preventDefault(); - if (!this.passphraseField.current) return; // unmounting - - await this.passphraseField.current.validate({ allowEmpty: false }); - if (!this.passphraseField.current.state.valid) { - this.passphraseField.current.focus(); - this.passphraseField.current.validate({ allowEmpty: false, focused: true }); - return; - } - - this.setState({ phase: Phase.PassphraseConfirm }); - }; - - private onPassPhraseConfirmNextClick = async (e: React.FormEvent): Promise => { - e.preventDefault(); - - if (this.state.passPhrase !== this.state.passPhraseConfirm) return; - - this.keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion(this.state.passPhrase); - this.setState({ - copied: false, - downloaded: false, - phase: Phase.ShowKey, - }); - }; - - private onSetAgainClick = (): void => { - this.setState({ - passPhrase: "", - passPhraseValid: false, - passPhraseConfirm: "", - phase: Phase.Passphrase, - }); - }; - - private onKeepItSafeBackClick = (): void => { - this.setState({ - phase: Phase.ShowKey, - }); - }; - - private onPassPhraseValidate = (result: IValidationResult): void => { - this.setState({ - passPhraseValid: !!result.valid, - }); - }; - - private onPassPhraseChange = (e: React.ChangeEvent): void => { - this.setState({ - passPhrase: e.target.value, - }); - }; - - private onPassPhraseConfirmChange = (e: React.ChangeEvent): void => { - this.setState({ - passPhraseConfirm: e.target.value, - }); - }; - - private renderPhasePassPhrase(): JSX.Element { - return ( -
-

- {_t( - "Warning: you should only set up key backup from a trusted computer.", - {}, - { b: (sub) => {sub} }, - )} -

-

- {_t( - "We'll store an encrypted copy of your keys on our server. " + - "Secure your backup with a Security Phrase.", - )} -

-

{_t("For maximum security, this should be different from your account password.")}

- -
-
- -
-
- - - -
- {_t("Advanced")} - - {_t("Set up with a Security Key")} - -
- - ); - } - - private renderPhasePassPhraseConfirm(): JSX.Element { - let matchText; - let changeText; - if (this.state.passPhraseConfirm === this.state.passPhrase) { - matchText = _t("That matches!"); - changeText = _t("Use a different passphrase?"); - } else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) { - // only tell them they're wrong if they've actually gone wrong. - // Security conscious readers will note that if you left element-web unattended - // on this screen, this would make it easy for a malicious person to guess - // your passphrase one letter at a time, but they could get this faster by - // just opening the browser's developer tools and reading it. - // Note that not having typed anything at all will not hit this clause and - // fall through so empty box === no hint. - matchText = _t("That doesn't match."); - changeText = _t("Go back to set it again."); - } - - let passPhraseMatch: JSX.Element | undefined; - if (matchText) { - passPhraseMatch = ( -
-
{matchText}
- - {changeText} - -
- ); - } - return ( -
-

{_t("Enter your Security Phrase a second time to confirm it.")}

-
-
-
- -
- {passPhraseMatch} -
-
- - - ); - } - - private renderPhaseShowKey(): JSX.Element { - return ( -
-

- {_t( - "Your Security Key is a safety net - you can use it to restore " + - "access to your encrypted messages if you forget your Security Phrase.", - )} -

-

{_t("Keep a copy of it somewhere secure, like a password manager or even a safe.")}

-
-
{_t("Your Security Key")}
-
-
- {this.keyBackupInfo.recovery_key} -
-
- - -
-
-
-
- ); - } - - private renderPhaseKeepItSafe(): JSX.Element { - let introText; - if (this.state.copied) { - introText = _t( - "Your Security Key has been copied to your clipboard, paste it to:", - {}, - { b: (s) => {s} }, - ); - } else if (this.state.downloaded) { - introText = _t("Your Security Key is in your Downloads folder.", {}, { b: (s) => {s} }); - } - return ( -
- {introText} -
    -
  • {_t("Print it and store it somewhere safe", {}, { b: (s) => {s} })}
  • -
  • {_t("Save it on a USB key or backup drive", {}, { b: (s) => {s} })}
  • -
  • {_t("Copy it to your personal cloud storage", {}, { b: (s) => {s} })}
  • -
- - - -
- ); - } - private renderBusyPhase(): JSX.Element { return (
@@ -422,35 +123,8 @@ export default class CreateKeyBackupDialog extends React.PureComponent - {_t( - "Without setting up Secure Message Recovery, you won't be able to restore your " + - "encrypted message history if you log out or use another session.", - )} - - - -
- ); - } - private titleForPhase(phase: Phase): string { switch (phase) { - case Phase.Passphrase: - return _t("Secure your backup with a Security Phrase"); - case Phase.PassphraseConfirm: - return _t("Confirm your Security Phrase"); - case Phase.OptOutConfirm: - return _t("Warning!"); - case Phase.ShowKey: - case Phase.KeepItSafe: - return _t("Make a copy of your Security Key"); case Phase.BackingUp: return _t("Starting backup…"); case Phase.Done: @@ -476,27 +150,12 @@ export default class CreateKeyBackupDialog extends React.PureComponent
{content}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a7b25faaa83..8991a6d4e9f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3600,37 +3600,14 @@ "Space Autocomplete": "Space Autocomplete", "Users": "Users", "User Autocomplete": "User Autocomplete", - "We'll store an encrypted copy of your keys on our server. Secure your backup with a Security Phrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a Security Phrase.", - "For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.", - "Enter a Security Phrase": "Enter a Security Phrase", - "Great! This Security Phrase looks strong enough.": "Great! This Security Phrase looks strong enough.", - "Set up with a Security Key": "Set up with a Security Key", - "That matches!": "That matches!", - "Use a different passphrase?": "Use a different passphrase?", - "That doesn't match.": "That doesn't match.", - "Go back to set it again.": "Go back to set it again.", - "Enter your Security Phrase a second time to confirm it.": "Enter your Security Phrase a second time to confirm it.", - "Repeat your Security Phrase…": "Repeat your Security Phrase…", - "Your Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your Security Phrase.": "Your Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your Security Phrase.", - "Keep a copy of it somewhere secure, like a password manager or even a safe.": "Keep a copy of it somewhere secure, like a password manager or even a safe.", - "Your Security Key": "Your Security Key", - "Your Security Key has been copied to your clipboard, paste it to:": "Your Security Key has been copied to your clipboard, paste it to:", - "Your Security Key is in your Downloads folder.": "Your Security Key is in your Downloads folder.", - "Print it and store it somewhere safe": "Print it and store it somewhere safe", - "Save it on a USB key or backup drive": "Save it on a USB key or backup drive", - "Copy it to your personal cloud storage": "Copy it to your personal cloud storage", "Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).", - "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.": "Without setting up Secure Message Recovery, you won't be able to restore your encrypted message history if you log out or use another session.", - "Set up Secure Message Recovery": "Set up Secure Message Recovery", - "Secure your backup with a Security Phrase": "Secure your backup with a Security Phrase", - "Confirm your Security Phrase": "Confirm your Security Phrase", - "Make a copy of your Security Key": "Make a copy of your Security Key", "Starting backup…": "Starting backup…", "Success!": "Success!", "Create key backup": "Create key backup", "Unable to create key backup": "Unable to create key backup", "Generate a Security Key": "Generate a Security Key", "We'll generate a Security Key for you to store somewhere safe, like a password manager or a safe.": "We'll generate a Security Key for you to store somewhere safe, like a password manager or a safe.", + "Enter a Security Phrase": "Enter a Security Phrase", "Use a secret phrase only you know, and optionally save a Security Key to use for backup.": "Use a secret phrase only you know, and optionally save a Security Key to use for backup.", "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.", "Enter your account password to confirm the upgrade:": "Enter your account password to confirm the upgrade:", @@ -3639,6 +3616,13 @@ "You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.", "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.", "Enter a Security Phrase only you know, as it's used to safeguard your data. To be secure, you shouldn't re-use your account password.": "Enter a Security Phrase only you know, as it's used to safeguard your data. To be secure, you shouldn't re-use your account password.", + "Great! This Security Phrase looks strong enough.": "Great! This Security Phrase looks strong enough.", + "That matches!": "That matches!", + "Use a different passphrase?": "Use a different passphrase?", + "That doesn't match.": "That doesn't match.", + "Go back to set it again.": "Go back to set it again.", + "Enter your Security Phrase a second time to confirm it.": "Enter your Security Phrase a second time to confirm it.", + "Confirm your Security Phrase": "Confirm your Security Phrase", "Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.": "Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.", "%(downloadButton)s or %(copyButton)s": "%(downloadButton)s or %(copyButton)s", "Your keys are now being backed up from this device.": "Your keys are now being backed up from this device.", diff --git a/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx b/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx new file mode 100644 index 00000000000..e7a91f3715e --- /dev/null +++ b/test/components/views/dialogs/security/CreateKeyBackupDialog-test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2023 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 { render, screen, waitFor } from "@testing-library/react"; +import React from "react"; +import { mocked } from "jest-mock"; + +import CreateKeyBackupDialog from "../../../../../src/async-components/views/dialogs/security/CreateKeyBackupDialog"; +import { createTestClient } from "../../../../test-utils"; +import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; + +jest.mock("../../../../../src/SecurityManager", () => ({ + accessSecretStorage: jest.fn().mockResolvedValue(undefined), +})); + +describe("CreateKeyBackupDialog", () => { + beforeEach(() => { + MatrixClientPeg.get = () => createTestClient(); + }); + + it("should display the spinner when creating backup", () => { + const { asFragment } = render(); + + // Check if the spinner is displayed + expect(screen.getByTestId("spinner")).toBeDefined(); + expect(asFragment()).toMatchSnapshot(); + }); + + it("should display the error message when backup creation failed", async () => { + const matrixClient = createTestClient(); + mocked(matrixClient.scheduleAllGroupSessionsForBackup).mockRejectedValue("my error"); + MatrixClientPeg.get = () => matrixClient; + + const { asFragment } = render(); + + // Check if the error message is displayed + await waitFor(() => expect(screen.getByText("Unable to create key backup")).toBeDefined()); + expect(asFragment()).toMatchSnapshot(); + }); + + it("should display the success dialog when the key backup is finished", async () => { + const onFinished = jest.fn(); + const { asFragment } = render(); + + await waitFor(() => + expect( + screen.getByText("Your keys are being backed up (the first backup could take a few minutes)."), + ).toBeDefined(), + ); + expect(asFragment()).toMatchSnapshot(); + + // Click on the OK button + screen.getByRole("button", { name: "OK" }).click(); + expect(onFinished).toHaveBeenCalledWith(true); + }); +}); diff --git a/test/components/views/dialogs/security/__snapshots__/CreateKeyBackupDialog-test.tsx.snap b/test/components/views/dialogs/security/__snapshots__/CreateKeyBackupDialog-test.tsx.snap new file mode 100644 index 00000000000..655be7884d8 --- /dev/null +++ b/test/components/views/dialogs/security/__snapshots__/CreateKeyBackupDialog-test.tsx.snap @@ -0,0 +1,168 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CreateKeyBackupDialog should display the error message when backup creation failed 1`] = ` + +
+ +
+ +`; + +exports[`CreateKeyBackupDialog should display the spinner when creating backup 1`] = ` + +
+

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\r=0&I~!$}yFUC%kv3S)P+Qbj8d`_;r2CYhIv z&P?c^p_A@~`>(pV75qQang?uj|LMyqxtC9GK9#;-*^D%IXOtpTK_+#{g^+z1z}h9Q zH8MU1(@yHRw~k7Rri?T5N#`vlUkJWWiF^t)(2Li;(TZ)D!FOK5{n?|m|COyAY|T@u zoo_0q)T+f1Jz*RdNo}v2qi{ri=u7j{%RvlVjooQi0n+Fn8*UXeL~`dp2v$qO1rrF4 zAPg9-7E+@+DtiIzFQj5sF7fJ0WDWFZkIb%FwFLd?l_e#DaEZNy`8(fA0E=3b?WC$jjGr;?HNsjHA7Y>n+)g$z#-d#O^?;`|fNeB`4>1>TcV|PaUDxYJpK}eV_Ig2SxxE>Q_pXcuGVy} znq%Z+rQ~GLdN(NvF(FyDIB^0u*Y4pv|1PF#;R*pS>j=u5X$_Q`CDeK06%_I$3o?c^ zp06}3ubF{GQZ`_D5*HunxwoZr#Lg#_pmgnIGmCt0GR{qGnjlI#!VH-fOF71ro60j@tb;^EYMvb#QfLTA{lTRs~ z-KFz~-}yH&z}*2v1cLo4noMg8p>Yx_k%GBw<=@P7acXAmcuiQz&T@R{pYmFiUyMHA z-_Lq&oK9)8SqUH7G+<&IuMC`sx=zV>NFw-4N9jYQ0f)Hcf|E~aSxUrff=hPgnANVc zw|uU2UKi94Y$XA2?b@3l9dlflW+yYv`X|<09n-;Clqf|#n7;#-b3Qch_?UG$Fg%ry zJ65`v(cr`r^_!SNO4Jn;w%DfH=ww`iuyLG%q@;WsmSQR#7%jc?A0kyZd&typ+h4`} zbC%*Z`isr#(qwad`Hue8ivpDJLiPCy7GmEvzx`@ytmWom${1YZFqoa>MJsBUOz$3XjP=+O%~nS7TOq_4XBcazuvka7Bd|e#q+Un|%y^{Sw!d8pUiG zJMK$QmX2ka@`+XF07|v@u0?eS|E;M<0lSTk&5xaTbfK`8$7ZD>WvIwJYHZUVR&yHXEz&EeI;K~ zmTr}t$CLCM&kdM5_o7oAKVJY_Ur zrzR)9jJ+RQ4sD~JIbY0VAr z{gU_l4im$q#O@;lX$YK4jF6jxptzfRqB;zZRXKg$oKV$V7FmV<=MNbx{fkAwI-%mi zWQht*J67{2%c^czQj{y!pE*=(C>#9{I8Kl@jnky!#Y;~-;_bX@gO@c=y;-;=(~GQq zjR}}}+zZBNI`^U*9Y8Q0=Bf)6!5FNj&t^Z)dj*N6`i=#O%hORkD=3PQU@{(XK)rcp z{i$Hvmn6{{yl2*mB82+Ji4b$U}zeJXfn--B+w| z74-;MM!XHJVdYCx_S{S_SeQDi-JO366T%F_!!30Rax7R4yX`+{a5<6Zs3_QF>sORd zj%LxxDo%FWIkqao_mrlDeFjbRh$LBMqG^ghZAnLcjG<;xsOGc&*2g=dR~WI&T@roU}$&TK;msU8741Q@oMittnX9N@hqNCC1!v5sq@2zO(fK#stSwdPTb?R3X?aC zrhJrtdEs<9y%pR56F9T{2v=hF7{Ih_k#zrB9l}vnuH_y7{JUw?x(0(CrZSEFa@kq= zmPrc1LJB>dR)q?udl|eIX(HnQ74F1__2;%gVL3PpcJ}uY{T$8M1|AYZi{XCqM2=^a zS}IjYlh{0!M+>8!_b+#|O=0zloB_TbT;VqMqqGZG z;3>=(qqoah+`iya-O=If87KzUTk8=L@y#|8+40xX2B|g-wcsRcQxEcRl;v{Nf zT&{Bt^L*dslfpbIKd-*#0Ms{{s~&lav4e literal 12971 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 YUFWf)L3S_z;CWn-{-am?|3CTv0)`V=SpWb4 diff --git a/res/media/callend.ogg b/res/media/callend.ogg index 927ce1f6340a6d9c65bcb03d244b6c3346060443..3eebf942dfb0888ac3e7c9007d0bd20d8290308b 100644 GIT binary patch literal 25434 zcmagGc_5Tu7dU>O*^GVcyG9XXDeKrZ_9Zg5vL_*B%~C4#*d-((TN=tvp@@=(LTMve z$~JA%O3NperQe-C@B4ng-@ks3XQuny=bq)Bd+xdCo_i+8efwMh2K?tzoSVsH7c1zM z0f;PeC?d*#e>A%S0iXYE^aoz-icZ9l4f(%|4T->~o|8|u@rANm{r5`3{)>hOzO~F?Ne3CO1`7sDVmDYQOZt!t$z1u3b$EiX^O4RPs!AMrg5VG(vUZ`$x9)a-W5-^ zGKCxv^gD%9@0-?1rYh?bH>8ra14KX`0E{Z7tSTigl4KhfxC;P@<|I6LQ2O3!>3glx z+(cdvJbTQDu$@iTMVs8$Hu>@%`3isU`4bJQ_uKy7nt#D<0H8bZSRNGS_zM>R0Fj}W zRqdWt9h~(xGnbf}-h=@t060iS+uX&-y1?SGtJ_3J*wp`Zdldyr`~`lT=^@82VbdOtG-ORP5Y7jvaBxsx8>1~?#-8L zJc6aFau1$T*zRrl9fk+jza_=iBg3W2?7D-G-uaaa48YRn<|BEfIqk57Z{D9k-p!Vi{@3JhyT3w){iTu8bM{PA zh)>=IldS~&B{kofE-$EaN~V;Xy&qW!zF1&DR46>?Y^V6Iz%hVEluHwKw)yXuf2Lfz zc+?Q3mXU0yTwhm}t$APd%7edO1|OTQ@EP)G+##8(G~aP{3DX&O5BaSBwlwAHPS23$ zc4s?Eb%8V2U-^-t?9B)g0eMbr%>J2_+VUta<+^{xNoEfX0FGa!M+9X4&;3REv4Db; zAiUK^HFZW^EZt%QCyHV{t|fR)rui%<%)t9+>>l@`K#%|D{_PvjpyQFZ*L?I>jIBy2gV?KUFfAqEx$?HO;JeCzMqtm#td~l; zuk`ZeZSxiO=kGgGR98~^uB1Y_;cpW_VmD!5Sv7H4^>JBsak&eb`N1Vcb@inxLp2|- zU;6O>cXR($aCvBP^_}?R$C4NBYV# z)dTw0^%tHx*|pYq8roHNxQ4tchmS)_)t=iMWvX368d@O`;D8_Q%Hcc_ga%D;5|1+% zC9S~!5#?>3^N{|$w11X7@`6XU^W|rr*eXH&U?T8i zrlcKU2}p{76)Ej-M%zJH!9mE`DuJHtjdCbM(nTVTPk=wJeK)0gU-7#)6JAIx_xOt2x zOpT}Q9#812Yq%ahbm>xKThmJ0mX+(DJ`~uF+OruNvv6{A*wr595*y|vWH}+?HdbWm z5(}{;?2Zk5HeoRmYw1=P$R=4B=yN!5*EI+d=5{DyF2={_Y^;xakk@kBXl$%cVc^`Z zK#%1>pTY6ixn=v&4u^@c*ipCGx$)RHC1F!PAy}+iQQB_UZX)f)k?< z`aqu|5$$Y7sZVouJ}I7(r@vVtdWW4-Wi{Pydu8gl8KpJN*)z0xtkmy?%#ybS|5?;$Py`?at02h0)FsRUn_8GDKx( zrvbTwsQAq6ASxF-$d$heWz5~~Kr2MGU1OZ?d=kFcZl?iJg}1&LHw{+1D7aTiVL){6 zrRH`?bIQNO$3*uw-((Y47|6HFVH3~kg642X>zh)~rmp_Ukg%?QN~4GC+dWij76Y=kdD4z$jf zMlWj2Pr8KYToVt0G&~`y`SHEbufp;{^RT%f3C?>Ad*0*Z>@*0KrkNUVFKQDt=F1DR zG~SXgI%~{Jr#frCB^Ts7vrEog)fe-fLn;fLoewm_d5>Ywdns)NQ4Tn56+j*r@Qakb z|JBGz+wIvQ)qCG8MzrC~lKEQ$XJ6R;e`{i6Xa2Tvi}mqYws6vc3Jew87(&4sj~@x6 zu77Jf(spaR?SeH$fphLY>^2>-UgA5)*fn8Zy6IjMu`|nQd-M&ig@3rdxYGBxRh3tob8}(6pw^%iFzfEuw#DE`#)aV|5L()pN@Lq=e9`(rB#nsDW`&<5Ghld zD1egMldVClzWCI0dto%m&bGdN(!Z%Zh3;(E+%LHo>eRSrQ|oxCA=F#ZkS6^}X}kSu z1Mbm}ZZ79JhiHs@MlaqxJMQ14^L9Fg)OS^Q@1?FEtYeFm4$qK>#y`oa^wu%Y=q0u@ zc~WC}I%NqunBLj?Z)H=LdO7w0a5x1EERu7yn&i)kihS|%hYxE^uahioYSLMvX-l_mH0Q>+9;z4*?GM?@oyJpB)l=C&aFQ7l0nV zDPUr4ZE0#|Ze?a_W?^A!N;9*tGBhzWv$8NYH`!)rWMpJwVhk^;K5d(kiQzUQ0|Pxn z16_RsJw1Ir0}C?~V>44@Q<|{}ZQC{zb7KQzLnD2C0|Q+HeVT!>fswwk3Dv+*Ur*mq zmkR$NsG*jwo{j;Ps;8?RAwghhy6U*E*Q$Osb9GoKp1)_4V`& zbPe^WItCE8p1!Ug)d;enuUl?2*9`hJxqryB8ZTALO)E++xN3E)i*Y|WmDCm(u;JL@ zdx*LXR*xS|A7B4UBpIMejK^CL^q=>3;{5Z2t*U5w`(Tt8DD2W@F5*DX^>e*>d48EQ z_Xna5#)&#zGBJOSW67drFF`84t`{!^wjx7TrAKyi(Fv0oReDV0Pzqg;ti4+XB;r?% zF2lKMipYZ5=V6hwyK;AbX?tGBsqgK(xyvCGL&wmaq?y|%F~bZYA)v+FRr6@`GfZZb zBKPS`Gr>j*xS20}3NS^PW6A)ugrc&*WX0!=x*Q^RIAbJAL|K&dxdEqu76X<7pWhBI zAn1Lfp-lTp-*XWq<`o$cB~(FF6by1`iGZIx%)4-OYiE&iA>hkx@_@kFaHnOY2%mcF za;ggQog4k6hwktJnD3N^^)>t^3b)B1CN;FO3N;sGQbswjv4`Pz)c%F>q;@kbt^><3gXEhL#GiX zzE{^Vv~#?EvRnYHwjk;V@KrH+l0KZc@GW{T#p`ej1`KZnKa}~wF0j!wSsMl3oEX~7 zg(Q6UJV^#HwR{x;oj}_HhocgU zFz^Pv2ButM0Da6LArD^8y8=ejc|779eT5WI9U@5;sC630TYP8 zYLChAo5#Tk2cs`I?F674VWsf;nJZq5Ov2MHxGw;oX;&@~Q_c0fwiJA^KAI#BSd}F5 zUSBx?x-lTpaCRW4FTQg8@xXhHD-~@R(6Rx<3?iy_G%V$GALQuE2NE(KHM$WzP16?! z8^M;qyXm6%ou9RodN;x$frcf~u%{-+^hV{W$0G)>G?fGKfT;pDpTtgH#Hr>9QoIpR zr$AK)=kC}cz=|dUs*q04^pAA2yuy;%-gGF6JL;gvF!7-jf2`lTtrtcF+U&Y+xmDi8 zGL&g9-k6JO2(t#Kd~GK3W}pR@mfiRg?`m8KE#(Q86GFrJDHG7g!l1%1vDP8AgW<)0 z;SJ8q5h6%Z0Uz~&qcrUbCy=*!@n>IgB{mb=7LO5SG=nNxFF|f?Xcn13c3jfG=>gUkk(;m){k%dp!4ggdo_6CM2 ziq<$GaU2l7=WI^O74x&(F8q?*c){uO-gZtU@KK(5^eb4B_e;W2;xCHYdzvNDrXRlK z^(%_v0wjlrJV2F;4t<^B?!R&@%t=Ye@H~Iv-TW#4?-!v?71J=$T?lKxjVZ#7b_=P4 zU1Q~NGS)}-CgwWtTwQL*LH|hF5~+bM14ADRW(|6}9PT{gY+W&o>T7m}3ZV+l9YczE zZLs1%#2xtSISwHYM!rV^;>P7z1oi=B{`yBbaOu{mru}%JzLhZF$P3mDW}oDwOv+76 z1qXdkGJ`wQ6juO3s&_fJKCY3!s1pVp&YZ!AsQn4`!!;Eyjd(6iEuR$lYVwX3$g1R8 z_!0?2fwxIXW$vf zw*msx1Ybkhf7Ipdn9J$yZ&DVW!e|&glgQ$0;j{tL;-f*YY8?^Up>#|fUj0NR5)6hW zgJ2iVDXHR^g3PTO*b%l0L*I9ia_9Vt2Gc9XL}jKoxVfLBVM39x#$Mx0OV#!##@blD zZ#oT#uLO&00#m3=KUD941m2o z*Wu)W0VfXlUEl5?4ZNK}X9lLT-V!t+L#m~4kNEin^Z@{Q;UW47X#zMP)Y zV9Bw3J{jL)+b6^nVF*p)XvveJr}Mjc^&ZTjP7G&M^AnC$!xh-10%A~+`=5#>sO$w5 z^r-ZdNeq%fk9LQux!jZ|>w#HmJ}IZ+Y^Rn;SAb%Dw zPjkgg0WrTX-6yV>^;pucYa|e74_(9KP{phUbd?crXj=lrZAKj3<#=eG=S$(^sUTm z-0B=yG#K4zhXWgjCpadNB|MnjJ{xo89xcQ8_^Iy?o@>5n*wxmBV#z9gVcb;a#;X!n zTc!X@F93akvG?KzYrt+ZhRh*b8~BqG_Ih?@Ew_vq)hB(PS6BK}b-^(o+XC)7q=L&Y zWC{4$m-$=bY2hZ28fMxX!}Q15$zye1Vgn!V#-ef#XygD$;<@}=m2wu;Dz#*5Ji z_mUu!#~p-ka^fEsVLcj^L>4xaho2;Jvo_K{DWx6OvkUsN<&XL<1X9kE!giT|ohF5T zm!JNSzXLkQ46Y%W9d7m>hf$xgO?+AcOC;MfX^w&8U-L`~3B^xcczd?bX62yqy{r0~ z5`~esl=cH4N+hN}nYEc(EJ@l_l^^}s|u zRO#86E?YmvkBg5_%XW9zcik%NxX{Z56k62sM7doLJ3Kw!1@4?h7Ie1>PZK%7%rHh3 zq=HaK>>a*XVPNth;Z+XDmD@dYQhd9iW|lu!l>{U^8(vBf@%bHJMETu0Vrtt~B}N}7 zN^zQfwG-Wo-5y*1H1EXGY9DZa9{yvB8vq&rm`tq=OYuvKzkPf6u5+Km z?qjc$aV*6aDnp2trM}C|gCp$@58CI&v4oSPmx|AG6aa3navXsP%HUEgwXRWysp)+- zx2Z`0Ql|ML^++@VxQOyonD_&ye%#3yP7x7dWj&=Q)o*hM@5^T0*_IPJ$_XVsqv{Wp z^5dHLyOGJ@)a85jMKo?`9VOMfPCnIk=dftTp61#oTGt{EYM% zu79FpoBC$AS^M`l3w+*{d>Vl37&8%9V{YCOQF5U$!?a}!>UqfTr^KE)oRDW--Br#2 z0pL3|V}JAvZA<6*MPA-!w;?x5=Li7GCV|3i<^CA2hNp0YTG;=k?xpOvlVMtvddHE71F%37M z!#W_UELUdeqUeGJiTBq6shq`;^Tz^0a=w~S#E~XK92bdqa=YF1du?#69H2EDSyOFj zNaxQWb7(@ZeIK$eDWsFvs|Qvvl} z8$s+LbX1+Wuz6k7kK>fvbG~7j|*|gRYPbOTWij@_t?FvFOTEI6`dFW(+wP8ka~DPFR=nMeBboB;4w)XF#F-0{m#sTE&Ni>Izt6%Cx$^L<7k+z z=d-prOr96(>O*4|&kg(MRTqv$F08Jt?E16rW~2W5Fvj?I)@yk0RNlEJ2^n&$#Z^67FfNUa=2P8 zZtUn4U~OtfcW;X`kcnF~3V)!Wjx5P~B z@qzLnlu8l->TzKG{ioNAya8rlKtM_4;a1)XoXjw41n&BRs@|T7;9_zr4!t2Bd044r ztaW??oyIzf19g_jf)P>&Hy{-&K6e;-Wa2+s&aDMs*B@a{GfWlzu(UJdYlCgIb$M4n z;w6GNN5N9WQF&}DqiGXhuFQ3SEvh06E+E_JGmMh<(H%F5grpu$lfj4$<88=%zyYJq zUPZ2HXkX*qieKUa8p-q-XHHIX)#swN=Jb6%5oj}ym}s(Vxf=J zlf^6NORCs3IC*qvW#x6BSY$LaxkwG{?cH*%sgW6rWfo)NHY!cv!IJM`grRATjb}Yx zIx@&1nxsI6)EkYTG4ia)(^i5VxnC=6IOlN_IAY#242d^q{oZ$e)-{lbbUKi_5x^+) z23*g0V=hES$Oa=$2shgbCr-`u?J?V&951D>a=~f#a$C}lNEtFs^2e|H56xwC@vMVf zktXPG>a!przl_>i4*-5FZU_H7MPZN&_};&Hg}3r1>097c?gzoChL1&;_`G?!NF$g$ zrhzrNRpMxcXEO9-{~ zd)Mm_(6|TN?P!)#Bn66Ea_6qCHaLrd>#fY2qDq7-{tZl5?>h(>Uth$7ZspvI2r#*C z48tjDdAH|s3SI8i6_ioEcz1LD=COgo%R|R%T4NbCNL`l_%bi)K3|JOUph})3@BK)s zBIlV3+;`u&dE$i`SMpDZdN|o5&dKOJP*||dBZmQ6@_W74S8oYRV;RX7j1J5{fkT1l zS+1%y1UP);qs<4F&)`kCd)ji$W=P)_M`{MoX6M3f`-00PvXM|H46p~P(BoXw+B;o8 z@im*JG1sfaP?aJjl5fkmXL!)GDv7B2r}^V%uN!|Ms2{)kYdKh<$nAsD;}gP&*zlPa zUWnwuL|YxjND|%Xh7wQ-|GkHbRS6C}2+)7X2mbhHmkaxTxH$@=2BvFcawMYsTq#r`Sb$=wX#tt#LpU?*(X@3sCMjAXF ze>n;HVT&=C7F$l(@2Pi3_>v4n&=$4G3qY$z_zMDdO^XBcmKqxQT7pQfkecObwQbq# zemvKRep;vRurjw1XVrmZ^Z}tsG2!?IdJxD< zQ9|pNF;zKS2-b%ZUgo5kPg7ToCPOisUp!3^%iE~wk)Ie)^MzVFMWO1^k3BdUin3hd*lOSaTl=TNhO5h_2h z4b=R3pdkgsXdOcO%FeNh!1qSN2dEh>uje-Lv!F$KY#W-qm31!_U!o<4fJ4&t$6YGB zk*Cqv2bKtd#e*_vmfXdO$d@h|lc|KP9Md_|wLj@SnX#$IXWBTay%8q}!Aw5gZw_4c zSY|Eqxt@0Jbm&7qI6Wrz;VLt^Muj+ADsbJ9<2^ivxRO@J;~2l9KXThxvzP;mo#EkeCwzc2mi8u|Rsr=IG9 zZP9vyR_J9(Q9pBR9wV!F%P3JDLtER4>Wl12A2?Tt0rYSHstmIOd940=1pY3O5sY=| zBk+Oq&-E&yQ1r487)C#dO>M{1x#@2|GkH5%`e~UP0q4>YT;`v*3BJGJdNH*YM#Wp~ z@wooD?$Z(SvqKAF^gs9hU4lkQQ2OuK15yt`H8IF1nBYUTx-!JoQ&n zUSn5=1_s(1w(~>g^0{SX9rign?1JK(-O*Q5ReE>QuvN&1^S@Vwe+uG%y5P4Y_kpTs zf_dm>i-TXyop-$7hM0U8qARkKCu8Df{gC_iv zs?z6_#-OJ#+7#v#r;z&NG`p|Oy7+F|n(PtM)PmAzVnhsp!9F>qG0&qN7!Mo+fI>VB# zT3>X*c+(vD-N~CT0y1r^Jv<-ZU4;qm0DP>CdKZ~MEt%Kk&^gUx<69WPcab(Vne&tFN1jj3o8vN==PRF$< z?ejzzG2OGfd6C)cYt~VtW}NQd0_88bSFfr>3u!O)AiNeLs=6`w-$)vs!P@DH`Vpl+@^H=8L({Ur2+*<5+cG zdiZ#;K-c**n%Rf`sp}^~p-hBsCbH}nM=tfmrf%Ek$SK;nBMEijE|81KwLyPiJ2{X@ z?x$^459dYR#nRtB;(W1s0<+%0=8+`FdePB(;#G@P$( z@ilez;{O`ycB&0>RpBYPHi3NPa-Q86h%KYF z0~&|ashir8O;zYw4NO7R(q;>!0FCCknr(9L;}uB}AbWpp!!-|qis8#IsCH$cu0zM( ze5aw)4&0Cv|ACwjw zo*`TO8sX050FO3bSfl_tiDR}4znsi?`EkB=;?YOo$a4p~Ub;5;DhN~95|2wJ0L&!iAD*E_$-yA}92eS2zmYT`Ah)itv^45Q$7Qo6vhSHa8D}&Q z7INm^0LSsmMIvP8E)JkxtlAxP2JAcEJ*AsiEpAi z)TZN3|2BJ1zoc3$kVt;E{lli1EJCE$6(F{&9M@4|bZ+Qa;T-8e;G5H+B5Q3f{0boW ze$&nmE%~}kq4r`Nd4crg&+P2Yh$uHbk(ZX#7u#*PTdDsW;fi2q&5)xLVv;p89Y)Yb zHoVe^qJ+2({mFG8_rgqGjvpVy|5<2h0{yZ8FFHiBHq#rF(wDtxcU|!PH65r}=Q};f zszw8WSMS-twR$bvVHh3)fF!%3R#%taTCPZLq_Y6O;p)T0%^MR1Dm%oIG6jIi(V6K` z1N)4@xLj%pjN@+O6^udQ_5-uM;@6X!tn(CF+J3O*4auPSe3#mh@Pbg3Q zFzX)BToO9bKyRwNhvUyV0|QeFY02E%^QQbb z*`sT32s`5$f?ndz^u0gtwVEti6AS)2Oh+Y2m=h99QS zDWx4XIj}Ytt0jkDI*>84`QFi?x!vz1Y$8Pf?IP)Hv?#AVuK1<|5W7Ykm=*j{tWslP z6np4cujZS@2ofWy5deXLZ(=L4YMBJ6Dl}V) z!%BQX+zi@8o!qyuG-%|X9Tv2%Khqtmd^LG6VM=h|Cu=cH4TZS@SbF5|{a5521?I-EKQCRc7B>{Plx_XdX+E6P(`nG$eO& zj{rE#Lp!3y_i~&jKUcvUx6xW9a97)gjip%7LzyuOlO$$@m{?bxRAsCx8wpBw0x)Eq zxuuaK62Da938YPHi6h&QT_gyK0 z@dO;;TRsqxk4qcH=sm=+lw%aW-;Pdf9-BNPK>J=U&;Li=*+h{TC4ZN*EC zl4YDytRWo8+BbepT%$z|bs+#v8zl|VTUpP8m3sUquJX-^LmW zVe9-uT(-g%N>_#=ExUL>UH(2yb)cQ)g!;+_(5;dHms8!)F+^EB5GuD5^AY2+Iw_fb z?NBrRW!24(#7Yidi8UXphy?!Bk>{k&FTJO@&iNsM7#c26yxn5zMha+YK$Iv(FI)0e z_Mx6mUxdEjy>a|xWZ4}nFl)Q!rOWHu+a4k1*wX$cA##+YO79{P2}SyGgf_Sg+An#SCdQjZJXS^s+3!*0}Jx;NB7egHlCzFf;4OyAma?Tm3$aL z+!$sZ0H)tb%I|!aQlP?@8t}U6Ov5Et(8IarMaAz-IrVjtWZY5wb?Iiw77zpqLOTuT*MazDD+`}K(6-4Qb^@+sJQq~M3lwEtFxwF{jMf>HVaGk&% zib08XWi@Lm6dKa$g)j}p{L-=*3hoCprK7YmBKp5;ORiJa_D#i1%9!+pnY%Xi7i=5j z1aX2uoNlFiaK9kK1I$+e)}ya!_*c=FTc4iDH!E+uuXk7NVbCk?SxyM5J&cikXC%1R z;6ypT!wfL7AhrC_)r|B$e}wD>mV}1X9?I=7(Bu}m@!H|^o!K-;0YhY#G7~@Jnoal` zGEH@^a)iD~tO^w)87D|iO4h7Bw3Pz)rM9ZTFIc%h%V>c6z?zJ8RNbjtcRMf&5VSi& z`8(QfY+W@70o??oz!F@I-+bQxz+A*1gSvBr!lYTN%9UH|^G?wTS?vd_$EIG}aYFfA zjxdPFpNgC&4LnIX?r;ag9gjf3j-5gmm{jl}7(uHFm!y7ec+c4o8zGPIDJE@1Dt@gV ze(t)Id@AOeTxr5d{`FWv6~81!_%n&3f$!JHc%C4W9=-S=c~nATp|alohb_bEjKbGD zc)TxVZAO+aMk72+ThyW#596-!Q`wp-C;yo? z$)(Kq_Vg(rzMN}gy7+pTnb)x(29G^`X<)GO!h9yr6$?u?Q>b)G$nvgunG;kNE*8m}B{Zs*qE`f)0g;nc8%O%!gD4TZoN0 zZmZvsjT&5T-V&Fz0 zk47WjuT9>a^L!VrcPb=2|J+UegTnxFIzz=*-8SI&5_FGQnzMzuI&j6ZyWuf9z6Hlk zzX6nVSYzYGncJ2zecao|GEL@v%{gR$t=5-eMqWJ6;DtNgxtL@^2LPrmKX2IHDvcCj z{J^8`Ea%HXkItC*KilT?<9^JOH^%%>oKsZZVAE~6Qi2|bm*xr$5E=q_1~+C~aX=@G zKXTehp`!U(;@abbY3)3HZ=%ZfCs1ArIKq4#;@ZbDOpN>E{n+gk6#(#n@lzepr-({^ zg*&N+qdmo$7t`Dkl5e=hrbCaCQY>`$@7NS8;|5ILtxD)h#P`KUV7K$Huef5lV-R3g zQS?UQ6AMI2CK2AGI&-ASoRC^Tu3fGY>sm2g&)t@24uI@C*g+o8md}GW2Tm&e@tGF^ z!+2m*QM{}u$%1)<+w!8%opr0<;1?@cag3T$jm;W^mGAqWzGt!jmFW%AuOf=+w< z(q%0Jz`%mZZvOilXq=*q_lS%V7WZyhdFqq;g`x;M`~5r>4gnW&?18ADa{Bk`Z1u`l z+prpey%81zz+Ia3pK`kKlPS)WGe0oTQp<365Y3- z?2vlmY4OXY1Op79#Xuy`rm2fVL zjK?w4B;zm`k13c2DBNw)x12b!uboiEMLtgx$ouSM@m=Y=@Uz+nrbqpag9q}QR7!RL z-+QZuVVZFn6M7Lg0r5-54|%}?9&io?Wvt(n7Uft9Hfzj;;<>o2sFq51Z@gYN=~#Yi`xl(N@>e z(SWbDHMMnhH6c<>ZH>R*+OUPDmb#Xv`qr&mHPlr#w6mLwY4-g)zsBzCs)JE zcKVL966*`=QWXcB=pbz$4~RY z?A_J(Ww=(>^IA1qQEBgXXADgdWOgTdKg1Oav%Ui12|6Qg{AjespQBYrW>woi{*hTy zrT|D!j|!!~Ry;K+?`7|SfEWz$xDL<>e#V^zvyunfjD{Dh1Y1snk0pcF+vllhp@u$kbQS%8tDd+61zPoA!K>=XS-%hs-#WzTzH$;~B@?FW@(hrJCpPar; zQSS&YefG6bpaHDkT`koWh%uF!$_xF{d!u0opkvUxA5LGM96!kozTzUAXAKp?`agF_ zD_x47nS1hU@-|4i`jp*AU#f(AOZJBW`o@!diEaW^ZV3zgw=ImEvNpX&;|PU?}?P1l;LI=QT^!zLN5w)NTqIQKS@#>UiOW&O>maoJr-?&uRfu6go?o@$0$!|;|Y^ZQ^(?9@T7?fP9wMEX2 zI{~OGpvi}G+`f=SBb-~}>VE!HQ@`f$NdWY8#&0b{ze{~NP@D7mX_3*-W7T{uD0JQ* zK0q2VS1OsG2skAw>BzURnnE7LYf+lX{l2?!)7E-;*4-KJSVnBU?R-h9%0sp1=dpHZ zp9I-%0a(-o~xom&u>+bE)T%f!wZo!%n6KdRuSa zJ9ON@9569JVB4mOPU~VaJpie`%C+nq+FXy9=rH*?QNHHUm2Sif66;nM%81wfBnEl2 z`PUCCATXzfIlpat^A4;SOqWX~#8ehA+7`whtho|9*KSg*bZljm!ED`Xg|5K!)JAD* zcl%!}^}K8sSZ;fGbh5b^I3PlgRP4zTjC_95BR+B>^{e`i!sE`i+vGt-CLBVJ-?Zb8 zq}(Hy&OavrxGBpzuM_$_g&XJ!kP4DnSKY@|np3Nv4Q3unpFRhZ?dn!NbpOu7a$w1FrgMNA;R9@8bj$AggeQJ3i7WEut&hu6juTJX<=nbF! zKoFH#trg?eZh?!@o3h2v{(X3K3V$^vV~0@TR;@U+EAMP`3HIb&i;FjbNoKrS8LDmA z^y0eQJjUQy+hxKh}eOjQ}!SY_j04nwhQU!KYjO9)bwy^Q)KI(Lyj17a{J_G0aC3hSZqx$k-w}O!#9^CyvMwJ*0H0#F0;eQ zmRB~j`?3_rOVf4LL0*ce^v7EcbO?4+OO8_s$IbjDoLm}|)xERkjo-JtQA-#9F?l