Ensure spaces in the spotlight dialog have rounded square avatars (#9480)
This commit is contained in:
parent
913af09e61
commit
eafc2d23a7
10 changed files with 73 additions and 50 deletions
|
@ -16,11 +16,10 @@ limitations under the License.
|
|||
|
||||
import React, { ComponentProps } from 'react';
|
||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||
import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials';
|
||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||
import classNames from "classnames";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||
|
||||
import BaseAvatar from './BaseAvatar';
|
||||
import ImageView from '../elements/ImageView';
|
||||
|
@ -39,11 +38,7 @@ interface IProps extends Omit<ComponentProps<typeof BaseAvatar>, "name" | "idNam
|
|||
oobData?: IOOBData & {
|
||||
roomId?: string;
|
||||
};
|
||||
width?: number;
|
||||
height?: number;
|
||||
resizeMethod?: ResizeMethod;
|
||||
viewAvatarOnClick?: boolean;
|
||||
className?: string;
|
||||
onClick?(): void;
|
||||
}
|
||||
|
||||
|
@ -72,10 +67,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||
}
|
||||
MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||
}
|
||||
|
||||
public static getDerivedStateFromProps(nextProps: IProps): IState {
|
||||
|
@ -133,7 +125,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
|
|||
public render() {
|
||||
const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props;
|
||||
|
||||
const roomName = room ? room.name : oobData.name;
|
||||
const roomName = room?.name ?? oobData.name;
|
||||
// If the room is a DM, we use the other user's ID for the color hash
|
||||
// in order to match the room avatar with their avatar
|
||||
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> {
|
|||
<BaseAvatar
|
||||
{...otherProps}
|
||||
className={classNames(className, {
|
||||
mx_RoomAvatar_isSpaceRoom: room?.isSpaceRoom(),
|
||||
mx_RoomAvatar_isSpaceRoom: (room?.getType() ?? this.props.oobData?.roomType) === RoomType.Space,
|
||||
})}
|
||||
name={roomName}
|
||||
idName={idName}
|
||||
|
|
|
@ -93,6 +93,7 @@ import { TooltipOption } from "./TooltipOption";
|
|||
import { isLocalRoom } from "../../../../utils/localRoom/isLocalRoom";
|
||||
import { useSlidingSyncRoomSearch } from "../../../../hooks/useSlidingSyncRoomSearch";
|
||||
import { shouldShowFeedback } from "../../../../utils/Feedback";
|
||||
import RoomAvatar from "../../avatars/RoomAvatar";
|
||||
|
||||
const MAX_RECENT_SEARCHES = 10;
|
||||
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
|
|||
shouldPeek: result.publicRoom.world_readable || cli.isGuest(),
|
||||
}, true, ev.type !== "click");
|
||||
};
|
||||
|
||||
return (
|
||||
<Option
|
||||
id={`mx_SpotlightDialog_button_result_${result.publicRoom.room_id}`}
|
||||
|
@ -674,13 +676,14 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
aria-describedby={`mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_alias`}
|
||||
aria-details={`mx_SpotlightDialog_button_result_${result.publicRoom.room_id}_details`}
|
||||
>
|
||||
<BaseAvatar
|
||||
<RoomAvatar
|
||||
className="mx_SearchResultAvatar"
|
||||
url={result?.publicRoom?.avatar_url
|
||||
? mediaFromMxc(result?.publicRoom?.avatar_url).getSquareThumbnailHttp(AVATAR_SIZE)
|
||||
: null}
|
||||
name={result.publicRoom.name}
|
||||
idName={result.publicRoom.room_id}
|
||||
oobData={{
|
||||
roomId: result.publicRoom.room_id,
|
||||
name: result.publicRoom.name,
|
||||
avatarUrl: result.publicRoom.avatar_url,
|
||||
roomType: result.publicRoom.room_type,
|
||||
}}
|
||||
width={AVATAR_SIZE}
|
||||
height={AVATAR_SIZE}
|
||||
/>
|
||||
|
|
|
@ -263,9 +263,9 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
|||
params: {
|
||||
email: this.props.invitedEmail,
|
||||
signurl: this.props.signUrl,
|
||||
room_name: this.props.oobData ? this.props.oobData.room_name : null,
|
||||
room_avatar_url: this.props.oobData ? this.props.oobData.avatarUrl : null,
|
||||
inviter_name: this.props.oobData ? this.props.oobData.inviterName : null,
|
||||
room_name: this.props.oobData?.name ?? null,
|
||||
room_avatar_url: this.props.oobData?.avatarUrl ?? null,
|
||||
inviter_name: this.props.oobData?.inviterName ?? null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ export interface IOOBData {
|
|||
inviterName?: string; // The display name of the person who invited us to the room
|
||||
// eslint-disable-next-line camelcase
|
||||
room_name?: string; // The name of the room, to be used until we are told better by the server
|
||||
roomType?: RoomType; // The type of the room, to be used until we are told better by the server
|
||||
roomType?: RoomType | string; // The type of the room, to be used until we are told better by the server
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX = "mx_threepid_invite_";
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import { Room, RoomMember, RoomType } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { avatarUrlForRoom } from "../src/Avatar";
|
||||
import { Media, mediaFromMxc } from "../src/customisations/Media";
|
||||
|
@ -46,6 +46,7 @@ describe("avatarUrlForRoom", () => {
|
|||
roomId,
|
||||
getMxcAvatarUrl: jest.fn(),
|
||||
isSpaceRoom: jest.fn(),
|
||||
getType: jest.fn(),
|
||||
getAvatarFallbackMember: jest.fn(),
|
||||
} as unknown as Room;
|
||||
dmRoomMap = {
|
||||
|
@ -70,6 +71,7 @@ describe("avatarUrlForRoom", () => {
|
|||
|
||||
it("should return null for a space room", () => {
|
||||
mocked(room.isSpaceRoom).mockReturnValue(true);
|
||||
mocked(room.getType).mockReturnValue(RoomType.Space);
|
||||
expect(avatarUrlForRoom(room, 128, 128)).toBeNull();
|
||||
});
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { RoomType } from "matrix-js-sdk/src/@types/event";
|
||||
|
||||
import InviteDialog from "../../../../src/components/views/dialogs/InviteDialog";
|
||||
import { KIND_INVITE } from "../../../../src/components/views/dialogs/InviteDialogTypes";
|
||||
|
@ -74,6 +75,7 @@ describe("InviteDialog", () => {
|
|||
|
||||
it("should label with space name", () => {
|
||||
mockClient.getRoom(roomId).isSpaceRoom = jest.fn().mockReturnValue(true);
|
||||
mockClient.getRoom(roomId).getType = jest.fn().mockReturnValue(RoomType.Space);
|
||||
mockClient.getRoom(roomId).name = "Space";
|
||||
render((
|
||||
<InviteDialog
|
||||
|
|
|
@ -140,6 +140,7 @@ describe('<UserInfo />', () => {
|
|||
describe('with a room', () => {
|
||||
const room = {
|
||||
roomId: '!fkfk',
|
||||
getType: jest.fn().mockReturnValue(undefined),
|
||||
isSpaceRoom: jest.fn().mockReturnValue(false),
|
||||
getMember: jest.fn().mockReturnValue(undefined),
|
||||
getMxcAvatarUrl: jest.fn().mockReturnValue('mock-avatar-url'),
|
||||
|
|
|
@ -15,18 +15,14 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
renderIntoDocument,
|
||||
Simulate,
|
||||
findRenderedDOMComponentWithClass,
|
||||
act,
|
||||
} from 'react-dom/test-utils';
|
||||
import { render, fireEvent, RenderResult, waitFor } from "@testing-library/react";
|
||||
import { Room, RoomMember, MatrixError, IContent } from 'matrix-js-sdk/src/matrix';
|
||||
|
||||
import { stubClient } from '../../../test-utils';
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import DMRoomMap from '../../../../src/utils/DMRoomMap';
|
||||
import RoomPreviewBar from '../../../../src/components/views/rooms/RoomPreviewBar';
|
||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
|
||||
jest.mock('../../../../src/IdentityAuthClient', () => {
|
||||
return jest.fn().mockImplementation(() => {
|
||||
|
@ -79,19 +75,18 @@ describe('<RoomPreviewBar />', () => {
|
|||
const defaultProps = {
|
||||
room: createRoom(roomId, userId),
|
||||
};
|
||||
const wrapper = renderIntoDocument<React.Component>(
|
||||
<RoomPreviewBar {...defaultProps} {...props} />,
|
||||
) as React.Component;
|
||||
return findRenderedDOMComponentWithClass(wrapper, 'mx_RoomPreviewBar') as HTMLDivElement;
|
||||
return render(<RoomPreviewBar {...defaultProps} {...props} />);
|
||||
};
|
||||
|
||||
const isSpinnerRendered = (element: Element) => !!element.querySelector('.mx_Spinner');
|
||||
const getMessage = (element: Element) => element.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_message');
|
||||
const getActions = (element: Element) => element.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_actions');
|
||||
const getPrimaryActionButton = (element: Element) =>
|
||||
getActions(element).querySelector('.mx_AccessibleButton_kind_primary');
|
||||
const getSecondaryActionButton = (element: Element) =>
|
||||
getActions(element).querySelector('.mx_AccessibleButton_kind_secondary');
|
||||
const isSpinnerRendered = (wrapper: RenderResult) => !!wrapper.container.querySelector('.mx_Spinner');
|
||||
const getMessage = (wrapper: RenderResult) =>
|
||||
wrapper.container.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_message');
|
||||
const getActions = (wrapper: RenderResult) =>
|
||||
wrapper.container.querySelector<HTMLDivElement>('.mx_RoomPreviewBar_actions');
|
||||
const getPrimaryActionButton = (wrapper: RenderResult) =>
|
||||
getActions(wrapper).querySelector('.mx_AccessibleButton_kind_primary');
|
||||
const getSecondaryActionButton = (wrapper: RenderResult) =>
|
||||
getActions(wrapper).querySelector('.mx_AccessibleButton_kind_secondary');
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
|
@ -128,6 +123,36 @@ describe('<RoomPreviewBar />', () => {
|
|||
expect(getMessage(component).textContent).toEqual('Join the conversation with an account');
|
||||
});
|
||||
|
||||
it("should send room oob data to start login", async () => {
|
||||
MatrixClientPeg.get().isGuest = jest.fn().mockReturnValue(true);
|
||||
const component = getComponent({
|
||||
oobData: {
|
||||
name: "Room Name",
|
||||
avatarUrl: "mxc://foo/bar",
|
||||
inviterName: "Charlie",
|
||||
},
|
||||
});
|
||||
|
||||
const dispatcherSpy = jest.fn();
|
||||
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
|
||||
|
||||
expect(getMessage(component).textContent).toEqual('Join the conversation with an account');
|
||||
fireEvent.click(getPrimaryActionButton(component));
|
||||
|
||||
await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({
|
||||
screenAfterLogin: {
|
||||
screen: 'room',
|
||||
params: expect.objectContaining({
|
||||
room_name: "Room Name",
|
||||
room_avatar_url: "mxc://foo/bar",
|
||||
inviter_name: "Charlie",
|
||||
}),
|
||||
},
|
||||
})));
|
||||
|
||||
defaultDispatcher.unregister(dispatcherRef);
|
||||
});
|
||||
|
||||
it('renders kicked message', () => {
|
||||
const room = createRoom(roomId, otherUserId);
|
||||
jest.spyOn(room, 'getMember').mockReturnValue(makeMockRoomMember({ isKicked: true }));
|
||||
|
@ -233,18 +258,14 @@ describe('<RoomPreviewBar />', () => {
|
|||
|
||||
it('joins room on primary button click', () => {
|
||||
const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
|
||||
act(() => {
|
||||
Simulate.click(getPrimaryActionButton(component));
|
||||
});
|
||||
fireEvent.click(getPrimaryActionButton(component));
|
||||
|
||||
expect(onJoinClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects invite on secondary button click', () => {
|
||||
const component = getComponent({ inviterName, room, onJoinClick, onRejectClick });
|
||||
act(() => {
|
||||
Simulate.click(getSecondaryActionButton(component));
|
||||
});
|
||||
fireEvent.click(getSecondaryActionButton(component));
|
||||
|
||||
expect(onRejectClick).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -296,9 +317,7 @@ describe('<RoomPreviewBar />', () => {
|
|||
await new Promise(setImmediate);
|
||||
expect(getPrimaryActionButton(component)).toBeTruthy();
|
||||
expect(getSecondaryActionButton(component)).toBeFalsy();
|
||||
act(() => {
|
||||
Simulate.click(getPrimaryActionButton(component));
|
||||
});
|
||||
fireEvent.click(getPrimaryActionButton(component));
|
||||
expect(onJoinClick).toHaveBeenCalled();
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import { Room, RoomType } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { VisibilityProvider } from "../../../../src/stores/room-list/filters/VisibilityProvider";
|
||||
import LegacyCallHandler from "../../../../src/LegacyCallHandler";
|
||||
|
@ -43,6 +43,7 @@ jest.mock("../../../../src/customisations/RoomList", () => ({
|
|||
const createRoom = (isSpaceRoom = false): Room => {
|
||||
return {
|
||||
isSpaceRoom: () => isSpaceRoom,
|
||||
getType: () => isSpaceRoom ? RoomType.Space : undefined,
|
||||
} as unknown as Room;
|
||||
};
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
IEventRelation,
|
||||
IUnsigned,
|
||||
IPusher,
|
||||
RoomType,
|
||||
} from 'matrix-js-sdk/src/matrix';
|
||||
import { normalize } from "matrix-js-sdk/src/utils";
|
||||
import { ReEmitter } from "matrix-js-sdk/src/ReEmitter";
|
||||
|
@ -447,6 +448,7 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl
|
|||
getAvatarUrl: () => 'mxc://avatar.url/room.png',
|
||||
getMxcAvatarUrl: () => 'mxc://avatar.url/room.png',
|
||||
isSpaceRoom: jest.fn().mockReturnValue(false),
|
||||
getType: jest.fn().mockReturnValue(undefined),
|
||||
isElementVideoRoom: jest.fn().mockReturnValue(false),
|
||||
getUnreadNotificationCount: jest.fn(() => 0),
|
||||
getEventReadUpTo: jest.fn(() => null),
|
||||
|
@ -544,6 +546,7 @@ export const mkSpace = (
|
|||
): MockedObject<Room> => {
|
||||
const space = mocked(mkRoom(client, spaceId, rooms));
|
||||
space.isSpaceRoom.mockReturnValue(true);
|
||||
space.getType.mockReturnValue(RoomType.Space);
|
||||
mocked(space.currentState).getStateEvents.mockImplementation(mockStateEventImplementation(children.map(roomId =>
|
||||
mkEvent({
|
||||
event: true,
|
||||
|
|
Loading…
Reference in a new issue