diff --git a/src/components/views/settings/devices/LoginWithQRSection.tsx b/src/components/views/settings/devices/LoginWithQRSection.tsx index 356084dec73..1f10272690e 100644 --- a/src/components/views/settings/devices/LoginWithQRSection.tsx +++ b/src/components/views/settings/devices/LoginWithQRSection.tsx @@ -15,8 +15,12 @@ limitations under the License. */ import React from "react"; +import { + IMSC3882GetLoginTokenCapability, + IServerVersions, + UNSTABLE_MSC3882_CAPABILITY, +} from "matrix-js-sdk/src/matrix"; -import type { IServerVersions } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../../languageHandler"; import AccessibleButton from "../../elements/AccessibleButton"; import SettingsSubsection from "../shared/SettingsSubsection"; @@ -24,6 +28,8 @@ import SettingsSubsection from "../shared/SettingsSubsection"; interface IProps { onShowQr: () => void; versions?: IServerVersions; + // we can't use the capabilities type from the js-sdk because it isn't exported + capabilities?: Record; } export default class LoginWithQRSection extends React.Component { @@ -33,7 +39,10 @@ export default class LoginWithQRSection extends React.Component { public render(): JSX.Element | null { // Needs server support for MSC3882 and MSC3886: - const msc3882Supported = !!this.props.versions?.unstable_features?.["org.matrix.msc3882"]; + // in r0 of MSC3882 it is exposed as a feature flag, but in r1 it is a capability + const capability = UNSTABLE_MSC3882_CAPABILITY.findIn(this.props.capabilities); + const msc3882Supported = + !!this.props.versions?.unstable_features?.["org.matrix.msc3882"] || !!capability?.enabled; const msc3886Supported = !!this.props.versions?.unstable_features?.["org.matrix.msc3886"]; const offerShowQr = msc3882Supported && msc3886Supported; diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx index 9697af802b6..9fe4528f5a8 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx @@ -82,6 +82,8 @@ interface IState { invitedRoomIds: Set; showLoginWithQR: Mode | null; versions?: IServerVersions; + // we can't use the capabilities type from the js-sdk because it isn't exported + capabilities?: Record; } export default class SecurityUserSettingsTab extends React.Component { @@ -116,6 +118,9 @@ export default class SecurityUserSettingsTab extends React.Component this.setState({ versions })); + MatrixClientPeg.get() + .getCapabilities() + .then((capabilities) => this.setState({ capabilities })); } public componentWillUnmount(): void { @@ -393,7 +398,11 @@ export default class SecurityUserSettingsTab extends React.Component - + ); diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index 0dbda9477a3..7305975be1a 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -132,6 +132,7 @@ const SessionManagerTab: React.FC = () => { const userId = matrixClient?.getUserId(); const currentUserMember = (userId && matrixClient?.getUser(userId)) || undefined; const clientVersions = useAsyncMemo(() => matrixClient.getVersions(), [matrixClient]); + const capabilities = useAsyncMemo(async () => matrixClient?.getCapabilities(), [matrixClient]); const onDeviceExpandToggle = (deviceId: ExtendedDevice["device_id"]): void => { if (expandedDeviceIds.includes(deviceId)) { @@ -279,7 +280,7 @@ const SessionManagerTab: React.FC = () => { /> )} - + ); }; diff --git a/test/components/views/settings/devices/LoginWithQR-test.tsx b/test/components/views/settings/devices/LoginWithQR-test.tsx index 25e8b50f381..e350c76dfba 100644 --- a/test/components/views/settings/devices/LoginWithQR-test.tsx +++ b/test/components/views/settings/devices/LoginWithQR-test.tsx @@ -18,6 +18,7 @@ import { cleanup, render, waitFor } from "@testing-library/react"; import { mocked } from "jest-mock"; import React from "react"; import { MSC3906Rendezvous, RendezvousFailureReason } from "matrix-js-sdk/src/rendezvous"; +import { LoginTokenPostResponse } from "matrix-js-sdk/src/@types/auth"; import LoginWithQR, { Click, Mode, Phase } from "../../../../../src/components/views/auth/LoginWithQR"; import type { MatrixClient } from "matrix-js-sdk/src/matrix"; @@ -87,8 +88,9 @@ describe("", () => { jest.spyOn(MSC3906Rendezvous.prototype, "verifyNewDeviceOnExistingDevice").mockResolvedValue(undefined); client.requestLoginToken.mockResolvedValue({ login_token: "token", - expires_in: 1000, - }); + expires_in: 1000, // this is as per MSC3882 r0 + expires_in_ms: 1000 * 1000, // this is as per MSC3882 r1 + } as LoginTokenPostResponse); // we force the type here so that it works with versions of js-sdk that don't have r1 support yet }); afterEach(() => { diff --git a/test/components/views/settings/devices/LoginWithQRSection-test.tsx b/test/components/views/settings/devices/LoginWithQRSection-test.tsx index 2f272acdec5..3f3957eeb20 100644 --- a/test/components/views/settings/devices/LoginWithQRSection-test.tsx +++ b/test/components/views/settings/devices/LoginWithQRSection-test.tsx @@ -16,7 +16,7 @@ limitations under the License. import { render } from "@testing-library/react"; import { mocked } from "jest-mock"; -import { IServerVersions, MatrixClient } from "matrix-js-sdk/src/matrix"; +import { IServerVersions, MatrixClient, UNSTABLE_MSC3882_CAPABILITY } from "matrix-js-sdk/src/matrix"; import React from "react"; import LoginWithQRSection from "../../../../../src/components/views/settings/devices/LoginWithQRSection"; @@ -69,6 +69,23 @@ describe("", () => { const { container } = render(getComponent({ versions: makeVersions({ "org.matrix.msc3882": true }) })); expect(container).toMatchSnapshot(); }); + + it("only MSC3882 r1 enabled", async () => { + const { container } = render( + getComponent({ capabilities: { [UNSTABLE_MSC3882_CAPABILITY.name]: { enabled: true } } }), + ); + expect(container).toMatchSnapshot(); + }); + + it("MSC3886 + MSC3882 r1 disabled", async () => { + const { container } = render( + getComponent({ + versions: makeVersions({ "org.matrix.msc3886": true }), + capabilities: { [UNSTABLE_MSC3882_CAPABILITY.name]: { enabled: false } }, + }), + ); + expect(container).toMatchSnapshot(); + }); }); describe("should render panel", () => { @@ -83,5 +100,19 @@ describe("", () => { ); expect(container).toMatchSnapshot(); }); + + it("MSC3882 r1 + MSC3886", async () => { + const { container } = render( + getComponent({ + versions: makeVersions({ + "org.matrix.msc3886": true, + }), + capabilities: { + [UNSTABLE_MSC3882_CAPABILITY.name]: { enabled: true }, + }, + }), + ); + expect(container).toMatchSnapshot(); + }); }); }); diff --git a/test/components/views/settings/devices/__snapshots__/LoginWithQRSection-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/LoginWithQRSection-test.tsx.snap index ccf2e4ccb51..ffdaff5f4e7 100644 --- a/test/components/views/settings/devices/__snapshots__/LoginWithQRSection-test.tsx.snap +++ b/test/components/views/settings/devices/__snapshots__/LoginWithQRSection-test.tsx.snap @@ -1,9 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[` should not render MSC3886 + MSC3882 r1 disabled 1`] = `
`; + exports[` should not render no support at all 1`] = `
`; exports[` should not render only MSC3882 enabled 1`] = `
`; +exports[` should not render only MSC3882 r1 enabled 1`] = `
`; + exports[` should render panel MSC3882 + MSC3886 1`] = `
should render panel MSC3882 + MSC3886 1`] = `
`; + +exports[` should render panel MSC3882 r1 + MSC3886 1`] = ` +
+
+
+

+ Sign in with QR code +

+
+
+
+

+ You can use this device to sign in a new device with a QR code. You will need to scan the QR code shown on this device with your device that's signed out. +

+
+ Show QR code +
+
+
+
+
+`; diff --git a/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx index 9c8777016cc..72fd4a6d9c0 100644 --- a/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import { fireEvent, render } from "@testing-library/react"; +import { UNSTABLE_MSC3882_CAPABILITY } from "matrix-js-sdk/src/matrix"; import React from "react"; import SecurityUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/SecurityUserSettingsTab"; @@ -45,10 +46,14 @@ describe("", () => { getIgnoredUsers: jest.fn(), getVersions: jest.fn().mockResolvedValue({ unstable_features: { - "org.matrix.msc3882": true, "org.matrix.msc3886": true, }, }), + getCapabilities: jest.fn().mockResolvedValue({ + [UNSTABLE_MSC3882_CAPABILITY.name]: { + enabled: true, + }, + }), }); const getComponent = () => ( diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx index 70e87485bb2..bb96fe4e006 100644 --- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx +++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx @@ -29,6 +29,7 @@ import { PUSHER_DEVICE_ID, PUSHER_ENABLED, IAuthData, + UNSTABLE_MSC3882_CAPABILITY, } from "matrix-js-sdk/src/matrix"; import { clearAllModals } from "../../../../../test-utils"; @@ -97,6 +98,7 @@ describe("", () => { setPusher: jest.fn(), setLocalNotificationSettings: jest.fn(), getVersions: jest.fn().mockResolvedValue({}), + getCapabilities: jest.fn().mockResolvedValue({}), }); const defaultProps = {}; @@ -1375,10 +1377,14 @@ describe("", () => { mockClient.getVersions.mockResolvedValue({ versions: [], unstable_features: { - "org.matrix.msc3882": true, "org.matrix.msc3886": true, }, }); + mockClient.getCapabilities.mockResolvedValue({ + [UNSTABLE_MSC3882_CAPABILITY.name]: { + enabled: true, + }, + }); }); it("renders qr code login section", async () => {