diff --git a/playwright/snapshots/user-menu/user-menu.spec.ts/user-menu-linux.png b/playwright/snapshots/user-menu/user-menu.spec.ts/user-menu-linux.png index f713a7dc98..e06fbbda1b 100644 Binary files a/playwright/snapshots/user-menu/user-menu.spec.ts/user-menu-linux.png and b/playwright/snapshots/user-menu/user-menu.spec.ts/user-menu-linux.png differ diff --git a/res/css/views/auth/_LoginWithQR.pcss b/res/css/views/auth/_LoginWithQR.pcss index c246388869..9d4744a389 100644 --- a/res/css/views/auth/_LoginWithQR.pcss +++ b/res/css/views/auth/_LoginWithQR.pcss @@ -11,6 +11,13 @@ Please see LICENSE files in the repository root for full details. margin-bottom: $spacing-16; } +.mx_LoginWithQRSection { + .mx_AccessibleButton_kind_primary + p { + color: var(--cpd-color-text-secondary); + margin-top: var(--cpd-space-2x); + } +} + .mx_LoginWithQRSection .mx_AccessibleButton svg { margin-right: $spacing-12; } @@ -135,9 +142,16 @@ Please see LICENSE files in the repository root for full details. padding: var(--cpd-space-3x); gap: 10px; - background-color: var(--cpd-color-bg-success-subtle); + background-color: var(--cpd-color-bg-subtle-secondary); svg { - color: var(--cpd-color-icon-success-primary); + color: var(--cpd-color-icon-secondary); + } + + &.mx_LoginWithQR_icon--success { + background-color: var(--cpd-color-bg-success-subtle); + svg { + color: var(--cpd-color-icon-success-primary); + } } &.mx_LoginWithQR_icon--critical { diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index ac6a8c8e0b..4bdb6e227b 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. */ import React, { createRef, ReactNode } from "react"; -import { discoverAndValidateOIDCIssuerWellKnown, Room } from "matrix-js-sdk/src/matrix"; +import { Room } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import defaultDispatcher from "../../dispatcher/dispatcher"; @@ -44,8 +44,6 @@ import { Icon as LiveIcon } from "../../../res/img/compound/live-8px.svg"; import { VoiceBroadcastRecording, VoiceBroadcastRecordingsStoreEvent } from "../../voice-broadcast"; import { SDKContext } from "../../contexts/SDKContext"; import { shouldShowFeedback } from "../../utils/Feedback"; -import { shouldShowQr } from "../views/settings/devices/LoginWithQRSection"; -import { Features } from "../../settings/Settings"; interface IProps { isPanelCollapsed: boolean; @@ -60,8 +58,6 @@ interface IState { isHighContrast: boolean; selectedSpace?: Room | null; showLiveAvatarAddon: boolean; - showQrLogin: boolean; - supportsQrLogin: boolean; } const toRightOf = (rect: PartialDOMRect): MenuProps => { @@ -98,8 +94,6 @@ export default class UserMenu extends React.Component { isHighContrast: this.isUserOnHighContrastTheme(), selectedSpace: SpaceStore.instance.activeSpaceRoom, showLiveAvatarAddon: this.context.voiceBroadcastRecordingsStore.hasCurrent(), - showQrLogin: false, - supportsQrLogin: false, }; OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); @@ -123,7 +117,6 @@ export default class UserMenu extends React.Component { ); this.dispatcherRef = defaultDispatcher.register(this.onAction); this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged); - this.checkQrLoginSupport(); } public componentWillUnmount(): void { @@ -138,29 +131,6 @@ export default class UserMenu extends React.Component { ); } - private checkQrLoginSupport = async (): Promise => { - if (!this.context.client || !SettingsStore.getValue(Features.OidcNativeFlow)) return; - - const { issuer } = await this.context.client.getAuthIssuer().catch(() => ({ issuer: undefined })); - if (issuer) { - const [oidcClientConfig, versions, wellKnown, isCrossSigningReady] = await Promise.all([ - discoverAndValidateOIDCIssuerWellKnown(issuer), - this.context.client.getVersions(), - this.context.client.waitForClientWellKnown(), - this.context.client.getCrypto()?.isCrossSigningReady(), - ]); - - const supportsQrLogin = shouldShowQr( - this.context.client, - !!isCrossSigningReady, - oidcClientConfig, - versions, - wellKnown, - ); - this.setState({ supportsQrLogin, showQrLogin: true }); - } - }; - private isUserOnDarkTheme(): boolean { if (SettingsStore.getValue("use_system_theme")) { return window.matchMedia("(prefers-color-scheme: dark)").matches; @@ -363,28 +333,13 @@ export default class UserMenu extends React.Component { ); } - let linkNewDeviceButton: JSX.Element | undefined; - if (this.state.showQrLogin) { - const extraProps: Omit< - React.ComponentProps, - "iconClassname" | "label" | "onClick" - > = {}; - if (!this.state.supportsQrLogin) { - extraProps.disabled = true; - extraProps.title = _t("user_menu|link_new_device_not_supported"); - extraProps.caption = _t("user_menu|link_new_device_not_supported_caption"); - extraProps.placement = "right"; - } - - linkNewDeviceButton = ( - this.onSettingsOpen(e, UserTab.SessionManager, { showMsc4108QrCode: true })} - /> - ); - } + const linkNewDeviceButton = ( + this.onSettingsOpen(e, UserTab.SessionManager, { showMsc4108QrCode: true })} + /> + ); let primaryOptionList = ( diff --git a/src/components/views/auth/LoginWithQRFlow.tsx b/src/components/views/auth/LoginWithQRFlow.tsx index 72511f2ccb..645613dafe 100644 --- a/src/components/views/auth/LoginWithQRFlow.tsx +++ b/src/components/views/auth/LoginWithQRFlow.tsx @@ -17,6 +17,7 @@ import CheckCircleSolidIcon from "@vector-im/compound-design-tokens/assets/web/i import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error"; import { Heading, MFAInput, Text } from "@vector-im/compound-web"; import classNames from "classnames"; +import { QrCodeIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; @@ -94,7 +95,10 @@ export default class LoginWithQRFlow extends React.Component
- {success ? : } +
{title} diff --git a/src/components/views/settings/devices/LoginWithQRSection.tsx b/src/components/views/settings/devices/LoginWithQRSection.tsx index f776d69765..c5efb35efc 100644 --- a/src/components/views/settings/devices/LoginWithQRSection.tsx +++ b/src/components/views/settings/devices/LoginWithQRSection.tsx @@ -18,12 +18,11 @@ import { DEVICE_CODE_SCOPE, } from "matrix-js-sdk/src/matrix"; import QrCodeIcon from "@vector-im/compound-design-tokens/assets/web/icons/qr-code"; +import { Text } from "@vector-im/compound-web"; import { _t } from "../../../../languageHandler"; import AccessibleButton from "../../elements/AccessibleButton"; import SettingsSubsection from "../shared/SettingsSubsection"; -import SettingsStore from "../../../../settings/SettingsStore"; -import { Features } from "../../../../settings/Settings"; import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext"; interface IProps { @@ -64,9 +63,8 @@ export function shouldShowQr( oidcClientConfig?.metadata?.grant_types_supported.includes(DEVICE_CODE_SCOPE); return ( - deviceAuthorizationGrantSupported && + !!deviceAuthorizationGrantSupported && msc4108Supported && - SettingsStore.getValue(Features.OidcNativeFlow) && !!cli.getCrypto()?.exportSecretsBundle && isCrossSigningReady ); @@ -85,19 +83,15 @@ const LoginWithQRSection: React.FC = ({ ? shouldShowQr(cli, !!isCrossSigningReady, oidcClientConfig, versions, wellKnown) : shouldShowQrLegacy(versions, wellKnown, capabilities); - // don't show anything if no method is available - if (!offerShowQr) { - return null; - } - return (

{_t("settings|sessions|sign_in_with_qr_description")}

- + {_t("settings|sessions|sign_in_with_qr_button")} + {!offerShowQr && {_t("settings|sessions|sign_in_with_qr_unsupported")}}
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 77fecde80e..c86520236f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -283,6 +283,8 @@ "security_code_prompt": "If asked, enter the code below on your other device.", "select_qr_code": "Select \"%(scanQRCode)s\"", "sign_in_new_device": "Sign in new device", + "unsupported_explainer": "Your account provider doesn't support signing into a new device with a QR code.", + "unsupported_heading": "QR code not supported", "waiting_for_device": "Waiting for device to sign in" }, "register_action": "Create Account", @@ -2855,6 +2857,7 @@ "sign_in_with_qr": "Link new device", "sign_in_with_qr_button": "Show QR code", "sign_in_with_qr_description": "Use a QR code to sign in to another device and set up secure messaging.", + "sign_in_with_qr_unsupported": "Not supported by your account provider", "sign_out": "Sign out of this session", "sign_out_all_other_sessions": "Sign out of all other sessions (%(otherSessionsCount)s)", "sign_out_confirm_description": { @@ -3829,8 +3832,6 @@ }, "user_menu": { "link_new_device": "Link new device", - "link_new_device_not_supported": "Not supported", - "link_new_device_not_supported_caption": "You need to sign in manually", "settings": "All settings", "switch_theme_dark": "Switch to dark mode", "switch_theme_light": "Switch to light mode" diff --git a/test/components/views/settings/devices/LoginWithQRSection-test.tsx b/test/components/views/settings/devices/LoginWithQRSection-test.tsx index 2a200ce3f8..6a5e043074 100644 --- a/test/components/views/settings/devices/LoginWithQRSection-test.tsx +++ b/test/components/views/settings/devices/LoginWithQRSection-test.tsx @@ -137,19 +137,19 @@ describe("", () => { test("no homeserver support", async () => { const { container } = render(getComponent({ versions: makeVersions({ "org.matrix.msc4108": false }) })); - expect(container.textContent).toBe(""); // show nothing + expect(container.textContent).toContain("Not supported by your account provider"); }); test("no support in crypto", async () => { client.getCrypto()!.exportSecretsBundle = undefined; const { container } = render(getComponent({ client })); - expect(container.textContent).toBe(""); // show nothing + expect(container.textContent).toContain("Not supported by your account provider"); }); test("failed to connect", async () => { fetchMock.catch(500); const { container } = render(getComponent({ client })); - expect(container.textContent).toBe(""); // show nothing + expect(container.textContent).toContain("Not supported by your account provider"); }); }); }); diff --git a/test/components/views/settings/devices/__snapshots__/LoginWithQRFlow-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/LoginWithQRFlow-test.tsx.snap index ec3bb4a690..9b6d83f4b6 100644 --- a/test/components/views/settings/devices/__snapshots__/LoginWithQRFlow-test.tsx.snap +++ b/test/components/views/settings/devices/__snapshots__/LoginWithQRFlow-test.tsx.snap @@ -14,7 +14,7 @@ exports[` errors renders authorization_expired 1`] = ` > errors renders check_code_mismatch 1`] = ` > errors renders device_already_exists 1`] = ` > errors renders device_not_found 1`] = ` > errors renders etag_missing 1`] = ` > errors renders expired 1`] = ` > errors renders expired 2`] = ` > errors renders homeserver_lacks_support 1`] = ` data-testid="login-with-qr" >
+ + +
+
+ Sessions + / + Link new device +
+
+
+
+ + +

- Something went wrong! + QR code not supported

- An unexpected error occurred. The request to connect your other device has been cancelled. + Your account provider doesn't support signing into a new device with a QR code.

errors renders homeserver_lacks_support 2`] = ` data-testid="login-with-qr" >
+ + +
+
+ Sessions + / + Link new device +
+
+
+
+ + +

- Something went wrong! + QR code not supported

- An unexpected error occurred. The request to connect your other device has been cancelled. + Your account provider doesn't support signing into a new device with a QR code.

errors renders insecure_channel_detected 1`] = ` > errors renders invalid_code 1`] = ` > errors renders other_device_already_signed_in 1`] = class="mx_LoginWithQR_main" >
errors renders other_device_not_signed_in 1`] = ` > errors renders rate_limited 1`] = ` > errors renders unexpected_message_received 1`] = ` > errors renders unknown 1`] = ` > errors renders unknown 2`] = ` > errors renders unsupported_algorithm 1`] = ` > errors renders unsupported_protocol 1`] = ` > errors renders unsupported_protocol 2`] = ` > errors renders user_cancelled 1`] = ` > errors renders user_cancelled 2`] = ` > errors renders user_declined 1`] = ` > errors renders user_declined 2`] = ` > MSC3906 should not render MSC3886 + get_login_token disabled 1`] = `
`; +exports[` MSC3906 should not render MSC3886 + get_login_token disabled 1`] = ` +
+
+
+

+ Link new device +

+
+
+
+

+ Use a QR code to sign in to another device and set up secure messaging. +

+
+ + + + + + Show QR code +
+

+ Not supported by your account provider +

+
+
+
+
+`; -exports[` MSC3906 should not render no support at all 1`] = `
`; +exports[` MSC3906 should not render no support at all 1`] = ` +
+
+
+

+ Link new device +

+
+
+
+

+ Use a QR code to sign in to another device and set up secure messaging. +

+
+ + + + + + Show QR code +
+

+ Not supported by your account provider +

+
+
+
+
+`; -exports[` MSC3906 should not render only get_login_token enabled 1`] = `
`; +exports[` MSC3906 should not render only get_login_token enabled 1`] = ` +
+
+
+

+ Link new device +

+
+
+
+

+ Use a QR code to sign in to another device and set up secure messaging. +

+
+ + + + + + Show QR code +
+

+ Not supported by your account provider +

+
+
+
+
+`; exports[` MSC3906 should render panel get_login_token + .well-known 1`] = `