Attempt to clarify the affect that the users_default has on power levels

This modifies displayed power levels such that:
 - If users_default is !== 0:
   - the power level 0 is displayed as "Restricted (0)"
   - the power level users_default is displayed as "Default ({users_default})"
 - Otherwise:
   - the power level 0 is displayed as "Default (0)"

When changing users_default, to say, 10, when the textual powers are rendered
again, they will take users_default into account. So those previously at 10
and which would have previously have been rendered "Custom of 10" will now
read "Default (10)". Conversely, those that were "Default (0)" will now read
"Restricted (0)".
This commit is contained in:
Luke Barnard 2017-11-13 12:08:53 +00:00
parent 88010fa26c
commit 52af7a7659
5 changed files with 72 additions and 32 deletions

View file

@ -15,19 +15,20 @@ limitations under the License.
*/ */
import { _t } from './languageHandler'; import { _t } from './languageHandler';
export function levelRoleMap() { export function levelRoleMap(usersDefault) {
return { return {
undefined: _t('Default'), undefined: _t('Default'),
0: _t('User'), 0: _t('Restricted'),
[usersDefault]: _t('Default'),
50: _t('Moderator'), 50: _t('Moderator'),
100: _t('Admin'), 100: _t('Admin'),
}; };
} }
export function textualPowerLevel(level, userDefault) { export function textualPowerLevel(level, usersDefault) {
const LEVEL_ROLE_MAP = this.levelRoleMap(); const LEVEL_ROLE_MAP = this.levelRoleMap(usersDefault);
if (LEVEL_ROLE_MAP[level]) { if (LEVEL_ROLE_MAP[level]) {
return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`); return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${usersDefault})`);
} else { } else {
return level; return level;
} }

View file

@ -25,6 +25,11 @@ module.exports = React.createClass({
propTypes: { propTypes: {
value: React.PropTypes.number.isRequired, value: React.PropTypes.number.isRequired,
// The maximum value that can be set with the power selector
maxValue: React.PropTypes.number.isRequired,
// Default user power level for the room
usersDefault: React.PropTypes.number.isRequired,
// if true, the <select/> should be a 'controlled' form element and updated by React // if true, the <select/> should be a 'controlled' form element and updated by React
// to reflect the current value, rather than left freeform. // to reflect the current value, rather than left freeform.
@ -42,21 +47,48 @@ module.exports = React.createClass({
return { return {
levelRoleMap: {}, levelRoleMap: {},
reverseRoles: {}, reverseRoles: {},
// List of power levels to show in the drop-down
options: [],
};
},
getDefaultProps: function() {
return {
maxValue: Infinity,
usersDefault: 0,
}; };
}, },
componentWillMount: function() { componentWillMount: function() {
this._initStateFromProps(this.props, true);
},
componentWillReceiveProps: function(newProps) {
this._initStateFromProps(newProps);
},
_initStateFromProps: function(newProps, initial) {
// This needs to be done now because levelRoleMap has translated strings // This needs to be done now because levelRoleMap has translated strings
const levelRoleMap = Roles.levelRoleMap(); const levelRoleMap = Roles.levelRoleMap(newProps.usersDefault);
const reverseRoles = {}; const reverseRoles = {};
Object.keys(levelRoleMap).forEach(function(key) { Object.keys(levelRoleMap).forEach(function(key) {
reverseRoles[levelRoleMap[key]] = key; reverseRoles[levelRoleMap[key]] = key;
}); });
const options = Object.keys(levelRoleMap).filter((l) => {
return l === undefined || l <= newProps.maxValue;
});
this.setState({ this.setState({
levelRoleMap, levelRoleMap,
reverseRoles, reverseRoles,
custom: levelRoleMap[this.props.value] === undefined, options,
}); });
if (initial) {
this.setState({
custom: levelRoleMap[newProps.value] === undefined,
});
}
}, },
onSelectChange: function(event) { onSelectChange: function(event) {
@ -94,7 +126,14 @@ module.exports = React.createClass({
if (this.props.disabled) { if (this.props.disabled) {
input = <span>{ this.props.value }</span>; input = <span>{ this.props.value }</span>;
} else { } else {
input = <input ref="custom" type="text" size="3" defaultValue={this.props.value} onBlur={this.onCustomBlur} onKeyDown={this.onCustomKeyDown} />; input = <input
ref="custom"
type="text"
size="3"
defaultValue={this.props.value}
onBlur={this.onCustomBlur}
onKeyDown={this.onCustomKeyDown}
/>;
} }
customPicker = <span> of { input }</span>; customPicker = <span> of { input }</span>;
} }
@ -110,13 +149,10 @@ module.exports = React.createClass({
select = <span>{ selectValue }</span>; select = <span>{ selectValue }</span>;
} else { } else {
// Each level must have a definition in this.state.levelRoleMap // Each level must have a definition in this.state.levelRoleMap
const levels = [0, 50, 100]; let options = this.state.options.map((level) => {
let options = levels.map((level) => {
return { return {
value: this.state.levelRoleMap[level], value: this.state.levelRoleMap[level],
// Give a userDefault (users_default in the power event) of 0 but text: Roles.textualPowerLevel(level, this.props.usersDefault),
// because level !== undefined, this should never be used.
text: Roles.textualPowerLevel(level, 0),
}; };
}); });
options.push({ value: "Custom", text: _t("Custom level") }); options.push({ value: "Custom", text: _t("Custom level") });

View file

@ -494,7 +494,6 @@ module.exports = withMatrixClient(React.createClass({
const defaultPerms = { const defaultPerms = {
can: {}, can: {},
muted: false, muted: false,
modifyLevel: false,
}; };
const room = this.props.matrixClient.getRoom(member.roomId); const room = this.props.matrixClient.getRoom(member.roomId);
if (!room) return defaultPerms; if (!room) return defaultPerms;
@ -516,13 +515,15 @@ module.exports = withMatrixClient(React.createClass({
}, },
_calculateCanPermissions: function(me, them, powerLevels) { _calculateCanPermissions: function(me, them, powerLevels) {
const isMe = me.userId === them.userId;
const can = { const can = {
kick: false, kick: false,
ban: false, ban: false,
mute: false, mute: false,
modifyLevel: false, modifyLevel: false,
modifyLevelMax: 0,
}; };
const canAffectUser = them.powerLevel < me.powerLevel; const canAffectUser = them.powerLevel < me.powerLevel || isMe;
if (!canAffectUser) { if (!canAffectUser) {
//console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel); //console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel);
return can; return can;
@ -531,16 +532,13 @@ module.exports = withMatrixClient(React.createClass({
(powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) || (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) ||
powerLevels.state_default powerLevels.state_default
); );
const levelToSend = (
(powerLevels.events ? powerLevels.events["m.room.message"] : null) ||
powerLevels.events_default
);
can.kick = me.powerLevel >= powerLevels.kick; can.kick = me.powerLevel >= powerLevels.kick;
can.ban = me.powerLevel >= powerLevels.ban; can.ban = me.powerLevel >= powerLevels.ban;
can.mute = me.powerLevel >= editPowerLevel; can.mute = me.powerLevel >= editPowerLevel;
can.toggleMod = me.powerLevel > them.powerLevel && them.powerLevel >= levelToSend; can.modifyLevel = me.powerLevel >= editPowerLevel && (isMe || me.powerLevel > them.powerLevel);
can.modifyLevel = me.powerLevel > them.powerLevel && me.powerLevel >= editPowerLevel; can.modifyLevelMax = me.powerLevel;
return can; return can;
}, },
@ -832,8 +830,11 @@ module.exports = withMatrixClient(React.createClass({
presenceCurrentlyActive = this.props.member.user.currentlyActive; presenceCurrentlyActive = this.props.member.user.currentlyActive;
} }
let roomMemberDetails = null; const room = this.props.matrixClient.getRoom(this.props.member.roomId);
const poweLevelEvent = room ? room.currentState.getStateEvents("m.room.power_levels", "") : null;
const powerLevelUsersDefault = poweLevelEvent.getContent().users_default;
let roomMemberDetails = null;
if (this.props.member.roomId) { // is in room if (this.props.member.roomId) { // is in room
const PowerSelector = sdk.getComponent('elements.PowerSelector'); const PowerSelector = sdk.getComponent('elements.PowerSelector');
const PresenceLabel = sdk.getComponent('rooms.PresenceLabel'); const PresenceLabel = sdk.getComponent('rooms.PresenceLabel');
@ -842,7 +843,9 @@ module.exports = withMatrixClient(React.createClass({
{ _t("Level:") } <b> { _t("Level:") } <b>
<PowerSelector controlled={true} <PowerSelector controlled={true}
value={parseInt(this.props.member.powerLevel)} value={parseInt(this.props.member.powerLevel)}
maxValue={this.state.can.modifyLevelMax}
disabled={!this.state.can.modifyLevel} disabled={!this.state.can.modifyLevel}
usersDefault={powerLevelUsersDefault}
onChange={this.onPowerChange} /> onChange={this.onPowerChange} />
</b> </b>
</div> </div>

View file

@ -910,31 +910,31 @@ module.exports = React.createClass({
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings"> <div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('The default role for new room members is') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('The default role for new room members is') } </span>
<PowerSelector ref="users_default" value={default_user_level} controlled={false} disabled={!can_change_levels || current_user_level < default_user_level} onChange={this.onPowerLevelsChanged} /> <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>
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To send messages, you must be a') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('To send messages, you must be a') } </span>
<PowerSelector ref="events_default" value={send_level} controlled={false} disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged} /> <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>
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To invite users into the room, you must be a') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('To invite users into the room, you must be a') } </span>
<PowerSelector ref="invite" value={invite_level} controlled={false} disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged} /> <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>
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To configure the room, you must be a') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('To configure the room, you must be a') } </span>
<PowerSelector ref="state_default" value={state_level} controlled={false} disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged} /> <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>
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To kick users, you must be a') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('To kick users, you must be a') } </span>
<PowerSelector ref="kick" value={kick_level} controlled={false} disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged} /> <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>
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To ban users, you must be a') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('To ban users, you must be a') } </span>
<PowerSelector ref="ban" value={ban_level} controlled={false} disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged} /> <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>
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">
<span className="mx_RoomSettings_powerLevelKey">{ _t('To remove other users\' messages, you must be a') } </span> <span className="mx_RoomSettings_powerLevelKey">{ _t('To remove other users\' messages, you must be a') } </span>
<PowerSelector ref="redact" value={redact_level} controlled={false} disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged} /> <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> </div>
{ Object.keys(events_levels).map(function(event_type, i) { { Object.keys(events_levels).map(function(event_type, i) {
@ -944,7 +944,7 @@ module.exports = React.createClass({
return ( return (
<div className="mx_RoomSettings_powerLevel" key={event_type}> <div className="mx_RoomSettings_powerLevel" key={event_type}>
<span className="mx_RoomSettings_powerLevelKey">{ label } </span> <span className="mx_RoomSettings_powerLevelKey">{ label } </span>
<PowerSelector ref={"event_levels_"+event_type} value={events_levels[event_type]} onChange={self.onPowerLevelsChanged} <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]} /> controlled={false} disabled={!can_change_levels || current_user_level < events_levels[event_type]} />
</div> </div>
); );

View file

@ -63,7 +63,7 @@
"This email address was not found": "This email address was not found", "This email address was not found": "This email address was not found",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.",
"Default": "Default", "Default": "Default",
"User": "User", "Restricted": "Restricted",
"Moderator": "Moderator", "Moderator": "Moderator",
"Admin": "Admin", "Admin": "Admin",
"Start a chat": "Start a chat", "Start a chat": "Start a chat",
@ -150,7 +150,6 @@
"%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s", "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s",
"%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s", "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s",
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s", "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
"Communities": "Communities",
"Message Pinning": "Message Pinning", "Message Pinning": "Message Pinning",
"%(displayName)s is typing": "%(displayName)s is typing", "%(displayName)s is typing": "%(displayName)s is typing",
"%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing", "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing",
@ -524,6 +523,7 @@
"Unverify": "Unverify", "Unverify": "Unverify",
"Verify...": "Verify...", "Verify...": "Verify...",
"No results": "No results", "No results": "No results",
"Communities": "Communities",
"Home": "Home", "Home": "Home",
"Integrations Error": "Integrations Error", "Integrations Error": "Integrations Error",
"Could not connect to the integration server": "Could not connect to the integration server", "Could not connect to the integration server": "Could not connect to the integration server",