fix member list scroll bug where invited 3PID users replace joined members (#34427)

* Update MemberListView.tsx

* Update MemberListViewModel.ts

* simplify 3PID invite keys to getStateKey and added regression test

* Use state_key for 3PID invite keys and set up test state before initial render

* Format common.tsx with oxfmt
This commit is contained in:
rizzler
2026-08-03 10:18:29 +00:00
committed by GitHub
parent 7ffb0ffced
commit fbe7dbd646
4 changed files with 24 additions and 5 deletions
@@ -167,7 +167,7 @@ export function useMemberListViewModel(roomId: string): MemberListViewState {
// Finally add the third party invites
for (const invited of threePidInvited) {
const key = invited.threePidInvite!.event.getContent().display_name;
const key = `threePidInvite-${invited.threePidInvite!.event.getStateKey()}`;
newMemberMap.set(key, invited);
}
@@ -45,7 +45,7 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
} else if (item.member) {
return `member-${item.member.userId}`;
} else {
return `threePidInvite-${item.threePidInvite.event.getContent().public_key}`;
return `threePidInvite-${item.threePidInvite.event.getStateKey()}`;
}
}, []);
@@ -11,7 +11,7 @@ import { act } from "react";
import { waitFor, fireEvent } from "jest-matrix-react";
import { type Room, type RoomMember, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { filterConsole } from "../../../../../test-utils";
import { filterConsole, mkThirdPartyInviteEvent } from "../../../../../test-utils";
import { type Rendered, renderMemberList } from "./common";
jest.mock("../../../../../../src/customisations/helpers/UIComponents", () => ({
@@ -262,4 +262,18 @@ describe("MemberListView and MemberlistHeaderView", () => {
});
});
});
describe("3PID invites", () => {
it("does not collapse invites with duplicate display names", async () => {
const threePidEvents = [
mkThirdPartyInviteEvent("@alice:localhost", "user@example.com", "!room:localhost"),
mkThirdPartyInviteEvent("@alice:localhost", "user@example.com", "!room:localhost"),
];
const { root } = await renderMemberList(true, undefined, 2, threePidEvents);
const tiles = root.container.querySelectorAll(".mx_MemberTileView");
// 6 joined + 2 3PID invites
expect(tiles).toHaveLength(8);
});
});
});
@@ -15,6 +15,7 @@ import { VirtuosoMockContext } from "@element-hq/web-shared-components";
import {
Room,
type MatrixClient,
type MatrixEvent,
type RoomState,
RoomMember,
User,
@@ -54,6 +55,7 @@ export async function renderMemberList(
enablePresence: boolean,
roomSetup?: (room: Room) => void,
usersPerLevel: number = 2,
threePidEvents: MatrixEvent[] = [],
): Promise<Rendered> {
TestUtils.stubClient();
const client = MatrixClientPeg.safeGet();
@@ -109,7 +111,8 @@ export async function renderMemberList(
memberListRoom.currentState = {
members: {},
getMember: jest.fn(),
getStateEvents: ((eventType, stateKey) => (stateKey === undefined ? [] : null)) as RoomState["getStateEvents"], // ignore 3pid invites
getStateEvents: TestUtils.mockStateEventImplementation(threePidEvents),
getInviteForThreePidToken: jest.fn().mockReturnValue(null),
getInvitedMemberCount: jest.fn().mockReturnValue(0),
getJoinedMemberCount: jest
.fn()
@@ -139,7 +142,9 @@ export async function renderMemberList(
},
);
await waitFor(async () => {
expect(root.container.querySelectorAll(".mx_MemberTileView")).toHaveLength(usersPerLevel * 3);
expect(root.container.querySelectorAll(".mx_MemberTileView")).toHaveLength(
usersPerLevel * 3 + threePidEvents.length,
);
});
const reRender = createReRenderFunction(client, memberListRoom);