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.
This commit is contained in:
Travis Ralston 2020-09-18 16:04:19 -06:00
parent 8ec7e7c714
commit 26b18811ce
4 changed files with 58 additions and 9 deletions

View file

@ -80,6 +80,8 @@ import { leaveRoomBehaviour } from "../../utils/membership";
import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog"; import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog";
import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore"; import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore";
import {UIFeature} from "../../settings/UIFeature"; import {UIFeature} from "../../settings/UIFeature";
import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore";
import GroupStore from "../../stores/GroupStore";
/** constants for MatrixChat.state.view */ /** constants for MatrixChat.state.view */
export enum Views { export enum Views {
@ -1016,6 +1018,18 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
private async createRoom(defaultPublic = false) { 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 CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, { defaultPublic }); const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, { defaultPublic });

View file

@ -343,6 +343,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
let secondarySection = null; let secondarySection = null;
if (prototypeCommunityName) { if (prototypeCommunityName) {
const communityId = CommunityPrototypeStore.instance.getSelectedCommunityId();
primaryHeader = ( primaryHeader = (
<div className="mx_UserMenu_contextMenu_name"> <div className="mx_UserMenu_contextMenu_name">
<span className="mx_UserMenu_contextMenu_displayName"> <span className="mx_UserMenu_contextMenu_displayName">
@ -350,24 +351,36 @@ export default class UserMenu extends React.Component<IProps, IState> {
</span> </span>
</div> </div>
); );
primaryOptionList = ( let settingsOption;
<IconizedContextMenuOptionList> let inviteOption;
if (CommunityPrototypeStore.instance.canInviteTo(communityId)) {
inviteOption = (
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconInvite"
label={_t("Invite")}
onClick={this.onCommunityInviteClick}
/>
);
}
if (CommunityPrototypeStore.instance.isAdminOf(communityId)) {
settingsOption = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings" iconClassName="mx_UserMenu_iconSettings"
label={_t("Settings")} label={_t("Settings")}
aria-label={_t("Community settings")} aria-label={_t("Community settings")}
onClick={this.onCommunitySettingsClick} onClick={this.onCommunitySettingsClick}
/> />
);
}
primaryOptionList = (
<IconizedContextMenuOptionList>
{settingsOption}
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_UserMenu_iconMembers" iconClassName="mx_UserMenu_iconMembers"
label={_t("Members")} label={_t("Members")}
onClick={this.onCommunityMembersClick} onClick={this.onCommunityMembersClick}
/> />
<IconizedContextMenuOption {inviteOption}
iconClassName="mx_UserMenu_iconInvite"
label={_t("Invite")}
onClick={this.onCommunityInviteClick}
/>
</IconizedContextMenuOptionList> </IconizedContextMenuOptionList>
); );
secondarySection = ( secondarySection = (

View file

@ -2057,6 +2057,8 @@
"Create a Group Chat": "Create a Group Chat", "Create a Group Chat": "Create a Group Chat",
"Explore rooms": "Explore rooms", "Explore rooms": "Explore rooms",
"Failed to reject invitation": "Failed to reject invitation", "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.", "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'?", "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", "Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",

View file

@ -24,9 +24,9 @@ import * as utils from "matrix-js-sdk/src/utils";
import { UPDATE_EVENT } from "./AsyncStore"; import { UPDATE_EVENT } from "./AsyncStore";
import FlairStore from "./FlairStore"; import FlairStore from "./FlairStore";
import TagOrderStore from "./TagOrderStore"; import TagOrderStore from "./TagOrderStore";
import { MatrixClientPeg } from "../MatrixClientPeg";
import GroupStore from "./GroupStore"; import GroupStore from "./GroupStore";
import dis from "../dispatcher/dispatcher"; import dis from "../dispatcher/dispatcher";
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
interface IState { interface IState {
// nothing of value - we use account data // nothing of value - we use account data
@ -77,7 +77,7 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
public getGeneralChat(communityId: string): Room { public getGeneralChat(communityId: string): Room {
const rooms = GroupStore.getGroupRooms(communityId) const rooms = GroupStore.getGroupRooms(communityId)
.map(r => MatrixClientPeg.get().getRoom(r.roomId)) .map(r => this.matrixClient.getRoom(r.roomId))
.filter(r => !!r); .filter(r => !!r);
let chat = rooms.find(r => { let chat = rooms.find(r => {
const idState = r.currentState.getStateEvents("im.vector.general_chat", ""); const idState = r.currentState.getStateEvents("im.vector.general_chat", "");
@ -88,6 +88,26 @@ export class CommunityPrototypeStore extends AsyncStoreWithClient<IState> {
return chat; // can be null 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<any> { protected async onAction(payload: ActionPayload): Promise<any> {
if (!this.matrixClient || !SettingsStore.getValue("feature_communities_v2_prototypes")) { if (!this.matrixClient || !SettingsStore.getValue("feature_communities_v2_prototypes")) {
return; return;