Merge pull request #9 from matrix-org/create_room

Wire in create room.
This commit is contained in:
David Baker 2015-07-16 17:37:48 +01:00
commit 03733e6a0f
13 changed files with 290 additions and 107 deletions

View File

@ -25,14 +25,15 @@ module.exports = React.createClass({
mixins: [PresetsController],
onValueChanged: function(ev) {
this.setState({preset: ev.target.value})
this.props.onChange(ev.target.value)
},
render: function() {
return (
<select className="mx_Presets" onChange={this.onValueChanged} defaultValue={this.state.preset}>
<option value="private_chat">Private Chat</option>
<option value="public_chat">Public Chat</option>
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
<option value={this.Presets.PrivateChat}>Private Chat</option>
<option value={this.Presets.PublicChat}>Public Chat</option>
<option value={this.Presets.Custom}>Custom</option>
</select>
);
}

View File

@ -0,0 +1,79 @@
/*
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 RoomAliasController = require("../../../../../src/controllers/atoms/create_room/RoomAlias");
module.exports = React.createClass({
displayName: 'RoomAlias',
mixins: [RoomAliasController],
onValueChanged: function(ev) {
this.props.onChange(ev.target.value);
},
onFocus: function(ev) {
var target = ev.target;
var curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "") {
setTimeout(function() {
target.value = "#:" + this.props.homeserver;
target.setSelectionRange(1, 1);
}, 0);
} else {
var suffix = ":" + this.props.homeserver;
setTimeout(function() {
target.setSelectionRange(
curr_val.startsWith("#") ? 1 : 0,
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length
);
}, 0);
}
}
},
onBlur: function(ev) {
var curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "#:" + this.props.homeserver) {
ev.target.value = "";
return;
}
if (curr_val != "") {
var new_val = ev.target.value;
var suffix = ":" + this.props.homeserver;
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
ev.target.value = new_val;
}
}
},
render: function() {
return (
<input type="text" className="mx_RoomAlias" placeholder="Alias (optional)"
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
value={this.props.alias}/>
);
}
});

View File

@ -1,36 +0,0 @@
/*
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 RoomNameTextboxController = require("../../../../../src/controllers/atoms/create_room/RoomNameTextbox");
module.exports = React.createClass({
displayName: 'RoomNameTextbox',
mixins: [RoomNameTextboxController],
onValueChanged: function(ev) {
this.setState({room_name: ev.target.value})
},
render: function() {
return (
<input type="text" className="mx_RoomNameTextbox" placeholder="ex. MyNewRoom" onChange={this.onValueChanged}/>
);
}
});

View File

@ -33,11 +33,15 @@ module.exports = React.createClass({
dis.dispatch({action: 'view_user_settings'});
},
onCreateRoomClick: function() {
dis.dispatch({action: 'view_create_room'});
},
render: function() {
return (
<div className="mx_DirectoryMenu">
<div className="mx_DirectoryMenu_options">
<div className="mx_RoomTile">
<div className="mx_RoomTile" onClick={this.onCreateRoomClick}>
<div className="mx_RoomTile_avatar">
<img src="img/create-big.png" width="42" height="42"/>
</div>

View File

@ -26,17 +26,19 @@ module.exports = React.createClass({
onAddUserId: function() {
this.addUser(this.refs.user_id_input.getDOMNode().value);
this.refs.user_id_input.getDOMNode().value = "";
},
render: function() {
var self = this;
return (
<div>
<ul className="mx_UserSelector_UserIdList" ref="list">
{this.state.selected_users.map(function(user_id, i) {
return <li key={user_id}>{user_id}</li>
{this.props.selected_users.map(function(user_id, i) {
return <li key={user_id}>{user_id} - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>
})}
</ul>
<input type="text" ref="user_id_input" className="mx_UserSelector_userIdInput" placeholder="ex. @bob:example.com"/>
<input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder="ex. @bob:example.com"/>
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">Add User</button>
</div>
);

View File

@ -22,11 +22,17 @@ var CreateRoomController = require("../../../../src/controllers/organisms/Create
var ComponentBroker = require('../../../../src/ComponentBroker');
var PresetValues = require('../../../../src/controllers/atoms/create_room/Presets').Presets;
var CreateRoomButton = ComponentBroker.get("atoms/create_room/CreateRoomButton");
var RoomNameTextbox = ComponentBroker.get("atoms/create_room/RoomNameTextbox");
var RoomTopic = ComponentBroker.get("atoms/create_room/RoomTopic");
var RoomAlias = ComponentBroker.get("atoms/create_room/RoomAlias");
var Presets = ComponentBroker.get("atoms/create_room/Presets");
var UserSelector = ComponentBroker.get("molecules/UserSelector");
var Loader = require("react-loader");
module.exports = React.createClass({
displayName: 'CreateRoom',
@ -40,15 +46,85 @@ module.exports = React.createClass({
return this.refs.name_textbox.getName();
},
getTopic: function() {
return this.refs.topic.getTopic();
},
getAliasLocalpart: function() {
return this.refs.alias.getAliasLocalpart();
},
getInvitedUsers: function() {
return this.refs.user_selector.getUserIds();
},
onPresetChanged: function(preset) {
switch (preset) {
case PresetValues.PrivateChat:
this.setState({
preset: preset,
is_private: true,
share_history: false,
});
break;
case PresetValues.PublicChat:
this.setState({
preset: preset,
is_private: false,
share_history: true,
});
break;
case PresetValues.Custom:
this.setState({
preset: preset,
});
break;
}
},
onPrivateChanged: function(ev) {
this.setState({
preset: PresetValues.Custom,
is_private: ev.target.checked,
});
},
onShareHistoryChanged: function(ev) {
this.setState({
preset: PresetValues.Custom,
share_history: ev.target.checked,
});
},
onTopicChange: function(ev) {
this.setState({
topic: ev.target.value,
});
},
onNameChange: function(ev) {
this.setState({
room_name: ev.target.value,
});
},
onInviteChanged: function(invited_users) {
this.setState({
invited_users: invited_users,
});
},
onAliasChanged: function(alias) {
this.setState({
alias: alias
})
},
render: function() {
var curr_phase = this.state.phase;
if (curr_phase == this.phases.CREATING) {
return (
<div>Creating...</div>
<Loader/>
);
} else {
var error_box = "";
@ -61,10 +137,15 @@ module.exports = React.createClass({
}
return (
<div className="mx_CreateRoom">
<label>Room Name <RoomNameTextbox ref="name_textbox" /></label>
<Presets ref="presets"/>
<UserSelector ref="user_selector"/>
<CreateRoomButton onCreateRoom={this.onCreateRoom} />
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder="Name"/> <br />
<textarea ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder="Description"/> <br />
<RoomAlias ref="alias" alias={this.state.alias} onChange={this.onAliasChanged}/> <br />
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br />
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br />
<label><input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/> Make this room private</label>
<label><input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/> Share message history with new users</label>
<label><input type="checkbox"/> Encrypt room</label>
<CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br />
{error_box}
</div>
);

View File

@ -25,28 +25,44 @@ var RightPanel = ComponentBroker.get('organisms/RightPanel');
var Login = ComponentBroker.get('templates/Login');
var UserSettings = ComponentBroker.get('organisms/UserSettings');
var Register = ComponentBroker.get('templates/Register');
var CreateRoom = ComponentBroker.get('organisms/CreateRoom');
var MatrixChatController = require("../../../../src/controllers/pages/MatrixChat");
// should be atomised
var Loader = require("react-loader");
var dis = require("../../../../src/dispatcher");
module.exports = React.createClass({
displayName: 'MatrixChat',
mixins: [MatrixChatController],
onRoomCreated: function(room_id) {
dis.dispatch({
action: "view_room",
room_id: room_id,
});
},
render: function() {
if (this.state.logged_in && this.state.ready) {
var page_element;
var right_panel = "";
if (this.state.page_type == this.PageTypes.RoomView) {
switch (this.state.page_type) {
case this.PageTypes.RoomView:
page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
right_panel = <RightPanel roomId={this.state.currentRoom} />
} else if (this.state.page_type == this.PageTypes.UserSettings) {
break;
case this.PageTypes.UserSettings:
page_element = <UserSettings />
break;
case this.PageTypes.CreateRoom:
page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/>
break;
}
return (

View File

@ -63,6 +63,8 @@ require('../skins/base/views/atoms/EnableNotificationsButton');
require('../skins/base/views/atoms/MessageTimestamp');
require('../skins/base/views/atoms/create_room/CreateRoomButton');
require('../skins/base/views/atoms/create_room/RoomNameTextbox');
require('../skins/base/views/atoms/create_room/RoomTopic');
require('../skins/base/views/atoms/create_room/RoomAlias');
require('../skins/base/views/atoms/create_room/Presets');
require('../skins/base/views/atoms/EditableText');
require('../skins/base/views/molecules/MatrixToolbar');

View File

@ -18,24 +18,23 @@ limitations under the License.
var React = require('react');
var Presets = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
};
module.exports = {
propTypes: {
default_preset: React.PropTypes.string
onChange: React.PropTypes.func,
preset: React.PropTypes.string
},
Presets: Presets,
getDefaultProps: function() {
return {
default_preset: 'private_chat',
onChange: function() {},
};
},
getInitialState: function() {
return {
preset: this.props.default_preset,
}
},
getPreset: function() {
return this.state.preset;
},
};

View File

@ -20,22 +20,30 @@ var React = require('react');
module.exports = {
propTypes: {
default_name: React.PropTypes.string
// Specifying a homeserver will make magical things happen when you,
// e.g. start typing in the room alias box.
homeserver: React.PropTypes.string,
alias: React.PropTypes.string,
onChange: React.PropTypes.func,
},
getDefaultProps: function() {
return {
default_name: '',
onChange: function() {},
alias: '',
};
},
getInitialState: function() {
return {
room_name: this.props.default_name,
}
},
getAliasLocalpart: function() {
var room_alias = this.props.alias;
getName: function() {
return this.state.room_name;
if (room_alias && this.props.homeserver) {
var suffix = ":" + this.props.homeserver;
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
room_alias = room_alias.slice(1, -suffix.length);
}
}
return room_alias;
},
};

View File

@ -20,38 +20,26 @@ var React = require('react');
module.exports = {
propTypes: {
initially_selected: React.PropTypes.arrayOf(React.PropTypes.string),
onChange: React.PropTypes.func,
selected_users: React.PropTypes.arrayOf(React.PropTypes.string),
},
getDefaultProps: function() {
return {
initially_selected: [],
onChange: function() {},
selected: [],
};
},
getInitialState: function() {
return {
selected_users: this.props.initially_selected,
}
},
addUser: function(user_id) {
if (this.state.selected_users.indexOf(user_id == -1)) {
this.setState({
selected_users: this.state.selected_users.concat([user_id]),
});
if (this.props.selected_users.indexOf(user_id == -1)) {
this.props.onChange(this.props.selected_users.concat([user_id]));
}
},
removeUser: function(user_id) {
this.setState({
selected_users: this.state.selected_users.filter(function(e) {
this.props.onChange(this.props.selected_users.filter(function(e) {
return e != user_id;
}),
});
}));
},
getUserIds: function() {
return this.state.selected_users;
}
};

View File

@ -18,6 +18,7 @@ limitations under the License.
var React = require("react");
var MatrixClientPeg = require("../../MatrixClientPeg");
var PresetValues = require('../atoms/create_room/Presets').Presets;
module.exports = {
propTypes: {
@ -41,25 +42,52 @@ module.exports = {
return {
phase: this.phases.CONFIG,
error_string: "",
is_private: true,
share_history: false,
default_preset: PresetValues.PrivateChat,
topic: '',
room_name: '',
invited_users: [],
};
},
onCreateRoom: function() {
var options = {};
var room_name = this.getName();
if (room_name) {
options.name = room_name;
if (this.state.room_name) {
options.name = this.state.room_name;
}
var preset = this.getPreset();
if (preset) {
options.preset = preset;
if (this.state.topic) {
options.topic = this.state.topic;
}
var invited_users = this.getInvitedUsers();
if (invited_users) {
options.invite = invited_users;
if (this.state.preset) {
if (this.state.preset != PresetValues.Custom) {
options.preset = this.state.preset;
} else {
options.initial_state = [
{
type: "m.room.join_rules",
content: {
"join_rules": this.state.is_private ? "invite" : "public"
}
},
{
type: "m.room.history_visibility",
content: {
"history_visibility": this.state.share_history ? "shared" : "invited"
}
},
];
}
}
options.invite = this.state.invited_users;
var alias = this.getAliasLocalpart();
if (alias) {
options.room_alias_name = alias;
}
var cli = MatrixClientPeg.get();
@ -77,11 +105,11 @@ module.exports = {
var self = this;
deferred.then(function () {
deferred.then(function (resp) {
self.setState({
phase: self.phases.CREATED,
});
self.props.onRoomCreated();
self.props.onRoomCreated(resp.room_id);
}, function(err) {
self.setState({
phase: self.phases.ERROR,

View File

@ -32,6 +32,11 @@ module.exports = {
PageTypes: {
RoomView: "room_view",
UserSettings: "user_settings",
CreateRoom: "create_room",
},
AuxPanel: {
RoomSettings: "room_settings",
},
getInitialState: function() {
@ -39,6 +44,7 @@ module.exports = {
logged_in: !!(MatrixClientPeg.get() && MatrixClientPeg.get().credentials),
ready: false,
page_type: this.PageTypes.RoomView,
aux_panel: null,
};
},
@ -143,6 +149,11 @@ module.exports = {
page_type: this.PageTypes.UserSettings,
});
break;
case 'view_create_room':
this.setState({
page_type: this.PageTypes.CreateRoom,
});
break;
}
},