From 6010350ce5d52b946217c6314a69a87fddef2d4b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Thu, 6 Apr 2017 17:02:35 +0100 Subject: [PATCH 1/2] Implement power-level changes in timeline Fixes https://github.com/vector-im/riot-web/issues/266 --- src/TextForEvent.js | 55 +++++++++++++++++++++++++ src/components/views/rooms/EventTile.js | 1 + 2 files changed, 56 insertions(+) diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 3e1659f392..2560264346 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -17,6 +17,13 @@ limitations under the License. var MatrixClientPeg = require("./MatrixClientPeg"); var CallHandler = require("./CallHandler"); +const roles = { + undefined: 'Default', + 0: 'User', + 50: 'Moderator', + 100: 'Admin', +}; + function textForMemberEvent(ev) { // XXX: SYJS-16 "sender is sometimes null for join messages" var senderName = ev.sender ? ev.sender.name : ev.getSender(); @@ -182,6 +189,53 @@ function textForEncryptionEvent(event) { return senderName + " turned on end-to-end encryption (algorithm " + event.getContent().algorithm + ")"; } +function formatPowerLevel(level, roles, userDefault) { + if (roles[level]) { + return roles[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`); + } else { + return level; + } +} + +// Currently will only display a change if a user's power level is changed +function textForPowerEvent(event) { + const senderName = event.sender ? event.sender.name : event.getSender(); + if (!event.getPrevContent() || !event.getPrevContent().users) { + return ''; + } + const userDefault = event.getContent().users_default || 0; + // Construct set of userIds + let users = []; + Object.keys(event.getContent().users).forEach( + (userId) => { + if (users.indexOf(userId) === -1) users.push(userId); + } + ); + Object.keys(event.getPrevContent().users).forEach( + (userId) => { + if (users.indexOf(userId) === -1) users.push(userId); + } + ); + let diff = []; + users.forEach((userId) => { + // Previous power level + const from = event.getPrevContent().users[userId]; + // Current power level + const to = event.getContent().users[userId]; + if (to !== from) { + diff.push( + userId + + ' from ' + formatPowerLevel(from, roles, userDefault) + + ' to ' + formatPowerLevel(to, roles, userDefault) + ); + } + }); + if (!diff.length) { + return ''; + } + return senderName + ' changed the power level of ' + diff.join(', '); +} + var handlers = { 'm.room.message': textForMessageEvent, 'm.room.name': textForRoomNameEvent, @@ -193,6 +247,7 @@ var handlers = { 'm.room.third_party_invite': textForThreePidInviteEvent, 'm.room.history_visibility': textForHistoryVisibilityEvent, 'm.room.encryption': textForEncryptionEvent, + 'm.room.power_levels': textForPowerEvent, }; module.exports = { diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 8b8e52ae83..9df0499eb2 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -40,6 +40,7 @@ var eventTileTypes = { 'm.room.third_party_invite' : 'messages.TextualEvent', 'm.room.history_visibility' : 'messages.TextualEvent', 'm.room.encryption' : 'messages.TextualEvent', + 'm.room.power_levels' : 'messages.TextualEvent', }; var MAX_READ_AVATARS = 5; From 8b4836b60ef8dfd8852e982df4abd7bc1f715cf0 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 10 Apr 2017 10:09:26 +0100 Subject: [PATCH 2/2] Refactor roles into Roles.js So that the mapping between a numerical power level and a "role" are done in one place. PowerSelector.js has been modified to use the same mapping. --- src/Roles.js | 29 +++++++++++++++ src/TextForEvent.js | 19 ++-------- .../views/elements/PowerSelector.js | 37 +++++++++++-------- 3 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 src/Roles.js diff --git a/src/Roles.js b/src/Roles.js new file mode 100644 index 0000000000..cef8670aad --- /dev/null +++ b/src/Roles.js @@ -0,0 +1,29 @@ +/* +Copyright 2017 Vector Creations Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +export const LEVEL_ROLE_MAP = { + undefined: 'Default', + 0: 'User', + 50: 'Moderator', + 100: 'Admin', +}; + +export function textualPowerLevel(level, userDefault) { + if (LEVEL_ROLE_MAP[level]) { + return LEVEL_ROLE_MAP[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`); + } else { + return level; + } +} diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 2560264346..40d6a49998 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -17,12 +17,7 @@ limitations under the License. var MatrixClientPeg = require("./MatrixClientPeg"); var CallHandler = require("./CallHandler"); -const roles = { - undefined: 'Default', - 0: 'User', - 50: 'Moderator', - 100: 'Admin', -}; +import * as Roles from './Roles'; function textForMemberEvent(ev) { // XXX: SYJS-16 "sender is sometimes null for join messages" @@ -189,14 +184,6 @@ function textForEncryptionEvent(event) { return senderName + " turned on end-to-end encryption (algorithm " + event.getContent().algorithm + ")"; } -function formatPowerLevel(level, roles, userDefault) { - if (roles[level]) { - return roles[level] + (level !== undefined ? ` (${level})` : ` (${userDefault})`); - } else { - return level; - } -} - // Currently will only display a change if a user's power level is changed function textForPowerEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); @@ -225,8 +212,8 @@ function textForPowerEvent(event) { if (to !== from) { diff.push( userId + - ' from ' + formatPowerLevel(from, roles, userDefault) + - ' to ' + formatPowerLevel(to, roles, userDefault) + ' from ' + Roles.textualPowerLevel(from, userDefault) + + ' to ' + Roles.textualPowerLevel(to, userDefault) ); } }); diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js index c7bfd4eec1..5eec464ead 100644 --- a/src/components/views/elements/PowerSelector.js +++ b/src/components/views/elements/PowerSelector.js @@ -16,17 +16,12 @@ limitations under the License. 'use strict'; -var React = require('react'); - -var roles = { - 0: 'User', - 50: 'Moderator', - 100: 'Admin', -}; +import React from 'react'; +import * as Roles from '../../../Roles'; var reverseRoles = {}; -Object.keys(roles).forEach(function(key) { - reverseRoles[roles[key]] = key; +Object.keys(Roles.LEVEL_ROLE_MAP).forEach(function(key) { + reverseRoles[Roles.LEVEL_ROLE_MAP[key]] = key; }); module.exports = React.createClass({ @@ -49,7 +44,7 @@ module.exports = React.createClass({ getInitialState: function() { return { - custom: (roles[this.props.value] === undefined), + custom: (Roles.LEVEL_ROLE_MAP[this.props.value] === undefined), }; }, @@ -99,22 +94,34 @@ module.exports = React.createClass({ selectValue = "Custom"; } else { - selectValue = roles[this.props.value] || "Custom"; + selectValue = Roles.LEVEL_ROLE_MAP[this.props.value] || "Custom"; } var select; if (this.props.disabled) { select = { selectValue }; } else { + // Each level must have a definition in LEVEL_ROLE_MAP + const levels = [0, 50, 100]; + let options = levels.map((level) => { + return { + value: Roles.LEVEL_ROLE_MAP[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), + } + }); + options.push({ value: "Custom", text: "Custom level" }); + options = options.map((op) => { + return ; + }); + select = ; }