From 26b18811ce9bfa2e86e743c221ae79be81238d26 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Sep 2020 16:04:19 -0600 Subject: [PATCH 1/2] Add some permission checks to the communities v2 prototype Prototype behaviour: * If you can't create a room in the community, say so. * The UX for this could probably be improved, but for now the intention is to not break muscle memory by hiding the create room option. * If you can't change settings in the community, or can't invite people, don't show those respective options. * Breaking muscle memory here is moderately okay. --- src/components/structures/MatrixChat.tsx | 14 ++++++++++++ src/components/structures/UserMenu.tsx | 27 ++++++++++++++++++------ src/i18n/strings/en_EN.json | 2 ++ src/stores/CommunityPrototypeStore.ts | 24 +++++++++++++++++++-- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index ea1f424af6..1fdb96a971 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -80,6 +80,8 @@ import { leaveRoomBehaviour } from "../../utils/membership"; import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog"; import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore"; import {UIFeature} from "../../settings/UIFeature"; +import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore"; +import GroupStore from "../../stores/GroupStore"; /** constants for MatrixChat.state.view */ export enum Views { @@ -1016,6 +1018,18 @@ export default class MatrixChat extends React.PureComponent { } private async createRoom(defaultPublic = false) { + const communityId = CommunityPrototypeStore.instance.getSelectedCommunityId(); + if (communityId) { + // double check the user will have permission to associate this room with the community + if (CommunityPrototypeStore.instance.isAdminOf(communityId)) { + Modal.createTrackedDialog('Pre-failure to create room', '', ErrorDialog, { + title: _t("Cannot create rooms in this community"), + description: _t("You do not have permission to create rooms in this community."), + }); + return; + } + } + const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog'); const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, { defaultPublic }); diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index 369d3b7720..17523290b9 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -343,6 +343,7 @@ export default class UserMenu extends React.Component { let secondarySection = null; if (prototypeCommunityName) { + const communityId = CommunityPrototypeStore.instance.getSelectedCommunityId(); primaryHeader = (
@@ -350,24 +351,36 @@ export default class UserMenu extends React.Component {
); - primaryOptionList = ( - + let settingsOption; + let inviteOption; + if (CommunityPrototypeStore.instance.canInviteTo(communityId)) { + inviteOption = ( + + ); + } + if (CommunityPrototypeStore.instance.isAdminOf(communityId)) { + settingsOption = ( + ); + } + primaryOptionList = ( + + {settingsOption} - + {inviteOption} ); secondarySection = ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d7360430ae..0cd0c6bc7b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2057,6 +2057,8 @@ "Create a Group Chat": "Create a Group Chat", "Explore rooms": "Explore rooms", "Failed to reject invitation": "Failed to reject invitation", + "Cannot create rooms in this community": "Cannot create rooms in this community", + "You do not have permission to create rooms in this community.": "You do not have permission to create rooms in this community.", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s", diff --git a/src/stores/CommunityPrototypeStore.ts b/src/stores/CommunityPrototypeStore.ts index db747d105c..4ff859d4fe 100644 --- a/src/stores/CommunityPrototypeStore.ts +++ b/src/stores/CommunityPrototypeStore.ts @@ -24,9 +24,9 @@ import * as utils from "matrix-js-sdk/src/utils"; import { UPDATE_EVENT } from "./AsyncStore"; import FlairStore from "./FlairStore"; import TagOrderStore from "./TagOrderStore"; -import { MatrixClientPeg } from "../MatrixClientPeg"; import GroupStore from "./GroupStore"; import dis from "../dispatcher/dispatcher"; +import { isNullOrUndefined } from "matrix-js-sdk/src/utils"; interface IState { // nothing of value - we use account data @@ -77,7 +77,7 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient { public getGeneralChat(communityId: string): Room { const rooms = GroupStore.getGroupRooms(communityId) - .map(r => MatrixClientPeg.get().getRoom(r.roomId)) + .map(r => this.matrixClient.getRoom(r.roomId)) .filter(r => !!r); let chat = rooms.find(r => { const idState = r.currentState.getStateEvents("im.vector.general_chat", ""); @@ -88,6 +88,26 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient { return chat; // can be null } + public isAdminOf(communityId: string): boolean { + const members = GroupStore.getGroupMembers(communityId); + const myMember = members.find(m => m.userId === this.matrixClient.getUserId()); + return myMember?.isPrivileged; + } + + public canInviteTo(communityId: string): boolean { + const generalChat = this.getGeneralChat(communityId); + if (!generalChat) return this.isAdminOf(communityId); + + const myMember = generalChat.getMember(this.matrixClient.getUserId()); + if (!myMember) return this.isAdminOf(communityId); + + const pl = generalChat.currentState.getStateEvents("m.room.power_levels", ""); + if (!pl) return this.isAdminOf(communityId); + + const invitePl = isNullOrUndefined(pl.invite) ? 50 : Number(pl.invite); + return invitePl <= myMember.powerLevel; + } + protected async onAction(payload: ActionPayload): Promise { if (!this.matrixClient || !SettingsStore.getValue("feature_communities_v2_prototypes")) { return; From d4c14b33997ddf524c20f8dee34be8eb009b3c79 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Sep 2020 16:11:18 -0600 Subject: [PATCH 2/2] Don't import things we don't use --- src/components/structures/MatrixChat.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 1fdb96a971..3f4b3115af 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -81,7 +81,6 @@ import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityProt import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore"; import {UIFeature} from "../../settings/UIFeature"; import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore"; -import GroupStore from "../../stores/GroupStore"; /** constants for MatrixChat.state.view */ export enum Views {