From 52af7a7659635665734c2278acb5d0f921bae1d4 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 13 Nov 2017 12:08:53 +0000 Subject: [PATCH 1/2] 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)". --- src/Roles.js | 11 ++-- .../views/elements/PowerSelector.js | 52 ++++++++++++++++--- src/components/views/rooms/MemberInfo.js | 21 ++++---- src/components/views/rooms/RoomSettings.js | 16 +++--- src/i18n/strings/en_EN.json | 4 +- 5 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/Roles.js b/src/Roles.js index 83d8192c67..438b6c1236 100644 --- a/src/Roles.js +++ b/src/Roles.js @@ -15,19 +15,20 @@ limitations under the License. */ import { _t } from './languageHandler'; -export function levelRoleMap() { +export function levelRoleMap(usersDefault) { return { undefined: _t('Default'), - 0: _t('User'), + 0: _t('Restricted'), + [usersDefault]: _t('Default'), 50: _t('Moderator'), 100: _t('Admin'), }; } -export function textualPowerLevel(level, userDefault) { - const LEVEL_ROLE_MAP = this.levelRoleMap(); +export function textualPowerLevel(level, usersDefault) { + const LEVEL_ROLE_MAP = this.levelRoleMap(usersDefault); if (LEVEL_ROLE_MAP[level]) { - return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`); + return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${usersDefault})`); } else { return level; } diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js index 50893850c1..6a31259494 100644 --- a/src/components/views/elements/PowerSelector.js +++ b/src/components/views/elements/PowerSelector.js @@ -25,6 +25,11 @@ module.exports = React.createClass({ propTypes: { 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 ; + input = ; } customPicker = of { input }; } @@ -110,13 +149,10 @@ module.exports = React.createClass({ select = { selectValue }; } else { // Each level must have a definition in this.state.levelRoleMap - const levels = [0, 50, 100]; - let options = levels.map((level) => { + let options = this.state.options.map((level) => { return { value: this.state.levelRoleMap[level], - // Give a userDefault (users_default in the power event) of 0 but - // because level !== undefined, this should never be used. - text: Roles.textualPowerLevel(level, 0), + text: Roles.textualPowerLevel(level, this.props.usersDefault), }; }); options.push({ value: "Custom", text: _t("Custom level") }); diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index c043b3714d..4d875ea24a 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -494,7 +494,6 @@ module.exports = withMatrixClient(React.createClass({ const defaultPerms = { can: {}, muted: false, - modifyLevel: false, }; const room = this.props.matrixClient.getRoom(member.roomId); if (!room) return defaultPerms; @@ -516,13 +515,15 @@ module.exports = withMatrixClient(React.createClass({ }, _calculateCanPermissions: function(me, them, powerLevels) { + const isMe = me.userId === them.userId; const can = { kick: false, ban: false, mute: false, modifyLevel: false, + modifyLevelMax: 0, }; - const canAffectUser = them.powerLevel < me.powerLevel; + const canAffectUser = them.powerLevel < me.powerLevel || isMe; if (!canAffectUser) { //console.log("Cannot affect user: %s >= %s", them.powerLevel, me.powerLevel); return can; @@ -531,16 +532,13 @@ module.exports = withMatrixClient(React.createClass({ (powerLevels.events ? powerLevels.events["m.room.power_levels"] : null) || powerLevels.state_default ); - const levelToSend = ( - (powerLevels.events ? powerLevels.events["m.room.message"] : null) || - powerLevels.events_default - ); can.kick = me.powerLevel >= powerLevels.kick; can.ban = me.powerLevel >= powerLevels.ban; can.mute = me.powerLevel >= editPowerLevel; - can.toggleMod = me.powerLevel > them.powerLevel && them.powerLevel >= levelToSend; - can.modifyLevel = me.powerLevel > them.powerLevel && me.powerLevel >= editPowerLevel; + can.modifyLevel = me.powerLevel >= editPowerLevel && (isMe || me.powerLevel > them.powerLevel); + can.modifyLevelMax = me.powerLevel; + return can; }, @@ -832,8 +830,11 @@ module.exports = withMatrixClient(React.createClass({ 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 const PowerSelector = sdk.getComponent('elements.PowerSelector'); const PresenceLabel = sdk.getComponent('rooms.PresenceLabel'); @@ -842,7 +843,9 @@ module.exports = withMatrixClient(React.createClass({ { _t("Level:") } diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index c7e839ab40..40a3209362 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -910,31 +910,31 @@ module.exports = React.createClass({
{ _t('The default role for new room members is') } - +
{ _t('To send messages, you must be a') } - +
{ _t('To invite users into the room, you must be a') } - +
{ _t('To configure the room, you must be a') } - +
{ _t('To kick users, you must be a') } - +
{ _t('To ban users, you must be a') } - +
{ _t('To remove other users\' messages, you must be a') } - +
{ Object.keys(events_levels).map(function(event_type, i) { @@ -944,7 +944,7 @@ module.exports = React.createClass({ return (
{ label } -
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4052d098c1..ae74da0a18 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -63,7 +63,7 @@ "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.", "Default": "Default", - "User": "User", + "Restricted": "Restricted", "Moderator": "Moderator", "Admin": "Admin", "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 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", - "Communities": "Communities", "Message Pinning": "Message Pinning", "%(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", @@ -524,6 +523,7 @@ "Unverify": "Unverify", "Verify...": "Verify...", "No results": "No results", + "Communities": "Communities", "Home": "Home", "Integrations Error": "Integrations Error", "Could not connect to the integration server": "Could not connect to the integration server", From 3fa1bece0a8ad73ab91c297a17e6babdc4e7b6c6 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 14 Nov 2017 13:06:54 +0000 Subject: [PATCH 2/2] Simplify further Also fix not-i18n-friendly "of" to be "=". --- .../views/elements/PowerSelector.js | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js index 2d90711739..d5c167fac9 100644 --- a/src/components/views/elements/PowerSelector.js +++ b/src/components/views/elements/PowerSelector.js @@ -59,14 +59,14 @@ module.exports = React.createClass({ }, componentWillMount: function() { - this._initStateFromProps(this.props, true); + this._initStateFromProps(this.props); }, componentWillReceiveProps: function(newProps) { this._initStateFromProps(newProps); }, - _initStateFromProps: function(newProps, initial) { + _initStateFromProps: function(newProps) { // This needs to be done now because levelRoleMap has translated strings const levelRoleMap = Roles.levelRoleMap(newProps.usersDefault); const options = Object.keys(levelRoleMap).filter((l) => { @@ -76,69 +76,53 @@ module.exports = React.createClass({ this.setState({ levelRoleMap, options, + custom: levelRoleMap[newProps.value] === undefined, }); - - if (initial) { - this.setState({ - custom: levelRoleMap[newProps.value] === undefined, - }); - } }, onSelectChange: function(event) { this.setState({ custom: event.target.value === "SELECT_VALUE_CUSTOM" }); if (event.target.value !== "SELECT_VALUE_CUSTOM") { - this.props.onChange(this.getValue()); + this.props.onChange(event.target.value); } }, onCustomBlur: function(event) { - this.props.onChange(this.getValue()); + this.props.onChange(parseInt(this.refs.custom.value)); }, onCustomKeyDown: function(event) { if (event.key == "Enter") { - this.props.onChange(this.getValue()); + this.props.onChange(parseInt(this.refs.custom.value)); } }, - getValue: function() { - if (this.refs.custom) { - return parseInt(this.refs.custom.value); - } - if (this.refs.select) { - return this.refs.select.value; - } - return undefined; - }, - render: function() { let customPicker; if (this.state.custom) { - let input; if (this.props.disabled) { - input = { _t( + customPicker = { _t( "Custom of %(powerLevel)s", { powerLevel: this.props.value }, ) }; } else { - input = = ; + /> + ; } - customPicker = of { input }; } let selectValue; if (this.state.custom) { selectValue = "SELECT_VALUE_CUSTOM"; } else { - selectValue = this.state.levelRoleMap[selectValue] ? + selectValue = this.state.levelRoleMap[this.props.value] ? this.props.value : "SELECT_VALUE_CUSTOM"; } let select;