From f37623f054bc542d6ee9b62d3b1f16927df7371f Mon Sep 17 00:00:00 2001 From: hayyaksi <193020925+hayaksi1@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:02:37 +0300 Subject: [PATCH] Update the member list invite button when power levels change (#34475) canInvite was only recomputed on third party invite state events, so granting or revoking the power to invite left the member list header button in its previous enabled or disabled state until the list was reopened. Recompute it on RoomStateEvent.Update as well, which also covers the space join rule branch in canInviteTo(), matching RoomSummaryCardViewModel. Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- .../memberlist/MemberListViewModel.ts | 7 +++++- .../memberlist/MemberListHeaderView-test.ts | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) 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");