2015-06-23 16:41:25 +01:00
|
|
|
/*
|
2016-01-07 04:06:39 +00:00
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2017-07-25 15:56:16 +01:00
|
|
|
Copyright 2017 Vector Creations Ltd
|
2018-03-19 16:47:12 +00:00
|
|
|
Copyright 2017, 2018 New Vector Ltd
|
2021-06-23 13:45:55 +02:00
|
|
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
2015-06-23 16:41:25 +01:00
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
2017-09-22 13:15:02 +01:00
|
|
|
|
|
|
|
|
import React from "react";
|
2023-08-07 09:24:58 +01:00
|
|
|
import {
|
|
|
|
|
MatrixEvent,
|
|
|
|
|
Room,
|
|
|
|
|
RoomEvent,
|
|
|
|
|
RoomMember,
|
|
|
|
|
RoomMemberEvent,
|
|
|
|
|
RoomState,
|
|
|
|
|
RoomStateEvent,
|
2023-08-08 08:16:04 +01:00
|
|
|
User,
|
|
|
|
|
UserEvent,
|
2023-08-08 11:12:12 +01:00
|
|
|
JoinRule,
|
|
|
|
|
EventType,
|
2023-08-09 08:18:41 +01:00
|
|
|
ClientEvent,
|
2023-08-07 09:24:58 +01:00
|
|
|
} from "matrix-js-sdk/src/matrix";
|
2021-12-09 09:10:23 +00:00
|
|
|
import { throttle } from "lodash";
|
|
|
|
|
|
2017-05-25 11:39:08 +01:00
|
|
|
import { _t } from "../../../languageHandler";
|
2020-05-13 20:41:41 -06:00
|
|
|
import dis from "../../../dispatcher/dispatcher";
|
2021-06-23 13:45:55 +02:00
|
|
|
import { isValid3pidInvite } from "../../../RoomInvite";
|
|
|
|
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
2020-09-08 10:19:51 +01:00
|
|
|
import BaseCard from "../right_panel/BaseCard";
|
2021-03-02 09:51:11 +00:00
|
|
|
import RoomAvatar from "../avatars/RoomAvatar";
|
|
|
|
|
import RoomName from "../elements/RoomName";
|
2021-06-23 13:45:55 +02:00
|
|
|
import TruncatedList from "../elements/TruncatedList";
|
|
|
|
|
import Spinner from "../elements/Spinner";
|
|
|
|
|
import SearchBox from "../../structures/SearchBox";
|
2022-02-09 14:42:08 +00:00
|
|
|
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
2021-06-23 13:45:55 +02:00
|
|
|
import EntityTile from "./EntityTile";
|
|
|
|
|
import MemberTile from "./MemberTile";
|
|
|
|
|
import BaseAvatar from "../avatars/BaseAvatar";
|
2021-10-08 16:04:26 -06:00
|
|
|
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
|
|
|
|
import { UIComponent } from "../../../settings/UIFeature";
|
2022-02-09 14:42:08 +00:00
|
|
|
import PosthogTrackers from "../../../PosthogTrackers";
|
2022-11-18 19:05:00 +00:00
|
|
|
import { SDKContext } from "../../../contexts/SDKContext";
|
2023-05-12 11:25:16 +01:00
|
|
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
2015-06-22 11:42:09 +01:00
|
|
|
|
2017-09-22 13:15:02 +01:00
|
|
|
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
2017-09-22 16:50:54 +01:00
|
|
|
const INITIAL_LOAD_NUM_INVITED = 5;
|
2017-09-22 17:01:14 +01:00
|
|
|
const SHOW_MORE_INCREMENT = 100;
|
2016-01-15 15:18:55 +00:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
interface IProps {
|
|
|
|
|
roomId: string;
|
2021-11-08 10:57:14 +01:00
|
|
|
searchQuery: string;
|
2021-06-23 13:45:55 +02:00
|
|
|
onClose(): void;
|
2021-11-08 10:57:14 +01:00
|
|
|
onSearchQueryChanged: (query: string) => void;
|
2021-06-23 13:45:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IState {
|
|
|
|
|
loading: boolean;
|
|
|
|
|
filteredJoinedMembers: Array<RoomMember>;
|
|
|
|
|
filteredInvitedMembers: Array<RoomMember | MatrixEvent>;
|
|
|
|
|
canInvite: boolean;
|
|
|
|
|
truncateAtJoined: number;
|
|
|
|
|
truncateAtInvited: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default class MemberList extends React.Component<IProps, IState> {
|
2023-03-01 15:59:27 +00:00
|
|
|
private readonly showPresence: boolean;
|
2021-06-23 13:45:55 +02:00
|
|
|
private mounted = false;
|
|
|
|
|
|
2022-12-16 12:29:59 +00:00
|
|
|
public static contextType = SDKContext;
|
2022-11-18 19:05:00 +00:00
|
|
|
public context!: React.ContextType<typeof SDKContext>;
|
|
|
|
|
|
2022-12-16 12:29:59 +00:00
|
|
|
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {
|
2020-08-29 12:14:16 +01:00
|
|
|
super(props);
|
2022-11-18 19:05:00 +00:00
|
|
|
this.state = this.getMembersState([], []);
|
2023-04-04 11:41:46 +01:00
|
|
|
this.showPresence = context?.memberListStore.isPresenceEnabled() ?? true;
|
2021-06-23 13:45:55 +02:00
|
|
|
this.mounted = true;
|
2022-11-18 19:05:00 +00:00
|
|
|
this.listenForMembersChanges();
|
2020-08-29 12:51:37 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private listenForMembersChanges(): void {
|
2023-06-15 08:46:19 +01:00
|
|
|
const cli = MatrixClientPeg.safeGet();
|
2022-02-24 14:39:25 +00:00
|
|
|
cli.on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
2022-02-22 12:18:08 +00:00
|
|
|
cli.on(RoomMemberEvent.Name, this.onRoomMemberName);
|
|
|
|
|
cli.on(RoomStateEvent.Events, this.onRoomStateEvent);
|
2016-07-14 10:05:40 +01:00
|
|
|
// We listen for changes to the lastPresenceTs which is essentially
|
|
|
|
|
// listening for all presence events (we display most of not all of
|
|
|
|
|
// the information contained in presence events).
|
2022-02-22 12:18:08 +00:00
|
|
|
cli.on(UserEvent.LastPresenceTs, this.onUserPresenceChange);
|
|
|
|
|
cli.on(UserEvent.Presence, this.onUserPresenceChange);
|
|
|
|
|
cli.on(UserEvent.CurrentlyActive, this.onUserPresenceChange);
|
2022-11-18 19:05:00 +00:00
|
|
|
cli.on(ClientEvent.Room, this.onRoom); // invites & joining after peek
|
|
|
|
|
cli.on(RoomEvent.MyMembership, this.onMyMembership);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-16 12:29:59 +00:00
|
|
|
public componentDidMount(): void {
|
2022-11-18 19:05:00 +00:00
|
|
|
this.updateListNow(true);
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2018-08-29 11:09:55 +02:00
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
public componentWillUnmount(): void {
|
2021-06-23 13:45:55 +02:00
|
|
|
this.mounted = false;
|
2017-10-11 17:56:17 +01:00
|
|
|
const cli = MatrixClientPeg.get();
|
2016-04-18 01:35:40 +01:00
|
|
|
if (cli) {
|
2022-02-24 14:39:25 +00:00
|
|
|
cli.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
|
2022-02-22 12:18:08 +00:00
|
|
|
cli.removeListener(RoomMemberEvent.Name, this.onRoomMemberName);
|
|
|
|
|
cli.removeListener(RoomEvent.MyMembership, this.onMyMembership);
|
|
|
|
|
cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvent);
|
|
|
|
|
cli.removeListener(ClientEvent.Room, this.onRoom);
|
|
|
|
|
cli.removeListener(UserEvent.LastPresenceTs, this.onUserPresenceChange);
|
|
|
|
|
cli.removeListener(UserEvent.Presence, this.onUserPresenceChange);
|
|
|
|
|
cli.removeListener(UserEvent.CurrentlyActive, this.onUserPresenceChange);
|
2015-06-22 11:42:09 +01:00
|
|
|
}
|
2016-08-10 13:39:47 +01:00
|
|
|
|
|
|
|
|
// cancel any pending calls to the rate_limited_funcs
|
2021-07-01 18:35:38 +01:00
|
|
|
this.updateList.cancel();
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2015-06-22 11:42:09 +01:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private get canInvite(): boolean {
|
2023-06-15 08:46:19 +01:00
|
|
|
const cli = MatrixClientPeg.safeGet();
|
2021-05-19 17:46:10 +05:30
|
|
|
const room = cli.getRoom(this.props.roomId);
|
2021-09-29 14:04:02 +01:00
|
|
|
|
2023-03-13 15:07:20 +00:00
|
|
|
return (
|
|
|
|
|
!!room?.canInvite(cli.getSafeUserId()) || !!(room?.isSpaceRoom() && room.getJoinRule() === JoinRule.Public)
|
|
|
|
|
);
|
2021-05-19 17:46:10 +05:30
|
|
|
}
|
|
|
|
|
|
2022-11-18 19:05:00 +00:00
|
|
|
private getMembersState(invitedMembers: Array<RoomMember>, joinedMembers: Array<RoomMember>): IState {
|
2018-09-03 11:13:56 +02:00
|
|
|
return {
|
|
|
|
|
loading: false,
|
2022-11-18 19:05:00 +00:00
|
|
|
filteredJoinedMembers: joinedMembers,
|
|
|
|
|
filteredInvitedMembers: invitedMembers,
|
2021-05-19 17:46:10 +05:30
|
|
|
canInvite: this.canInvite,
|
2018-09-03 11:13:56 +02:00
|
|
|
|
|
|
|
|
// ideally we'd size this to the page height, but
|
|
|
|
|
// in practice I find that a little constraining
|
|
|
|
|
truncateAtJoined: INITIAL_LOAD_NUM_MEMBERS,
|
|
|
|
|
truncateAtInvited: INITIAL_LOAD_NUM_INVITED,
|
|
|
|
|
};
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2018-09-03 11:13:56 +02:00
|
|
|
|
2023-05-12 09:49:37 +01:00
|
|
|
private onUserPresenceChange = (event: MatrixEvent | undefined, user: User): void => {
|
2015-09-18 18:39:16 +01:00
|
|
|
// Attach a SINGLE listener for global presence changes then locate the
|
|
|
|
|
// member tile and re-render it. This is more efficient than every tile
|
2018-12-20 14:56:18 -07:00
|
|
|
// ever attaching their own listener.
|
2017-10-11 17:56:17 +01:00
|
|
|
const tile = this.refs[user.userId];
|
2016-04-18 01:35:40 +01:00
|
|
|
if (tile) {
|
2021-06-23 13:45:55 +02:00
|
|
|
this.updateList(); // reorder the membership list
|
2015-09-18 18:39:16 +01:00
|
|
|
}
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2016-01-20 14:41:48 +00:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private onRoom = (room: Room): void => {
|
2015-09-18 18:39:16 +01:00
|
|
|
if (room.roomId !== this.props.roomId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// We listen for room events because when we accept an invite
|
|
|
|
|
// we need to wait till the room is fully populated with state
|
|
|
|
|
// before refreshing the member list else we get a stale list.
|
2022-11-18 19:05:00 +00:00
|
|
|
this.updateListNow(true);
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2018-09-13 18:43:24 +02:00
|
|
|
|
2023-05-12 09:49:37 +01:00
|
|
|
private onMyMembership = (room: Room, membership: string, oldMembership?: string): void => {
|
2022-11-18 19:05:00 +00:00
|
|
|
if (room.roomId === this.props.roomId && membership === "join" && oldMembership !== "join") {
|
|
|
|
|
// we just joined the room, load the member list
|
|
|
|
|
this.updateListNow(true);
|
2018-09-13 18:43:24 +02:00
|
|
|
}
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2015-09-18 18:39:16 +01:00
|
|
|
|
2022-02-24 14:39:25 +00:00
|
|
|
private onRoomStateUpdate = (state: RoomState): void => {
|
|
|
|
|
if (state.roomId !== this.props.roomId) return;
|
2021-06-23 13:45:55 +02:00
|
|
|
this.updateList();
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2015-09-18 18:39:16 +01:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private onRoomMemberName = (ev: MatrixEvent, member: RoomMember): void => {
|
2018-09-07 14:05:26 +02:00
|
|
|
if (member.roomId !== this.props.roomId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-23 13:45:55 +02:00
|
|
|
this.updateList();
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2015-10-28 15:15:35 +00:00
|
|
|
|
2022-02-24 14:39:25 +00:00
|
|
|
private onRoomStateEvent = (event: MatrixEvent): void => {
|
|
|
|
|
if (event.getRoomId() === this.props.roomId && event.getType() === EventType.RoomThirdPartyInvite) {
|
2021-06-23 13:45:55 +02:00
|
|
|
this.updateList();
|
2016-01-22 15:11:36 +00:00
|
|
|
}
|
2021-05-19 14:31:04 +05:30
|
|
|
|
2021-05-19 17:46:10 +05:30
|
|
|
if (this.canInvite !== this.state.canInvite) this.setState({ canInvite: this.canInvite });
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2016-01-22 15:11:36 +00:00
|
|
|
|
2021-07-01 18:35:38 +01:00
|
|
|
private updateList = throttle(
|
|
|
|
|
() => {
|
2022-11-18 19:05:00 +00:00
|
|
|
this.updateListNow(false);
|
2021-07-01 18:35:38 +01:00
|
|
|
},
|
|
|
|
|
500,
|
|
|
|
|
{ leading: true, trailing: true },
|
|
|
|
|
);
|
2019-01-03 21:55:52 -07:00
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
// XXX: exported for tests
|
|
|
|
|
public async updateListNow(showLoadingSpinner?: boolean): Promise<void> {
|
2022-11-18 19:05:00 +00:00
|
|
|
if (!this.mounted) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (showLoadingSpinner) {
|
|
|
|
|
this.setState({ loading: true });
|
|
|
|
|
}
|
|
|
|
|
const { joined, invited } = await this.context.memberListStore.loadMemberList(
|
|
|
|
|
this.props.roomId,
|
|
|
|
|
this.props.searchQuery,
|
|
|
|
|
);
|
|
|
|
|
if (!this.mounted) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-23 13:45:55 +02:00
|
|
|
this.setState({
|
2018-09-13 18:43:24 +02:00
|
|
|
loading: false,
|
2022-11-18 19:05:00 +00:00
|
|
|
filteredJoinedMembers: joined,
|
|
|
|
|
filteredInvitedMembers: invited,
|
2021-06-23 13:45:55 +02:00
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2015-06-24 13:48:39 +01:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private createOverflowTileJoined = (overflowCount: number, totalCount: number): JSX.Element => {
|
|
|
|
|
return this.createOverflowTile(overflowCount, totalCount, this.showMoreJoinedMemberList);
|
2020-09-04 09:24:12 +01:00
|
|
|
};
|
2017-09-22 16:50:54 +01:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private createOverflowTileInvited = (overflowCount: number, totalCount: number): JSX.Element => {
|
|
|
|
|
return this.createOverflowTile(overflowCount, totalCount, this.showMoreInvitedMemberList);
|
2020-09-04 09:24:12 +01:00
|
|
|
};
|
2017-09-22 16:50:54 +01:00
|
|
|
|
2022-01-10 12:57:20 +00:00
|
|
|
private createOverflowTile = (overflowCount: number, totalCount: number, onClick: () => void): JSX.Element => {
|
2016-01-21 11:41:28 +00:00
|
|
|
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
2017-07-25 15:56:16 +01:00
|
|
|
const text = _t("and %(count)s others...", { count: overflowCount });
|
2016-01-21 11:41:28 +00:00
|
|
|
return (
|
2021-07-23 10:23:45 +01:00
|
|
|
<EntityTile
|
|
|
|
|
className="mx_EntityTile_ellipsis"
|
|
|
|
|
avatarJsx={
|
2023-08-24 04:48:35 +01:00
|
|
|
<BaseAvatar url={require("../../../../res/img/ellipsis.svg").default} name="..." size="36px" />
|
2021-07-23 10:23:45 +01:00
|
|
|
}
|
|
|
|
|
name={text}
|
|
|
|
|
presenceState="online"
|
|
|
|
|
suppressOnHover={true}
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
/>
|
2016-01-21 11:41:28 +00:00
|
|
|
);
|
2020-09-04 09:24:12 +01:00
|
|
|
};
|
2015-11-30 15:13:28 +00:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private showMoreJoinedMemberList = (): void => {
|
2016-01-21 15:57:59 +00:00
|
|
|
this.setState({
|
2017-09-22 17:01:14 +01:00
|
|
|
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
|
2017-09-22 16:50:54 +01:00
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2017-09-22 16:50:54 +01:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private showMoreInvitedMemberList = (): void => {
|
2017-09-22 16:50:54 +01:00
|
|
|
this.setState({
|
2017-09-22 17:01:14 +01:00
|
|
|
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
|
2016-01-21 15:57:59 +00:00
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2015-11-30 15:13:28 +00:00
|
|
|
|
2022-11-18 19:05:00 +00:00
|
|
|
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>, snapshot?: any): void {
|
|
|
|
|
if (prevProps.searchQuery !== this.props.searchQuery) {
|
|
|
|
|
this.updateListNow(false);
|
2018-12-20 14:56:18 -07:00
|
|
|
}
|
2022-11-18 19:05:00 +00:00
|
|
|
}
|
2015-11-30 15:13:28 +00:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private onSearchQueryChanged = (searchQuery: string): void => {
|
2021-11-08 10:57:14 +01:00
|
|
|
this.props.onSearchQueryChanged(searchQuery);
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2016-01-19 14:51:26 +00:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private onPending3pidInviteClick = (inviteEvent: MatrixEvent): void => {
|
2019-03-28 20:38:15 -06:00
|
|
|
dis.dispatch({
|
|
|
|
|
action: "view_3pid_invite",
|
|
|
|
|
event: inviteEvent,
|
|
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
2019-03-28 20:38:15 -06:00
|
|
|
|
2023-04-04 11:41:46 +01:00
|
|
|
private getPending3PidInvites(): MatrixEvent[] {
|
2018-01-25 12:18:02 +00:00
|
|
|
// include 3pid invites (m.room.third_party_invite) state events.
|
|
|
|
|
// The HS may have already converted these into m.room.member invites so
|
|
|
|
|
// we shouldn't add them if the 3pid invite state key (token) is in the
|
|
|
|
|
// member invite (content.third_party_invite.signed.token)
|
2023-06-15 08:46:19 +01:00
|
|
|
const room = MatrixClientPeg.safeGet().getRoom(this.props.roomId);
|
2018-01-25 12:18:02 +00:00
|
|
|
|
|
|
|
|
if (room) {
|
|
|
|
|
return room.currentState.getStateEvents("m.room.third_party_invite").filter(function (e) {
|
2019-03-29 11:45:07 -06:00
|
|
|
if (!isValid3pidInvite(e)) return false;
|
2018-01-25 12:18:02 +00:00
|
|
|
|
|
|
|
|
// discard all invites which have a m.room.member event since we've
|
|
|
|
|
// already added them.
|
2023-04-04 11:41:46 +01:00
|
|
|
const memberEvent = room.currentState.getInviteForThreePidToken(e.getStateKey()!);
|
2018-01-25 12:18:02 +00:00
|
|
|
if (memberEvent) return false;
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-04-04 11:41:46 +01:00
|
|
|
|
|
|
|
|
return [];
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2018-01-25 12:18:02 +00:00
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
private makeMemberTiles(members: Array<RoomMember | MatrixEvent>): JSX.Element[] {
|
2019-07-03 08:38:08 +01:00
|
|
|
return members.map((m) => {
|
2021-06-23 13:45:55 +02:00
|
|
|
if (m instanceof RoomMember) {
|
2019-07-03 08:38:08 +01:00
|
|
|
// Is a Matrix invite
|
2021-06-23 13:45:55 +02:00
|
|
|
return <MemberTile key={m.userId} member={m} ref={m.userId} showPresence={this.showPresence} />;
|
2019-07-03 08:38:08 +01:00
|
|
|
} else {
|
|
|
|
|
// Is a 3pid invite
|
2021-07-23 10:23:45 +01:00
|
|
|
return (
|
|
|
|
|
<EntityTile
|
|
|
|
|
key={m.getStateKey()}
|
|
|
|
|
name={m.getContent().display_name}
|
|
|
|
|
suppressOnHover={true}
|
|
|
|
|
onClick={() => this.onPending3pidInviteClick(m)}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2019-07-03 08:38:08 +01:00
|
|
|
}
|
2015-11-30 15:13:28 +00:00
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2015-11-30 15:13:28 +00:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private getChildrenJoined = (start: number, end: number): Array<JSX.Element> => {
|
2021-06-29 13:11:58 +01:00
|
|
|
return this.makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end));
|
2020-08-29 12:51:37 +01:00
|
|
|
};
|
2017-09-22 16:50:54 +01:00
|
|
|
|
2021-06-23 13:45:55 +02:00
|
|
|
private getChildCountJoined = (): number => this.state.filteredJoinedMembers.length;
|
|
|
|
|
|
|
|
|
|
private getChildrenInvited = (start: number, end: number): Array<JSX.Element> => {
|
|
|
|
|
let targets = this.state.filteredInvitedMembers;
|
|
|
|
|
if (end > this.state.filteredInvitedMembers.length) {
|
|
|
|
|
targets = targets.concat(this.getPending3PidInvites());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.makeMemberTiles(targets.slice(start, end));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private getChildCountInvited = (): number => {
|
|
|
|
|
return this.state.filteredInvitedMembers.length + (this.getPending3PidInvites() || []).length;
|
2021-06-29 13:11:58 +01:00
|
|
|
};
|
2017-09-22 16:50:54 +01:00
|
|
|
|
2023-02-13 17:01:43 +00:00
|
|
|
public render(): React.ReactNode {
|
2018-08-29 10:30:25 +02:00
|
|
|
if (this.state.loading) {
|
2020-09-08 10:19:51 +01:00
|
|
|
return (
|
|
|
|
|
<BaseCard className="mx_MemberList" onClose={this.props.onClose}>
|
|
|
|
|
<Spinner />
|
|
|
|
|
</BaseCard>
|
|
|
|
|
);
|
2018-08-29 10:30:25 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-15 08:46:19 +01:00
|
|
|
const cli = MatrixClientPeg.safeGet();
|
2018-10-24 13:50:58 +02:00
|
|
|
const room = cli.getRoom(this.props.roomId);
|
2023-05-12 11:25:16 +01:00
|
|
|
let inviteButton: JSX.Element | undefined;
|
2019-05-11 19:45:24 +01:00
|
|
|
|
2021-10-08 16:04:26 -06:00
|
|
|
if (room?.getMyMembership() === "join" && shouldShowComponent(UIComponent.InviteUsers)) {
|
2020-08-31 10:12:12 -06:00
|
|
|
let inviteButtonText = _t("Invite to this room");
|
2022-03-22 17:07:37 -06:00
|
|
|
if (room.isSpaceRoom()) {
|
2021-03-02 09:51:11 +00:00
|
|
|
inviteButtonText = _t("Invite to this space");
|
2020-08-31 10:12:12 -06:00
|
|
|
}
|
|
|
|
|
|
2023-05-12 11:25:16 +01:00
|
|
|
if (this.state.canInvite) {
|
|
|
|
|
inviteButton = (
|
|
|
|
|
<AccessibleButton className="mx_MemberList_invite" onClick={this.onInviteButtonClick}>
|
|
|
|
|
<span>{inviteButtonText}</span>
|
|
|
|
|
</AccessibleButton>
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
inviteButton = (
|
|
|
|
|
<AccessibleTooltipButton
|
|
|
|
|
className="mx_MemberList_invite"
|
|
|
|
|
onClick={null}
|
|
|
|
|
disabled
|
|
|
|
|
tooltip={_t("You do not have permission to invite users")}
|
|
|
|
|
>
|
|
|
|
|
<span>{inviteButtonText}</span>
|
|
|
|
|
</AccessibleTooltipButton>
|
|
|
|
|
);
|
|
|
|
|
}
|
2015-11-30 15:13:28 +00:00
|
|
|
}
|
2016-02-08 15:05:35 +00:00
|
|
|
|
2018-10-24 13:50:58 +02:00
|
|
|
let invitedHeader;
|
|
|
|
|
let invitedSection;
|
2021-06-23 13:45:55 +02:00
|
|
|
if (this.getChildCountInvited() > 0) {
|
2018-10-24 13:50:58 +02:00
|
|
|
invitedHeader = <h2>{_t("Invited")}</h2>;
|
2021-06-23 13:45:55 +02:00
|
|
|
invitedSection = (
|
|
|
|
|
<TruncatedList
|
|
|
|
|
className="mx_MemberList_section mx_MemberList_invited"
|
|
|
|
|
truncateAt={this.state.truncateAtInvited}
|
|
|
|
|
createOverflowElement={this.createOverflowTileInvited}
|
|
|
|
|
getChildren={this.getChildrenInvited}
|
|
|
|
|
getChildCount={this.getChildCountInvited}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2018-10-24 13:50:58 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-08 10:19:51 +01:00
|
|
|
const footer = (
|
|
|
|
|
<SearchBox
|
|
|
|
|
className="mx_MemberList_query mx_textinput_icon mx_textinput_search"
|
2021-07-19 22:43:11 +01:00
|
|
|
placeholder={_t("Filter room members")}
|
2021-08-19 15:48:14 +02:00
|
|
|
onSearch={this.onSearchQueryChanged}
|
2021-11-08 10:57:14 +01:00
|
|
|
initialValue={this.props.searchQuery}
|
2021-08-19 15:48:14 +02:00
|
|
|
/>
|
2015-11-30 15:13:28 +00:00
|
|
|
);
|
2020-09-08 10:19:51 +01:00
|
|
|
|
2021-03-02 09:51:11 +00:00
|
|
|
let scopeHeader;
|
2022-03-22 17:07:37 -06:00
|
|
|
if (room?.isSpaceRoom()) {
|
2021-03-02 09:51:11 +00:00
|
|
|
scopeHeader = (
|
|
|
|
|
<div className="mx_RightPanel_scopeHeader">
|
2023-08-24 04:48:35 +01:00
|
|
|
<RoomAvatar room={room} size="32px" />
|
2021-03-02 09:51:11 +00:00
|
|
|
<RoomName room={room} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-08 10:19:51 +01:00
|
|
|
return (
|
|
|
|
|
<BaseCard
|
|
|
|
|
className="mx_MemberList"
|
2021-03-02 09:51:11 +00:00
|
|
|
header={
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
{scopeHeader}
|
|
|
|
|
{inviteButton}
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
}
|
2020-09-08 10:19:51 +01:00
|
|
|
footer={footer}
|
|
|
|
|
onClose={this.props.onClose}
|
|
|
|
|
>
|
|
|
|
|
<div className="mx_MemberList_wrapper">
|
2021-06-23 13:45:55 +02:00
|
|
|
<TruncatedList
|
|
|
|
|
className="mx_MemberList_section mx_MemberList_joined"
|
|
|
|
|
truncateAt={this.state.truncateAtJoined}
|
|
|
|
|
createOverflowElement={this.createOverflowTileJoined}
|
|
|
|
|
getChildren={this.getChildrenJoined}
|
|
|
|
|
getChildCount={this.getChildCountJoined}
|
|
|
|
|
/>
|
2020-09-08 10:19:51 +01:00
|
|
|
{invitedHeader}
|
|
|
|
|
{invitedSection}
|
|
|
|
|
</div>
|
|
|
|
|
</BaseCard>
|
|
|
|
|
);
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
2018-10-24 13:50:58 +02:00
|
|
|
|
2022-02-09 14:42:08 +00:00
|
|
|
private onInviteButtonClick = (ev: ButtonEvent): void => {
|
|
|
|
|
PosthogTrackers.trackInteraction("WebRightPanelMemberListInviteButton", ev);
|
|
|
|
|
|
2023-06-15 08:46:19 +01:00
|
|
|
if (MatrixClientPeg.safeGet().isGuest()) {
|
2021-06-29 13:11:58 +01:00
|
|
|
dis.dispatch({ action: "require_registration" });
|
2018-10-24 13:50:58 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 17:07:37 -06:00
|
|
|
// open the room inviter
|
2018-10-24 13:50:58 +02:00
|
|
|
dis.dispatch({
|
|
|
|
|
action: "view_invite",
|
|
|
|
|
roomId: this.props.roomId,
|
|
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
};
|
|
|
|
|
}
|