Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Improve usability of the decryption banner #11012

Closed
88 changes: 3 additions & 85 deletions cypress/e2e/crypto/decryption-failure.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,84 +161,7 @@ describe("Decryption Failure Bar", () => {
cy.get(".mx_VerificationPanel_verified_section .mx_E2EIcon_verified").should("exist");
cy.findByRole("button", { name: "Got it" }).click();

cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
cy.findByText("Open another device to load encrypted messages").should("be.visible");
});

checkTimelineNarrow();

cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
"DecryptionFailureBar prompts user to open another device, with Resend Key Requests button",
{
widths: [320, 640],
},
);

cy.intercept("/_matrix/client/r0/sendToDevice/m.room_key_request/*").as("keyRequest");
cy.findByRole("button", { name: "Resend key requests" }).click();
cy.wait("@keyRequest");
cy.get(".mx_DecryptionFailureBar_end").within(() => {
cy.findByText("Resend key requests").should("not.exist");
cy.findByRole("button", { name: "View your device list" }).should("be.visible");
});

checkTimelineNarrow();

cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
"DecryptionFailureBar prompts user to open another device, without Resend Key Requests button",
{
widths: [320, 640],
},
);
},
);

it(
"should prompt the user to reset keys, if this device isn't verified " +
"and there are no other verified devices or backups",
() => {
cy.loginBot(homeserver, testUser.username, testUser.password, { bootstrapCrossSigning: true }).then(
async (cli) => {
await cli.logout(true);
},
);

cy.botSendMessage(bot, roomId, "test");
cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
cy.findByText("Reset your keys to prevent future decryption errors").should("be.visible");
});

checkTimelineNarrow();

cy.get(".mx_DecryptionFailureBar").percySnapshotElement("DecryptionFailureBar prompts user to reset keys", {
widths: [320, 640],
});

cy.findByRole("button", { name: "Reset" }).click();

// Set up key backup
cy.get(".mx_Dialog").within(() => {
cy.findByRole("button", { name: "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.findByRole("button", { name: "Download" }).click();
cy.get(".mx_Dialog_primary:not([disabled])").should("have.length", 3);
cy.findByRole("button", { name: "Continue" }).click();
cy.findByRole("button", { name: "Done" }).click();
});

cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
cy.findByText("Some messages could not be decrypted").should("be.visible");
});

checkTimelineNarrow(false); // button should not be rendered here

cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
"DecryptionFailureBar displays general message with no call to action",
{
widths: [320, 640],
},
);
cy.get(".mx_DecryptionFailureBar").should("not.exist");
},
);

Expand All @@ -257,11 +180,8 @@ describe("Decryption Failure Bar", () => {
widths: [320, 640],
});

checkTimelineNarrow();

cy.wait(5000);
cy.get(".mx_DecryptionFailureBar .mx_Spinner").should("not.exist");
cy.findByTestId("decryption-failure-bar-icon").should("be.visible");
cy.get(".mx_DecryptionFailureBar").should("not.exist");

cy.get(".mx_RoomView_messagePanel").scrollTo("top");
cy.get(".mx_DecryptionFailureBar").should("not.exist");
Expand All @@ -270,8 +190,6 @@ describe("Decryption Failure Bar", () => {
cy.get(".mx_DecryptionFailureBar").should("not.exist");

cy.get(".mx_RoomView_messagePanel").scrollTo("bottom");
cy.get(".mx_DecryptionFailureBar").should("exist");

checkTimelineNarrow();
cy.get(".mx_DecryptionFailureBar").should("not.exist");
});
});
3 changes: 1 addition & 2 deletions res/css/views/rooms/_DecryptionFailureBar.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,12 @@ limitations under the License.
grid-area: headline;

font-weight: var(--font-semi-bold);
font-size: $font-16px;
align-self: center;
}

.mx_DecryptionFailureBar_start_message {
grid-area: message;

font-size: $font-12px;
color: $secondary-content;
}
}
Expand Down
90 changes: 18 additions & 72 deletions src/components/views/rooms/DecryptionFailureBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@ import { CryptoEvent } from "matrix-js-sdk/src/crypto";

import Modal from "../../../Modal";
import { _t } from "../../../languageHandler";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import AccessibleButton from "../elements/AccessibleButton";
import { OpenToTabPayload } from "../../../dispatcher/payloads/OpenToTabPayload";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import SetupEncryptionDialog from "../dialogs/security/SetupEncryptionDialog";
import { SetupEncryptionStore } from "../../../stores/SetupEncryptionStore";
import Spinner from "../elements/Spinner";
import { useSettingValue } from "../../../hooks/useSettings";

interface IProps {
failures: MatrixEvent[];
Expand All @@ -38,6 +35,7 @@ const WAIT_PERIOD = 5000;

export const DecryptionFailureBar: React.FC<IProps> = ({ failures }) => {
const context = useContext(MatrixClientContext);
const developerMode = useSettingValue<boolean>("developerMode");

// Display a spinner for a few seconds before presenting an error message,
// in case keys are about to arrive
Expand Down Expand Up @@ -134,16 +132,6 @@ export const DecryptionFailureBar: React.FC<IProps> = ({ failures }) => {
Modal.createDialog(SetupEncryptionDialog);
};

const onDeviceListClick = (): void => {
const payload: OpenToTabPayload = { action: Action.ViewUserDeviceSettings };
defaultDispatcher.dispatch(payload);
};

const onResetClick = (): void => {
const store = SetupEncryptionStore.sharedInstance();
store.resetConfirm();
};

const statusIndicator = waiting ? (
<Spinner w={24} h={24} />
) : (
Expand All @@ -154,78 +142,38 @@ export const DecryptionFailureBar: React.FC<IProps> = ({ failures }) => {
let headline: JSX.Element;
let message: JSX.Element;
let button = <React.Fragment />;
let keyRequestButton = <React.Fragment />;
Comment on lines 144 to +145
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we only need one of button and keyRequestButton now

if (waiting) {
className = "mx_DecryptionFailureBar";
headline = <React.Fragment>{_t("Decrypting messages…")}</React.Fragment>;
message = (
<React.Fragment>
{_t("Please wait as we try to decrypt your messages. This may take a few moments.")}
{_t(
"Please wait while your messages are decrypted. This may take a few moments. Opening Element on another device can speed this up.",
)}
</React.Fragment>
);
} else if (needsVerification) {
if (hasOtherVerifiedDevices || hasKeyBackup) {
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
headline = <React.Fragment>{_t("Verify this device to access all messages")}</React.Fragment>;
message = (
<React.Fragment>
{_t("This device was unable to decrypt some messages because it has not been verified yet.")}
</React.Fragment>
);
button = (
<AccessibleButton
className="mx_DecryptionFailureBar_end_button"
kind="primary"
onClick={onVerifyClick}
data-testid="decryption-failure-bar-button"
>
{_t("Verify")}
</AccessibleButton>
);
} else {
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
headline = <React.Fragment>{_t("Reset your keys to prevent future decryption errors")}</React.Fragment>;
message = (
<React.Fragment>
{_t(
"You will not be able to access old undecryptable messages, " +
"but resetting your keys will allow you to receive new messages.",
)}
</React.Fragment>
);
button = (
<AccessibleButton
className="mx_DecryptionFailureBar_end_button"
kind="primary"
onClick={onResetClick}
data-testid="decryption-failure-bar-button"
>
{_t("Reset")}
</AccessibleButton>
);
}
} else if (hasOtherVerifiedDevices) {
} else if (needsVerification && (hasOtherVerifiedDevices || hasKeyBackup)) {
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
headline = <React.Fragment>{_t("Open another device to load encrypted messages")}</React.Fragment>;
headline = <React.Fragment>{_t("Verify this device to access all messages")}</React.Fragment>;
message = (
<React.Fragment>
{_t(
"This device is requesting decryption keys from your other devices. " +
"Opening one of your other devices may speed this up.",
)}
{_t("This device was unable to decrypt some messages because it has not been verified yet.")}
</React.Fragment>
);
button = (
<AccessibleButton
className="mx_DecryptionFailureBar_end_button"
kind="primary_outline"
onClick={onDeviceListClick}
kind="primary"
onClick={onVerifyClick}
data-testid="decryption-failure-bar-button"
>
{_t("View your device list")}
{_t("Verify")}
</AccessibleButton>
);
} else {
className = "mx_DecryptionFailureBar";
// In developerMode, we want to be able to resend manually key requests
} else if (developerMode && !needsVerification && hasOtherVerifiedDevices && anyUnrequestedSessions) {
Comment on lines +174 to +175
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should really be inside the relevant if clause:

Suggested change
// In developerMode, we want to be able to resend manually key requests
} else if (developerMode && !needsVerification && hasOtherVerifiedDevices && anyUnrequestedSessions) {
} else if (developerMode && !needsVerification && hasOtherVerifiedDevices && anyUnrequestedSessions) {
// In developerMode, show a button to resend key requests if there are other devices we could request keys from.

className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
headline = <React.Fragment>{_t("Some messages could not be decrypted")}</React.Fragment>;
message = (
<React.Fragment>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per our DM: this message is incorrect now

Expand All @@ -235,11 +183,6 @@ export const DecryptionFailureBar: React.FC<IProps> = ({ failures }) => {
)}
</React.Fragment>
);
}

let keyRequestButton = <React.Fragment />;
if (!needsVerification && hasOtherVerifiedDevices && anyUnrequestedSessions) {
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
keyRequestButton = (
<AccessibleButton
className="mx_DecryptionFailureBar_end_button"
Expand All @@ -250,6 +193,9 @@ export const DecryptionFailureBar: React.FC<IProps> = ({ failures }) => {
{_t("Resend key requests")}
</AccessibleButton>
);
} else {
// When we are unable to decrypt the message (no other verified devices), we hide the banner
return null;
}

return (
Expand Down
7 changes: 1 addition & 6 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1884,14 +1884,9 @@
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.",
"Phone Number": "Phone Number",
"Decrypting messages…": "Decrypting messages…",
"Please wait as we try to decrypt your messages. This may take a few moments.": "Please wait as we try to decrypt your messages. This may take a few moments.",
"Please wait while your messages are decrypted. This may take a few moments. Opening Element on another device can speed this up.": "Please wait while your messages are decrypted. This may take a few moments. Opening Element on another device can speed this up.",
"Verify this device to access all messages": "Verify this device to access all messages",
"This device was unable to decrypt some messages because it has not been verified yet.": "This device was unable to decrypt some messages because it has not been verified yet.",
"Reset your keys to prevent future decryption errors": "Reset your keys to prevent future decryption errors",
"You will not be able to access old undecryptable messages, but resetting your keys will allow you to receive new messages.": "You will not be able to access old undecryptable messages, but resetting your keys will allow you to receive new messages.",
"Open another device to load encrypted messages": "Open another device to load encrypted messages",
"This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.": "This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.",
"View your device list": "View your device list",
"Some messages could not be decrypted": "Some messages could not be decrypted",
"Unfortunately, there are no other verified devices to request decryption keys from. Signing in and verifying other devices may help avoid this situation in the future.": "Unfortunately, there are no other verified devices to request decryption keys from. Signing in and verifying other devices may help avoid this situation in the future.",
"Resend key requests": "Resend key requests",
Expand Down
Loading