Fix invite dialog showing the same user multiple times (#11308)

* Fix invite dialog showing the same user multiple times

* Add test

* Improve coverage
This commit is contained in:
Michael Telatynski 2023-07-24 10:12:37 +01:00 committed by GitHub
parent b6e373c65b
commit a70fcfd0bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 4 deletions

View file

@ -21,6 +21,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { uniqBy } from "lodash";
import { Icon as InfoIcon } from "../../../../res/img/element-icons/info.svg";
import { Icon as EmailPillAvatarIcon } from "../../../../res/img/icon-email-pill-avatar.svg";
@ -384,6 +385,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
this.state = {
targets: [], // array of Member objects (see interface above)
filterText: this.props.initialText || "",
// Mutates alreadyInvited set so that buildSuggestions doesn't duplicate any users
recents: InviteDialog.buildRecents(alreadyInvited),
numRecentsShown: INITIAL_ROOMS_SHOWN,
suggestions: this.buildSuggestions(alreadyInvited),
@ -476,6 +478,8 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
}
recents.push({ userId, user: toMember(roomMember), lastActive: lastEventTs });
// We mutate the given set so that any later callers avoid duplicating these users
excludedTargetIds.add(userId);
}
if (!recents) logger.warn("[Invite:Recents] No recents to suggest!");
@ -821,7 +825,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
if (!this.state.busy) {
let filterText = this.state.filterText;
let targets = this.state.targets.map((t) => t); // cheap clone for mutation
const idx = targets.indexOf(member);
const idx = targets.findIndex((m) => m.userId === member.userId);
if (idx >= 0) {
targets.splice(idx, 1);
} else {
@ -945,11 +949,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
if (unableToAddMore) {
this.setState({
filterText: unableToAddMore.join(" "),
targets: [...this.state.targets, ...toAdd],
targets: uniqBy([...this.state.targets, ...toAdd], (t) => t.userId),
});
} else {
this.setState({
targets: [...this.state.targets, ...toAdd],
targets: uniqBy([...this.state.targets, ...toAdd], (t) => t.userId),
});
}
};
@ -1001,6 +1005,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
// The type of u is a pain to define but members of both mixins have the 'userId' property
const notAlreadyExists = (u: any): boolean => {
return (
!this.state.recents.some((m) => m.userId === u.userId) &&
!sourceMembers.some((m) => m.userId === u.userId) &&
!priorityAdditionalMembers.some((m) => m.userId === u.userId) &&
!otherAdditionalMembers.some((m) => m.userId === u.userId)

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import React from "react";
import { render, screen } from "@testing-library/react";
import { fireEvent, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { RoomType } from "matrix-js-sdk/src/@types/event";
import { MatrixClient, MatrixError, Room } from "matrix-js-sdk/src/matrix";
@ -366,6 +366,35 @@ describe("InviteDialog", () => {
]);
});
it("should not allow pasting the same user multiple times", async () => {
render(<InviteDialog kind={InviteKind.Invite} roomId={roomId} onFinished={jest.fn()} />);
const input = screen.getByTestId("invite-dialog-input");
input.focus();
await userEvent.paste(`${bobId}`);
await userEvent.paste(`${bobId}`);
await userEvent.paste(`${bobId}`);
expect(input).toHaveValue("");
await expect(screen.findAllByText(bobId, { selector: "a" })).resolves.toHaveLength(1);
});
it("should add to selection on click of user tile", async () => {
render(<InviteDialog kind={InviteKind.Invite} roomId={roomId} onFinished={jest.fn()} />);
const input = screen.getByTestId("invite-dialog-input");
input.focus();
await userEvent.keyboard(`${aliceId}`);
const btn = await screen.findByText(aliceId, {
selector: ".mx_InviteDialog_tile_nameStack_userId .mx_InviteDialog_tile--room_highlight",
});
fireEvent.click(btn);
const tile = await screen.findByText(aliceId, { selector: ".mx_InviteDialog_userTile_name" });
expect(tile).toBeInTheDocument();
});
describe("when inviting a user with an unknown profile", () => {
beforeEach(async () => {
render(<InviteDialog kind={InviteKind.Dm} onFinished={jest.fn()} />);