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:
parent
b6e373c65b
commit
a70fcfd0bc
2 changed files with 38 additions and 4 deletions
|
@ -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)
|
||||
|
|
|
@ -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()} />);
|
||||
|
|
Loading…
Reference in a new issue