Delete groups (legacy communities system) (#8027)
* Remove deprecated feature_communities_v2_prototypes * Update _components * i18n * delint * Cut out a bit more dead code * Carve into legacy components * Carve into mostly the room list code * Carve into instances of "groupId" * Carve out more of what comes up with "groups" * Carve out some settings * ignore related groups state * Remove instances of spacesEnabled * Fix some obvious issues * Remove now-unused css * Fix variable naming for legacy components * Update i18n * Misc cleanup from manual review * Update snapshot for changed flag * Appease linters * rethemedex * Remove now-unused AddressPickerDialog * Make ConfirmUserActionDialog's member a required prop * Remove useless override from RightPanelStore * Remove extraneous CSS * Update i18n * Demo: "Communities are now Spaces" landing page * Restore linkify for group IDs * Demo: Dialog on click for communities->spaces notice * i18n for demos * i18n post-merge * Update copy * Appease the linter * Post-merge cleanup * Re-add spaces_learn_more_url to the new SdkConfig place * Round 1 of post-merge fixes * i18n Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
03c80707c9
commit
fce36ec826
171 changed files with 317 additions and 12160 deletions
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import HeaderButton from './HeaderButton';
|
||||
import HeaderButtons, { HeaderKind } from './HeaderButtons';
|
||||
import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases';
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
|
||||
|
||||
const GROUP_PHASES = [
|
||||
RightPanelPhases.GroupMemberInfo,
|
||||
RightPanelPhases.GroupMemberList,
|
||||
];
|
||||
const ROOM_PHASES = [
|
||||
RightPanelPhases.GroupRoomList,
|
||||
RightPanelPhases.GroupRoomInfo,
|
||||
];
|
||||
|
||||
interface IProps {}
|
||||
|
||||
@replaceableComponent("views.right_panel.GroupHeaderButtons")
|
||||
export default class GroupHeaderButtons extends HeaderButtons {
|
||||
constructor(props: IProps) {
|
||||
super(props, HeaderKind.Group);
|
||||
}
|
||||
|
||||
protected onAction(payload: ActionPayload) {
|
||||
if (payload.action === Action.ViewUser) {
|
||||
if ((payload as ViewUserPayload).member) {
|
||||
RightPanelStore.instance.setCards([
|
||||
{ phase: RightPanelPhases.GroupRoomInfo },
|
||||
{ phase: RightPanelPhases.GroupMemberList },
|
||||
{ phase: RightPanelPhases.RoomMemberInfo, state: { member: payload.member } },
|
||||
]);
|
||||
} else {
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
}
|
||||
} else if (payload.action === "view_group") {
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
} else if (payload.action === "view_group_room") {
|
||||
this.setPhase(
|
||||
RightPanelPhases.GroupRoomInfo,
|
||||
{ groupRoomId: payload.groupRoomId, groupId: payload.groupId },
|
||||
);
|
||||
} else if (payload.action === "view_group_room_list") {
|
||||
this.setPhase(RightPanelPhases.GroupRoomList);
|
||||
} else if (payload.action === "view_group_member_list") {
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
} else if (payload.action === "view_group_user") {
|
||||
this.setPhase(RightPanelPhases.GroupMemberInfo, { member: payload.member });
|
||||
}
|
||||
}
|
||||
|
||||
private onMembersClicked = () => {
|
||||
if (this.state.phase === RightPanelPhases.GroupMemberInfo) {
|
||||
// send the active phase to trigger a toggle
|
||||
this.setPhase(RightPanelPhases.GroupMemberInfo);
|
||||
} else {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RightPanelPhases.GroupMemberList);
|
||||
}
|
||||
};
|
||||
|
||||
private onRoomsClicked = () => {
|
||||
// This toggles for us, if needed
|
||||
this.setPhase(RightPanelPhases.GroupRoomList);
|
||||
};
|
||||
|
||||
renderButtons() {
|
||||
return <>
|
||||
<HeaderButton
|
||||
name="groupMembersButton"
|
||||
title={_t('Members')}
|
||||
isHighlighted={this.isPhase(GROUP_PHASES)}
|
||||
onClick={this.onMembersClicked}
|
||||
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
||||
/>
|
||||
<HeaderButton
|
||||
name="roomsButton"
|
||||
title={_t('Rooms')}
|
||||
isHighlighted={this.isPhase(ROOM_PHASES)}
|
||||
onClick={this.onRoomsClicked}
|
||||
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
||||
/>
|
||||
</>;
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ import { NotificationColor } from '../../../stores/notifications/NotificationCol
|
|||
|
||||
export enum HeaderKind {
|
||||
Room = "room",
|
||||
Group = "group",
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -91,9 +90,7 @@ export default abstract class HeaderButtons<P = {}> extends React.Component<IPro
|
|||
|
||||
private onRightPanelStoreUpdate = () => {
|
||||
if (this.unmounted) return;
|
||||
let phase = RightPanelStore.instance.currentCard.phase;
|
||||
if (!RightPanelStore.instance.isOpenForGroup) {phase = null;}
|
||||
this.setState({ phase });
|
||||
this.setState({ phase: RightPanelStore.instance.currentCard.phase });
|
||||
};
|
||||
|
||||
// XXX: Make renderButtons a prop
|
||||
|
|
|
@ -39,7 +39,6 @@ import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
|
|||
import SdkConfig from '../../../SdkConfig';
|
||||
import RoomViewStore from "../../../stores/RoomViewStore";
|
||||
import MultiInviter from "../../../utils/MultiInviter";
|
||||
import GroupStore from "../../../stores/GroupStore";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import E2EIcon from "../rooms/E2EIcon";
|
||||
import { useTypedEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
|
@ -69,7 +68,6 @@ import RoomName from "../elements/RoomName";
|
|||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import ConfirmSpaceUserActionDialog from "../dialogs/ConfirmSpaceUserActionDialog";
|
||||
import { bulkSpaceBehaviour } from "../../../utils/space";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
|
@ -753,7 +751,7 @@ const MuteToggleButton: React.FC<IBaseRoomProps> = ({ member, room, powerLevels,
|
|||
// if muting self, warn as it may be irreversible
|
||||
if (target === cli.getUserId()) {
|
||||
try {
|
||||
if (!(await warnSelfDemote(SpaceStore.spacesEnabled && room?.isSpaceRoom()))) return;
|
||||
if (!(await warnSelfDemote(room?.isSpaceRoom()))) return;
|
||||
} catch (e) {
|
||||
logger.error("Failed to warn about self demotion: ", e);
|
||||
return;
|
||||
|
@ -847,7 +845,7 @@ const RoomAdminToolsContainer: React.FC<IBaseRoomProps> = ({
|
|||
stopUpdating={stopUpdating}
|
||||
/>;
|
||||
}
|
||||
if (me.powerLevel >= redactPowerLevel && (!SpaceStore.spacesEnabled || !room.isSpaceRoom())) {
|
||||
if (me.powerLevel >= redactPowerLevel && !room.isSpaceRoom()) {
|
||||
redactButton = (
|
||||
<RedactMessagesButton member={member} startUpdating={startUpdating} stopUpdating={stopUpdating} />
|
||||
);
|
||||
|
@ -885,99 +883,6 @@ const RoomAdminToolsContainer: React.FC<IBaseRoomProps> = ({
|
|||
return <div />;
|
||||
};
|
||||
|
||||
export interface GroupMember {
|
||||
userId: string;
|
||||
displayname?: string; // XXX: GroupMember objects are inconsistent :((
|
||||
avatarUrl?: string;
|
||||
}
|
||||
|
||||
const GroupAdminToolsSection: React.FC<{
|
||||
groupId: string;
|
||||
groupMember: GroupMember;
|
||||
startUpdating(): void;
|
||||
stopUpdating(): void;
|
||||
}> = ({ children, groupId, groupMember, startUpdating, stopUpdating }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
|
||||
const [isPrivileged, setIsPrivileged] = useState(false);
|
||||
const [isInvited, setIsInvited] = useState(false);
|
||||
|
||||
// Listen to group store changes
|
||||
useEffect(() => {
|
||||
let unmounted = false;
|
||||
|
||||
const onGroupStoreUpdated = () => {
|
||||
if (unmounted) return;
|
||||
setIsPrivileged(GroupStore.isUserPrivileged(groupId));
|
||||
setIsInvited(GroupStore.getGroupInvitedMembers(groupId).some(
|
||||
(m) => m.userId === groupMember.userId,
|
||||
));
|
||||
};
|
||||
|
||||
GroupStore.registerListener(groupId, onGroupStoreUpdated);
|
||||
onGroupStoreUpdated();
|
||||
// Handle unmount
|
||||
return () => {
|
||||
unmounted = true;
|
||||
GroupStore.unregisterListener(onGroupStoreUpdated);
|
||||
};
|
||||
}, [groupId, groupMember.userId]);
|
||||
|
||||
if (isPrivileged) {
|
||||
const onKick = async () => {
|
||||
const { finished } = Modal.createDialog(ConfirmUserActionDialog, {
|
||||
matrixClient: cli,
|
||||
groupMember,
|
||||
action: isInvited ? _t('Disinvite') : _t('Remove from community'),
|
||||
title: isInvited ? _t('Disinvite this user from community?')
|
||||
: _t('Remove this user from community?'),
|
||||
danger: true,
|
||||
});
|
||||
|
||||
const [proceed] = await finished;
|
||||
if (!proceed) return;
|
||||
|
||||
startUpdating();
|
||||
cli.removeUserFromGroup(groupId, groupMember.userId).then(() => {
|
||||
// return to the user list
|
||||
dis.dispatch({
|
||||
action: Action.ViewUser,
|
||||
member: null,
|
||||
});
|
||||
}).catch((e) => {
|
||||
Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
|
||||
title: _t('Error'),
|
||||
description: isInvited ?
|
||||
_t('Failed to withdraw invitation') :
|
||||
_t('Failed to remove user from community'),
|
||||
});
|
||||
logger.log(e);
|
||||
}).finally(() => {
|
||||
stopUpdating();
|
||||
});
|
||||
};
|
||||
|
||||
const kickButton = (
|
||||
<AccessibleButton className="mx_UserInfo_field mx_UserInfo_destructive" onClick={onKick}>
|
||||
{ isInvited ? _t('Disinvite') : _t('Remove from community') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
||||
// No make/revoke admin API yet
|
||||
/*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
|
||||
giveModButton = <AccessibleButton className="mx_UserInfo_field" onClick={this.onModToggle}>
|
||||
{giveOpLabel}
|
||||
</AccessibleButton>;*/
|
||||
|
||||
return <GenericAdminToolsContainer>
|
||||
{ kickButton }
|
||||
{ children }
|
||||
</GenericAdminToolsContainer>;
|
||||
}
|
||||
|
||||
return <div />;
|
||||
};
|
||||
|
||||
const useIsSynapseAdmin = (cli: MatrixClient) => {
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
useEffect(() => {
|
||||
|
@ -1135,7 +1040,7 @@ const PowerLevelEditor: React.FC<{
|
|||
} else if (myUserId === target) {
|
||||
// If we are changing our own PL it can only ever be decreasing, which we cannot reverse.
|
||||
try {
|
||||
if (!(await warnSelfDemote(SpaceStore.spacesEnabled && room?.isSpaceRoom()))) return;
|
||||
if (!(await warnSelfDemote(room?.isSpaceRoom()))) return;
|
||||
} catch (e) {
|
||||
logger.error("Failed to warn about self demotion: ", e);
|
||||
}
|
||||
|
@ -1233,10 +1138,9 @@ export const useDevices = (userId: string) => {
|
|||
const BasicUserInfo: React.FC<{
|
||||
room: Room;
|
||||
member: User | RoomMember;
|
||||
groupId: string;
|
||||
devices: IDevice[];
|
||||
isRoomEncrypted: boolean;
|
||||
}> = ({ room, member, groupId, devices, isRoomEncrypted }) => {
|
||||
}> = ({ room, member, devices, isRoomEncrypted }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
|
||||
const powerLevels = useRoomPowerLevels(cli, room);
|
||||
|
@ -1338,16 +1242,6 @@ const BasicUserInfo: React.FC<{
|
|||
{ synapseDeactivateButton }
|
||||
</RoomAdminToolsContainer>
|
||||
);
|
||||
} else if (groupId) {
|
||||
adminToolsContainer = (
|
||||
<GroupAdminToolsSection
|
||||
groupId={groupId}
|
||||
groupMember={member}
|
||||
startUpdating={startUpdating}
|
||||
stopUpdating={stopUpdating}>
|
||||
{ synapseDeactivateButton }
|
||||
</GroupAdminToolsSection>
|
||||
);
|
||||
} else if (synapseDeactivateButton) {
|
||||
adminToolsContainer = (
|
||||
<GenericAdminToolsContainer>
|
||||
|
@ -1367,10 +1261,10 @@ const BasicUserInfo: React.FC<{
|
|||
if (!isRoomEncrypted) {
|
||||
if (!cryptoEnabled) {
|
||||
text = _t("This client does not support end-to-end encryption.");
|
||||
} else if (room && (!SpaceStore.spacesEnabled || !room.isSpaceRoom())) {
|
||||
} else if (room && !room.isSpaceRoom()) {
|
||||
text = _t("Messages in this room are not end-to-end encrypted.");
|
||||
}
|
||||
} else if (!SpaceStore.spacesEnabled || !room.isSpaceRoom()) {
|
||||
} else if (!room.isSpaceRoom()) {
|
||||
text = _t("Messages in this room are end-to-end encrypted.");
|
||||
}
|
||||
|
||||
|
@ -1452,7 +1346,7 @@ const BasicUserInfo: React.FC<{
|
|||
canInvite={roomPermissions.canInvite}
|
||||
isIgnored={isIgnored}
|
||||
member={member as RoomMember}
|
||||
isSpace={SpaceStore.spacesEnabled && room?.isSpaceRoom()}
|
||||
isSpace={room?.isSpaceRoom()}
|
||||
/>
|
||||
|
||||
{ adminToolsContainer }
|
||||
|
@ -1461,7 +1355,7 @@ const BasicUserInfo: React.FC<{
|
|||
</React.Fragment>;
|
||||
};
|
||||
|
||||
export type Member = User | RoomMember | GroupMember;
|
||||
export type Member = User | RoomMember;
|
||||
|
||||
const UserInfoHeader: React.FC<{
|
||||
member: Member;
|
||||
|
@ -1540,7 +1434,7 @@ const UserInfoHeader: React.FC<{
|
|||
e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />;
|
||||
}
|
||||
|
||||
const displayName = (member as RoomMember).rawDisplayName || (member as GroupMember).displayname;
|
||||
const displayName = (member as RoomMember).rawDisplayName;
|
||||
return <React.Fragment>
|
||||
{ avatarElement }
|
||||
|
||||
|
@ -1566,10 +1460,8 @@ const UserInfoHeader: React.FC<{
|
|||
|
||||
interface IProps {
|
||||
user: Member;
|
||||
groupId?: string;
|
||||
room?: Room;
|
||||
phase: RightPanelPhases.RoomMemberInfo
|
||||
| RightPanelPhases.GroupMemberInfo
|
||||
| RightPanelPhases.SpaceMemberInfo
|
||||
| RightPanelPhases.EncryptionPanel;
|
||||
onClose(): void;
|
||||
|
@ -1579,7 +1471,6 @@ interface IProps {
|
|||
|
||||
const UserInfo: React.FC<IProps> = ({
|
||||
user,
|
||||
groupId,
|
||||
room,
|
||||
onClose,
|
||||
phase = RightPanelPhases.RoomMemberInfo,
|
||||
|
@ -1601,10 +1492,10 @@ const UserInfo: React.FC<IProps> = ({
|
|||
const classes = ["mx_UserInfo"];
|
||||
|
||||
let cardState: IRightPanelCardState;
|
||||
// We have no previousPhase for when viewing a UserInfo from a Group or without a Room at this time
|
||||
// We have no previousPhase for when viewing a UserInfo without a Room at this time
|
||||
if (room && phase === RightPanelPhases.EncryptionPanel) {
|
||||
cardState = { member };
|
||||
} else if (room?.isSpaceRoom() && SpaceStore.spacesEnabled) {
|
||||
} else if (room?.isSpaceRoom()) {
|
||||
cardState = { spaceId: room.roomId };
|
||||
}
|
||||
|
||||
|
@ -1615,13 +1506,11 @@ const UserInfo: React.FC<IProps> = ({
|
|||
let content;
|
||||
switch (phase) {
|
||||
case RightPanelPhases.RoomMemberInfo:
|
||||
case RightPanelPhases.GroupMemberInfo:
|
||||
case RightPanelPhases.SpaceMemberInfo:
|
||||
content = (
|
||||
<BasicUserInfo
|
||||
room={room}
|
||||
member={member as User}
|
||||
groupId={groupId as string}
|
||||
devices={devices}
|
||||
isRoomEncrypted={isRoomEncrypted}
|
||||
/>
|
||||
|
@ -1649,7 +1538,7 @@ const UserInfo: React.FC<IProps> = ({
|
|||
}
|
||||
|
||||
let scopeHeader;
|
||||
if (SpaceStore.spacesEnabled && room?.isSpaceRoom()) {
|
||||
if (room?.isSpaceRoom()) {
|
||||
scopeHeader = <div data-test-id='space-header' className="mx_RightPanel_scopeHeader">
|
||||
<RoomAvatar room={room} height={32} width={32} />
|
||||
<RoomName room={room} />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue