Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Fix invite dialog showing the same user multiple times (#11308)
Browse files Browse the repository at this point in the history
* Fix invite dialog showing the same user multiple times

* Add test

* Improve coverage
  • Loading branch information
t3chguy authored Jul 24, 2023
1 parent b6e373c commit a70fcfd
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
11 changes: 8 additions & 3 deletions src/components/views/dialogs/InviteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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!");

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
});
}
};
Expand Down Expand Up @@ -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)
Expand Down
31 changes: 30 additions & 1 deletion test/components/views/dialogs/InviteDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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()} />);
Expand Down

0 comments on commit a70fcfd

Please sign in to comment.