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

Commit eafc2d2

Browse files
authored
Ensure spaces in the spotlight dialog have rounded square avatars (#9480)
1 parent 913af09 commit eafc2d2

File tree

10 files changed

+73
-50
lines changed

10 files changed

+73
-50
lines changed

src/components/views/avatars/RoomAvatar.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ limitations under the License.
1616

1717
import React, { ComponentProps } from 'react';
1818
import { Room } from 'matrix-js-sdk/src/models/room';
19-
import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials';
2019
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
2120
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
2221
import classNames from "classnames";
23-
import { EventType } from "matrix-js-sdk/src/@types/event";
22+
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
2423

2524
import BaseAvatar from './BaseAvatar';
2625
import ImageView from '../elements/ImageView';
@@ -39,11 +38,7 @@ interface IProps extends Omit<ComponentProps<typeof BaseAvatar>, "name" | "idNam
3938
oobData?: IOOBData & {
4039
roomId?: string;
4140
};
42-
width?: number;
43-
height?: number;
44-
resizeMethod?: ResizeMethod;
4541
viewAvatarOnClick?: boolean;
46-
className?: string;
4742
onClick?(): void;
4843
}
4944

@@ -72,10 +67,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
7267
}
7368

7469
public componentWillUnmount() {
75-
const cli = MatrixClientPeg.get();
76-
if (cli) {
77-
cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
78-
}
70+
MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
7971
}
8072

8173
public static getDerivedStateFromProps(nextProps: IProps): IState {
@@ -133,7 +125,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
133125
public render() {
134126
const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props;
135127

136-
const roomName = room ? room.name : oobData.name;
128+
const roomName = room?.name ?? oobData.name;
137129
// If the room is a DM, we use the other user's ID for the color hash
138130
// in order to match the room avatar with their avatar
139131
const idName = room ? (DMRoomMap.shared().getUserIdForRoomId(room.roomId) ?? room.roomId) : oobData.roomId;
@@ -142,7 +134,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
142134
<BaseAvatar
143135
{...otherProps}
144136
className={classNames(className, {
145-
mx_RoomAvatar_isSpaceRoom: room?.isSpaceRoom(),
137+
mx_RoomAvatar_isSpaceRoom: (room?.getType() ?? this.props.oobData?.roomType) === RoomType.Space,
146138
})}
147139
name={roomName}
148140
idName={idName}

src/components/views/dialogs/spotlight/SpotlightDialog.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import { TooltipOption } from "./TooltipOption";
9393
import { isLocalRoom } from "../../../../utils/localRoom/isLocalRoom";
9494
import { useSlidingSyncRoomSearch } from "../../../../hooks/useSlidingSyncRoomSearch";
9595
import { shouldShowFeedback } from "../../../../utils/Feedback";
96+
import RoomAvatar from "../../avatars/RoomAvatar";
9697

9798
const MAX_RECENT_SEARCHES = 10;
9899
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
@@ -656,6 +657,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
656657
shouldPeek: result.publicRoom.world_readable || cli.isGuest(),
657658
}, true, ev.type !== "click");
658659
};
660+
659661
return (
660662
<Option
661663
id={`mx_SpotlightDialog_button_result_${result.publicRoom.room_id}`}
@@ -674,13 +676,14 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
674676
aria-describedby={`mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_alias`}
675677
aria-details={`mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_details`}
676678
>
677-
<BaseAvatar
679+
<RoomAvatar
678680
className="mx_SearchResultAvatar"
679-
url={result?.publicRoom?.avatar_url
680-
? mediaFromMxc(result?.publicRoom?.avatar_url).getSquareThumbnailHttp(AVATAR_SIZE)
681-
: null}
682-
name={result.publicRoom.name}
683-
idName={result.publicRoom.room_id}
681+
oobData={{
682+
roomId: result.publicRoom.room_id,
683+
name: result.publicRoom.name,
684+
avatarUrl: result.publicRoom.avatar_url,
685+
roomType: result.publicRoom.room_type,
686+
}}
684687
width={AVATAR_SIZE}
685688
height={AVATAR_SIZE}
686689
/>

src/components/views/rooms/RoomPreviewBar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,9 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
263263
params: {
264264
email: this.props.invitedEmail,
265265
signurl: this.props.signUrl,
266-
room_name: this.props.oobData ? this.props.oobData.room_name : null,
267-
room_avatar_url: this.props.oobData ? this.props.oobData.avatarUrl : null,
268-
inviter_name: this.props.oobData ? this.props.oobData.inviterName : null,
266+
room_name: this.props.oobData?.name ?? null,
267+
room_avatar_url: this.props.oobData?.avatarUrl ?? null,
268+
inviter_name: this.props.oobData?.inviterName ?? null,
269269
},
270270
};
271271
}

src/stores/ThreepidInviteStore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export interface IOOBData {
5656
inviterName?: string; // The display name of the person who invited us to the room
5757
// eslint-disable-next-line camelcase
5858
room_name?: string; // The name of the room, to be used until we are told better by the server
59-
roomType?: RoomType; // The type of the room, to be used until we are told better by the server
59+
roomType?: RoomType | string; // The type of the room, to be used until we are told better by the server
6060
}
6161

6262
const STORAGE_PREFIX = "mx_threepid_invite_";

test/Avatar-test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import { mocked } from "jest-mock";
18-
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
18+
import { Room, RoomMember, RoomType } from "matrix-js-sdk/src/matrix";
1919

2020
import { avatarUrlForRoom } from "../src/Avatar";
2121
import { Media, mediaFromMxc } from "../src/customisations/Media";
@@ -46,6 +46,7 @@ describe("avatarUrlForRoom", () => {
4646
roomId,
4747
getMxcAvatarUrl: jest.fn(),
4848
isSpaceRoom: jest.fn(),
49+
getType: jest.fn(),
4950
getAvatarFallbackMember: jest.fn(),
5051
} as unknown as Room;
5152
dmRoomMap = {
@@ -70,6 +71,7 @@ describe("avatarUrlForRoom", () => {
7071

7172
it("should return null for a space room", () => {
7273
mocked(room.isSpaceRoom).mockReturnValue(true);
74+
mocked(room.getType).mockReturnValue(RoomType.Space);
7375
expect(avatarUrlForRoom(room, 128, 128)).toBeNull();
7476
});
7577

test/components/views/dialogs/InviteDialog-test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
import React from "react";
1818
import { render, screen } from "@testing-library/react";
19+
import { RoomType } from "matrix-js-sdk/src/@types/event";
1920

2021
import InviteDialog from "../../../../src/components/views/dialogs/InviteDialog";
2122
import { KIND_INVITE } from "../../../../src/components/views/dialogs/InviteDialogTypes";
@@ -74,6 +75,7 @@ describe("InviteDialog", () => {
7475

7576
it("should label with space name", () => {
7677
mockClient.getRoom(roomId).isSpaceRoom = jest.fn().mockReturnValue(true);
78+
mockClient.getRoom(roomId).getType = jest.fn().mockReturnValue(RoomType.Space);
7779
mockClient.getRoom(roomId).name = "Space";
7880
render((
7981
<InviteDialog

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ describe('<UserInfo />', () => {
140140
describe('with a room', () => {
141141
const room = {
142142
roomId: '!fkfk',
143+
getType: jest.fn().mockReturnValue(undefined),
143144
isSpaceRoom: jest.fn().mockReturnValue(false),
144145
getMember: jest.fn().mockReturnValue(undefined),
145146
getMxcAvatarUrl: jest.fn().mockReturnValue('mock-avatar-url'),

test/components/views/rooms/RoomPreviewBar-test.tsx

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@ limitations under the License.
1515
*/
1616

1717
import React from 'react';
18-
import {
19-
renderIntoDocument,
20-
Simulate,
21-
findRenderedDOMComponentWithClass,
22-
act,
23-
} from 'react-dom/test-utils';
18+
import { render, fireEvent, RenderResult, waitFor } from "@testing-library/react";
2419
import { Room, RoomMember, MatrixError, IContent } from 'matrix-js-sdk/src/matrix';
2520

2621
import { stubClient } from '../../../test-utils';
2722
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
2823
import DMRoomMap from '../../../../src/utils/DMRoomMap';
2924
import RoomPreviewBar from '../../../../src/components/views/rooms/RoomPreviewBar';
25+
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
3026

3127
jest.mock('../../../../src/IdentityAuthClient', () => {
3228
return jest.fn().mockImplementation(() => {
@@ -79,19 +75,18 @@ describe('<RoomPreviewBar />', () => {
7975
const defaultProps = {
8076
room: createRoom(roomId, userId),
8177
};
82-
const wrapper = renderIntoDocument<React.Component>(
83-
<RoomPreviewBar {...defaultProps} {...props} />,
84-
) as React.Component;
85-
return findRenderedDOMComponentWithClass(wrapper, 'mx_RoomPreviewBar') as HTMLDivElement;
78+
return render(<RoomPreviewBar {...defaultProps} {...props} />);
8679
};
8780

88-
const isSpinnerRendered = (element: Element) => !!element.querySelector('.mx_Spinner');
89-
const getMessage = (element: Element) => element.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_message');
90-
const getActions = (element: Element) => element.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_actions');
91-
const getPrimaryActionButton = (element: Element) =>
92-
getActions(element).querySelector('.mx_AccessibleButton_kind_primary');
93-
const getSecondaryActionButton = (element: Element) =>
94-
getActions(element).querySelector('.mx_AccessibleButton_kind_secondary');
81+
const isSpinnerRendered = (wrapper: RenderResult) => !!wrapper.container.querySelector('.mx_Spinner');
82+
const getMessage = (wrapper: RenderResult) =>
83+
wrapper.container.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_message');
84+
const getActions = (wrapper: RenderResult) =>
85+
wrapper.container.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_actions');
86+
const getPrimaryActionButton = (wrapper: RenderResult) =>
87+
getActions(wrapper).querySelector('.mx_AccessibleButton_kind_primary');
88+
const getSecondaryActionButton = (wrapper: RenderResult) =>
89+
getActions(wrapper).querySelector('.mx_AccessibleButton_kind_secondary');
9590

9691
beforeEach(() => {
9792
stubClient();
@@ -128,6 +123,36 @@ describe('<RoomPreviewBar />', () => {
128123
expect(getMessage(component).textContent).toEqual('Join the conversation with an account');
129124
});
130125

126+
it("should send room oob data to start login", async () => {
127+
MatrixClientPeg.get().isGuest = jest.fn().mockReturnValue(true);
128+
const component = getComponent({
129+
oobData: {
130+
name: "Room Name",
131+
avatarUrl: "mxc://foo/bar",
132+
inviterName: "Charlie",
133+
},
134+
});
135+
136+
const dispatcherSpy = jest.fn();
137+
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
138+
139+
expect(getMessage(component).textContent).toEqual('Join the conversation with an account');
140+
fireEvent.click(getPrimaryActionButton(component));
141+
142+
await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({
143+
screenAfterLogin: {
144+
screen: 'room',
145+
params: expect.objectContaining({
146+
room_name: "Room Name",
147+
room_avatar_url: "mxc://foo/bar",
148+
inviter_name: "Charlie",
149+
}),
150+
},
151+
})));
152+
153+
defaultDispatcher.unregister(dispatcherRef);
154+
});
155+
131156
it('renders kicked message', () => {
132157
const room = createRoom(roomId, otherUserId);
133158
jest.spyOn(room, 'getMember').mockReturnValue(makeMockRoomMember({ isKicked: true }));
@@ -233,18 +258,14 @@ describe('<RoomPreviewBar />', () => {
233258

234259
it('joins room on primary button click', () => {
235260
const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
236-
act(() => {
237-
Simulate.click(getPrimaryActionButton(component));
238-
});
261+
fireEvent.click(getPrimaryActionButton(component));
239262

240263
expect(onJoinClick).toHaveBeenCalled();
241264
});
242265

243266
it('rejects invite on secondary button click', () => {
244267
const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
245-
act(() => {
246-
Simulate.click(getSecondaryActionButton(component));
247-
});
268+
fireEvent.click(getSecondaryActionButton(component));
248269

249270
expect(onRejectClick).toHaveBeenCalled();
250271
});
@@ -296,9 +317,7 @@ describe('<RoomPreviewBar />', () => {
296317
await new Promise(setImmediate);
297318
expect(getPrimaryActionButton(component)).toBeTruthy();
298319
expect(getSecondaryActionButton(component)).toBeFalsy();
299-
act(() => {
300-
Simulate.click(getPrimaryActionButton(component));
301-
});
320+
fireEvent.click(getPrimaryActionButton(component));
302321
expect(onJoinClick).toHaveBeenCalled();
303322
};
304323

test/stores/room-list/filters/VisibilityProvider-test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import { mocked } from "jest-mock";
18-
import { Room } from "matrix-js-sdk/src/matrix";
18+
import { Room, RoomType } from "matrix-js-sdk/src/matrix";
1919

2020
import { VisibilityProvider } from "../../../../src/stores/room-list/filters/VisibilityProvider";
2121
import LegacyCallHandler from "../../../../src/LegacyCallHandler";
@@ -43,6 +43,7 @@ jest.mock("../../../../src/customisations/RoomList", () => ({
4343
const createRoom = (isSpaceRoom = false): Room => {
4444
return {
4545
isSpaceRoom: () => isSpaceRoom,
46+
getType: () => isSpaceRoom ? RoomType.Space : undefined,
4647
} as unknown as Room;
4748
};
4849

test/test-utils/test-utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
IEventRelation,
3232
IUnsigned,
3333
IPusher,
34+
RoomType,
3435
} from 'matrix-js-sdk/src/matrix';
3536
import { normalize } from "matrix-js-sdk/src/utils";
3637
import { ReEmitter } from "matrix-js-sdk/src/ReEmitter";
@@ -447,6 +448,7 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl
447448
getAvatarUrl: () => 'mxc://avatar.url/room.png',
448449
getMxcAvatarUrl: () => 'mxc://avatar.url/room.png',
449450
isSpaceRoom: jest.fn().mockReturnValue(false),
451+
getType: jest.fn().mockReturnValue(undefined),
450452
isElementVideoRoom: jest.fn().mockReturnValue(false),
451453
getUnreadNotificationCount: jest.fn(() => 0),
452454
getEventReadUpTo: jest.fn(() => null),
@@ -544,6 +546,7 @@ export const mkSpace = (
544546
): MockedObject<Room> => {
545547
const space = mocked(mkRoom(client, spaceId, rooms));
546548
space.isSpaceRoom.mockReturnValue(true);
549+
space.getType.mockReturnValue(RoomType.Space);
547550
mocked(space.currentState).getStateEvents.mockImplementation(mockStateEventImplementation(children.map(roomId =>
548551
mkEvent({
549552
event: true,

0 commit comments

Comments
 (0)