diff --git a/apps/web/src/components/viewmodels/memberlist/MemberListViewModel.ts b/apps/web/src/components/viewmodels/memberlist/MemberListViewModel.ts index 1c14cc712a..f285a59366 100644 --- a/apps/web/src/components/viewmodels/memberlist/MemberListViewModel.ts +++ b/apps/web/src/components/viewmodels/memberlist/MemberListViewModel.ts @@ -218,7 +218,12 @@ export function useMemberListViewModel(roomId: string): MemberListViewState { }); useTypedEventEmitter(cli, RoomStateEvent.Update, (state: RoomState) => { - if (state.roomId === roomId) loadMembers(); + if (state.roomId === roomId) { + loadMembers(); + // Power level and join rule changes both surface here, and both can change whether we may + // invite. Mirrors RoomSummaryCardViewModel, which recomputes canInviteTo on the same event. + setCanInvite(getCanUserInviteToThisRoom()); + } }); useTypedEventEmitter(cli, RoomMemberEvent.Name, (_: MatrixEvent, member: SdkRoomMember) => { diff --git a/apps/web/test/unit-tests/components/views/rooms/memberlist/MemberListHeaderView-test.ts b/apps/web/test/unit-tests/components/views/rooms/memberlist/MemberListHeaderView-test.ts index b9904edf21..18016ceaf7 100644 --- a/apps/web/test/unit-tests/components/views/rooms/memberlist/MemberListHeaderView-test.ts +++ b/apps/web/test/unit-tests/components/views/rooms/memberlist/MemberListHeaderView-test.ts @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. */ import { act, fireEvent, screen, waitFor } from "jest-matrix-react"; -import { RoomMember, User, RoomEvent } from "matrix-js-sdk/src/matrix"; +import { RoomMember, User, RoomEvent, RoomStateEvent, type RoomState } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { mocked } from "jest-mock"; @@ -99,6 +99,26 @@ describe("MemberListHeaderView", () => { ); }); + it("Updates the invite button when a power level change grants invite rights", async () => { + const { memberListRoom, client, reRender } = rendered; + jest.spyOn(memberListRoom, "getMyMembership").mockReturnValue(KnownMembership.Join); + jest.spyOn(memberListRoom, "canInvite").mockReturnValue(false); + await reRender(); + await waitFor(() => + expect(screen.getByRole("button", { name: "Invite" })).toHaveAttribute("aria-disabled", "true"), + ); + + // Grant the right to invite, and announce it the way a power level change does + jest.spyOn(memberListRoom, "canInvite").mockReturnValue(true); + act(() => { + client.emit(RoomStateEvent.Update, { roomId: memberListRoom.roomId } as RoomState); + }); + + await waitFor(() => + expect(screen.getByRole("button", { name: "Invite" })).not.toHaveAttribute("aria-disabled", "true"), + ); + }); + it("Opens room inviter on button click", async () => { const { memberListRoom, reRender } = rendered; jest.spyOn(defaultDispatcher, "dispatch");