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

Commit

Permalink
Always show link new device flow even if unsupported
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Telatynski <[email protected]>
  • Loading branch information
t3chguy committed Oct 14, 2024
1 parent c71dc6b commit 78345d5
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 71 deletions.
18 changes: 16 additions & 2 deletions res/css/views/auth/_LoginWithQR.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down
61 changes: 8 additions & 53 deletions src/components/structures/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
Expand All @@ -60,8 +58,6 @@ interface IState {
isHighContrast: boolean;
selectedSpace?: Room | null;
showLiveAvatarAddon: boolean;
showQrLogin: boolean;
supportsQrLogin: boolean;
}

const toRightOf = (rect: PartialDOMRect): MenuProps => {
Expand Down Expand Up @@ -98,8 +94,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
isHighContrast: this.isUserOnHighContrastTheme(),
selectedSpace: SpaceStore.instance.activeSpaceRoom,
showLiveAvatarAddon: this.context.voiceBroadcastRecordingsStore.hasCurrent(),
showQrLogin: false,
supportsQrLogin: false,
};

OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
Expand All @@ -123,7 +117,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
this.dispatcherRef = defaultDispatcher.register(this.onAction);
this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged);
this.checkQrLoginSupport();
}

public componentWillUnmount(): void {
Expand All @@ -138,29 +131,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
}

private checkQrLoginSupport = async (): Promise<void> => {
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;
Expand Down Expand Up @@ -363,28 +333,13 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
}

let linkNewDeviceButton: JSX.Element | undefined;
if (this.state.showQrLogin) {
const extraProps: Omit<
React.ComponentProps<typeof IconizedContextMenuOption>,
"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 = (
<IconizedContextMenuOption
{...extraProps}
iconClassName="mx_UserMenu_iconQr"
label={_t("user_menu|link_new_device")}
onClick={(e) => this.onSettingsOpen(e, UserTab.SessionManager, { showMsc4108QrCode: true })}
/>
);
}
const linkNewDeviceButton = (
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconQr"
label={_t("user_menu|link_new_device")}
onClick={(e) => this.onSettingsOpen(e, UserTab.SessionManager, { showMsc4108QrCode: true })}
/>
);

let primaryOptionList = (
<IconizedContextMenuOptionList>
Expand Down
22 changes: 18 additions & 4 deletions src/components/views/auth/LoginWithQRFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -94,7 +95,10 @@ export default class LoginWithQRFlow extends React.Component<XOR<Props, MSC3906P

switch (this.props.phase) {
case Phase.Error: {
let success = false;
backButton = false;

let Icon = ErrorIcon;
let success: boolean | null = false;
let title: string | undefined;
let message: ReactNode | undefined;

Expand Down Expand Up @@ -138,6 +142,7 @@ export default class LoginWithQRFlow extends React.Component<XOR<Props, MSC3906P

case ClientRendezvousFailureReason.OtherDeviceAlreadySignedIn:
success = true;
Icon = CheckCircleSolidIcon;
title = _t("auth|qr_code_login|error_other_device_already_signed_in_title");
message = _t("auth|qr_code_login|error_other_device_already_signed_in");
break;
Expand All @@ -157,6 +162,15 @@ export default class LoginWithQRFlow extends React.Component<XOR<Props, MSC3906P
message = _t("auth|qr_code_login|error_etag_missing");
break;

case LegacyRendezvousFailureReason.HomeserverLacksSupport:
case ClientRendezvousFailureReason.HomeserverLacksSupport:
success = null;
Icon = QrCodeIcon;
backButton = true;
title = _t("auth|qr_code_login|unsupported_heading");
message = _t("auth|qr_code_login|unsupported_explainer");
break;

case MSC4108FailureReason.DeviceAlreadyExists:
case MSC4108FailureReason.DeviceNotFound:
case MSC4108FailureReason.UnexpectedMessageReceived:
Expand All @@ -168,15 +182,15 @@ export default class LoginWithQRFlow extends React.Component<XOR<Props, MSC3906P
break;
}
className = "mx_LoginWithQR_error";
backButton = false;
main = (
<>
<div
className={classNames("mx_LoginWithQR_icon", {
"mx_LoginWithQR_icon--critical": !success,
"mx_LoginWithQR_icon--critical": success === false,
"mx_LoginWithQR_icon--success": success === true,
})}
>
{success ? <CheckCircleSolidIcon width="32px" /> : <ErrorIcon width="32px" />}
<Icon width="32px" height="32px" />
</div>
<Heading as="h1" size="sm" weight="semibold">
{title}
Expand Down
14 changes: 4 additions & 10 deletions src/components/views/settings/devices/LoginWithQRSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
);
Expand All @@ -85,19 +83,15 @@ const LoginWithQRSection: React.FC<IProps> = ({
? shouldShowQr(cli, !!isCrossSigningReady, oidcClientConfig, versions, wellKnown)
: shouldShowQrLegacy(versions, wellKnown, capabilities);

// don't show anything if no method is available
if (!offerShowQr) {
return null;
}

return (
<SettingsSubsection heading={_t("settings|sessions|sign_in_with_qr")}>
<div className="mx_LoginWithQRSection">
<p className="mx_SettingsTab_subsectionText">{_t("settings|sessions|sign_in_with_qr_description")}</p>
<AccessibleButton onClick={onShowQr} kind="primary">
<AccessibleButton onClick={onShowQr} kind="primary" disabled={!offerShowQr}>
<QrCodeIcon height={20} width={20} />
{_t("settings|sessions|sign_in_with_qr_button")}
</AccessibleButton>
{!offerShowQr && <Text size="sm">{_t("settings|sessions|sign_in_with_qr_unsupported")}</Text>}
</div>
</SettingsSubsection>
);
Expand Down
5 changes: 3 additions & 2 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -2855,6 +2857,7 @@
"show_details": "Show details",
"sign_in_with_qr": "Link new device",
"sign_in_with_qr_button": "Show QR code",
"sign_in_with_qr_unsupported": "Not supported by your account provider",
"sign_in_with_qr_description": "Use a QR code to sign in to another device and set up secure messaging.",
"sign_out": "Sign out of this session",
"sign_out_all_other_sessions": "Sign out of all other sessions (%(otherSessionsCount)s)",
Expand Down Expand Up @@ -3832,8 +3835,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"
Expand Down

0 comments on commit 78345d5

Please sign in to comment.