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

Commit edfad2b

Browse files
committed
UserInfo step 2: DeviceItem
1 parent 9d2d47b commit edfad2b

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

src/components/views/right_panel/UserInfo.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,20 @@ function useHasCrossSigningKeys(
162162
export function DeviceItem({ userId, device }: { userId: string; device: IDevice }): JSX.Element {
163163
const cli = useContext(MatrixClientContext);
164164
const isMe = userId === cli.getUserId();
165-
const deviceTrust = cli.checkDeviceTrust(userId, device.deviceId);
166165
const userTrust = cli.checkUserTrust(userId);
167-
// For your own devices, we use the stricter check of cross-signing
168-
// verification to encourage everyone to trust their own devices via
169-
// cross-signing so that other users can then safely trust you.
170-
// For other people's devices, the more general verified check that
171-
// includes locally verified devices can be used.
172-
const isVerified = isMe ? deviceTrust.isCrossSigningVerified() : deviceTrust.isVerified();
166+
167+
/** is the device verified? */
168+
const isVerified = useAsyncMemo(async () => {
169+
const deviceTrust = await cli.getCrypto()?.getDeviceVerificationStatus(userId, device.deviceId);
170+
if (!deviceTrust) return false;
171+
172+
// For your own devices, we use the stricter check of cross-signing
173+
// verification to encourage everyone to trust their own devices via
174+
// cross-signing so that other users can then safely trust you.
175+
// For other people's devices, the more general verified check that
176+
// includes locally verified devices can be used.
177+
return isMe ? deviceTrust.crossSigningVerified : deviceTrust.isVerified();
178+
}, [cli, userId, device]);
173179

174180
const classes = classNames("mx_UserInfo_device", {
175181
mx_UserInfo_device_verified: isVerified,
@@ -200,7 +206,10 @@ export function DeviceItem({ userId, device }: { userId: string; device: IDevice
200206
let trustedLabel: string | undefined;
201207
if (userTrust.isVerified()) trustedLabel = isVerified ? _t("Trusted") : _t("Not trusted");
202208

203-
if (isVerified) {
209+
if (isVerified === undefined) {
210+
// we're still deciding if the device is verified
211+
return <div className={classes} title={device.deviceId} />;
212+
} else if (isVerified) {
204213
return (
205214
<div className={classes} title={device.deviceId}>
206215
<div className={iconClasses} />

test/components/views/right_panel/UserInfo-test.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,16 @@ import React from "react";
1818
import { fireEvent, render, screen, waitFor, cleanup, act, within } from "@testing-library/react";
1919
import userEvent from "@testing-library/user-event";
2020
import { Mocked, mocked } from "jest-mock";
21-
import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
21+
import {
22+
Room,
23+
User,
24+
MatrixClient,
25+
RoomMember,
26+
MatrixEvent,
27+
EventType,
28+
CryptoApi,
29+
DeviceVerificationStatus,
30+
} from "matrix-js-sdk/src/matrix";
2231
import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
2332
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
2433
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
@@ -79,6 +88,7 @@ const defaultUser = new User(defaultUserId);
7988
let mockRoom: Mocked<Room>;
8089
let mockSpace: Mocked<Room>;
8190
let mockClient: Mocked<MatrixClient>;
91+
let mockCrypto: Mocked<CryptoApi>;
8292

8393
beforeEach(() => {
8494
mockRoom = mocked({
@@ -115,6 +125,10 @@ beforeEach(() => {
115125
getEventReadUpTo: jest.fn(),
116126
} as unknown as Room);
117127

128+
mockCrypto = mocked({
129+
getDeviceVerificationStatus: jest.fn(),
130+
} as unknown as CryptoApi);
131+
118132
mockClient = mocked({
119133
getUser: jest.fn(),
120134
isGuest: jest.fn().mockReturnValue(false),
@@ -141,6 +155,7 @@ beforeEach(() => {
141155
setPowerLevel: jest.fn(),
142156
downloadKeys: jest.fn(),
143157
getStoredDevicesForUser: jest.fn(),
158+
getCrypto: jest.fn().mockReturnValue(mockCrypto),
144159
} as unknown as MatrixClient);
145160

146161
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
@@ -370,10 +385,10 @@ describe("<DeviceItem />", () => {
370385
mockClient.checkUserTrust.mockReturnValue({ isVerified: () => isVerified } as UserTrustLevel);
371386
};
372387
const setMockDeviceTrust = (isVerified = false, isCrossSigningVerified = false) => {
373-
mockClient.checkDeviceTrust.mockReturnValue({
388+
mockCrypto.getDeviceVerificationStatus.mockResolvedValue({
374389
isVerified: () => isVerified,
375-
isCrossSigningVerified: () => isCrossSigningVerified,
376-
} as DeviceTrustLevel);
390+
crossSigningVerified: isCrossSigningVerified,
391+
} as DeviceVerificationStatus);
377392
};
378393

379394
const mockVerifyDevice = jest.spyOn(mockVerification, "verifyDevice");
@@ -384,7 +399,7 @@ describe("<DeviceItem />", () => {
384399
});
385400

386401
afterEach(() => {
387-
mockClient.checkDeviceTrust.mockReset();
402+
mockCrypto.getDeviceVerificationStatus.mockReset();
388403
mockClient.checkUserTrust.mockReset();
389404
mockVerifyDevice.mockClear();
390405
});
@@ -393,32 +408,36 @@ describe("<DeviceItem />", () => {
393408
mockVerifyDevice.mockRestore();
394409
});
395410

396-
it("with unverified user and device, displays button without a label", () => {
411+
it("with unverified user and device, displays button without a label", async () => {
397412
renderComponent();
413+
await act(flushPromises);
398414

399415
expect(screen.getByRole("button", { name: device.getDisplayName()! })).toBeInTheDocument;
400416
expect(screen.queryByText(/trusted/i)).not.toBeInTheDocument();
401417
});
402418

403-
it("with verified user only, displays button with a 'Not trusted' label", () => {
419+
it("with verified user only, displays button with a 'Not trusted' label", async () => {
404420
setMockUserTrust(true);
405421
renderComponent();
422+
await act(flushPromises);
406423

407424
expect(screen.getByRole("button", { name: `${device.getDisplayName()} Not trusted` })).toBeInTheDocument;
408425
});
409426

410-
it("with verified device only, displays no button without a label", () => {
427+
it("with verified device only, displays no button without a label", async () => {
411428
setMockDeviceTrust(true);
412429
renderComponent();
430+
await act(flushPromises);
413431

414432
expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument();
415433
expect(screen.queryByText(/trusted/)).not.toBeInTheDocument();
416434
});
417435

418-
it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", () => {
436+
it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", async () => {
419437
mockClient.getSafeUserId.mockReturnValueOnce(defaultUserId);
420438
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
421439
renderComponent();
440+
await act(flushPromises);
422441

423442
// set trust to be false for isVerified, true for isCrossSigningVerified
424443
setMockDeviceTrust(false, true);
@@ -428,10 +447,11 @@ describe("<DeviceItem />", () => {
428447
expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument();
429448
});
430449

431-
it("with verified user and device, displays no button and a 'Trusted' label", () => {
450+
it("with verified user and device, displays no button and a 'Trusted' label", async () => {
432451
setMockUserTrust(true);
433452
setMockDeviceTrust(true);
434453
renderComponent();
454+
await act(flushPromises);
435455

436456
expect(screen.queryByRole("button")).not.toBeInTheDocument;
437457
expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument();
@@ -441,6 +461,7 @@ describe("<DeviceItem />", () => {
441461
it("does not call verifyDevice if client.getUser returns null", async () => {
442462
mockClient.getUser.mockReturnValueOnce(null);
443463
renderComponent();
464+
await act(flushPromises);
444465

445466
const button = screen.getByRole("button", { name: device.getDisplayName()! });
446467
expect(button).toBeInTheDocument;
@@ -455,6 +476,7 @@ describe("<DeviceItem />", () => {
455476
// even more mocking
456477
mockClient.isGuest.mockReturnValueOnce(true);
457478
renderComponent();
479+
await act(flushPromises);
458480

459481
const button = screen.getByRole("button", { name: device.getDisplayName()! });
460482
expect(button).toBeInTheDocument;

0 commit comments

Comments
 (0)