From 6746ce2da396cbc59fe4e96ac0b060b24d6a0a0f Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Fri, 3 Mar 2023 13:34:38 +0100 Subject: [PATCH] Fix start DM via right panel (#10278) --- src/components/views/right_panel/UserInfo.tsx | 32 +++++++++----- .../views/right_panel/UserInfo-test.tsx | 43 +++++++++++-------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 41cd87e107..88f5c7c006 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -121,13 +121,17 @@ export const getE2EStatus = (cli: MatrixClient, userId: string, devices: IDevice return anyDeviceUnverified ? E2EStatus.Warning : E2EStatus.Verified; }; -async function openDMForUser(matrixClient: MatrixClient, user: RoomMember): Promise { - const startDMUser = new DirectoryMember({ +/** + * Converts the member to a DirectoryMember and starts a DM with them. + */ +async function openDmForUser(matrixClient: MatrixClient, user: Member): Promise { + const avatarUrl = user instanceof User ? user.avatarUrl : user.getMxcAvatarUrl(); + const startDmUser = new DirectoryMember({ user_id: user.userId, display_name: user.rawDisplayName, - avatar_url: user.getMxcAvatarUrl(), + avatar_url: avatarUrl, }); - startDmOnFirstMessage(matrixClient, [startDMUser]); + startDmOnFirstMessage(matrixClient, [startDmUser]); } type SetUpdating = (updating: boolean) => void; @@ -310,7 +314,7 @@ function DevicesSection({ ); } -const MessageButton = ({ member }: { member: RoomMember }): JSX.Element => { +const MessageButton = ({ member }: { member: Member }): JSX.Element => { const cli = useContext(MatrixClientContext); const [busy, setBusy] = useState(false); @@ -320,7 +324,7 @@ const MessageButton = ({ member }: { member: RoomMember }): JSX.Element => { onClick={async () => { if (busy) return; setBusy(true); - await openDMForUser(cli, member); + await openDmForUser(cli, member); setBusy(false); }} className="mx_UserInfo_field" @@ -332,7 +336,7 @@ const MessageButton = ({ member }: { member: RoomMember }): JSX.Element => { }; export const UserOptionsSection: React.FC<{ - member: RoomMember; + member: Member; isIgnored: boolean; canInvite: boolean; isSpace?: boolean; @@ -360,8 +364,9 @@ export const UserOptionsSection: React.FC<{ }, [cli, member]); const ignore = useCallback(async () => { + const name = (member instanceof User ? member.displayName : member.name) || member.userId; const { finished } = Modal.createDialog(QuestionDialog, { - title: _t("Ignore %(user)s", { user: member.name }), + title: _t("Ignore %(user)s", { user: name }), description: (
{_t( @@ -394,7 +399,7 @@ export const UserOptionsSection: React.FC<{ ); - if (member.roomId && !isSpace) { + if (member instanceof RoomMember && member.roomId && !isSpace) { const onReadReceiptButton = function (): void { const room = cli.getRoom(member.roomId); dis.dispatch({ @@ -415,7 +420,7 @@ export const UserOptionsSection: React.FC<{ }); }; - const room = cli.getRoom(member.roomId); + const room = member instanceof RoomMember ? cli.getRoom(member.roomId) : undefined; if (room?.getEventReadUpTo(member.userId)) { readReceiptButton = ( @@ -431,7 +436,12 @@ export const UserOptionsSection: React.FC<{ ); } - if (canInvite && (member?.membership ?? "leave") === "leave" && shouldShowComponent(UIComponent.InviteUsers)) { + if ( + member instanceof RoomMember && + canInvite && + (member?.membership ?? "leave") === "leave" && + shouldShowComponent(UIComponent.InviteUsers) + ) { const roomId = member && member.roomId ? member.roomId : SdkContextClass.instance.roomViewStore.getRoomId(); const onInviteUserButton = async (ev: ButtonEvent): Promise => { try { diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index 1f95c72950..fe943a88aa 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -43,6 +43,12 @@ import MultiInviter from "../../../../src/utils/MultiInviter"; import * as mockVerification from "../../../../src/verification"; import Modal from "../../../../src/Modal"; import { E2EStatus } from "../../../../src/utils/ShieldUtils"; +import { DirectoryMember, startDmOnFirstMessage } from "../../../../src/utils/direct-messages"; + +jest.mock("../../../../src/utils/direct-messages", () => ({ + ...jest.requireActual("../../../../src/utils/direct-messages"), + startDmOnFirstMessage: jest.fn(), +})); jest.mock("../../../../src/dispatcher/dispatcher"); @@ -121,7 +127,7 @@ const mockClient = mocked({ setPowerLevel: jest.fn(), } as unknown as MatrixClient); -const defaultUserId = "@test:test"; +const defaultUserId = "@user:example.com"; const defaultUser = new User(defaultUserId); beforeEach(() => { @@ -550,23 +556,6 @@ describe("", () => { }); }); - it("calling .invite with a null roomId still calls .invite and shows default error message", async () => { - inviteSpy.mockRejectedValue({ this: "could be anything" }); - - // render the component and click the button - renderComponent({ canInvite: true, member: { ...member, roomId: null } }); - const inviteButton = screen.getByRole("button", { name: /invite/i }); - expect(inviteButton).toBeInTheDocument(); - await userEvent.click(inviteButton); - - expect(inviteSpy).toHaveBeenCalledTimes(1); - - // check that the default test error message is displayed - await waitFor(() => { - expect(screen.getByText(/operation failed/i)).toBeInTheDocument(); - }); - }); - it("shows a modal before ignoring the user", async () => { const originalCreateDialog = Modal.createDialog; const modalSpy = (Modal.createDialog = jest.fn().mockReturnValue({ @@ -612,6 +601,24 @@ describe("", () => { await userEvent.click(screen.getByRole("button", { name: "Unignore" })); expect(mockClient.setIgnoredUsers).toHaveBeenCalledWith([]); }); + + it.each([ + ["for a RoomMember", member, member.getMxcAvatarUrl()], + ["for a User", defaultUser, defaultUser.avatarUrl], + ])( + "clicking »message« %s should start a DM", + async (test: string, member: RoomMember | User, expectedAvatarUrl: string | undefined) => { + renderComponent({ member }); + await userEvent.click(screen.getByText("Message")); + expect(startDmOnFirstMessage).toHaveBeenCalledWith(mockClient, [ + new DirectoryMember({ + user_id: member.userId, + display_name: member.rawDisplayName, + avatar_url: expectedAvatarUrl, + }), + ]); + }, + ); }); describe("", () => {