From ef00a1624d34604e6883e8a20b9da26a51efc5fb Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 8 Jan 2016 03:22:38 +0000 Subject: [PATCH 1/4] fix up RoomSettings somewhat and implement room colors --- src/SlashCommands.js | 24 ++- src/Tinter.js | 12 ++ src/components/structures/MatrixChat.js | 10 + src/components/structures/RoomView.js | 45 ++++- src/components/views/rooms/RoomHeader.js | 4 +- src/components/views/rooms/RoomSettings.js | 171 ++++++++++++++---- src/components/views/settings/ChangeAvatar.js | 7 +- 7 files changed, 231 insertions(+), 42 deletions(-) diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 363560f0c6..35216aa403 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -43,11 +43,27 @@ var commands = { return reject("Usage: /nick "); }, - // Takes an #rrggbb colourcode and retints the UI (just for debugging) + // Changes the colorscheme of your current room tint: function(room_id, args) { - Tinter.tint(args); - return success(); - }, + + if (args) { + var matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/); + if (matches) { + Tinter.tint(matches[1], matches[4]); + var colorScheme = {} + colorScheme.primary_color = matches[1]; + if (matches[4]) { + colorScheme.secondary_color = matches[4]; + } + return success( + MatrixClientPeg.get().setRoomAccountData( + room_id, "m.room.color_scheme", colorScheme + ) + ); + } + } + return reject("Usage: /tint []"); + }, encrypt: function(room_id, args) { if (args == "on") { diff --git a/src/Tinter.js b/src/Tinter.js index a9b754ffde..817518d6b2 100644 --- a/src/Tinter.js +++ b/src/Tinter.js @@ -126,6 +126,11 @@ module.exports = { cached = true; } + if (!primaryColor) { + primaryColor = "#76CFA6"; // Vector green + secondaryColor = "#EAF5F0"; // Vector light green + } + if (!secondaryColor) { var x = 0.16; // average weighting factor calculated from vector green & light green var rgb = hexToRgb(primaryColor); @@ -145,6 +150,13 @@ module.exports = { tertiaryColor = rgbToHex(rgb1); } + if (colors[0] === primaryColor && + colors[1] === secondaryColor && + colors[2] === tertiaryColor) + { + return; + } + colors = [primaryColor, secondaryColor, tertiaryColor]; // go through manually fixing up the stylesheets. diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index d732a54922..56885ce499 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -30,6 +30,7 @@ var Registration = require("./login/Registration"); var PostRegistration = require("./login/PostRegistration"); var Modal = require("../../Modal"); +var Tinter = require("../../Tinter"); var sdk = require('../../index'); var MatrixTools = require('../../MatrixTools'); var linkifyMatrix = require("../../linkify-matrix"); @@ -358,7 +359,16 @@ module.exports = React.createClass({ if (room) { var theAlias = MatrixTools.getCanonicalAliasForRoom(room); if (theAlias) presentedId = theAlias; + + var color_scheme_event = room.getAccountData("m.room.color_scheme"); + var color_scheme = {}; + if (color_scheme_event) { + color_scheme = color_scheme_event.getContent(); + // XXX: we should validate the event + } + Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); } + this.notifyNewScreen('room/'+presentedId); newState.ready = true; } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9ab99d9cba..08dfe75584 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -37,6 +37,7 @@ var TabComplete = require("../../TabComplete"); var MemberEntry = require("../../TabCompleteEntries").MemberEntry; var Resend = require("../../Resend"); var dis = require("../../dispatcher"); +var Tinter = require("../../Tinter"); var PAGINATE_SIZE = 20; var INITIAL_SIZE = 20; @@ -81,6 +82,7 @@ module.exports = React.createClass({ this.dispatcherRef = dis.register(this.onAction); MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); MatrixClientPeg.get().on("Room.name", this.onRoomName); + MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData); MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping); MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember); @@ -115,6 +117,7 @@ module.exports = React.createClass({ if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); + MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData); MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping); MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); @@ -122,6 +125,8 @@ module.exports = React.createClass({ } window.removeEventListener('resize', this.onResize); + + Tinter.tint(); // reset colourscheme }, onAction: function(payload) { @@ -235,6 +240,29 @@ module.exports = React.createClass({ } }, + updateTint: function() { + var room = MatrixClientPeg.get().getRoom(this.props.roomId); + if (!room) return; + + var color_scheme_event = room.getAccountData("m.room.color_scheme"); + var color_scheme = {}; + if (color_scheme_event) { + color_scheme = color_scheme_event.getContent(); + // XXX: we should validate the event + } + Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); + }, + + onRoomAccountData: function(room, event) { + if (room.roomId == this.props.roomId) { + if (event.getType === "m.room.color_scheme") { + var color_scheme = event.getContent(); + // XXX: we should validate the event + Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); + } + } + }, + onRoomReceipt: function(receiptEvent, room) { if (room.roomId == this.props.roomId) { this.forceUpdate(); @@ -337,6 +365,8 @@ module.exports = React.createClass({ this.scrollToBottom(); this.sendReadReceipt(); + + this.updateTint(); }, componentDidUpdate: function() { @@ -711,7 +741,7 @@ module.exports = React.createClass({ return ret; }, - uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { + uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels, new_color_scheme) { var old_name = this.state.room.name; var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); @@ -777,6 +807,14 @@ module.exports = React.createClass({ ); } + if (new_color_scheme) { + deferreds.push( + MatrixClientPeg.get().setRoomAccountData( + this.state.room.roomId, "m.room.color_scheme", new_color_scheme + ) + ); + } + if (deferreds.length) { var self = this; q.all(deferreds).fail(function(err) { @@ -866,17 +904,20 @@ module.exports = React.createClass({ var new_join_rule = this.refs.room_settings.getJoinRules(); var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); var new_power_levels = this.refs.room_settings.getPowerLevels(); + var new_color_scheme = this.refs.room_settings.getColorScheme(); this.uploadNewState( new_name, new_topic, new_join_rule, new_history_visibility, - new_power_levels + new_power_levels, + new_color_scheme ); }, onCancelClick: function() { + this.updateTint(); this.setState({editingRoomSettings: false}); }, diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 8b5435e46a..cdfbc0bfc8 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -116,7 +116,7 @@ module.exports = React.createClass({ } name = -
+
{ this.props.room.name }
{ searchStatus }
@@ -151,7 +151,7 @@ module.exports = React.createClass({ header =
-
+
{ roomAvatar }
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 211ecbd71a..0864dc15c7 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -16,8 +16,23 @@ limitations under the License. var React = require('react'); var MatrixClientPeg = require('../../../MatrixClientPeg'); +var Tinter = require('../../../Tinter'); var sdk = require('../../../index'); +var room_colors = [ + // magic room default values courtesy of Ribot + ["#76cfa6", "#eaf5f0"], + ["#81bddb", "#eaf1f4"], + ["#bd79cb", "#f3eaf5"], + ["#c65d94", "#f5eaef"], + ["#e55e5e", "#f5eaea"], + ["#eca46f", "#f5eeea"], + ["#dad658", "#f5f4ea"], + ["#80c553", "#eef5ea"], + ["#bb814e", "#eee8e3"], + ["#595959", "#ececec"], +]; + module.exports = React.createClass({ displayName: 'RoomSettings', @@ -26,8 +41,37 @@ module.exports = React.createClass({ }, getInitialState: function() { + // work out the initial color index + var room_color_index = undefined; + var color_scheme_event = this.props.room.getAccountData("m.room.color_scheme"); + if (color_scheme_event) { + var color_scheme = color_scheme_event.getContent(); + if (color_scheme.primary_color) color_scheme.primary_color = color_scheme.primary_color.toLowerCase(); + if (color_scheme.secondary_color) color_scheme.secondary_color = color_scheme.secondary_color.toLowerCase(); + // XXX: we should validate these values + for (var i = 0; i < room_colors.length; i++) { + var room_color = room_colors[i]; + if (room_color[0] === color_scheme.primary_color && + room_color[1] === color_scheme.secondary_color) + { + room_color_index = i; + break; + } + } + if (room_color_index === undefined) { + // append the unrecognised colours to our palette + room_color_index = room_colors.length; + room_colors[room_color_index] = [ color_scheme.primary_color, color_scheme.secondary_color ]; + } + } + else { + room_color_index = 0; + } + return { - power_levels_changed: false + power_levels_changed: false, + color_scheme_changed: false, + color_scheme_index: room_color_index, }; }, @@ -70,6 +114,25 @@ module.exports = React.createClass({ }); }, + getColorScheme: function() { + if (!this.state.color_scheme_changed) return undefined; + + return { + primary_color: room_colors[this.state.color_scheme_index][0], + secondary_color: room_colors[this.state.color_scheme_index][1], + }; + }, + + onColorSchemeChanged: function(index) { + // preview what the user just changed the scheme to. + Tinter.tint(room_colors[index][0], room_colors[index][1]); + + this.setState({ + color_scheme_changed: true, + color_scheme_index: index, + }); + }, + render: function() { var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); @@ -139,25 +202,91 @@ module.exports = React.createClass({ } var can_set_room_avatar = current_user_level >= room_avatar_level; + var self = this; + + var room_colors_section = +
+

Room Colour

+
+ {room_colors.map(function(room_color, i) { + var selected; + if (i === self.state.color_scheme_index) { + selected = +
+ ./ +
+ } + var boundClick = self.onColorSchemeChanged.bind(this, i) + return ( +
+ { selected } +
+
+ ); + })} +
+
; + var change_avatar; if (can_set_room_avatar) { - change_avatar =
-

Room Icon

- -
; + change_avatar = +
+

Room Icon

+ +
; } var banned = this.props.room.getMembersWithMembership("ban"); + var events_levels_section; + if (events_levels.length) { + events_levels_section = +
+

Event levels

+
+ {Object.keys(events_levels).map(function(event_type, i) { + return ( +
+ + +
+ ); + })} +
+
; + } + + var banned_users_section; + if (banned.length) { + banned_users_section = +
+

Banned users

+
+ {banned.map(function(member, i) { + return ( +
+ {member.userId} +
+ ); + })} +
+
; + } + return (