diff --git a/skins/base/css/common.css b/skins/base/css/common.css
index 4c2e7c0b..aa10e409 100644
--- a/skins/base/css/common.css
+++ b/skins/base/css/common.css
@@ -28,6 +28,7 @@ div.error {
}
h2 {
+ color: #80cef4;
font-weight: 400;
font-size: 20px;
margin-top: 16px;
diff --git a/skins/base/css/molecules/RoomHeader.css b/skins/base/css/molecules/RoomHeader.css
index d0e86ad9..dbb7910f 100644
--- a/skins/base/css/molecules/RoomHeader.css
+++ b/skins/base/css/molecules/RoomHeader.css
@@ -27,11 +27,10 @@ limitations under the License.
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
- display: flex;
+ display: flex;
}
.mx_RoomHeader_leftRow {
- display: table-row;
height: 48px;
-webkit-box-ordinal-group: 1;
@@ -44,7 +43,6 @@ limitations under the License.
}
.mx_RoomHeader_rightRow {
- display: table-row;
height: 48px;
background-color: #fff;
border-radius: 48px;
@@ -88,6 +86,7 @@ limitations under the License.
.mx_RoomHeader_avatar {
display: table-cell;
+ width: 48px;
height: 50px;
vertical-align: middle;
}
diff --git a/skins/base/css/molecules/RoomTile.css b/skins/base/css/molecules/RoomTile.css
index 6e19395e..b258ea33 100644
--- a/skins/base/css/molecules/RoomTile.css
+++ b/skins/base/css/molecules/RoomTile.css
@@ -29,8 +29,8 @@ limitations under the License.
padding-top: 3px;
padding-bottom: 3px;
vertical-align: middle;
- width: 32px;
- height: 32px;
+ width: 40px;
+ height: 40px;
}
.mx_RoomTile_avatar img {
@@ -38,6 +38,12 @@ limitations under the License.
background-color: #dbdbdb;
}
+.mx_RoomTile_nameBadge {
+ display: table;
+ width: 100%;
+ height: 50px;
+}
+
.mx_RoomTile_name {
display: table-cell;
vertical-align: middle;
@@ -45,8 +51,13 @@ limitations under the License.
text-overflow: ellipsis;
}
+.mx_RoomTile_badgeCell {
+ display: table-cell;
+ vertical-align: middle;
+ width: 26px;
+}
+
.mx_RoomTile_badge {
- float: right;
background-color: #80cef4;
color: #fff;
border-radius: 26px;
diff --git a/skins/base/css/organisms/LeftPanel.css b/skins/base/css/organisms/LeftPanel.css
index 70a4b6f7..bfe26f11 100644
--- a/skins/base/css/organisms/LeftPanel.css
+++ b/skins/base/css/organisms/LeftPanel.css
@@ -44,7 +44,6 @@ limitations under the License.
padding-left: 16px;
padding-right: 16px;
- /* background-color: #0ff; */
height: 100%;
overflow-y: scroll;
}
diff --git a/skins/base/css/organisms/RoomView.css b/skins/base/css/organisms/RoomView.css
index bc1a1692..e1f3ea6b 100644
--- a/skins/base/css/organisms/RoomView.css
+++ b/skins/base/css/organisms/RoomView.css
@@ -64,7 +64,6 @@ limitations under the License.
width: 100%;
height: 100%;
margin-bottom: 60px;
- /* background-color: #ff0; */
overflow-y: scroll;
}
@@ -78,6 +77,14 @@ limitations under the License.
width: 100%;
}
+.mx_RoomView_MessageList h2 {
+ clear: both;
+ margin-top: 32px;
+ margin-bottom: 8px;
+ padding-bottom: 6px;
+ border-bottom: 1px solid #a8dbf3;
+}
+
.mx_RoomView_invitePrompt {
}
diff --git a/skins/base/views/molecules/DateSeparator.js b/skins/base/views/molecules/DateSeparator.js
new file mode 100644
index 00000000..061ce66d
--- /dev/null
+++ b/skins/base/views/molecules/DateSeparator.js
@@ -0,0 +1,56 @@
+/*
+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 days = [
+ "Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday"
+];
+
+module.exports = React.createClass({
+ displayName: 'DateSeparator',
+ render: function() {
+ var date = new Date(this.props.ts);
+ var today = new Date();
+ var yesterday = new Date();
+ yesterday.setDate(today.getDate() - 1);
+ var label;
+ if (date.toDateString() === today.toDateString()) {
+ label = "Today";
+ }
+ else if (date.toDateString() === yesterday.toDateString()) {
+ label = "Yesterday";
+ }
+ else if (today.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
+ label = days[date.getDay()];
+ }
+ else {
+ label = date.toDateString();
+ }
+
+ return (
+
{ label }
+ );
+ }
+});
diff --git a/skins/base/views/molecules/MRoomMemberTile.js b/skins/base/views/molecules/MRoomMemberTile.js
index 51a22b08..b13b44a8 100644
--- a/skins/base/views/molecules/MRoomMemberTile.js
+++ b/skins/base/views/molecules/MRoomMemberTile.js
@@ -52,12 +52,14 @@ module.exports = React.createClass({
render: function() {
// XXX: for now, just cheekily borrow the css from message tile...
+ var timestamp = this.props.last ? : null;
+
return (
-
+ { timestamp }
{this.getMemberEventText()}
diff --git a/skins/base/views/molecules/RoomHeader.js b/skins/base/views/molecules/RoomHeader.js
index 62f24484..33d43302 100644
--- a/skins/base/views/molecules/RoomHeader.js
+++ b/skins/base/views/molecules/RoomHeader.js
@@ -17,14 +17,20 @@ limitations under the License.
'use strict';
var React = require('react');
+var ComponentBroker = require('../../../../src/ComponentBroker');
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var RoomHeaderController = require("../../../../src/controllers/molecules/RoomHeader");
+var EditableText = ComponentBroker.get("atoms/EditableText");
module.exports = React.createClass({
displayName: 'RoomHeader',
mixins: [RoomHeaderController],
+ onNameChange: function(new_name) {
+ MatrixClientPeg.get().setRoomName(this.props.room.roomId, new_name);
+ },
+
render: function() {
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
@@ -52,7 +58,9 @@ module.exports = React.createClass({
-
{ this.props.room.name }
+
+
+
{ topic }
@@ -76,4 +84,3 @@ module.exports = React.createClass({
);
},
});
-
diff --git a/skins/base/views/molecules/RoomTile.js b/skins/base/views/molecules/RoomTile.js
index 00d16cf0..44709577 100644
--- a/skins/base/views/molecules/RoomTile.js
+++ b/skins/base/views/molecules/RoomTile.js
@@ -43,10 +43,17 @@ module.exports = React.createClass({
else if (this.props.unread) {
badge = 1
;
}
+ var nameCell;
+ if (badge) {
+ nameCell = ;
+ }
+ else {
+ nameCell = {name}
;
+ }
return (
-
{name}{ badge }
+ { nameCell }
);
}
diff --git a/skins/base/views/templates/Login.js b/skins/base/views/templates/Login.js
index f71e3070..0fd64ce5 100644
--- a/skins/base/views/templates/Login.js
+++ b/skins/base/views/templates/Login.js
@@ -19,6 +19,7 @@ limitations under the License.
var React = require('react');
var ComponentBroker = require("../../../../src/ComponentBroker");
+var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
var ProgressBar = ComponentBroker.get("molecules/ProgressBar");
var Loader = require("react-loader");
@@ -28,15 +29,44 @@ var LoginController = require("../../../../src/controllers/templates/Login");
var ServerConfig = ComponentBroker.get("molecules/ServerConfig");
module.exports = React.createClass({
+ DEFAULT_HS_URL: 'https://matrix.org',
+ DEFAULT_IS_URL: 'https://matrix.org',
+
displayName: 'Login',
mixins: [LoginController],
+ getInitialState: function() {
+ return {
+ serverConfigVisible: false
+ };
+ },
+
+ componentWillMount: function() {
+ this.onHSChosen();
+ this.customHsUrl = this.DEFAULT_HS_URL;
+ this.customIsUrl = this.DEFAULT_IS_URL;
+ },
+
getHsUrl: function() {
- return this.refs.serverConfig.getHsUrl();
+ if (this.state.serverConfigVisible) {
+ return this.customHsUrl;
+ } else {
+ return this.DEFAULT_HS_URL;
+ }
},
getIsUrl: function() {
- return this.refs.serverConfig.getIsUrl();
+ if (this.state.serverConfigVisible) {
+ return this.customIsUrl;
+ } else {
+ return this.DEFAULT_IS_URL;
+ }
+ },
+
+ onServerConfigVisibleChange: function(ev) {
+ this.setState({
+ serverConfigVisible: ev.target.checked
+ }, this.onHsUrlChanged);
},
/**
@@ -49,15 +79,46 @@ module.exports = React.createClass({
};
},
+ onHsUrlChanged: function() {
+ this.customHsUrl = this.refs.serverConfig.getHsUrl();
+ this.customIsUrl = this.refs.serverConfig.getIsUrl();
+ MatrixClientPeg.replaceUsingUrls(
+ this.getHsUrl(),
+ this.getIsUrl()
+ );
+ this.setState({
+ hs_url: this.getHsUrl(),
+ is_url: this.getIsUrl()
+ });
+ // XXX: HSes do not have to offer password auth, so we
+ // need to update and maybe show a different component
+ // when a new HS is entered.
+ /*if (this.updateHsTimeout) {
+ clearTimeout(this.updateHsTimeout);
+ }
+ var self = this;
+ this.updateHsTimeout = setTimeout(function() {
+ self.onHSChosen();
+ }, 500);*/
+ },
+
componentForStep: function(step) {
switch (step) {
case 'choose_hs':
+ var serverConfigStyle = {};
+ if (!this.state.serverConfigVisible) {
+ serverConfigStyle.display = 'none';
+ }
return (
-
+
+ Use custom server options (advanced)
+
+
+
);
// XXX: clearly these should be separate organisms
@@ -67,6 +128,7 @@ module.exports = React.createClass({
@@ -94,7 +156,6 @@ module.exports = React.createClass({
render: function() {
return (
);
diff --git a/skins/base/views/templates/Register.js b/skins/base/views/templates/Register.js
index 930228b8..23367378 100644
--- a/skins/base/views/templates/Register.js
+++ b/skins/base/views/templates/Register.js
@@ -39,6 +39,11 @@ module.exports = React.createClass({
};
},
+ componentWillMount: function() {
+ this.customHsUrl = this.DEFAULT_HS_URL;
+ this.customIsUrl = this.DEFAULT_IS_URL;
+ },
+
getRegFormVals: function() {
return {
email: this.refs.email.getDOMNode().value,
@@ -50,7 +55,7 @@ module.exports = React.createClass({
getHsUrl: function() {
if (this.state.serverConfigVisible) {
- return this.refs.serverConfig.getHsUrl();
+ return this.customHsUrl;
} else {
return this.DEFAULT_HS_URL;
}
@@ -58,7 +63,7 @@ module.exports = React.createClass({
getIsUrl: function() {
if (this.state.serverConfigVisible) {
- return this.refs.serverConfig.getIsUrl();
+ return this.customIsUrl;
} else {
return this.DEFAULT_IS_URL;
}
@@ -82,6 +87,8 @@ module.exports = React.createClass({
},
onServerUrlChanged: function(newUrl) {
+ this.customHsUrl = this.refs.serverConfig.getHsUrl();
+ this.customIsUrl = this.refs.serverConfig.getIsUrl();
this.forceUpdate();
},
@@ -104,7 +111,7 @@ module.exports = React.createClass({
Use custom server options (advanced)
diff --git a/src/ComponentBroker.js b/src/ComponentBroker.js
index 431f4766..0f606dd3 100644
--- a/src/ComponentBroker.js
+++ b/src/ComponentBroker.js
@@ -98,6 +98,7 @@ require('../skins/base/views/organisms/RightPanel');
require('../skins/base/views/molecules/RoomCreate');
require('../skins/base/views/molecules/RoomDropTarget');
require('../skins/base/views/molecules/DirectoryMenu');
+require('../skins/base/views/molecules/DateSeparator');
require('../skins/base/views/atoms/voip/VideoFeed');
require('../skins/base/views/molecules/voip/VideoView');
require('../skins/base/views/molecules/voip/CallView');
diff --git a/src/controllers/organisms/MemberList.js b/src/controllers/organisms/MemberList.js
index a511816d..1213db9b 100644
--- a/src/controllers/organisms/MemberList.js
+++ b/src/controllers/organisms/MemberList.js
@@ -62,6 +62,7 @@ module.exports = {
},
roomMembers: function(limit) {
+ if (!this.props.roomId) return {};
var cli = MatrixClientPeg.get();
var all_members = cli.getRoom(this.props.roomId).currentState.members;
var all_user_ids = Object.keys(all_members);
diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js
index 9078d558..f1a26ee4 100644
--- a/src/controllers/organisms/RoomView.js
+++ b/src/controllers/organisms/RoomView.js
@@ -36,6 +36,8 @@ var tileTypes = {
'm.call.hangup': ComponentBroker.get('molecules/voip/MCallHangupTile')
};
+var DateSeparator = ComponentBroker.get('molecules/DateSeparator');
+
module.exports = {
getInitialState: function() {
return {
@@ -231,22 +233,33 @@ module.exports = {
var TileType = tileTypes[mxEv.getType()];
var continuation = false;
var last = false;
+ var dateSeparator = null;
if (i == this.state.room.timeline.length - 1) {
last = true;
}
- if (i > 0 &&
- count < this.state.messageCap - 1 &&
- this.state.room.timeline[i].sender &&
- this.state.room.timeline[i - 1].sender &&
- this.state.room.timeline[i].sender.userId ===
+ if (i > 0 && count < this.state.messageCap - 1) {
+ if (this.state.room.timeline[i].sender &&
+ this.state.room.timeline[i - 1].sender &&
+ this.state.room.timeline[i].sender.userId ===
this.state.room.timeline[i - 1].sender.userId)
- {
- continuation = true;
- }
+ {
+ continuation = true;
+ }
+
+ var ts0 = this.state.room.timeline[i - 1].getTs();
+ var ts1 = this.state.room.timeline[i].getTs();
+ if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) {
+ dateSeparator = ;
+ continuation = false;
+ }
+ }
if (!TileType) continue;
ret.unshift(
);
+ if (dateSeparator) {
+ ret.unshift(dateSeparator);
+ }
++count;
}
return ret;
diff --git a/src/controllers/templates/Login.js b/src/controllers/templates/Login.js
index 07ace740..3d25a910 100644
--- a/src/controllers/templates/Login.js
+++ b/src/controllers/templates/Login.js
@@ -38,8 +38,7 @@ module.exports = {
this.setState({ step: step, errorText: '', busy: false });
},
- onHSChosen: function(ev) {
- ev.preventDefault();
+ onHSChosen: function() {
MatrixClientPeg.replaceUsingUrls(
this.getHsUrl(),
this.getIsUrl()
diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js
index 664b36b3..89a3872d 100644
--- a/src/controllers/templates/Register.js
+++ b/src/controllers/templates/Register.js
@@ -337,6 +337,14 @@ module.exports = {
});
} else if (error.httpStatus == 401) {
newState.errorText = "Authorisation failed!";
+ } else if (error.httpStatus >= 400 && error.httpStatus < 500) {
+ newState.errorText = "Registration failed!";
+ } else if (error.httpStatus >= 500 && error.httpStatus < 600) {
+ newState.errorText = "Server error during registration!";
+ } else if (error.name == "M_MISSING_PARAM") {
+ // The HS hasn't remembered the login params from
+ // the first try when the login email was sent.
+ newState.errorText = "This home server does not support resuming registration.";
}
self.setState(newState);
}