Merge branch 'vector' into irc-style-commands

pull/1/head
Kegan Dougal 2015-07-16 17:41:30 +01:00
commit 91943d8a45
16 changed files with 323 additions and 118 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

@ -32,13 +32,14 @@ module.exports = React.createClass({
var ev = this.props.mxEvent;
// XXX: SYJS-16
var senderName = ev.sender ? ev.sender.name : "Someone";
var targetName = ev.target ? ev.target.name : "Someone";
switch (ev.getContent().membership) {
case 'invite':
return senderName + " invited " + ev.target.name + ".";
return senderName + " invited " + targetName + ".";
case 'join':
return senderName + " joined the room.";
return targetName + " joined the room.";
case 'leave':
return senderName + " left the room.";
return targetName + " left the room.";
}
},
@ -47,7 +48,7 @@ module.exports = React.createClass({
return (
<div className="mx_MessageTile">
<div className="mx_MessageTile_avatar">
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40"/>
<img src={ this.props.mxEvent.target ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.target, 40, 40, "crop") : null } width="40" height="40"/>
</div>
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
<span className="mx_SenderProfile"></span>

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

@ -31,6 +31,24 @@ module.exports = React.createClass({
displayName: 'Login',
mixins: [LoginController],
getHsUrl: function() {
return this.refs.serverConfig.getHsUrl();
},
getIsUrl: function() {
return this.refs.serverConfig.getIsUrl();
},
/**
* Gets the form field values for the current login stage
*/
getFormVals: function() {
return {
'username': this.refs.user.getDOMNode().value,
'password': this.refs.pass.getDOMNode().value
};
},
componentForStep: function(step) {
switch (step) {
case 'choose_hs':

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;
}
},

View File

@ -41,18 +41,18 @@ module.exports = {
onHSChosen: function(ev) {
ev.preventDefault();
MatrixClientPeg.replaceUsingUrls(
this.refs.serverConfig.getHsUrl(),
this.refs.serverConfig.getIsUrl()
this.getHsUrl(),
this.getIsUrl()
);
this.setState({
hs_url: this.refs.serverConfig.getHsUrl(),
is_url: this.refs.serverConfig.getIsUrl()
hs_url: this.getHsUrl(),
is_url: this.getIsUrl()
});
this.setStep("fetch_stages");
var cli = MatrixClientPeg.get();
this.setState({busy: true});
var that = this;
cli.loginFlows().then(function(result) {
cli.loginFlows().done(function(result) {
that.setState({
flows: result.flows,
currentStep: 1,
@ -69,9 +69,12 @@ module.exports = {
ev.preventDefault();
this.setState({busy: true});
var that = this;
var formVals = this.getFormVals();
MatrixClientPeg.get().login('m.login.password', {
'user': that.refs.user.getDOMNode().value,
'password': that.refs.pass.getDOMNode().value
'user': formVals.username,
'password': formVals.password
}).done(function(data) {
// XXX: we assume this means we're logged in, but there could be a next stage
MatrixClientPeg.replace(Matrix.createClient({