Merge remote-tracking branch 'origin/develop' into read_receipts

This commit is contained in:
David Baker 2015-11-16 16:33:39 +00:00
commit bb59e9276b
32 changed files with 250 additions and 147 deletions

View file

@ -37,9 +37,8 @@
"react-dnd-html5-backend": "^2.0.0", "react-dnd-html5-backend": "^2.0.0",
"react-dom": "^0.14.2", "react-dom": "^0.14.2",
"react-gemini-scrollbar": "^2.0.1", "react-gemini-scrollbar": "^2.0.1",
"react-loader": "^1.4.0",
"sanitize-html": "^1.0.0",
"velocity-animate": "^1.2.3" "velocity-animate": "^1.2.3"
"sanitize-html": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"babel": "^5.8.23", "babel": "^5.8.23",

View file

@ -61,12 +61,12 @@ module.exports = {
}, },
componentWillUnmount: function() { componentWillUnmount: function() {
if (this.refs.messageWrapper) { if (this.refs.messagePanel) {
var messageWrapper = ReactDOM.findDOMNode(this.refs.messageWrapper); var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
messageWrapper.removeEventListener('drop', this.onDrop); messagePanel.removeEventListener('drop', this.onDrop);
messageWrapper.removeEventListener('dragover', this.onDragOver); messagePanel.removeEventListener('dragover', this.onDragOver);
messageWrapper.removeEventListener('dragleave', this.onDragLeaveOrEnd); messagePanel.removeEventListener('dragleave', this.onDragLeaveOrEnd);
messageWrapper.removeEventListener('dragend', this.onDragLeaveOrEnd); messagePanel.removeEventListener('dragend', this.onDragLeaveOrEnd);
} }
dis.unregister(this.dispatcherRef); dis.unregister(this.dispatcherRef);
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
@ -100,10 +100,9 @@ module.exports = {
// Call state has changed so we may be loading video elements // Call state has changed so we may be loading video elements
// which will obscure the message log. // which will obscure the message log.
// scroll to bottom // scroll to bottom
var messageWrapper = this.refs.messageWrapper; var scrollNode = this._getScrollNode();
if (messageWrapper) { if (scrollNode) {
var messageWrapperScroll = ReactDOM.findDOMNode(messageWrapper).children[2]; scrollNode.scrollTop = scrollNode.scrollHeight;
messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight;
} }
} }
@ -117,6 +116,17 @@ module.exports = {
} }
}, },
_getScrollNode: function() {
var panel = ReactDOM.findDOMNode(this.refs.messagePanel);
if (!panel) return null;
if (panel.classList.contains('gm-prevented')) {
return panel;
} else {
return panel.children[2]; // XXX: Fragile!
}
},
onSyncStateChange: function(state) { onSyncStateChange: function(state) {
this.setState({ this.setState({
syncState: state syncState: state
@ -143,11 +153,11 @@ module.exports = {
if (this.state.joining) return; if (this.state.joining) return;
if (room.roomId != this.props.roomId) return; if (room.roomId != this.props.roomId) return;
if (this.refs.messageWrapper) { var scrollNode = this._getScrollNode();
var messageWrapperScroll = ReactDOM.findDOMNode(this.refs.messageWrapper).children[2]; if (scrollNode) {
this.atBottom = ( this.atBottom = (
messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= scrollNode.scrollHeight - scrollNode.scrollTop <=
(messageWrapperScroll.clientHeight + 150) (scrollNode.clientHeight + 150) // 150?
); );
} }
@ -236,15 +246,15 @@ module.exports = {
}, },
componentDidMount: function() { componentDidMount: function() {
if (this.refs.messageWrapper) { if (this.refs.messagePanel) {
var messageWrapper = ReactDOM.findDOMNode(this.refs.messageWrapper); var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
messageWrapper.addEventListener('drop', this.onDrop); messagePanel.addEventListener('drop', this.onDrop);
messageWrapper.addEventListener('dragover', this.onDragOver); messagePanel.addEventListener('dragover', this.onDragOver);
messageWrapper.addEventListener('dragleave', this.onDragLeaveOrEnd); messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd);
messageWrapper.addEventListener('dragend', this.onDragLeaveOrEnd); messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd);
var messageWrapperScroll = messageWrapper.children[2]; var messageWrapperScroll = this._getScrollNode();
messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight;
@ -257,9 +267,9 @@ module.exports = {
}, },
componentDidUpdate: function() { componentDidUpdate: function() {
if (!this.refs.messageWrapper) return; if (!this.refs.messagePanel) return;
var messageWrapperScroll = ReactDOM.findDOMNode(this.refs.messageWrapper).children[2]; var messageWrapperScroll = this._getScrollNode();
if (this.state.paginating && !this.waiting_for_paginate) { if (this.state.paginating && !this.waiting_for_paginate) {
var heightGained = messageWrapperScroll.scrollHeight - this.oldScrollHeight; var heightGained = messageWrapperScroll.scrollHeight - this.oldScrollHeight;
@ -277,8 +287,8 @@ module.exports = {
}, },
fillSpace: function() { fillSpace: function() {
if (!this.refs.messageWrapper) return; if (!this.refs.messagePanel) return;
var messageWrapperScroll = ReactDOM.findDOMNode(this.refs.messageWrapper).children[2]; var messageWrapperScroll = this._getScrollNode();
if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) { if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) {
this.setState({paginating: true}); this.setState({paginating: true});
@ -335,10 +345,10 @@ module.exports = {
}, },
onMessageListScroll: function(ev) { onMessageListScroll: function(ev) {
if (this.refs.messageWrapper) { if (this.refs.messagePanel) {
var messageWrapperScroll = ReactDOM.findDOMNode(this.refs.messageWrapper).children[2]; var messageWrapperScroll = this._getScrollNode();
var wasAtBottom = this.atBottom; var wasAtBottom = this.atBottom;
this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight; this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight + 1;
if (this.atBottom && !wasAtBottom) { if (this.atBottom && !wasAtBottom) {
this.forceUpdate(); // remove unread msg count this.forceUpdate(); // remove unread msg count
} }

View file

@ -17,12 +17,14 @@ limitations under the License.
.mx_MemberAvatar_image { .mx_MemberAvatar_image {
z-index: 20; z-index: 20;
border-radius: 20px; border-radius: 20px;
position: relative;
} }
.mx_MemberAvatar_initial { .mx_MemberAvatar_initial {
position: absolute; position: absolute;
color: #fff; color: #fff;
text-align: center; text-align: center;
speak: none;
} }
.mx_MemberAvatar { .mx_MemberAvatar {

View file

@ -22,4 +22,5 @@ limitations under the License.
color: #fff; color: #fff;
text-align: center; text-align: center;
font-weight: normal ! important; font-weight: normal ! important;
speak: none;
} }

View file

@ -21,5 +21,12 @@ limitations under the License.
-webkit-justify-content: center; -webkit-justify-content: center;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%;
height: 100%; height: 100%;
flex: 1;
-webkit-flex: 1;
}
.mx_MatrixChat_middlePanel .mx_Spinner {
height: auto;
} }

View file

@ -47,6 +47,14 @@ a:visited {
color: #76cfa6; color: #76cfa6;
} }
/* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48.
Stop the scrollbar view from pushing out the container's overall sizing, which causes
flexbox to adapt to the new size and cause the view to keep growing.
*/
.gm-scrollbar-container .gm-scroll-view {
position: absolute;
}
.mx_ContextualMenu_background { .mx_ContextualMenu_background {
position: fixed; position: fixed;
top: 0; top: 0;
@ -99,7 +107,7 @@ a:visited {
height: 100%; height: 100%;
background-color: #000; background-color: #000;
opacity: 0.2; opacity: 0.2;
z-index: 2000; z-index: 4000;
} }
.mx_Dialog_wrapper { .mx_Dialog_wrapper {
@ -124,7 +132,7 @@ a:visited {
background-color: #fff; background-color: #fff;
color: #747474; color: #747474;
text-align: center; text-align: center;
z-index: 2010; z-index: 4010;
font-weight: 300; font-weight: 300;
font-size: 16px; font-size: 16px;
position: relative; position: relative;

View file

@ -16,29 +16,25 @@ limitations under the License.
.mx_MessageComposer_wrapper { .mx_MessageComposer_wrapper {
max-width: 960px; max-width: 960px;
height: 70px;
vertical-align: middle; vertical-align: middle;
margin: auto; margin: auto;
background-color: #fff;
border-top: 2px solid #e1dddd; border-top: 2px solid #e1dddd;
} }
.mx_MessageComposer_row { .mx_MessageComposer_row {
display: table-row; display: table-row;
width: 100%; width: 100%;
height: 70px;
} }
.mx_MessageComposer .mx_MessageComposer_avatar { .mx_MessageComposer .mx_MessageComposer_avatar {
display: table-cell; display: table-cell;
padding-left: 10px; padding-left: 10px;
padding-right: 28px; padding-right: 28px;
height: 70px; vertical-align: middle;
} }
.mx_MessageComposer .mx_MessageComposer_avatar img { .mx_MessageComposer .mx_MessageComposer_avatar .mx_MemberAvatar {
margin-top: 18px; display: block;
border-radius: 20px;
} }
.mx_MessageComposer_input { .mx_MessageComposer_input {
@ -49,11 +45,12 @@ limitations under the License.
} }
.mx_MessageComposer_input textarea { .mx_MessageComposer_input textarea {
display: block;
font-size: 15px; font-size: 15px;
width: 100%; width: 100%;
height: 1.2em; padding: 0px;
padding-top: 0.7em; margin-top: 6px;
padding-bottom: 0.7em; margin-bottom: 6px;
border: 0px; border: 0px;
resize: none; resize: none;
outline: none; outline: none;

View file

@ -90,9 +90,9 @@ limitations under the License.
.mx_RoomHeader_simpleHeader { .mx_RoomHeader_simpleHeader {
line-height: 83px; line-height: 83px;
color: #76cfa6; color: #454545;
font-weight: 400; font-size: 24px;
font-size: 20px; font-weight: bold;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
@ -102,7 +102,7 @@ limitations under the License.
vertical-align: middle; vertical-align: middle;
height: 28px; height: 28px;
color: #454545; color: #454545;
font-weight: 800; font-weight: bold;
font-size: 24px; font-size: 24px;
padding-left: 19px; padding-left: 19px;
padding-right: 16px; padding-right: 16px;

View file

@ -104,9 +104,7 @@ limitations under the License.
} }
.mx_RoomTile_unread, .mx_RoomTile_unread,
.mx_RoomTile_highlight, .mx_RoomTile_highlight {
.mx_RoomTile_selected
{
font-weight: bold; font-weight: bold;
} }

View file

@ -236,8 +236,8 @@ limitations under the License.
order: 5; order: 5;
width: 100%; width: 100%;
-webkit-flex: 0 0 70px; -webkit-flex: 0;
flex: 0 0 70px; flex: 0;
margin-right: 2px; margin-right: 2px;
} }

View file

@ -97,7 +97,8 @@ limitations under the License.
/* XXX: Hack: apparently if you try to nest a flex-box /* XXX: Hack: apparently if you try to nest a flex-box
* within a non-flex-box within a flex-box, the height * within a non-flex-box within a flex-box, the height
* of the innermost element gets miscalculated if the * of the innermost element gets miscalculated if the
* parents are both auto. * parents are both auto. Height has to be auto here
* for RoomView to correctly fit when the Toolbar is shown.
* Ideally we'd launch straight into the RoomView at this * Ideally we'd launch straight into the RoomView at this
* point, but instead we fudge it and make the middlePanel * point, but instead we fudge it and make the middlePanel
* flex itself. * flex itself.

View file

@ -17,6 +17,18 @@ limitations under the License.
.mx_Login { .mx_Login {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
overflow: auto;
} }
.mx_Login h2 { .mx_Login h2 {
@ -28,8 +40,10 @@ limitations under the License.
.mx_Login_box { .mx_Login_box {
width: 300px; width: 300px;
min-height: 450px;
padding-top: 50px;
padding-bottom: 50px;
margin: auto; margin: auto;
padding-top: 100px;
} }
.mx_Login_logo { .mx_Login_logo {

View file

@ -50,7 +50,7 @@ module.exports = React.createClass({
return ( return (
<span className="mx_MemberAvatar" {...this.props}> <span className="mx_MemberAvatar" {...this.props}>
<span className="mx_MemberAvatar_initial" <span className="mx_MemberAvatar_initial" aria-hidden="true"
style={{ fontSize: (this.props.width * 0.75) + "px", style={{ fontSize: (this.props.width * 0.75) + "px",
width: this.props.width + "px", width: this.props.width + "px",
lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span> lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>

View file

@ -57,7 +57,7 @@ module.exports = React.createClass({
return ( return (
<span> <span>
<span className="mx_RoomAvatar_initial" <span className="mx_RoomAvatar_initial" aria-hidden="true"
style={{ fontSize: (this.props.width * 0.75) + "px", style={{ fontSize: (this.props.width * 0.75) + "px",
width: this.props.width + "px", width: this.props.width + "px",
lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span> lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>

View file

@ -21,9 +21,6 @@ var React = require('react');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar') var ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
var Loader = require("react-loader");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'ChangeAvatar', displayName: 'ChangeAvatar',
mixins: [ChangeAvatarController], mixins: [ChangeAvatarController],
@ -70,6 +67,7 @@ module.exports = React.createClass({
</div> </div>
); );
case this.Phases.Uploading: case this.Phases.Uploading:
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<Loader /> <Loader />
); );

View file

@ -20,8 +20,6 @@ var React = require('react');
var sdk = require('matrix-react-sdk'); var sdk = require('matrix-react-sdk');
var ChangeDisplayNameController = require("matrix-react-sdk/lib/controllers/molecules/ChangeDisplayName"); var ChangeDisplayNameController = require("matrix-react-sdk/lib/controllers/molecules/ChangeDisplayName");
var Loader = require("react-loader");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'ChangeDisplayName', displayName: 'ChangeDisplayName',
@ -39,6 +37,7 @@ module.exports = React.createClass({
render: function() { render: function() {
if (this.state.busy) { if (this.state.busy) {
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<Loader /> <Loader />
); );

View file

@ -19,8 +19,6 @@ limitations under the License.
var React = require('react'); var React = require('react');
var ChangePasswordController = require('matrix-react-sdk/lib/controllers/molecules/ChangePassword') var ChangePasswordController = require('matrix-react-sdk/lib/controllers/molecules/ChangePassword')
var Loader = require("react-loader");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'ChangePassword', displayName: 'ChangePassword',
@ -64,6 +62,7 @@ module.exports = React.createClass({
</div> </div>
); );
case this.Phases.Uploading: case this.Phases.Uploading:
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<Loader /> <Loader />

View file

@ -30,15 +30,25 @@ module.exports = React.createClass({
var content = this.props.mxEvent.getContent(); var content = this.props.mxEvent.getContent();
var cli = MatrixClientPeg.get(); var cli = MatrixClientPeg.get();
return ( var httpUrl = cli.mxcUrlToHttp(content.url);
<span className="mx_MFileTile"> var text = this.presentableTextForFile(content);
<div className="mx_MImageTile_download">
<a href={cli.mxcUrlToHttp(content.url)} target="_blank"> if (httpUrl) {
<img src="img/download.png" width="10" height="12"/> return (
Download {this.presentableTextForFile(content)} <span className="mx_MFileTile">
</a> <div className="mx_MImageTile_download">
</div> <a href={cli.mxcUrlToHttp(content.url)} target="_blank">
<img src="img/download.png" width="10" height="12"/>
Download {text}
</a>
</div>
</span>
);
} else {
var extra = text ? ': '+text : '';
return <span className="mx_MFileTile">
Invalid file{extra}
</span> </span>
); }
}, },
}); });

View file

@ -73,18 +73,33 @@ module.exports = React.createClass({
var imgStyle = {}; var imgStyle = {};
if (thumbHeight) imgStyle['height'] = thumbHeight; if (thumbHeight) imgStyle['height'] = thumbHeight;
return ( var thumbUrl = cli.mxcUrlToHttp(content.url, 480, 360);
<span className="mx_MImageTile"> if (thumbUrl) {
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }> return (
<img className="mx_MImageTile_thumbnail" src={cli.mxcUrlToHttp(content.url, 480, 360)} alt={content.body} style={imgStyle} /> <span className="mx_MImageTile">
</a> <a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
<div className="mx_MImageTile_download"> <img className="mx_MImageTile_thumbnail" src={thumbUrl} alt={content.body} style={imgStyle} />
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
<img src="img/download.png" width="10" height="12"/>
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
</a> </a>
</div> <div className="mx_MImageTile_download">
</span> <a href={cli.mxcUrlToHttp(content.url)} target="_blank">
); <img src="img/download.png" width="10" height="12"/>
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
</a>
</div>
</span>
);
} else if (content.body) {
return (
<span className="mx_MImageTile">
Image '{content.body}' cannot be displayed.
</span>
);
} else {
return (
<span className="mx_MImageTile">
This image cannot be displayed.
</span>
);
}
}, },
}); });

View file

@ -34,7 +34,6 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
var EnableNotificationsButton = sdk.getComponent("atoms.EnableNotificationsButton");
return ( return (
<div className="mx_MatrixToolbar"> <div className="mx_MatrixToolbar">
<img className="mx_MatrixToolbar_warning" src="img/warning.png" width="28" height="28" alt="/!\"/> <img className="mx_MatrixToolbar_warning" src="img/warning.png" width="28" height="28" alt="/!\"/>

View file

@ -17,7 +17,6 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var Loader = require("../atoms/Spinner");
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
@ -47,6 +46,7 @@ module.exports = React.createClass({
} }
if (this.state.creatingRoom) { if (this.state.creatingRoom) {
var Loader = sdk.getComponent("atoms.Spinner");
spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>; spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>;
} }

View file

@ -61,7 +61,7 @@ module.exports = React.createClass({
<MemberAvatar member={me} width={24} height={24} /> <MemberAvatar member={me} width={24} height={24} />
</div> </div>
<div className="mx_MessageComposer_input"> <div className="mx_MessageComposer_input">
<textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message..." /> <textarea ref="textarea" rows="1" onKeyDown={this.onKeyDown} onKeyUp={this.onKeyUp} placeholder="Type a message..." />
</div> </div>
<div className="mx_MessageComposer_upload" onClick={this.onUploadClick}> <div className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
<img src="img/upload.png" width="17" height="22"/> <img src="img/upload.png" width="17" height="22"/>

View file

@ -43,9 +43,9 @@ var roomTileSource = {
originalList: props.roomSubList, originalList: props.roomSubList,
originalIndex: props.roomSubList.findRoomTile(props.room).index, originalIndex: props.roomSubList.findRoomTile(props.room).index,
targetList: props.roomSubList, // at first target is same as original targetList: props.roomSubList, // at first target is same as original
lastTargetRoom: null, // lastTargetRoom: null,
lastYOffset: null, // lastYOffset: null,
lastYDelta: null, // lastYDelta: null,
}; };
if (props.roomSubList.debug) console.log("roomTile beginDrag for " + item.room.roomId); if (props.roomSubList.debug) console.log("roomTile beginDrag for " + item.room.roomId);
@ -123,7 +123,7 @@ var roomTileTarget = {
hover: function(props, monitor) { hover: function(props, monitor) {
var item = monitor.getItem(); var item = monitor.getItem();
var off = monitor.getClientOffset(); //var off = monitor.getClientOffset();
// console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver()); // console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver());
//console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList); //console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList);
@ -150,7 +150,7 @@ var roomTileTarget = {
// stop us from flickering between our droptarget and the previous room. // stop us from flickering between our droptarget and the previous room.
// whenever the cursor changes direction we have to reset the flicker-damping. // whenever the cursor changes direction we have to reset the flicker-damping.
/*
var yDelta = off.y - item.lastYOffset; var yDelta = off.y - item.lastYOffset;
if ((yDelta > 0 && item.lastYDelta < 0) || if ((yDelta > 0 && item.lastYDelta < 0) ||
@ -170,6 +170,7 @@ var roomTileTarget = {
if (yDelta) item.lastYDelta = yDelta; if (yDelta) item.lastYDelta = yDelta;
item.lastYOffset = off.y; item.lastYOffset = off.y;
*/
} }
else if (switchedTarget) { else if (switchedTarget) {
if (!props.roomSubList.findRoomTile(item.room).room) { if (!props.roomSubList.findRoomTile(item.room).room) {
@ -216,10 +217,12 @@ var RoomTile = React.createClass({
// //console.log("room " + this.props.room.roomId + " has dropTarget clientOffset " + this.props.clientOffset.x + "," + this.props.clientOffset.y); // //console.log("room " + this.props.room.roomId + " has dropTarget clientOffset " + this.props.clientOffset.x + "," + this.props.clientOffset.y);
// } // }
/*
if (this.props.room._dragging) { if (this.props.room._dragging) {
var RoomDropTarget = sdk.getComponent("molecules.RoomDropTarget"); var RoomDropTarget = sdk.getComponent("molecules.RoomDropTarget");
return <RoomDropTarget placeholder={true}/>; return <RoomDropTarget placeholder={true}/>;
} }
*/
var myUserId = MatrixClientPeg.get().credentials.userId; var myUserId = MatrixClientPeg.get().credentials.userId;
var me = this.props.room.currentState.members[myUserId]; var me = this.props.room.currentState.members[myUserId];

View file

@ -29,10 +29,16 @@ module.exports = React.createClass({
showHelpPopup: function() { showHelpPopup: function() {
var ErrorDialog = sdk.getComponent('organisms.ErrorDialog'); var ErrorDialog = sdk.getComponent('organisms.ErrorDialog');
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: 'Custom Server Options', title: 'Custom Server Options',
description: "You can use the custom server options to log into other Matrix servers by specifying a different Home server URL. This allows you to use Vector with an existing Matrix account on a different Home server. You can also set a cutom Identity server but this will affect people ability to find you if you use a server in a group other than tha main Matrix.org group.", description: <span>
button: "Dismiss", You can use the custom server options to log into other Matrix servers by specifying a different Home server URL.<br/>
focus: true This allows you to use Vector with an existing Matrix account on a different Home server.<br/>
<br/>
You can also set a custom Identity server but this will affect people's ability to find you
if you use a server in a group other than the main Matrix.org group.
</span>,
button: "Dismiss",
focus: true,
}); });
}, },
@ -40,9 +46,9 @@ module.exports = React.createClass({
return ( return (
<div className="mx_ServerConfig"> <div className="mx_ServerConfig">
<label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">Home server URL</label> <label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">Home server URL</label>
<input className="mx_Login_field" id="hsurl" type="text" value={this.state.hs_url} onChange={this.hsChanged} /> <input className="mx_Login_field" id="hsurl" type="text" placeholder={this.state.original_hs_url} value={this.state.hs_url} onChange={this.hsChanged} />
<label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">Identity server URL</label> <label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">Identity server URL</label>
<input className="mx_Login_field" type="text" value={this.state.is_url} onChange={this.isChanged} /> <input className="mx_Login_field" id="isurl" type="text" placeholder={this.state.original_is_url} value={this.state.is_url} onChange={this.isChanged} />
<a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>What does this mean?</a> <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>What does this mean?</a>
</div> </div>
); );

View file

@ -24,9 +24,6 @@ var sdk = require('matrix-react-sdk')
var PresetValues = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets').Presets; var PresetValues = require('matrix-react-sdk/lib/controllers/atoms/create_room/Presets').Presets;
var Loader = require("react-loader");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'CreateRoom', displayName: 'CreateRoom',
mixins: [CreateRoomController], mixins: [CreateRoomController],
@ -122,6 +119,7 @@ module.exports = React.createClass({
render: function() { render: function() {
var curr_phase = this.state.phase; var curr_phase = this.state.phase;
if (curr_phase == this.phases.CREATING) { if (curr_phase == this.phases.CREATING) {
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<Loader/> <Loader/>
); );

View file

@ -18,7 +18,6 @@ limitations under the License.
var React = require('react'); var React = require('react');
var classNames = require('classnames'); var classNames = require('classnames');
var Loader = require('react-loader');
var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList') var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList')
var GeminiScrollbar = require('react-gemini-scrollbar'); var GeminiScrollbar = require('react-gemini-scrollbar');
@ -78,6 +77,7 @@ module.exports = React.createClass({
inviteTile: function() { inviteTile: function() {
if (this.state.inviting) { if (this.state.inviting) {
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<Loader /> <Loader />
); );

View file

@ -23,8 +23,6 @@ var Modal = require('matrix-react-sdk/lib/Modal');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher'); var dis = require('matrix-react-sdk/lib/dispatcher');
var Loader = require("react-loader");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'RoomDirectory', displayName: 'RoomDirectory',
@ -121,6 +119,7 @@ module.exports = React.createClass({
render: function() { render: function() {
if (this.state.loading) { if (this.state.loading) {
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<div className="mx_RoomDirectory"> <div className="mx_RoomDirectory">
<Loader /> <Loader />
@ -136,7 +135,9 @@ module.exports = React.createClass({
<input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/> <input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
<div className="mx_RoomDirectory_tableWrapper"> <div className="mx_RoomDirectory_tableWrapper">
<table className="mx_RoomDirectory_table"> <table className="mx_RoomDirectory_table">
<tr><th width="45%">Room</th><th width="45%">Alias</th><th width="10%">Members</th></tr> <thead>
<tr><th width="45%">Room</th><th width="45%">Alias</th><th width="10%">Members</th></tr>
</thead>
{ this.getRows(this.state.roomAlias) } { this.getRows(this.state.roomAlias) }
</table> </table>
</div> </div>

View file

@ -224,7 +224,7 @@ var RoomSubList = React.createClass({
room={ room } room={ room }
roomSubList={ self } roomSubList={ self }
key={ room.roomId } key={ room.roomId }
collapsed={ self.props.collapsed } collapsed={ self.props.collapsed || false}
selected={ selected } selected={ selected }
unread={ self.props.activityMap[room.roomId] === 1 } unread={ self.props.activityMap[room.roomId] === 1 }
highlight={ self.props.activityMap[room.roomId] === 2 } highlight={ self.props.activityMap[room.roomId] === 2 }
@ -265,7 +265,7 @@ var RoomSubList = React.createClass({
return connectDropTarget( return connectDropTarget(
<div> <div>
<h2 onClick={ this.onClick } className="mx_RoomSubList_label">{ this.props.collapsed ? '' : this.props.label } <h2 onClick={ this.onClick } className="mx_RoomSubList_label">{ this.props.collapsed ? '' : this.props.label }
<img className="mx_RoomSubList_chevron" src={ this.state.hidden ? "/img/list-open.png" : "/img/list-close.png" } width="10" height="10"/> <img className="mx_RoomSubList_chevron" src={ this.state.hidden ? "img/list-open.png" : "img/list-close.png" } width="10" height="10"/>
</h2> </h2>
{ subList } { subList }
</div> </div>

View file

@ -29,9 +29,6 @@ var filesize = require('filesize');
var GeminiScrollbar = require('react-gemini-scrollbar'); var GeminiScrollbar = require('react-gemini-scrollbar');
var RoomViewController = require('../../../../controllers/organisms/RoomView') var RoomViewController = require('../../../../controllers/organisms/RoomView')
var Loader = require("react-loader");
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'RoomView', displayName: 'RoomView',
mixins: [RoomViewController], mixins: [RoomViewController],
@ -104,9 +101,9 @@ module.exports = React.createClass({
}, },
scrollToBottom: function() { scrollToBottom: function() {
if (!this.refs.messageWrapper) return; var scrollNode = this._getScrollNode();
var messageWrapper = ReactDOM.findDOMNode(this.refs.messageWrapper).children[2]; if (!scrollNode) return;
messageWrapper.scrollTop = messageWrapper.scrollHeight; scrollNode.scrollTop = scrollNode.scrollHeight;
}, },
render: function() { render: function() {
@ -133,6 +130,7 @@ module.exports = React.createClass({
var myUserId = MatrixClientPeg.get().credentials.userId; var myUserId = MatrixClientPeg.get().credentials.userId;
if (this.state.room.currentState.members[myUserId].membership == 'invite') { if (this.state.room.currentState.members[myUserId].membership == 'invite') {
if (this.state.joining || this.state.rejecting) { if (this.state.joining || this.state.rejecting) {
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<div className="mx_RoomView"> <div className="mx_RoomView">
<Loader /> <Loader />
@ -262,6 +260,7 @@ module.exports = React.createClass({
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />; aux = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
} }
else if (this.state.uploadingRoomSettings) { else if (this.state.uploadingRoomSettings) {
var Loader = sdk.getComponent("atoms.Spinner");
aux = <Loader/>; aux = <Loader/>;
} }
else if (this.state.searching) { else if (this.state.searching) {
@ -300,7 +299,7 @@ module.exports = React.createClass({
{ conferenceCallNotification } { conferenceCallNotification }
{ aux } { aux }
</div> </div>
<GeminiScrollbar autoshow={true} ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }> <GeminiScrollbar autoshow={true} ref="messagePanel" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
<div className="mx_RoomView_messageListWrapper"> <div className="mx_RoomView_messageListWrapper">
{ fileDropTarget } { fileDropTarget }
<ol className="mx_RoomView_MessageList" aria-live="polite"> <ol className="mx_RoomView_MessageList" aria-live="polite">
@ -316,7 +315,7 @@ module.exports = React.createClass({
{statusBar} {statusBar}
</div> </div>
</div> </div>
<MessageComposer room={this.state.room} uploadFile={this.uploadFile} /> <MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} />
</div> </div>
); );
} }

View file

@ -19,8 +19,6 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var UserSettingsController = require('matrix-react-sdk/lib/controllers/organisms/UserSettings') var UserSettingsController = require('matrix-react-sdk/lib/controllers/organisms/UserSettings')
var Loader = require("react-loader");
var Modal = require('matrix-react-sdk/lib/Modal'); var Modal = require('matrix-react-sdk/lib/Modal');
module.exports = React.createClass({ module.exports = React.createClass({
@ -68,6 +66,7 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
var Loader = sdk.getComponent("atoms.Spinner");
switch (this.state.phase) { switch (this.state.phase) {
case this.Phases.Loading: case this.Phases.Loading:
return <Loader /> return <Loader />

View file

@ -17,12 +17,11 @@ limitations under the License.
'use strict'; 'use strict';
var React = require('react'); var React = require('react');
var ReactDOM = require('react-dom');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var Loader = require("react-loader");
var LoginController = require('matrix-react-sdk/lib/controllers/templates/Login') var LoginController = require('matrix-react-sdk/lib/controllers/templates/Login')
var config = require('../../../../../config.json'); var config = require('../../../../../config.json');
@ -32,20 +31,37 @@ module.exports = React.createClass({
mixins: [LoginController], mixins: [LoginController],
getInitialState: function() { getInitialState: function() {
// TODO: factor out all localstorage stuff into its own home.
// This is common to Login, Register and MatrixClientPeg
var localStorage = window.localStorage;
var hs_url, is_url;
if (localStorage) {
hs_url = localStorage.getItem("mx_hs_url");
is_url = localStorage.getItem("mx_is_url");
}
return { return {
serverConfigVisible: false customHsUrl: hs_url || config.default_hs_url,
customIsUrl: is_url || config.default_is_url,
serverConfigVisible: (hs_url && hs_url !== config.default_hs_url ||
is_url && is_url !== config.default_is_url)
}; };
}, },
componentWillMount: function() { componentDidMount: function() {
this.onHSChosen(); this.onHSChosen();
this.customHsUrl = config.default_hs_url; },
this.customIsUrl = config.default_is_url;
componentDidUpdate: function() {
if (!this.state.focusFired && this.refs.user) {
this.refs.user.focus();
this.setState({ focusFired: true });
}
}, },
getHsUrl: function() { getHsUrl: function() {
if (this.state.serverConfigVisible) { if (this.state.serverConfigVisible) {
return this.customHsUrl; return this.state.customHsUrl;
} else { } else {
return config.default_hs_url; return config.default_hs_url;
} }
@ -53,7 +69,7 @@ module.exports = React.createClass({
getIsUrl: function() { getIsUrl: function() {
if (this.state.serverConfigVisible) { if (this.state.serverConfigVisible) {
return this.customIsUrl; return this.state.customIsUrl;
} else { } else {
return config.default_is_url; return config.default_is_url;
} }
@ -62,7 +78,7 @@ module.exports = React.createClass({
onServerConfigVisibleChange: function(ev) { onServerConfigVisibleChange: function(ev) {
this.setState({ this.setState({
serverConfigVisible: ev.target.checked serverConfigVisible: ev.target.checked
}, this.onHsUrlChanged); }, this.onHSChosen);
}, },
/** /**
@ -79,16 +95,22 @@ module.exports = React.createClass({
var newHsUrl = this.refs.serverConfig.getHsUrl().trim(); var newHsUrl = this.refs.serverConfig.getHsUrl().trim();
var newIsUrl = this.refs.serverConfig.getIsUrl().trim(); var newIsUrl = this.refs.serverConfig.getIsUrl().trim();
if (newHsUrl == this.customHsUrl && if (newHsUrl == this.state.customHsUrl &&
newIsUrl == this.customIsUrl) newIsUrl == this.state.customIsUrl)
{ {
return; return;
} }
else { else {
this.customHsUrl = newHsUrl; this.setState({
this.customIsUrl = newIsUrl; customHsUrl: newHsUrl,
customIsUrl: newIsUrl,
});
} }
// XXX: why are we replacing the MatrixClientPeg here when we're about
// to do it again 1s later in the setTimeout to onHSChosen? -- matthew
// Commenting it out for now to see what breaks.
/*
MatrixClientPeg.replaceUsingUrls( MatrixClientPeg.replaceUsingUrls(
this.getHsUrl(), this.getHsUrl(),
this.getIsUrl() this.getIsUrl()
@ -97,6 +119,8 @@ module.exports = React.createClass({
hs_url: this.getHsUrl(), hs_url: this.getHsUrl(),
is_url: this.getIsUrl() is_url: this.getIsUrl()
}); });
*/
// XXX: HSes do not have to offer password auth, so we // XXX: HSes do not have to offer password auth, so we
// need to update and maybe show a different component // need to update and maybe show a different component
// when a new HS is entered. // when a new HS is entered.
@ -123,7 +147,7 @@ module.exports = React.createClass({
<label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label> <label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label>
<div style={serverConfigStyle}> <div style={serverConfigStyle}>
<ServerConfig ref="serverConfig" <ServerConfig ref="serverConfig"
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl} defaultHsUrl={this.state.customHsUrl} defaultIsUrl={this.state.customIsUrl}
onHsUrlChanged={this.onHsUrlChanged} onHsUrlChanged={this.onHsUrlChanged}
/> />
</div> </div>
@ -158,6 +182,7 @@ module.exports = React.createClass({
}, },
loginContent: function() { loginContent: function() {
var Loader = sdk.getComponent("atoms.Spinner");
var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null; var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
return ( return (
<div> <div>

View file

@ -21,8 +21,6 @@ var React = require('react');
var sdk = require('matrix-react-sdk') var sdk = require('matrix-react-sdk')
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg') var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg')
var Loader = require("react-loader");
var RegisterController = require('../../../../controllers/templates/Register') var RegisterController = require('../../../../controllers/templates/Register')
var config = require('../../../../../config.json'); var config = require('../../../../../config.json');
@ -32,14 +30,28 @@ module.exports = React.createClass({
mixins: [RegisterController], mixins: [RegisterController],
getInitialState: function() { getInitialState: function() {
return { // TODO: factor out all localstorage stuff into its own home.
serverConfigVisible: false // This is common to Login, Register and MatrixClientPeg
}; var localStorage = window.localStorage;
}, var hs_url, is_url;
if (localStorage) {
hs_url = localStorage.getItem("mx_hs_url");
is_url = localStorage.getItem("mx_is_url");
}
componentWillMount: function() { // make sure we have our MatrixClient set up whatever
this.customHsUrl = config.default_hs_url; // Useful for debugging only.
this.customIsUrl = config.default_is_url; // MatrixClientPeg.replaceUsingUrls(
// hs_url || config.default_hs_url,
// is_url || config.default_is_url
// );
return {
customHsUrl: hs_url || config.default_hs_url,
customIsUrl: is_url || config.default_is_url,
serverConfigVisible: (hs_url && hs_url !== config.default_hs_url ||
is_url && is_url !== config.default_is_url)
}
}, },
getRegFormVals: function() { getRegFormVals: function() {
@ -53,7 +65,7 @@ module.exports = React.createClass({
getHsUrl: function() { getHsUrl: function() {
if (this.state.serverConfigVisible) { if (this.state.serverConfigVisible) {
return this.customHsUrl; return this.state.customHsUrl;
} else { } else {
return config.default_hs_url; return config.default_hs_url;
} }
@ -61,7 +73,7 @@ module.exports = React.createClass({
getIsUrl: function() { getIsUrl: function() {
if (this.state.serverConfigVisible) { if (this.state.serverConfigVisible) {
return this.customIsUrl; return this.state.customIsUrl;
} else { } else {
return config.default_is_url; return config.default_is_url;
} }
@ -74,8 +86,10 @@ module.exports = React.createClass({
}, },
onServerUrlChanged: function(newUrl) { onServerUrlChanged: function(newUrl) {
this.customHsUrl = this.refs.serverConfig.getHsUrl(); this.setState({
this.customIsUrl = this.refs.serverConfig.getIsUrl(); customHsUrl: this.refs.serverConfig.getHsUrl(),
customIsUrl: this.refs.serverConfig.getIsUrl(),
});
this.forceUpdate(); this.forceUpdate();
}, },
@ -92,16 +106,16 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<form onSubmit={this.onInitialStageSubmit}> <form onSubmit={this.onInitialStageSubmit}>
<input className="mx_Login_field" type="text" ref="email" placeholder="Email address" defaultValue={this.savedParams.email} /><br /> <input className="mx_Login_field" type="text" ref="email" autoFocus={true} placeholder="Email address" defaultValue={this.savedParams.email} /><br />
<input className="mx_Login_field" type="text" ref="username" placeholder="User name" defaultValue={this.savedParams.username} /><br /> <input className="mx_Login_field" type="text" ref="username" placeholder="User name" defaultValue={this.savedParams.username} /><br />
<input className="mx_Login_field" type="password" ref="password" placeholder="Password" defaultValue={this.savedParams.password} /><br /> <input className="mx_Login_field" type="password" ref="password" placeholder="Password" defaultValue={this.savedParams.password} /><br />
<input className="mx_Login_field" type="password" ref="confirmPassword" placeholder="Confirm password" defaultValue={this.savedParams.confirmPassword} /><br /> <input className="mx_Login_field" type="password" ref="confirmPassword" placeholder="Confirm password" defaultValue={this.savedParams.confirmPassword} /><br />
<input className="mx_Login_checkbox" id="advanced" type="checkbox" value={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} /> <input className="mx_Login_checkbox" id="advanced" type="checkbox" checked={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
<label htmlFor="advanced">Use custom server options (advanced)</label> <label htmlFor="advanced">Use custom server options (advanced)</label>
<div style={serverConfigStyle}> <div style={serverConfigStyle}>
<ServerConfig ref="serverConfig" <ServerConfig ref="serverConfig"
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl} defaultHsUrl={this.state.customHsUrl} defaultIsUrl={this.state.customIsUrl}
onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} /> onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} />
</div> </div>
<br /> <br />
@ -128,6 +142,7 @@ module.exports = React.createClass({
registerContent: function() { registerContent: function() {
if (this.state.busy) { if (this.state.busy) {
var Loader = sdk.getComponent("atoms.Spinner");
return ( return (
<Loader /> <Loader />
); );