Pull in changes from develop
This commit is contained in:
commit
e2cedbe9d7
15 changed files with 453 additions and 133 deletions
|
@ -29,6 +29,7 @@ import classnames from 'classnames';
|
|||
|
||||
import GroupStoreCache from '../../stores/GroupStoreCache';
|
||||
import GroupStore from '../../stores/GroupStore';
|
||||
import FlairStore from '../../stores/FlairStore';
|
||||
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||
import {makeGroupPermalink, makeUserPermalink} from "../../matrix-to";
|
||||
|
@ -429,6 +430,7 @@ export default React.createClass({
|
|||
editing: false,
|
||||
saving: false,
|
||||
uploadingAvatar: false,
|
||||
avatarChanged: false,
|
||||
membershipBusy: false,
|
||||
publicityBusy: false,
|
||||
inviterProfile: null,
|
||||
|
@ -590,6 +592,10 @@ export default React.createClass({
|
|||
this.setState({
|
||||
uploadingAvatar: false,
|
||||
profileForm: newProfileForm,
|
||||
|
||||
// Indicate that FlairStore needs to be poked to show this change
|
||||
// in TagTile (TagPanel), Flair and GroupTile (MyGroups).
|
||||
avatarChanged: true,
|
||||
});
|
||||
}).catch((e) => {
|
||||
this.setState({uploadingAvatar: false});
|
||||
|
@ -615,6 +621,11 @@ export default React.createClass({
|
|||
});
|
||||
dis.dispatch({action: 'panel_disable'});
|
||||
this._initGroupStore(this.props.groupId);
|
||||
|
||||
if (this.state.avatarChanged) {
|
||||
// XXX: Evil - poking a store should be done from an async action
|
||||
FlairStore.refreshGroupProfile(this._matrixClient, this.props.groupId);
|
||||
}
|
||||
}).catch((e) => {
|
||||
this.setState({
|
||||
saving: false,
|
||||
|
@ -625,6 +636,10 @@ export default React.createClass({
|
|||
title: _t('Error'),
|
||||
description: _t('Failed to update community'),
|
||||
});
|
||||
}).finally(() => {
|
||||
this.setState({
|
||||
avatarChanged: false,
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
||||
|
|
|
@ -74,6 +74,21 @@ export default withMatrixClient(React.createClass({
|
|||
contentHeader = groupNodes.length > 0 ? <h3>{ _t('Your Communities') }</h3> : <div />;
|
||||
content = groupNodes.length > 0 ?
|
||||
<GeminiScrollbar>
|
||||
<div className="mx_MyGroups_microcopy">
|
||||
<p>
|
||||
{ _t(
|
||||
"Did you know: you can use communities to filter your Riot.im experience!",
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
"To set up a filter, drag a community avatar over to the filter panel on " +
|
||||
"the far left hand side of the screen. You can click on an avatar in the " +
|
||||
"filter panel at any time to see only the rooms and people associated " +
|
||||
"with that community.",
|
||||
) }
|
||||
</p>
|
||||
</div>
|
||||
<div className="mx_MyGroups_joinedGroups">
|
||||
{ groupNodes }
|
||||
</div>
|
||||
|
|
|
@ -45,7 +45,7 @@ const TagPanel = React.createClass({
|
|||
componentWillMount: function() {
|
||||
this.unmounted = false;
|
||||
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
||||
this.context.matrixClient.on("sync", this.onClientSync);
|
||||
this.context.matrixClient.on("sync", this._onClientSync);
|
||||
|
||||
this._tagOrderStoreToken = TagOrderStore.addListener(() => {
|
||||
if (this.unmounted) {
|
||||
|
@ -63,7 +63,7 @@ const TagPanel = React.createClass({
|
|||
componentWillUnmount() {
|
||||
this.unmounted = true;
|
||||
this.context.matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
|
||||
this.context.matrixClient.removeListener("sync", this.onClientSync);
|
||||
this.context.matrixClient.removeListener("sync", this._onClientSync);
|
||||
if (this._filterStoreToken) {
|
||||
this._filterStoreToken.remove();
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ const TagPanel = React.createClass({
|
|||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient));
|
||||
},
|
||||
|
||||
onClientSync(syncState, prevState) {
|
||||
_onClientSync(syncState, prevState) {
|
||||
// Consider the client reconnected if there is no error with syncing.
|
||||
// This means the state could be RECONNECTING, SYNCING or PREPARED.
|
||||
const reconnected = syncState !== "ERROR" && prevState !== syncState;
|
||||
|
@ -84,7 +84,7 @@ const TagPanel = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onClick(e) {
|
||||
onMouseDown(e) {
|
||||
dis.dispatch({action: 'deselect_tags'});
|
||||
},
|
||||
|
||||
|
@ -128,7 +128,9 @@ const TagPanel = React.createClass({
|
|||
<GeminiScrollbar
|
||||
className="mx_TagPanel_scroller"
|
||||
autoshow={true}
|
||||
onClick={this.onClick}
|
||||
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
|
||||
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6253
|
||||
onMouseDown={this.onMouseDown}
|
||||
>
|
||||
<Droppable
|
||||
droppableId="tag-panel-droppable"
|
||||
|
|
|
@ -79,6 +79,7 @@ const SIMPLE_SETTINGS = [
|
|||
{ id: "Pill.shouldHidePillAvatar" },
|
||||
{ id: "TextualBody.disableBigEmoji" },
|
||||
{ id: "VideoView.flipVideoHorizontally" },
|
||||
{ id: "TagPanel.disableTagPanel" },
|
||||
];
|
||||
|
||||
// These settings must be defined in SettingsStore
|
||||
|
|
|
@ -42,6 +42,9 @@ module.exports = React.createClass({
|
|||
// should the user be able to change the value? false by default.
|
||||
disabled: PropTypes.bool,
|
||||
onChange: PropTypes.func,
|
||||
|
||||
// Optional key to pass as the second argument to `onChange`
|
||||
powerLevelKey: PropTypes.string,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -84,17 +87,17 @@ module.exports = React.createClass({
|
|||
onSelectChange: function(event) {
|
||||
this.setState({ custom: event.target.value === "SELECT_VALUE_CUSTOM" });
|
||||
if (event.target.value !== "SELECT_VALUE_CUSTOM") {
|
||||
this.props.onChange(event.target.value);
|
||||
this.props.onChange(event.target.value, this.props.powerLevelKey);
|
||||
}
|
||||
},
|
||||
|
||||
onCustomBlur: function(event) {
|
||||
this.props.onChange(parseInt(this.refs.custom.value));
|
||||
this.props.onChange(parseInt(this.refs.custom.value), this.props.powerLevelKey);
|
||||
},
|
||||
|
||||
onCustomKeyDown: function(event) {
|
||||
if (event.key == "Enter") {
|
||||
this.props.onChange(parseInt(this.refs.custom.value));
|
||||
this.props.onChange(parseInt(this.refs.custom.value), this.props.powerLevelKey);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -55,20 +55,29 @@ export default React.createClass({
|
|||
componentWillMount() {
|
||||
this.unmounted = false;
|
||||
if (this.props.tag[0] === '+') {
|
||||
FlairStore.getGroupProfileCached(
|
||||
this.context.matrixClient,
|
||||
this.props.tag,
|
||||
).then((profile) => {
|
||||
if (this.unmounted) return;
|
||||
this.setState({profile});
|
||||
}).catch((err) => {
|
||||
console.warn('Could not fetch group profile for ' + this.props.tag, err);
|
||||
});
|
||||
FlairStore.addListener('updateGroupProfile', this._onFlairStoreUpdated);
|
||||
this._onFlairStoreUpdated();
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unmounted = true;
|
||||
if (this.props.tag[0] === '+') {
|
||||
FlairStore.removeListener('updateGroupProfile', this._onFlairStoreUpdated);
|
||||
}
|
||||
},
|
||||
|
||||
_onFlairStoreUpdated() {
|
||||
if (this.unmounted) return;
|
||||
FlairStore.getGroupProfileCached(
|
||||
this.context.matrixClient,
|
||||
this.props.tag,
|
||||
).then((profile) => {
|
||||
if (this.unmounted) return;
|
||||
this.setState({profile});
|
||||
}).catch((err) => {
|
||||
console.warn('Could not fetch group profile for ' + this.props.tag, err);
|
||||
});
|
||||
},
|
||||
|
||||
onClick: function(e) {
|
||||
|
@ -145,7 +154,13 @@ export default React.createClass({
|
|||
</div> : <div />;
|
||||
return <AccessibleButton className={className} onClick={this.onClick}>
|
||||
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
||||
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
||||
<BaseAvatar
|
||||
name={name}
|
||||
idName={this.props.tag}
|
||||
url={httpUrl}
|
||||
width={avatarHeight}
|
||||
height={avatarHeight}
|
||||
/>
|
||||
{ tip }
|
||||
{ contextButton }
|
||||
</div>
|
||||
|
|
|
@ -59,7 +59,7 @@ const GroupTile = React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onClick: function(e) {
|
||||
onMouseDown: function(e) {
|
||||
e.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'view_group',
|
||||
|
@ -79,7 +79,9 @@ const GroupTile = React.createClass({
|
|||
const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
|
||||
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
||||
) : null;
|
||||
return <AccessibleButton className="mx_GroupTile" onClick={this.onClick}>
|
||||
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
|
||||
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6156
|
||||
return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown}>
|
||||
<Droppable droppableId="my-groups-droppable" type="draggable-TagTile">
|
||||
{ (droppableProvided, droppableSnapshot) => (
|
||||
<div ref={droppableProvided.innerRef}>
|
||||
|
@ -97,13 +99,23 @@ const GroupTile = React.createClass({
|
|||
{...provided.dragHandleProps}
|
||||
>
|
||||
<div className="mx_GroupTile_avatar">
|
||||
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
||||
<BaseAvatar
|
||||
name={name}
|
||||
idName={this.props.groupId}
|
||||
url={httpUrl}
|
||||
width={avatarHeight}
|
||||
height={avatarHeight} />
|
||||
</div>
|
||||
</div>
|
||||
{ /* Instead of a blank placeholder, use a copy of the avatar itself. */ }
|
||||
{ provided.placeholder ?
|
||||
<div className="mx_GroupTile_avatar">
|
||||
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
||||
<BaseAvatar
|
||||
name={name}
|
||||
idName={this.props.groupId}
|
||||
url={httpUrl}
|
||||
width={avatarHeight}
|
||||
height={avatarHeight} />
|
||||
</div> :
|
||||
<div />
|
||||
}
|
||||
|
|
|
@ -117,7 +117,6 @@ module.exports = React.createClass({
|
|||
|
||||
propTypes: {
|
||||
room: PropTypes.object.isRequired,
|
||||
onSaveClick: PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -132,7 +131,8 @@ module.exports = React.createClass({
|
|||
join_rule: this._yankValueFromEvent("m.room.join_rules", "join_rule"),
|
||||
history_visibility: this._yankValueFromEvent("m.room.history_visibility", "history_visibility"),
|
||||
guest_access: this._yankValueFromEvent("m.room.guest_access", "guest_access"),
|
||||
power_levels_changed: false,
|
||||
powerLevels: this._yankContentFromEvent("m.room.power_levels", {}),
|
||||
powerLevelsChanged: false,
|
||||
tags_changed: false,
|
||||
tags: tags,
|
||||
// isRoomPublished is loaded async in componentWillMount so when the component
|
||||
|
@ -151,7 +151,7 @@ module.exports = React.createClass({
|
|||
|
||||
MatrixClientPeg.get().getRoomDirectoryVisibility(
|
||||
this.props.room.roomId,
|
||||
).done((result) => {
|
||||
).done((result = {}) => {
|
||||
this.setState({ isRoomPublished: result.visibility === "public" });
|
||||
this._originalIsRoomPublished = result.visibility === "public";
|
||||
}, (err) => {
|
||||
|
@ -272,8 +272,8 @@ module.exports = React.createClass({
|
|||
|
||||
|
||||
// power levels
|
||||
const powerLevels = this._getPowerLevels();
|
||||
if (powerLevels) {
|
||||
const powerLevels = this.state.powerLevels;
|
||||
if (this.state.powerLevelsChanged) {
|
||||
promises.push(MatrixClientPeg.get().sendStateEvent(
|
||||
roomId, "m.room.power_levels", powerLevels, "",
|
||||
));
|
||||
|
@ -384,36 +384,32 @@ module.exports = React.createClass({
|
|||
return strA !== strB;
|
||||
},
|
||||
|
||||
_getPowerLevels: function() {
|
||||
if (!this.state.power_levels_changed) return undefined;
|
||||
onPowerLevelsChanged: function(value, powerLevelKey) {
|
||||
const powerLevels = Object.assign({}, this.state.powerLevels);
|
||||
const eventsLevelPrefix = "event_levels_";
|
||||
|
||||
let powerLevels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
|
||||
powerLevels = powerLevels ? powerLevels.getContent() : {};
|
||||
value = parseInt(value);
|
||||
|
||||
for (const key of Object.keys(this.refs).filter((k) => k.startsWith("event_levels_"))) {
|
||||
const eventType = key.substring("event_levels_".length);
|
||||
powerLevels.events[eventType] = parseInt(this.refs[key].getValue());
|
||||
if (powerLevelKey.startsWith(eventsLevelPrefix)) {
|
||||
// deep copy "events" object, Object.assign itself won't deep copy
|
||||
powerLevels["events"] = Object.assign({}, this.state.powerLevels["events"] || {});
|
||||
powerLevels["events"][powerLevelKey.slice(eventsLevelPrefix.length)] = value;
|
||||
} else {
|
||||
powerLevels[powerLevelKey] = value;
|
||||
}
|
||||
|
||||
const newPowerLevels = {
|
||||
ban: parseInt(this.refs.ban.getValue()),
|
||||
kick: parseInt(this.refs.kick.getValue()),
|
||||
redact: parseInt(this.refs.redact.getValue()),
|
||||
invite: parseInt(this.refs.invite.getValue()),
|
||||
events_default: parseInt(this.refs.events_default.getValue()),
|
||||
state_default: parseInt(this.refs.state_default.getValue()),
|
||||
users_default: parseInt(this.refs.users_default.getValue()),
|
||||
users: powerLevels.users,
|
||||
events: powerLevels.events,
|
||||
};
|
||||
|
||||
return newPowerLevels;
|
||||
this.setState({
|
||||
powerLevels,
|
||||
powerLevelsChanged: true,
|
||||
});
|
||||
},
|
||||
|
||||
onPowerLevelsChanged: function() {
|
||||
this.setState({
|
||||
power_levels_changed: true,
|
||||
});
|
||||
_yankContentFromEvent: function(stateEventType, defaultValue) {
|
||||
// E.g.("m.room.name") would yank the content of "m.room.name"
|
||||
const event = this.props.room.currentState.getStateEvents(stateEventType, '');
|
||||
if (!event) {
|
||||
return defaultValue;
|
||||
}
|
||||
return event.getContent() || defaultValue;
|
||||
},
|
||||
|
||||
_yankValueFromEvent: function(stateEventType, keyName, defaultValue) {
|
||||
|
@ -633,29 +629,61 @@ module.exports = React.createClass({
|
|||
|
||||
const cli = MatrixClientPeg.get();
|
||||
const roomState = this.props.room.currentState;
|
||||
const user_id = cli.credentials.userId;
|
||||
const myUserId = cli.credentials.userId;
|
||||
|
||||
const power_level_event = roomState.getStateEvents('m.room.power_levels', '');
|
||||
const power_levels = power_level_event ? power_level_event.getContent() : {};
|
||||
const events_levels = power_levels.events || {};
|
||||
const user_levels = power_levels.users || {};
|
||||
const powerLevels = this.state.powerLevels;
|
||||
const eventsLevels = powerLevels.events || {};
|
||||
const userLevels = powerLevels.users || {};
|
||||
|
||||
const ban_level = parseIntWithDefault(power_levels.ban, 50);
|
||||
const kick_level = parseIntWithDefault(power_levels.kick, 50);
|
||||
const redact_level = parseIntWithDefault(power_levels.redact, 50);
|
||||
const invite_level = parseIntWithDefault(power_levels.invite, 50);
|
||||
const send_level = parseIntWithDefault(power_levels.events_default, 0);
|
||||
const state_level = power_level_event ? parseIntWithDefault(power_levels.state_default, 50) : 0;
|
||||
const default_user_level = parseIntWithDefault(power_levels.users_default, 0);
|
||||
const powerLevelDescriptors = {
|
||||
users_default: {
|
||||
desc: _t('The default role for new room members is'),
|
||||
defaultValue: 0,
|
||||
},
|
||||
events_default: {
|
||||
desc: _t('To send messages, you must be a'),
|
||||
defaultValue: 0,
|
||||
},
|
||||
invite: {
|
||||
desc: _t('To invite users into the room, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
state_default: {
|
||||
desc: _t('To configure the room, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
kick: {
|
||||
desc: _t('To kick users, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
ban: {
|
||||
desc: _t('To ban users, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
redact: {
|
||||
desc: _t('To remove other users\' messages, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
};
|
||||
|
||||
this._populateDefaultPlEvents(events_levels, state_level, send_level);
|
||||
const banLevel = parseIntWithDefault(powerLevels.ban, powerLevelDescriptors.ban.defaultValue);
|
||||
const defaultUserLevel = parseIntWithDefault(
|
||||
powerLevels.users_default,
|
||||
powerLevelDescriptors.users_default.defaultValue,
|
||||
);
|
||||
|
||||
let current_user_level = user_levels[user_id];
|
||||
if (current_user_level === undefined) {
|
||||
current_user_level = default_user_level;
|
||||
this._populateDefaultPlEvents(
|
||||
eventsLevels,
|
||||
parseIntWithDefault(powerLevels.state_default, powerLevelDescriptors.state_default.defaultValue),
|
||||
parseIntWithDefault(powerLevels.events_default, powerLevelDescriptors.events_default.defaultValue),
|
||||
);
|
||||
|
||||
let currentUserLevel = userLevels[myUserId];
|
||||
if (currentUserLevel === undefined) {
|
||||
currentUserLevel = defaultUserLevel;
|
||||
}
|
||||
|
||||
const can_change_levels = roomState.mayClientSendStateEvent("m.room.power_levels", cli);
|
||||
const canChangeLevels = roomState.mayClientSendStateEvent("m.room.power_levels", cli);
|
||||
|
||||
const canSetTag = !cli.isGuest();
|
||||
|
||||
|
@ -668,15 +696,16 @@ module.exports = React.createClass({
|
|||
/>;
|
||||
|
||||
let userLevelsSection;
|
||||
if (Object.keys(user_levels).length) {
|
||||
if (Object.keys(userLevels).length) {
|
||||
userLevelsSection =
|
||||
<div>
|
||||
<h3>{ _t('Privileged Users') }</h3>
|
||||
<ul className="mx_RoomSettings_userLevels">
|
||||
{ Object.keys(user_levels).map(function(user, i) {
|
||||
{ Object.keys(userLevels).map(function(user, i) {
|
||||
return (
|
||||
<li className="mx_RoomSettings_userLevel" key={user}>
|
||||
{ _t("%(user)s is a", {user: user}) } <PowerSelector value={user_levels[user]} disabled={true} />
|
||||
{ _t("%(user)s is a", {user: user}) }
|
||||
<PowerSelector value={userLevels[user]} disabled={true} />
|
||||
</li>
|
||||
);
|
||||
}) }
|
||||
|
@ -689,7 +718,7 @@ module.exports = React.createClass({
|
|||
const banned = this.props.room.getMembersWithMembership("ban");
|
||||
let bannedUsersSection;
|
||||
if (banned.length) {
|
||||
const canBanUsers = current_user_level >= ban_level;
|
||||
const canBanUsers = currentUserLevel >= banLevel;
|
||||
bannedUsersSection =
|
||||
<div>
|
||||
<h3>{ _t('Banned users') }</h3>
|
||||
|
@ -711,13 +740,13 @@ module.exports = React.createClass({
|
|||
if (this._yankValueFromEvent("m.room.create", "m.federate", true) === false) {
|
||||
unfederatableSection = (
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
{ _t('This room is not accessible by remote Matrix servers') }.
|
||||
{ _t('This room is not accessible by remote Matrix servers') }.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let leaveButton = null;
|
||||
const myMember = this.props.room.getMember(user_id);
|
||||
const myMember = this.props.room.getMember(myUserId);
|
||||
if (myMember) {
|
||||
if (myMember.membership === "join") {
|
||||
leaveButton = (
|
||||
|
@ -800,6 +829,50 @@ module.exports = React.createClass({
|
|||
</div>;
|
||||
}
|
||||
|
||||
const powerSelectors = Object.keys(powerLevelDescriptors).map((key, index) => {
|
||||
const descriptor = powerLevelDescriptors[key];
|
||||
|
||||
const value = parseIntWithDefault(powerLevels[key], descriptor.defaultValue);
|
||||
return <div key={index} className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">
|
||||
{ descriptor.desc }
|
||||
</span>
|
||||
<PowerSelector
|
||||
value={value}
|
||||
usersDefault={defaultUserLevel}
|
||||
controlled={false}
|
||||
disabled={!canChangeLevels || currentUserLevel < value}
|
||||
powerLevelKey={key} // Will be sent as the second parameter to `onChange`
|
||||
onChange={this.onPowerLevelsChanged}
|
||||
/>
|
||||
</div>;
|
||||
});
|
||||
|
||||
const eventPowerSelectors = Object.keys(eventsLevels).map(function(eventType, i) {
|
||||
let label = plEventsToLabels[eventType];
|
||||
if (label) {
|
||||
label = _t(label);
|
||||
} else {
|
||||
label = _t(
|
||||
"To send events of type <eventType/>, you must be a", {},
|
||||
{ 'eventType': <code>{ eventType }</code> },
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="mx_RoomSettings_powerLevel" key={eventType}>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ label } </span>
|
||||
<PowerSelector
|
||||
value={eventsLevels[eventType]}
|
||||
usersDefault={defaultUserLevel}
|
||||
controlled={false}
|
||||
disabled={!canChangeLevels || currentUserLevel < eventsLevels[eventType]}
|
||||
powerLevelKey={"event_levels_" + eventType}
|
||||
onChange={self.onPowerLevelsChanged}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSettings">
|
||||
|
||||
|
@ -899,49 +972,9 @@ module.exports = React.createClass({
|
|||
|
||||
<h3>{ _t('Permissions') }</h3>
|
||||
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('The default role for new room members is') } </span>
|
||||
<PowerSelector ref="users_default" value={default_user_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < default_user_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send messages, you must be a') } </span>
|
||||
<PowerSelector ref="events_default" value={send_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To invite users into the room, you must be a') } </span>
|
||||
<PowerSelector ref="invite" value={invite_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To configure the room, you must be a') } </span>
|
||||
<PowerSelector ref="state_default" value={state_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To kick users, you must be a') } </span>
|
||||
<PowerSelector ref="kick" value={kick_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To ban users, you must be a') } </span>
|
||||
<PowerSelector ref="ban" value={ban_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
<div className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ _t('To remove other users\' messages, you must be a') } </span>
|
||||
<PowerSelector ref="redact" value={redact_level} usersDefault={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged} />
|
||||
</div>
|
||||
|
||||
{ Object.keys(events_levels).map(function(event_type, i) {
|
||||
let label = plEventsToLabels[event_type];
|
||||
if (label) label = _t(label);
|
||||
else label = _t("To send events of type <eventType/>, you must be a", {}, { 'eventType': <code>{ event_type }</code> });
|
||||
return (
|
||||
<div className="mx_RoomSettings_powerLevel" key={event_type}>
|
||||
<span className="mx_RoomSettings_powerLevelKey">{ label } </span>
|
||||
<PowerSelector ref={"event_levels_"+event_type} value={events_levels[event_type]} usersDefault={default_user_level} onChange={self.onPowerLevelsChanged}
|
||||
controlled={false} disabled={!can_change_levels || current_user_level < events_levels[event_type]} />
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
|
||||
{ unfederatableSection }
|
||||
{ powerSelectors }
|
||||
{ eventPowerSelectors }
|
||||
{ unfederatableSection }
|
||||
</div>
|
||||
|
||||
{ userLevelsSection }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue