From 0039ccf20338869e0ae465d5cd879757b3c1b3d3 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Mon, 20 Jul 2015 15:07:51 +0100 Subject: [PATCH 01/11] Add ability to edit room settings --- skins/base/css/organisms/RoomView.css | 4 +- skins/base/views/molecules/RoomHeader.js | 36 ++++++++++-- skins/base/views/organisms/RoomView.js | 74 +++++++++++++++++++++++- src/ComponentBroker.js | 1 + src/controllers/molecules/RoomHeader.js | 18 +++++- src/controllers/organisms/RoomView.js | 8 +-- 6 files changed, 125 insertions(+), 16 deletions(-) diff --git a/skins/base/css/organisms/RoomView.css b/skins/base/css/organisms/RoomView.css index 9341f213..88f52aa6 100644 --- a/skins/base/css/organisms/RoomView.css +++ b/skins/base/css/organisms/RoomView.css @@ -60,7 +60,7 @@ limitations under the License. order: 3; width: 100%; - height: 100%; + flex: 1; margin-top: 18px; margin-bottom: 18px; @@ -101,7 +101,7 @@ limitations under the License. .mx_RoomView_statusAreaBox { max-width: 720px; - margin: auto; + margin: auto; border-top: 1px solid #a8dbf3; } diff --git a/skins/base/views/molecules/RoomHeader.js b/skins/base/views/molecules/RoomHeader.js index a6769977..6d01c6a4 100644 --- a/skins/base/views/molecules/RoomHeader.js +++ b/skins/base/views/molecules/RoomHeader.js @@ -33,10 +33,13 @@ module.exports = React.createClass({ } }, + getRoomName: function() { + return this.refs.name_edit.getDOMNode().value; + }, + render: function() { var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); - topic = topic ? <div className="mx_RoomHeader_topic">{ topic.getContent().topic }</div> : null; var callButtons; if (this.state) { @@ -52,6 +55,28 @@ module.exports = React.createClass({ } } + var name = null; + var topic_el = null; + var save_button = null; + var settings_button = null; + if (this.props.editing) { + name = <input type="text" defaultValue={this.props.room.name} ref="name_edit"/>; + // if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div> + save_button = ( + <div className="mx_RoomHeader_button"onClick={this.props.onSaveClick}> + Save + </div> + ); + } else { + name = <EditableText initialValue={this.props.room.name} onValueChanged={this.onNameChange} />; + if (topic) topic_el = <div className="mx_RoomHeader_topic">{ topic.getContent().topic }</div>; + settings_button = ( + <div className="mx_RoomHeader_button" onClick={this.props.onSettingsClick}> + <img src="img/settings.png" width="32" height="32"/> + </div> + ); + } + return ( <div className="mx_RoomHeader"> <div className="mx_RoomHeader_wrapper"> @@ -61,15 +86,14 @@ module.exports = React.createClass({ </div> <div className="mx_RoomHeader_info"> <div className="mx_RoomHeader_name"> - <EditableText initialValue={this.props.room.name} onValueChanged={this.onNameChange} /> + { name } </div> - { topic } + { topic_el } </div> </div> <div className="mx_RoomHeader_rightRow"> - <div className="mx_RoomHeader_button"> - <img src="img/settings.png" width="32" height="32"/> - </div> + { save_button } + { settings_button } <div className="mx_RoomHeader_button"> <img src="img/search.png" width="32" height="32"/> </div> diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index a93937f0..3e1fb6c6 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -28,6 +28,7 @@ var MessageTile = ComponentBroker.get('molecules/MessageTile'); var RoomHeader = ComponentBroker.get('molecules/RoomHeader'); var MessageComposer = ComponentBroker.get('molecules/MessageComposer'); var CallView = ComponentBroker.get("molecules/voip/CallView"); +var RoomSettings = ComponentBroker.get("molecules/RoomSettings"); var RoomViewController = require("../../../../src/controllers/organisms/RoomView"); @@ -38,6 +39,68 @@ module.exports = React.createClass({ displayName: 'RoomView', mixins: [RoomViewController], + onSettingsClick: function() { + this.setState({editingRoomSettings: true}); + }, + + onSaveClick: function() { + this.setState({editingRoomSettings: false}); + + var new_name = this.refs.header.getRoomName(); + var new_topic = this.refs.room_settings.getTopic(); + var new_join_rule = this.refs.room_settings.getJoinRules(); + var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); + + var old_name = this.state.room.name; + + var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); + if (old_topic) { + old_topic = old_topic.getContent().topic; + } else { + old_topic = ""; + } + + var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); + if (old_join_rule) { + old_join_rule = old_join_rule.getContent().join_rule; + } else { + old_join_rule = "invite"; + } + + var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); + console.log(old_history_visibility); + if (old_history_visibility) { + old_history_visibility = old_history_visibility.getContent().history_visibility; + } else { + old_history_visibility = "shared"; + } + + + if (old_name != new_name && new_name != undefined) { + MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name); + } + + if (old_topic != new_topic && new_topic != undefined) { + MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic); + } + + if (old_join_rule != new_join_rule && new_join_rule != undefined) { + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.join_rules", { + join_rule: new_join_rule, + }, "" + ); + } + + if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.history_visibility", { + history_visibility: new_history_visibility, + }, "" + ); + } + }, + render: function() { if (!this.state.room) { return ( @@ -103,11 +166,19 @@ module.exports = React.createClass({ } } + var roomEdit = null; + + if (this.state.editingRoomSettings) { + roomEdit = <RoomSettings ref="room_settings" room={this.state.room} />; + } + return ( <div className="mx_RoomView"> - <RoomHeader room={this.state.room} /> + <RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} + onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick}/> <div className="mx_RoomView_auxPanel"> <CallView room={this.state.room}/> + { roomEdit } </div> <div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={this.onMessageListScroll}> <div className="mx_RoomView_messageListWrapper"> @@ -129,4 +200,3 @@ module.exports = React.createClass({ } }, }); - diff --git a/src/ComponentBroker.js b/src/ComponentBroker.js index 8a7bd663..56164d3d 100644 --- a/src/ComponentBroker.js +++ b/src/ComponentBroker.js @@ -92,6 +92,7 @@ require('../skins/base/views/molecules/UserSelector'); require('../skins/base/views/organisms/UserSettings'); require('../skins/base/views/molecules/ChangeAvatar'); require('../skins/base/views/molecules/ChangePassword'); +require('../skins/base/views/molecules/RoomSettings'); // new for vector require('../skins/base/views/organisms/LeftPanel'); require('../skins/base/views/organisms/RightPanel'); diff --git a/src/controllers/molecules/RoomHeader.js b/src/controllers/molecules/RoomHeader.js index 5bd51e44..2ef99953 100644 --- a/src/controllers/molecules/RoomHeader.js +++ b/src/controllers/molecules/RoomHeader.js @@ -21,10 +21,25 @@ limitations under the License. * this.state.call_state = the UI state of the call (see CallHandler) */ +var React = require('react'); var dis = require("../../dispatcher"); var CallHandler = require("../../CallHandler"); module.exports = { + propTypes: { + room: React.PropTypes.object.isRequired, + editing: React.PropTypes.bool, + onSettingsClick: React.PropTypes.func, + onSaveClick: React.PropTypes.func, + }, + + getDefaultProps: function() { + return { + editing: false, + onSettingsClick: function() {}, + onSaveClick: function() {}, + }; + }, componentDidMount: function() { this.dispatcherRef = dis.register(this.onAction); @@ -43,7 +58,7 @@ module.exports = { onAction: function(payload) { // if we were given a room_id to track, don't handle anything else. - if (payload.room_id && this.props.room && + if (payload.room_id && this.props.room && this.props.room.roomId !== payload.room_id) { return; } @@ -78,4 +93,3 @@ module.exports = { }); } }; - diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js index a5bae754..4f6c4d3c 100644 --- a/src/controllers/organisms/RoomView.js +++ b/src/controllers/organisms/RoomView.js @@ -44,7 +44,8 @@ module.exports = { getInitialState: function() { return { room: this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null, - messageCap: INITIAL_SIZE + messageCap: INITIAL_SIZE, + editingRoomSettings: false, } }, @@ -99,7 +100,7 @@ module.exports = { // we'll only be showing a spinner. if (this.state.joining) return; if (room.roomId != this.props.roomId) return; - + if (this.refs.messageWrapper) { var messageWrapper = this.refs.messageWrapper.getDOMNode(); this.atBottom = messageWrapper.scrollHeight - messageWrapper.scrollTop <= messageWrapper.clientHeight; @@ -300,7 +301,7 @@ module.exports = { dateSeparator = <DateSeparator key={ts1} ts={ts1}/>; continuation = false; } - } + } if (!TileType) continue; ret.unshift( <TileType key={mxEv.getId()} mxEvent={mxEv} continuation={continuation} last={last}/> @@ -313,4 +314,3 @@ module.exports = { return ret; } }; - From eae09728208a87734e72be03eff4216145a96f93 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Mon, 20 Jul 2015 16:28:23 +0100 Subject: [PATCH 02/11] Add files. Add power levels to room settings --- skins/base/css/molecules/RoomSettings.css | 46 ++++++++ skins/base/views/molecules/RoomSettings.js | 128 +++++++++++++++++++++ skins/base/views/organisms/RoomView.js | 1 - src/controllers/molecules/RoomSettings.js | 25 ++++ 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 skins/base/css/molecules/RoomSettings.css create mode 100644 skins/base/views/molecules/RoomSettings.js create mode 100644 src/controllers/molecules/RoomSettings.js diff --git a/skins/base/css/molecules/RoomSettings.css b/skins/base/css/molecules/RoomSettings.css new file mode 100644 index 00000000..5e325294 --- /dev/null +++ b/skins/base/css/molecules/RoomSettings.css @@ -0,0 +1,46 @@ +/* +Copyright 2015 OpenMarket 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. +*/ + +.mx_RoomSettings_power_levels { + display: table; + margin: 5px 0; +} + +.mx_RoomSettings_power_levels > div { + display: table-row; +} + +.mx_RoomSettings_power_levels > div > * { + display: table-cell; + + margin: 0 10px; +} + + +.mx_RoomSettings_user_levels { + display: table; + margin: 5px 0; +} + +.mx_RoomSettings_user_levels > div { + display: table-row; +} + +.mx_RoomSettings_user_levels > div > * { + display: table-cell; + + margin: 0 10px; +} diff --git a/skins/base/views/molecules/RoomSettings.js b/skins/base/views/molecules/RoomSettings.js new file mode 100644 index 00000000..358764c6 --- /dev/null +++ b/skins/base/views/molecules/RoomSettings.js @@ -0,0 +1,128 @@ +/* +Copyright 2015 OpenMarket 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. +*/ + +'use strict'; + +var React = require('react'); +var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); + +var RoomSettingsController = require("../../../../src/controllers/molecules/RoomSettings"); + +module.exports = React.createClass({ + displayName: 'RoomSettings', + mixins: [RoomSettingsController], + + getTopic: function() { + return this.refs.topic.getDOMNode().value; + }, + + getJoinRules: function() { + return this.refs.is_private.getDOMNode().checked ? "invite" : "public"; + }, + + getHistoryVisibility: function() { + return this.refs.share_history.getDOMNode().checked ? "shared" : "invited"; + }, + + render: function() { + var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); + if (topic) topic = topic.getContent().topic; + + var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', ''); + if (join_rule) join_rule = join_rule.getContent().join_rule; + + var history_visibility = this.props.room.currentState.getStateEvents('m.room.history_visibility', ''); + if (history_visibility) history_visibility = history_visibility.getContent().history_visibility; + + var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); + power_levels = power_levels.getContent(); + + var ban_level = parseInt(power_levels.ban); + var kick_level = parseInt(power_levels.kick); + var redact_level = parseInt(power_levels.redact); + var invite_level = parseInt(power_levels.invite); + var send_level = parseInt(power_levels.events_default); + var state_level = parseInt(power_levels.state_default); + var default_user_level = parseInt(power_levels.users_default); + + var user_levels = power_levels.users; + + var user_id = MatrixClientPeg.get().credentials.userId; + + var current_user_level = user_levels[user_id]; + if (current_user_level == undefined) current_user_level = default_user_level; + + var power_level_level = power_levels.events["m.room.power_levels"]; + if (power_level_level == undefined) { + power_level_level = state_level; + } + + var can_change_levels = current_user_level >= power_level_level; + + return ( + <div className="mx_RoomSettings"> + <textarea placeholder="Description" defaultValue={topic} ref="topic"/> <br/> + <label><input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private</label> <br/> + <label><input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users</label> <br/> + <label><input type="checkbox" /> Encrypt room</label> <br/> + + Power levels: + <div className="mx_RoomSettings_power_levels"> + <div> + <label htmlFor="mx_RoomSettings_ban_level">Ban level</label> + <input type="text" defaultValue={ban_level} size="3" id="mx_RoomSettings_ban_level" disabled={!can_change_levels || current_user_level < ban_level}/> + </div> + <div> + <label htmlFor="mx_RoomSettings_kick_level">Kick level</label> + <input type="text" defaultValue={kick_level} size="3" id="mx_RoomSettings_kick_level" disabled={!can_change_levels || current_user_level < kick_level}/> + </div> + <div> + <label htmlFor="mx_RoomSettings_redact_level">Redact level</label> + <input type="text" defaultValue={redact_level} size="3" id="mx_RoomSettings_redact_level" disabled={!can_change_levels || current_user_level < redact_level}/> + </div> + <div> + <label htmlFor="mx_RoomSettings_invite_level">Invite level</label> + <input type="text" defaultValue={invite_level} size="3" id="mx_RoomSettings_invite_level" disabled={!can_change_levels || current_user_level < invite_level}/> + </div> + <div> + <label htmlFor="mx_RoomSettings_event_level">Send event level</label> + <input type="text" defaultValue={send_level} size="3" id="mx_RoomSettings_event_level" disabled={!can_change_levels || current_user_level < send_level}/> + </div> + <div> + <label htmlFor="mx_RoomSettings_state_level">Set state level</label> + <input type="text" defaultValue={state_level} size="3" id="mx_RoomSettings_state_level" disabled={!can_change_levels || current_user_level < state_level}/> + </div> + <div> + <label htmlFor="mx_RoomSettings_user_level">Default user level</label> + <input type="text" defaultValue={default_user_level} size="3" id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level}/> + </div> + </div> + + User levels: + <div className="mx_RoomSettings_user_levels"> + {Object.keys(user_levels).map(function(user, i) { + return ( + <div key={user}> + <label htmlFor={"mx_RoomSettings_user_"+i}>{user}</label> + <input type="text" defaultValue={user_levels[user]} size="3" id={"mx_RoomSettings_user_"+i} disabled/> + </div> + ); + })} + </div> + </div> + ); + } +}); diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index 3e1fb6c6..f6703c3c 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -68,7 +68,6 @@ module.exports = React.createClass({ } var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); - console.log(old_history_visibility); if (old_history_visibility) { old_history_visibility = old_history_visibility.getContent().history_visibility; } else { diff --git a/src/controllers/molecules/RoomSettings.js b/src/controllers/molecules/RoomSettings.js new file mode 100644 index 00000000..546290ee --- /dev/null +++ b/src/controllers/molecules/RoomSettings.js @@ -0,0 +1,25 @@ +/* +Copyright 2015 OpenMarket 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. +*/ + +'use strict'; + +var React = require('react'); + +module.exports = { + propTypes: { + room: React.PropTypes.object.isRequired, + }, +}; From 3a7ebf73eb2e572524d9ad032b808364132be7a6 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Mon, 20 Jul 2015 17:31:40 +0100 Subject: [PATCH 03/11] Wire up changing of power levels --- skins/base/views/molecules/RoomSettings.js | 49 ++++++++++++++++++---- skins/base/views/organisms/RoomView.js | 7 ++++ src/controllers/molecules/RoomSettings.js | 6 +++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/skins/base/views/molecules/RoomSettings.js b/skins/base/views/molecules/RoomSettings.js index 358764c6..14197154 100644 --- a/skins/base/views/molecules/RoomSettings.js +++ b/skins/base/views/molecules/RoomSettings.js @@ -37,6 +37,33 @@ module.exports = React.createClass({ return this.refs.share_history.getDOMNode().checked ? "shared" : "invited"; }, + getPowerLevels: function() { + if (!this.state.power_levels_changed) return undefined; + + var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); + power_levels = power_levels.getContent(); + + var new_power_levels = { + ban: parseInt(this.refs.ban.getDOMNode().value), + kick: parseInt(this.refs.kick.getDOMNode().value), + redact: parseInt(this.refs.redact.getDOMNode().value), + invite: parseInt(this.refs.invite.getDOMNode().value), + events_default: parseInt(this.refs.events_default.getDOMNode().value), + state_default: parseInt(this.refs.state_default.getDOMNode().value), + users_default: parseInt(this.refs.users_default.getDOMNode().value), + users: power_levels.users, + events: power_levels.events, + }; + + return new_power_levels; + }, + + onPowerLevelsChanged: function() { + this.setState({ + power_levels_changed: true + }); + }, + render: function() { var topic = this.props.room.currentState.getStateEvents('m.room.topic', ''); if (topic) topic = topic.getContent().topic; @@ -83,31 +110,39 @@ module.exports = React.createClass({ <div className="mx_RoomSettings_power_levels"> <div> <label htmlFor="mx_RoomSettings_ban_level">Ban level</label> - <input type="text" defaultValue={ban_level} size="3" id="mx_RoomSettings_ban_level" disabled={!can_change_levels || current_user_level < ban_level}/> + <input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level" + disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/> </div> <div> <label htmlFor="mx_RoomSettings_kick_level">Kick level</label> - <input type="text" defaultValue={kick_level} size="3" id="mx_RoomSettings_kick_level" disabled={!can_change_levels || current_user_level < kick_level}/> + <input type="text" defaultValue={kick_level} size="3" ref="kick" id="mx_RoomSettings_kick_level" + disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/> </div> <div> <label htmlFor="mx_RoomSettings_redact_level">Redact level</label> - <input type="text" defaultValue={redact_level} size="3" id="mx_RoomSettings_redact_level" disabled={!can_change_levels || current_user_level < redact_level}/> + <input type="text" defaultValue={redact_level} size="3" ref="redact" id="mx_RoomSettings_redact_level" + disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/> </div> <div> <label htmlFor="mx_RoomSettings_invite_level">Invite level</label> - <input type="text" defaultValue={invite_level} size="3" id="mx_RoomSettings_invite_level" disabled={!can_change_levels || current_user_level < invite_level}/> + <input type="text" defaultValue={invite_level} size="3" ref="invite" id="mx_RoomSettings_invite_level" + disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/> </div> <div> <label htmlFor="mx_RoomSettings_event_level">Send event level</label> - <input type="text" defaultValue={send_level} size="3" id="mx_RoomSettings_event_level" disabled={!can_change_levels || current_user_level < send_level}/> + <input type="text" defaultValue={send_level} size="3" ref="events_default" id="mx_RoomSettings_event_level" + disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/> </div> <div> <label htmlFor="mx_RoomSettings_state_level">Set state level</label> - <input type="text" defaultValue={state_level} size="3" id="mx_RoomSettings_state_level" disabled={!can_change_levels || current_user_level < state_level}/> + <input type="text" defaultValue={state_level} size="3" ref="state_default" id="mx_RoomSettings_state_level" + disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/> </div> <div> <label htmlFor="mx_RoomSettings_user_level">Default user level</label> - <input type="text" defaultValue={default_user_level} size="3" id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level}/> + <input type="text" defaultValue={default_user_level} size="3" ref="users_default" + id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level} + onChange={this.onPowerLevelsChanged}/> </div> </div> diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index 62c750bf..56699165 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -50,6 +50,7 @@ module.exports = React.createClass({ var new_topic = this.refs.room_settings.getTopic(); 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 old_name = this.state.room.name; @@ -98,6 +99,12 @@ module.exports = React.createClass({ }, "" ); } + + if (new_power_levels) { + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.power_levels", new_power_levels, "" + ); + } }, render: function() { diff --git a/src/controllers/molecules/RoomSettings.js b/src/controllers/molecules/RoomSettings.js index 546290ee..fe7cd634 100644 --- a/src/controllers/molecules/RoomSettings.js +++ b/src/controllers/molecules/RoomSettings.js @@ -22,4 +22,10 @@ module.exports = { propTypes: { room: React.PropTypes.object.isRequired, }, + + getInitialState: function() { + return { + power_levels_changed: false + }; + } }; From af190f286c533203e8f1264b5545cb7eff5f37b9 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 10:33:41 +0100 Subject: [PATCH 04/11] Add event power levels to room settings --- skins/base/css/molecules/RoomSettings.css | 22 +++------------------- skins/base/views/molecules/RoomSettings.js | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/skins/base/css/molecules/RoomSettings.css b/skins/base/css/molecules/RoomSettings.css index 5e325294..4100b9e3 100644 --- a/skins/base/css/molecules/RoomSettings.css +++ b/skins/base/css/molecules/RoomSettings.css @@ -14,32 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_RoomSettings_power_levels { +.mx_RoomSettings_settings { display: table; margin: 5px 0; } -.mx_RoomSettings_power_levels > div { +.mx_RoomSettings_settings > div { display: table-row; } -.mx_RoomSettings_power_levels > div > * { - display: table-cell; - - margin: 0 10px; -} - - -.mx_RoomSettings_user_levels { - display: table; - margin: 5px 0; -} - -.mx_RoomSettings_user_levels > div { - display: table-row; -} - -.mx_RoomSettings_user_levels > div > * { +.mx_RoomSettings_settings > div > * { display: table-cell; margin: 0 10px; diff --git a/skins/base/views/molecules/RoomSettings.js b/skins/base/views/molecules/RoomSettings.js index 14197154..4f562da0 100644 --- a/skins/base/views/molecules/RoomSettings.js +++ b/skins/base/views/molecules/RoomSettings.js @@ -86,6 +86,7 @@ module.exports = React.createClass({ var default_user_level = parseInt(power_levels.users_default); var user_levels = power_levels.users; + var events_levels = power_levels.events; var user_id = MatrixClientPeg.get().credentials.userId; @@ -107,7 +108,7 @@ module.exports = React.createClass({ <label><input type="checkbox" /> Encrypt room</label> <br/> Power levels: - <div className="mx_RoomSettings_power_levels"> + <div className="mx_RoomSettings_power_levels mx_RoomSettings_settings"> <div> <label htmlFor="mx_RoomSettings_ban_level">Ban level</label> <input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level" @@ -147,7 +148,7 @@ module.exports = React.createClass({ </div> User levels: - <div className="mx_RoomSettings_user_levels"> + <div className="mx_RoomSettings_user_levels mx_RoomSettings_settings"> {Object.keys(user_levels).map(function(user, i) { return ( <div key={user}> @@ -157,6 +158,18 @@ module.exports = React.createClass({ ); })} </div> + + Event levels: + <div className="mx_RoomSettings_event_lvels mx_RoomSettings_settings"> + {Object.keys(events_levels).map(function(event_type, i) { + return ( + <div key={event_type}> + <label htmlFor={"mx_RoomSettings_event_"+i}>{event_type}</label> + <input type="text" defaultValue={events_levels[event_type]} size="3" id={"mx_RoomSettings_event_"+i} disabled/> + </div> + ); + })} + </div> </div> ); } From 2bec7ec981b9a83b31c438920b85eec7169dcf16 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 11:02:36 +0100 Subject: [PATCH 05/11] Add spinner while uploading state --- skins/base/views/organisms/RoomView.js | 64 ++++++++++++++++++++------ src/controllers/organisms/RoomView.js | 1 + 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index 4f19b9b3..74c3f1ee 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -23,6 +23,7 @@ var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var ComponentBroker = require('../../../../src/ComponentBroker'); var classNames = require("classnames"); var filesize = require('filesize'); +var q = require('q'); var MessageTile = ComponentBroker.get('molecules/MessageTile'); var RoomHeader = ComponentBroker.get('molecules/RoomHeader'); @@ -44,7 +45,10 @@ module.exports = React.createClass({ }, onSaveClick: function() { - this.setState({editingRoomSettings: false}); + this.setState({ + editingRoomSettings: false, + uploadingRoomSettings: true, + }); var new_name = this.refs.header.getRoomName(); var new_topic = this.refs.room_settings.getTopic(); @@ -75,36 +79,65 @@ module.exports = React.createClass({ old_history_visibility = "shared"; } + var deferreds = []; if (old_name != new_name && new_name != undefined) { - MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name); + deferreds.push( + MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) + ); } if (old_topic != new_topic && new_topic != undefined) { - MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic); + deferreds.push( + MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) + ); } if (old_join_rule != new_join_rule && new_join_rule != undefined) { - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.join_rules", { - join_rule: new_join_rule, - }, "" + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.join_rules", { + join_rule: new_join_rule, + }, "" + ) ); } if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.history_visibility", { - history_visibility: new_history_visibility, - }, "" + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.history_visibility", { + history_visibility: new_history_visibility, + }, "" + ) ); } if (new_power_levels) { - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.power_levels", new_power_levels, "" + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.power_levels", new_power_levels, "" + ) ); } + + console.log("deferreds " + deferreds.length); + + if (deferreds.length) { + var self = this; + q.all(deferreds).fail(function(err) { + // TODO: Handle err + }).finally(function() { + self.setState({ + uploadingRoomSettings: false, + }); + }); + } else { + this.setState({ + editingRoomSettings: false, + uploadingRoomSettings: false, + }); + } }, render: function() { @@ -179,6 +212,11 @@ module.exports = React.createClass({ roomEdit = <RoomSettings ref="room_settings" room={this.state.room} />; } + if (this.state.uploadingRoomSettings) { + console.log("Uploading"); + roomEdit = <Loader/>; + } + return ( <div className="mx_RoomView"> <RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js index 4f6c4d3c..b02b2937 100644 --- a/src/controllers/organisms/RoomView.js +++ b/src/controllers/organisms/RoomView.js @@ -46,6 +46,7 @@ module.exports = { room: this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null, messageCap: INITIAL_SIZE, editingRoomSettings: false, + uploadingRoomSettings: false, } }, From 0e9074b0de945d040428ded3b019ed6da7bc4409 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 11:22:11 +0100 Subject: [PATCH 06/11] Remove console.logs --- skins/base/views/organisms/RoomView.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index 74c3f1ee..df4c157e 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -121,12 +121,11 @@ module.exports = React.createClass({ ); } - console.log("deferreds " + deferreds.length); - if (deferreds.length) { var self = this; q.all(deferreds).fail(function(err) { // TODO: Handle err + console.error(err); }).finally(function() { self.setState({ uploadingRoomSettings: false, @@ -213,7 +212,6 @@ module.exports = React.createClass({ } if (this.state.uploadingRoomSettings) { - console.log("Uploading"); roomEdit = <Loader/>; } From 085e07c5b1360a14ed9aca5f06e08cf64b7b3a5a Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 11:26:08 +0100 Subject: [PATCH 07/11] Display error on fail --- skins/base/views/organisms/RoomView.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index df4c157e..124f74cd 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -21,6 +21,7 @@ var React = require('react'); var MatrixClientPeg = require("../../../../src/MatrixClientPeg"); var ComponentBroker = require('../../../../src/ComponentBroker'); +var Modal = require("../../../../src/Modal"); var classNames = require("classnames"); var filesize = require('filesize'); var q = require('q'); @@ -30,6 +31,7 @@ var RoomHeader = ComponentBroker.get('molecules/RoomHeader'); var MessageComposer = ComponentBroker.get('molecules/MessageComposer'); var CallView = ComponentBroker.get("molecules/voip/CallView"); var RoomSettings = ComponentBroker.get("molecules/RoomSettings"); +var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog"); var RoomViewController = require("../../../../src/controllers/organisms/RoomView"); @@ -124,8 +126,10 @@ module.exports = React.createClass({ if (deferreds.length) { var self = this; q.all(deferreds).fail(function(err) { - // TODO: Handle err - console.error(err); + Modal.createDialog(ErrorDialog, { + title: "Failed to set state", + description: err.toString() + }); }).finally(function() { self.setState({ uploadingRoomSettings: false, From 76c014b9ef7fd4ab519ab106e97c7a6938ea1acd Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 13:33:01 +0100 Subject: [PATCH 08/11] Deal with the insanity if there are no power levels --- skins/base/views/molecules/RoomSettings.js | 60 +++++++++++++++------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/skins/base/views/molecules/RoomSettings.js b/skins/base/views/molecules/RoomSettings.js index 4f562da0..2eecd096 100644 --- a/skins/base/views/molecules/RoomSettings.js +++ b/skins/base/views/molecules/RoomSettings.js @@ -75,31 +75,55 @@ module.exports = React.createClass({ if (history_visibility) history_visibility = history_visibility.getContent().history_visibility; var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', ''); - power_levels = power_levels.getContent(); - var ban_level = parseInt(power_levels.ban); - var kick_level = parseInt(power_levels.kick); - var redact_level = parseInt(power_levels.redact); - var invite_level = parseInt(power_levels.invite); - var send_level = parseInt(power_levels.events_default); - var state_level = parseInt(power_levels.state_default); - var default_user_level = parseInt(power_levels.users_default); + if (power_levels) { + power_levels = power_levels.getContent(); - var user_levels = power_levels.users; - var events_levels = power_levels.events; + var ban_level = parseInt(power_levels.ban); + var kick_level = parseInt(power_levels.kick); + var redact_level = parseInt(power_levels.redact); + var invite_level = parseInt(power_levels.invite || 0); + var send_level = parseInt(power_levels.events_default || 0); + var state_level = parseInt(power_levels.state_default || 0); + var default_user_level = parseInt(power_levels.users_default || 0); - var user_id = MatrixClientPeg.get().credentials.userId; + if (power_levels.ban == undefined) ban_level = 50; + if (power_levels.kick == undefined) kick_level = 50; + if (power_levels.redact == undefined) redact_level = 50; - var current_user_level = user_levels[user_id]; - if (current_user_level == undefined) current_user_level = default_user_level; + var user_levels = power_levels.users || []; + var events_levels = power_levels.events || []; - var power_level_level = power_levels.events["m.room.power_levels"]; - if (power_level_level == undefined) { - power_level_level = state_level; + var user_id = MatrixClientPeg.get().credentials.userId; + + var current_user_level = user_levels[user_id]; + if (current_user_level == undefined) current_user_level = default_user_level; + + var power_level_level = events_levels["m.room.power_levels"]; + if (power_level_level == undefined) { + power_level_level = state_level; + } + + var can_change_levels = current_user_level >= power_level_level; + } else { + var ban_level = 50; + var kick_level = 50; + var redact_level = 50; + var invite_level = 0; + var send_level = 0; + var state_level = 0; + var default_user_level = 0; + + var user_levels = []; + var events_levels = []; + + var current_user_level = 0; + + var power_level_level = 0; + + var can_change_levels = false; } - var can_change_levels = current_user_level >= power_level_level; - return ( <div className="mx_RoomSettings"> <textarea placeholder="Description" defaultValue={topic} ref="topic"/> <br/> From d81260c92aa1db7449294d9ee9bc4749a2f4bf0e Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 14:13:59 +0100 Subject: [PATCH 09/11] Use getDefaultProps instead of setting porps --- skins/base/views/organisms/ErrorDialog.js | 29 +++-------------- src/controllers/organisms/ErrorDialog.js | 39 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 src/controllers/organisms/ErrorDialog.js diff --git a/skins/base/views/organisms/ErrorDialog.js b/skins/base/views/organisms/ErrorDialog.js index 847e796f..fd34a734 100644 --- a/skins/base/views/organisms/ErrorDialog.js +++ b/skins/base/views/organisms/ErrorDialog.js @@ -17,7 +17,7 @@ limitations under the License. 'use strict'; /* - * Usage: + * Usage: * Modal.createDialog(ErrorDialog, { * title: "some text", (default: "Error") * description: "some more text", @@ -28,31 +28,11 @@ limitations under the License. */ var React = require('react'); +var ErrorDialogController = require("../../../../src/controllers/organisms/ErrorDialog"); module.exports = React.createClass({ displayName: 'ErrorDialog', - - // can't use getDefaultProps, see Modal.js - componentWillMount: function() { - if (!this.props.title) { - this.props.title = "Error"; - } - if (!this.props.description) { - this.props.description = "An error has occurred."; - } - if (!this.props.button) { - this.props.button = "OK"; - } - if (this.props.focus === undefined) { - this.props.focus = true; - } - if (!this.props.onClose) { - var self = this; - this.props.onClose = function() { - self.props.onFinished(); - }; - } - }, + mixins: [ErrorDialogController], render: function() { return ( @@ -61,11 +41,10 @@ module.exports = React.createClass({ {this.props.title} </div> {this.props.description}<br /> - <button onClick={this.props.onClose} autoFocus={this.props.focus}> + <button onClick={this.props.onFinished} autoFocus={this.props.focus}> {this.props.button} </button> </div> ); } }); - diff --git a/src/controllers/organisms/ErrorDialog.js b/src/controllers/organisms/ErrorDialog.js new file mode 100644 index 00000000..73f66c87 --- /dev/null +++ b/src/controllers/organisms/ErrorDialog.js @@ -0,0 +1,39 @@ +/* +Copyright 2015 OpenMarket 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. +*/ + +'use strict'; + +var React = require("react"); + +module.exports = { + propTypes: { + title: React.PropTypes.string, + description: React.PropTypes.string, + button: React.PropTypes.string, + focus: React.PropTypes.bool, + onFinished: React.PropTypes.func.isRequired, + }, + + getDefaultProps: function() { + var self = this; + return { + title: "Error", + description: "An error has occurred.", + button: "OK", + focus: true, + }; + }, +}; From 16846c36fd8d878f4465bca4553ce465dd3221c0 Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 14:46:42 +0100 Subject: [PATCH 10/11] Handle default named rooms when editing room names --- skins/base/views/molecules/RoomHeader.js | 8 +++++--- skins/base/views/organisms/RoomView.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/skins/base/views/molecules/RoomHeader.js b/skins/base/views/molecules/RoomHeader.js index fdf4d7e5..e08f2651 100644 --- a/skins/base/views/molecules/RoomHeader.js +++ b/skins/base/views/molecules/RoomHeader.js @@ -28,7 +28,7 @@ module.exports = React.createClass({ mixins: [RoomHeaderController], onNameChange: function(new_name) { - if (this.props.room.name != new_name) { + if (this.props.room.name != new_name && new_name) { MatrixClientPeg.get().setRoomName(this.props.room.roomId, new_name); } }, @@ -69,8 +69,10 @@ module.exports = React.createClass({ var topic_el = null; var save_button = null; var settings_button = null; + var actual_name = this.props.room.currentState.getStateEvents('m.room.name', ''); + if (actual_name) actual_name = actual_name.getContent().name; if (this.props.editing) { - name = <input type="text" defaultValue={this.props.room.name} ref="name_edit"/>; + name = <input type="text" defaultValue={actual_name} placeHolder="Name" ref="name_edit"/>; // if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div> save_button = ( <div className="mx_RoomHeader_button"onClick={this.props.onSaveClick}> @@ -78,7 +80,7 @@ module.exports = React.createClass({ </div> ); } else { - name = <EditableText initialValue={this.props.room.name} onValueChanged={this.onNameChange} />; + name = <EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />; if (topic) topic_el = <div className="mx_RoomHeader_topic">{ topic.getContent().topic }</div>; settings_button = ( <div className="mx_RoomHeader_button" onClick={this.props.onSettingsClick}> diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index 124f74cd..ffd7bad5 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -83,7 +83,7 @@ module.exports = React.createClass({ var deferreds = []; - if (old_name != new_name && new_name != undefined) { + if (old_name != new_name && new_name != undefined && new_name) { deferreds.push( MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) ); From c5d84562bad78980d4a42a78ccfd6dd4a433ffaa Mon Sep 17 00:00:00 2001 From: Erik Johnston <erikj@matrix.org> Date: Tue, 21 Jul 2015 15:24:10 +0100 Subject: [PATCH 11/11] Move logic from view to controller --- skins/base/views/molecules/RoomHeader.js | 2 +- skins/base/views/organisms/RoomView.js | 91 ++---------------------- src/controllers/organisms/RoomView.js | 90 +++++++++++++++++++++++ 3 files changed, 98 insertions(+), 85 deletions(-) diff --git a/skins/base/views/molecules/RoomHeader.js b/skins/base/views/molecules/RoomHeader.js index e08f2651..7ad001d6 100644 --- a/skins/base/views/molecules/RoomHeader.js +++ b/skins/base/views/molecules/RoomHeader.js @@ -72,7 +72,7 @@ module.exports = React.createClass({ var actual_name = this.props.room.currentState.getStateEvents('m.room.name', ''); if (actual_name) actual_name = actual_name.getContent().name; if (this.props.editing) { - name = <input type="text" defaultValue={actual_name} placeHolder="Name" ref="name_edit"/>; + name = <input type="text" defaultValue={actual_name} placeholder="Name" ref="name_edit"/>; // if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div> save_button = ( <div className="mx_RoomHeader_button"onClick={this.props.onSaveClick}> diff --git a/skins/base/views/organisms/RoomView.js b/skins/base/views/organisms/RoomView.js index ffd7bad5..6ecd02d7 100644 --- a/skins/base/views/organisms/RoomView.js +++ b/skins/base/views/organisms/RoomView.js @@ -31,7 +31,6 @@ var RoomHeader = ComponentBroker.get('molecules/RoomHeader'); var MessageComposer = ComponentBroker.get('molecules/MessageComposer'); var CallView = ComponentBroker.get("molecules/voip/CallView"); var RoomSettings = ComponentBroker.get("molecules/RoomSettings"); -var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog"); var RoomViewController = require("../../../../src/controllers/organisms/RoomView"); @@ -58,89 +57,13 @@ module.exports = React.createClass({ var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); var new_power_levels = this.refs.room_settings.getPowerLevels(); - var old_name = this.state.room.name; - - var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); - if (old_topic) { - old_topic = old_topic.getContent().topic; - } else { - old_topic = ""; - } - - var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); - if (old_join_rule) { - old_join_rule = old_join_rule.getContent().join_rule; - } else { - old_join_rule = "invite"; - } - - var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); - if (old_history_visibility) { - old_history_visibility = old_history_visibility.getContent().history_visibility; - } else { - old_history_visibility = "shared"; - } - - var deferreds = []; - - if (old_name != new_name && new_name != undefined && new_name) { - deferreds.push( - MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) - ); - } - - if (old_topic != new_topic && new_topic != undefined) { - deferreds.push( - MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) - ); - } - - if (old_join_rule != new_join_rule && new_join_rule != undefined) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.join_rules", { - join_rule: new_join_rule, - }, "" - ) - ); - } - - if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.history_visibility", { - history_visibility: new_history_visibility, - }, "" - ) - ); - } - - if (new_power_levels) { - deferreds.push( - MatrixClientPeg.get().sendStateEvent( - this.state.room.roomId, "m.room.power_levels", new_power_levels, "" - ) - ); - } - - if (deferreds.length) { - var self = this; - q.all(deferreds).fail(function(err) { - Modal.createDialog(ErrorDialog, { - title: "Failed to set state", - description: err.toString() - }); - }).finally(function() { - self.setState({ - uploadingRoomSettings: false, - }); - }); - } else { - this.setState({ - editingRoomSettings: false, - uploadingRoomSettings: false, - }); - } + this.uploadNewState( + new_name, + new_topic, + new_join_rule, + new_history_visibility, + new_power_levels + ); }, render: function() { diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js index b02b2937..311a3e1e 100644 --- a/src/controllers/organisms/RoomView.js +++ b/src/controllers/organisms/RoomView.js @@ -21,6 +21,10 @@ var React = require("react"); var q = require("q"); var ContentMessages = require("../../ContentMessages"); var WhoIsTyping = require("../../WhoIsTyping"); +var Modal = require("../../Modal"); +var ComponentBroker = require('../../ComponentBroker'); + +var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog"); var dis = require("../../dispatcher"); @@ -313,5 +317,91 @@ module.exports = { ++count; } return ret; + }, + + uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { + var old_name = this.state.room.name; + + var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); + if (old_topic) { + old_topic = old_topic.getContent().topic; + } else { + old_topic = ""; + } + + var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); + if (old_join_rule) { + old_join_rule = old_join_rule.getContent().join_rule; + } else { + old_join_rule = "invite"; + } + + var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); + if (old_history_visibility) { + old_history_visibility = old_history_visibility.getContent().history_visibility; + } else { + old_history_visibility = "shared"; + } + + var deferreds = []; + + if (old_name != new_name && new_name != undefined && new_name) { + deferreds.push( + MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) + ); + } + + if (old_topic != new_topic && new_topic != undefined) { + deferreds.push( + MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) + ); + } + + if (old_join_rule != new_join_rule && new_join_rule != undefined) { + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.join_rules", { + join_rule: new_join_rule, + }, "" + ) + ); + } + + if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.history_visibility", { + history_visibility: new_history_visibility, + }, "" + ) + ); + } + + if (new_power_levels) { + deferreds.push( + MatrixClientPeg.get().sendStateEvent( + this.state.room.roomId, "m.room.power_levels", new_power_levels, "" + ) + ); + } + + if (deferreds.length) { + var self = this; + q.all(deferreds).fail(function(err) { + Modal.createDialog(ErrorDialog, { + title: "Failed to set state", + description: err.toString() + }); + }).finally(function() { + self.setState({ + uploadingRoomSettings: false, + }); + }); + } else { + this.setState({ + editingRoomSettings: false, + uploadingRoomSettings: false, + }); + } } };