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

Commit

Permalink
set disabled state on invite to room button in memberlist (#11893)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerry authored Nov 21, 2023
1 parent 2f0eb8f commit 20fd111
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 37 deletions.
58 changes: 22 additions & 36 deletions src/components/views/rooms/MemberList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
RoomStateEvent,
User,
UserEvent,
JoinRule,
EventType,
ClientEvent,
} from "matrix-js-sdk/src/matrix";
Expand All @@ -54,6 +53,8 @@ import { shouldShowComponent } from "../../../customisations/helpers/UIComponent
import { UIComponent } from "../../../settings/UIFeature";
import PosthogTrackers from "../../../PosthogTrackers";
import { SDKContext } from "../../../contexts/SDKContext";
import { canInviteTo } from "../../../utils/room/canInviteTo";
import { inviteToRoom } from "../../../utils/room/inviteToRoom";

const INITIAL_LOAD_NUM_MEMBERS = 30;
const INITIAL_LOAD_NUM_INVITED = 5;
Expand Down Expand Up @@ -132,9 +133,7 @@ export default class MemberList extends React.Component<IProps, IState> {
const cli = MatrixClientPeg.safeGet();
const room = cli.getRoom(this.props.roomId);

return (
!!room?.canInvite(cli.getSafeUserId()) || !!(room?.isSpaceRoom() && room.getJoinRule() === JoinRule.Public)
);
return !!room && canInviteTo(room);
}

private getMembersState(invitedMembers: Array<RoomMember>, joinedMembers: Array<RoomMember>): IState {
Expand Down Expand Up @@ -365,32 +364,25 @@ export default class MemberList extends React.Component<IProps, IState> {
let inviteButton: JSX.Element | undefined;

if (room?.getMyMembership() === "join" && shouldShowComponent(UIComponent.InviteUsers)) {
let inviteButtonText = _t("room|invite_this_room");
if (room.isSpaceRoom()) {
inviteButtonText = _t("space|invite_this_space");
}
const inviteButtonText = room.isSpaceRoom() ? _t("space|invite_this_space") : _t("room|invite_this_room");

const button = (
<Button
size="sm"
kind="secondary"
className="mx_MemberList_invite"
onClick={this.onInviteButtonClick}
disabled={!this.state.canInvite}
>
<UserAddIcon width="1em" height="1em" />
{inviteButtonText}
</Button>
);

if (this.state.canInvite) {
inviteButton = (
<Button
size="sm"
kind="secondary"
className="mx_MemberList_invite"
onClick={this.onInviteButtonClick}
>
<UserAddIcon width="1em" height="1em" />
{inviteButtonText}
</Button>
);
inviteButton = button;
} else {
inviteButton = (
<Tooltip label={_t("member_list|invite_button_no_perms_tooltip")}>
<Button size="sm" kind="secondary" className="mx_MemberList_invite" onClick={() => {}}>
<UserAddIcon width="1em" height="1em" />
{inviteButtonText}
</Button>
</Tooltip>
);
inviteButton = <Tooltip label={_t("member_list|invite_button_no_perms_tooltip")}>{button}</Tooltip>;
}
}

Expand Down Expand Up @@ -454,15 +446,9 @@ export default class MemberList extends React.Component<IProps, IState> {
private onInviteButtonClick = (ev: ButtonEvent): void => {
PosthogTrackers.trackInteraction("WebRightPanelMemberListInviteButton", ev);

if (MatrixClientPeg.safeGet().isGuest()) {
dis.dispatch({ action: "require_registration" });
return;
}
const cli = MatrixClientPeg.safeGet();
const room = cli.getRoom(this.props.roomId)!;

// open the room inviter
dis.dispatch({
action: "view_invite",
roomId: this.props.roomId,
});
inviteToRoom(room);
};
}
106 changes: 105 additions & 1 deletion test/components/views/rooms/MemberList-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,37 @@ limitations under the License.
*/

import React from "react";
import { act, render, RenderResult, screen } from "@testing-library/react";
import { act, fireEvent, render, RenderResult, screen } from "@testing-library/react";
import { Room, MatrixClient, RoomState, RoomMember, User, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { compare } from "matrix-js-sdk/src/utils";
import { mocked, MockedObject } from "jest-mock";

import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import * as TestUtils from "../../../test-utils";
import MemberList from "../../../../src/components/views/rooms/MemberList";
import { SDKContext } from "../../../../src/contexts/SDKContext";
import { TestSdkContext } from "../../../TestSdkContext";
import {
filterConsole,
flushPromises,
getMockClientWithEventEmitter,
mockClientMethodsUser,
} from "../../../test-utils";
import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents";
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";

jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({
shouldShowComponent: jest.fn(),
}));

function generateRoomId() {
return "!" + Math.random().toString().slice(2, 10) + ":domain";
}

describe("MemberList", () => {
filterConsole(
"Age for event was not available, using `now - origin_server_ts` as a fallback. If the device clock is not correct issues might occur.",
);
function createRoom(opts = {}) {
const room = new Room(generateRoomId(), client, client.getUserId()!);
if (opts) {
Expand Down Expand Up @@ -331,5 +347,93 @@ describe("MemberList", () => {
);
expect(await screen.findByText(/User's server unreachable/)).toBeInTheDocument();
});

describe("Invite button", () => {
const roomId = "!room:server.org";
let client!: MockedObject<MatrixClient>;
let room!: Room;

beforeEach(function () {
mocked(shouldShowComponent).mockReturnValue(true);
client = getMockClientWithEventEmitter({
...mockClientMethodsUser(),
getRoom: jest.fn(),
hasLazyLoadMembersEnabled: jest.fn(),
});
room = new Room(roomId, client, client.getSafeUserId());
client.getRoom.mockReturnValue(room);
});

afterEach(() => {
jest.restoreAllMocks();
});

const renderComponent = () => {
const context = new TestSdkContext();
context.client = client;
render(
<SDKContext.Provider value={context}>
<MemberList
searchQuery=""
onClose={jest.fn()}
onSearchQueryChanged={jest.fn()}
roomId={room.roomId}
/>
</SDKContext.Provider>,
);
};

it("does not render invite button when current user is not a member", async () => {
renderComponent();
await flushPromises();

expect(screen.queryByText("Invite to this room")).not.toBeInTheDocument();
});

it("does not render invite button UI customisation hides invites", async () => {
mocked(shouldShowComponent).mockReturnValue(false);
renderComponent();
await flushPromises();

expect(screen.queryByText("Invite to this room")).not.toBeInTheDocument();
});

it("renders disabled invite button when current user is a member but does not have rights to invite", async () => {
jest.spyOn(room, "getMyMembership").mockReturnValue("join");
jest.spyOn(room, "canInvite").mockReturnValue(false);

renderComponent();
await flushPromises();

// button rendered but disabled
expect(screen.getByText("Invite to this room")).toBeDisabled();
});

it("renders enabled invite button when current user is a member and has rights to invite", async () => {
jest.spyOn(room, "getMyMembership").mockReturnValue("join");
jest.spyOn(room, "canInvite").mockReturnValue(true);

renderComponent();
await flushPromises();

expect(screen.getByText("Invite to this room")).not.toBeDisabled();
});

it("opens room inviter on button click", async () => {
jest.spyOn(defaultDispatcher, "dispatch");
jest.spyOn(room, "getMyMembership").mockReturnValue("join");
jest.spyOn(room, "canInvite").mockReturnValue(true);

renderComponent();
await flushPromises();

fireEvent.click(screen.getByText("Invite to this room"));

expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
action: "view_invite",
roomId,
});
});
});
});
});

0 comments on commit 20fd111

Please sign in to comment.