diff --git a/src/SlashCommands.js b/src/SlashCommands.js index 363560f0c6..1dd7ecb08f 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, "org.matrix.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 7245a5825b..3e7949b65d 100644 --- a/src/Tinter.js +++ b/src/Tinter.js @@ -127,6 +127,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); @@ -146,6 +151,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 ede2ab8f46..e5af2a86b5 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"); @@ -418,7 +419,16 @@ module.exports = React.createClass({ if (room) { var theAlias = MatrixTools.getCanonicalAliasForRoom(room); if (theAlias) presentedId = theAlias; + + var color_scheme_event = room.getAccountData("org.matrix.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 7f723edfd7..4392975b9a 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; @@ -84,6 +85,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); @@ -142,6 +144,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); @@ -149,6 +152,8 @@ module.exports = React.createClass({ } window.removeEventListener('resize', this.onResize); + + Tinter.tint(); // reset colourscheme }, onAction: function(payload) { @@ -262,6 +267,29 @@ module.exports = React.createClass({ } }, + updateTint: function() { + var room = MatrixClientPeg.get().getRoom(this.props.roomId); + if (!room) return; + + var color_scheme_event = room.getAccountData("org.matrix.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 === "org.matrix.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) { var readMarkerEventId = this.state.room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId); @@ -390,6 +418,8 @@ module.exports = React.createClass({ this.scrollToBottom(); this.sendReadReceipt(); + + this.updateTint(); }, componentDidUpdate: function() { @@ -876,6 +906,14 @@ module.exports = React.createClass({ ); } + if (newVals.color_scheme) { + deferreds.push( + MatrixClientPeg.get().setRoomAccountData( + this.state.room.roomId, "org.matrix.room.color_scheme", newVals.color_scheme + ) + ); + } + deferreds.push( MatrixClientPeg.get().setGuestAccess(this.state.room.roomId, { allowRead: newVals.guest_read, @@ -974,11 +1012,13 @@ module.exports = React.createClass({ history_visibility: this.refs.room_settings.getHistoryVisibility(), power_levels: this.refs.room_settings.getPowerLevels(), guest_join: this.refs.room_settings.canGuestsJoin(), - guest_read: this.refs.room_settings.canGuestsRead() + guest_read: this.refs.room_settings.canGuestsRead(), + color_scheme: this.refs.room_settings.getColorScheme(), }); }, 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 9e07385d65..c5e37521c5 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("org.matrix.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, }; }, @@ -78,6 +122,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'); @@ -151,16 +214,80 @@ 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(self, 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 (