diff --git a/cypress/e2e/crypto/crypto.spec.ts b/cypress/e2e/crypto/crypto.spec.ts index 306e94cb97..7ffd290862 100644 --- a/cypress/e2e/crypto/crypto.spec.ts +++ b/cypress/e2e/crypto/crypto.spec.ts @@ -183,6 +183,10 @@ describe("Cryptography", function () { cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); cy.contains(".mx_Dialog_title", "Setting up keys").should("exist"); cy.contains(".mx_Dialog_title", "Setting up keys").should("not.exist"); + + cy.contains("Secure Backup successful").should("exist"); + cy.contains("Done").click(); + cy.contains("Secure Backup successful").should("not.exist"); }); return; }); diff --git a/cypress/e2e/crypto/decryption-failure.spec.ts b/cypress/e2e/crypto/decryption-failure.spec.ts index b9e3265b76..13e3c56aba 100644 --- a/cypress/e2e/crypto/decryption-failure.spec.ts +++ b/cypress/e2e/crypto/decryption-failure.spec.ts @@ -181,12 +181,14 @@ describe("Decryption Failure Bar", () => { cy.contains(".mx_DecryptionFailureBar_button", "Reset").click(); + // Set up key backup cy.get(".mx_Dialog").within(() => { cy.contains(".mx_Dialog_primary", "Continue").click(); cy.get(".mx_CreateSecretStorageDialog_recoveryKey code").invoke("text").as("securityKey"); // Clicking download instead of Copy because of https://github.com/cypress-io/cypress/issues/2851 cy.contains(".mx_AccessibleButton", "Download").click(); cy.contains(".mx_Dialog_primary:not([disabled])", "Continue").click(); + cy.contains("Done").click(); }); cy.get(".mx_DecryptionFailureBar .mx_DecryptionFailureBar_message_headline").should( diff --git a/res/css/_components.pcss b/res/css/_components.pcss index cda1278df9..b589bd6636 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -50,6 +50,7 @@ @import "./components/views/spaces/_QuickThemeSwitcher.pcss"; @import "./components/views/typography/_Caption.pcss"; @import "./compound/_Icon.pcss"; +@import "./compound/_SuccessDialog.pcss"; @import "./structures/_AutoHideScrollbar.pcss"; @import "./structures/_AutocompleteInput.pcss"; @import "./structures/_BackdropPanel.pcss"; diff --git a/res/css/compound/_Icon.pcss b/res/css/compound/_Icon.pcss index 4a1d832675..e12006a32e 100644 --- a/res/css/compound/_Icon.pcss +++ b/res/css/compound/_Icon.pcss @@ -29,10 +29,22 @@ limitations under the License. color: $accent; } +.mx_Icon_bg-accent-light { + background-color: rgba($accent, 0.1); +} + .mx_Icon_alert { color: $alert; } +.mx_Icon_circle-40 { + border-radius: 20px; + flex: 0 0 40px; + height: 40px; + padding: 0 12px; + width: 40px; +} + .mx_Icon_8 { flex: 0 0 8px; height: 8px; diff --git a/res/css/compound/_SuccessDialog.pcss b/res/css/compound/_SuccessDialog.pcss new file mode 100644 index 0000000000..61f98a97df --- /dev/null +++ b/res/css/compound/_SuccessDialog.pcss @@ -0,0 +1,48 @@ +/* +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. +*/ + +.mx_SuccessDialog { + text-align: center; + + .mx_Icon { + mask-border: $spacing-16; + } + + .mx_Dialog_header { + margin: 0 0 $spacing-16; + padding: 0; + } + + .mx_Dialog_title { + margin: 0; + } + + .mx_Dialog_content { + color: $secondary-content; + margin: 0 0 $spacing-40; + } + + .mx_Dialog_buttons { + .mx_Dialog_buttons_row { + justify-content: center; + + button.mx_Dialog_primary { + height: 48px; + min-width: 328px; + } + } + } +} diff --git a/res/css/views/dialogs/security/_CreateSecretStorageDialog.pcss b/res/css/views/dialogs/security/_CreateSecretStorageDialog.pcss index 2c624e835a..5dc4089862 100644 --- a/res/css/views/dialogs/security/_CreateSecretStorageDialog.pcss +++ b/res/css/views/dialogs/security/_CreateSecretStorageDialog.pcss @@ -23,6 +23,14 @@ limitations under the License. /* never asked. */ width: 560px; + &.mx_SuccessDialog { + padding: 56px; /* 80px from design - 24px wrapper padding */ + + .mx_Dialog_title { + margin-bottom: $spacing-16; + } + } + .mx_SettingsFlag { display: flex; } diff --git a/res/img/element-icons/check.svg b/res/img/element-icons/check.svg new file mode 100644 index 0000000000..afbd40cf10 --- /dev/null +++ b/res/img/element-icons/check.svg @@ -0,0 +1,20 @@ + + + + + diff --git a/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx b/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx index 358bc376ed..1f2e7fd6b9 100644 --- a/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx +++ b/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx @@ -23,6 +23,7 @@ import { TrustInfo } from "matrix-js-sdk/src/crypto/backup"; import { CrossSigningKeys, UIAFlow } from "matrix-js-sdk/src/matrix"; import { IRecoveryKey } from "matrix-js-sdk/src/crypto/api"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; +import classNames from "classnames"; import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import { _t, _td } from "../../../../languageHandler"; @@ -48,6 +49,7 @@ import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; import Spinner from "../../../../components/views/elements/Spinner"; import InteractiveAuthDialog from "../../../../components/views/dialogs/InteractiveAuthDialog"; import { IValidationResult } from "../../../../components/views/elements/Validation"; +import { Icon as CheckmarkIcon } from "../../../../../res/img/element-icons/check.svg"; // I made a mistake while converting this and it has to be fixed! enum Phase { @@ -59,6 +61,7 @@ enum Phase { PassphraseConfirm = "passphrase_confirm", ShowKey = "show_key", Storing = "storing", + Stored = "stored", ConfirmSkip = "confirm_skip", } @@ -361,7 +364,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent +

{_t("Your keys are now being backed up from this device.")}

+ this.props.onFinished(true)} + hasCancel={false} + /> + + ); + } + private renderPhaseLoadError(): JSX.Element { return (
@@ -837,11 +856,27 @@ export default class CreateSecretStorageDialog extends React.PureComponent; + } + + return null; + } + + private get classNames(): string { + return classNames("mx_CreateSecretStorageDialog", { + mx_SuccessDialog: this.state.phase === Phase.Stored, + }); + } + public render(): React.ReactNode { let content; if (this.state.error) { @@ -884,6 +919,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { mx_Dialog_fixedWidth: this.props.fixedWidth, })} > + {this.props.top}
{