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:
parent
8ec7e7c714
commit
26b18811ce
4 changed files with 58 additions and 9 deletions
|
@ -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 });
|
||||||
|
|
||||||
|
|
|
@ -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 = (
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue