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-dom": "^0.14.2",
"react-gemini-scrollbar": "^2.0.1",
"react-loader": "^1.4.0",
"sanitize-html": "^1.0.0",
"velocity-animate": "^1.2.3"
"sanitize-html": "^1.0.0"
},
"devDependencies": {
"babel": "^5.8.23",

View File

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

View File

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

View File

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

View File

@ -21,5 +21,12 @@ limitations under the License.
-webkit-justify-content: center;
align-items: center;
justify-content: center;
width: 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;
}
/* 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 {
position: fixed;
top: 0;
@ -99,7 +107,7 @@ a:visited {
height: 100%;
background-color: #000;
opacity: 0.2;
z-index: 2000;
z-index: 4000;
}
.mx_Dialog_wrapper {
@ -124,7 +132,7 @@ a:visited {
background-color: #fff;
color: #747474;
text-align: center;
z-index: 2010;
z-index: 4010;
font-weight: 300;
font-size: 16px;
position: relative;

View File

@ -16,29 +16,25 @@ limitations under the License.
.mx_MessageComposer_wrapper {
max-width: 960px;
height: 70px;
vertical-align: middle;
margin: auto;
background-color: #fff;
border-top: 2px solid #e1dddd;
}
.mx_MessageComposer_row {
display: table-row;
width: 100%;
height: 70px;
}
.mx_MessageComposer .mx_MessageComposer_avatar {
display: table-cell;
padding-left: 10px;
padding-right: 28px;
height: 70px;
vertical-align: middle;
}
.mx_MessageComposer .mx_MessageComposer_avatar img {
margin-top: 18px;
border-radius: 20px;
.mx_MessageComposer .mx_MessageComposer_avatar .mx_MemberAvatar {
display: block;
}
.mx_MessageComposer_input {
@ -49,17 +45,18 @@ limitations under the License.
}
.mx_MessageComposer_input textarea {
display: block;
font-size: 15px;
width: 100%;
height: 1.2em;
padding-top: 0.7em;
padding-bottom: 0.7em;
padding: 0px;
margin-top: 6px;
margin-bottom: 6px;
border: 0px;
resize: none;
outline: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
box-shadow: none;
/* needed for FF */
font-family: 'Myriad Pro', Helvetica, Arial, Sans-Serif;

View File

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

View File

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

View File

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

View File

@ -97,7 +97,8 @@ limitations under the License.
/* XXX: Hack: apparently if you try to nest a flex-box
* within a non-flex-box within a flex-box, the height
* 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
* point, but instead we fudge it and make the middlePanel
* flex itself.

View File

@ -17,6 +17,18 @@ limitations under the License.
.mx_Login {
width: 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 {
@ -28,8 +40,10 @@ limitations under the License.
.mx_Login_box {
width: 300px;
min-height: 450px;
padding-top: 50px;
padding-bottom: 50px;
margin: auto;
padding-top: 100px;
}
.mx_Login_logo {

View File

@ -50,7 +50,7 @@ module.exports = React.createClass({
return (
<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",
width: this.props.width + "px",
lineHeight: this.props.height*1.2 + "px" }}>{ initial }</span>

View File

@ -57,7 +57,7 @@ module.exports = React.createClass({
return (
<span>
<span className="mx_RoomAvatar_initial"
<span className="mx_RoomAvatar_initial" aria-hidden="true"
style={{ fontSize: (this.props.width * 0.75) + "px",
width: this.props.width + "px",
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 ChangeAvatarController = require('matrix-react-sdk/lib/controllers/molecules/ChangeAvatar')
var Loader = require("react-loader");
module.exports = React.createClass({
displayName: 'ChangeAvatar',
mixins: [ChangeAvatarController],
@ -70,6 +67,7 @@ module.exports = React.createClass({
</div>
);
case this.Phases.Uploading:
var Loader = sdk.getComponent("atoms.Spinner");
return (
<Loader />
);

View File

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

View File

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

View File

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

View File

@ -73,18 +73,33 @@ module.exports = React.createClass({
var imgStyle = {};
if (thumbHeight) imgStyle['height'] = thumbHeight;
return (
<span className="mx_MImageTile">
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
<img className="mx_MImageTile_thumbnail" src={cli.mxcUrlToHttp(content.url, 480, 360)} alt={content.body} style={imgStyle} />
</a>
<div className="mx_MImageTile_download">
<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" })
var thumbUrl = cli.mxcUrlToHttp(content.url, 480, 360);
if (thumbUrl) {
return (
<span className="mx_MImageTile">
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
<img className="mx_MImageTile_thumbnail" src={thumbUrl} alt={content.body} style={imgStyle} />
</a>
</div>
</span>
);
<div className="mx_MImageTile_download">
<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() {
var EnableNotificationsButton = sdk.getComponent("atoms.EnableNotificationsButton");
return (
<div className="mx_MatrixToolbar">
<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';
var React = require('react');
var Loader = require("../atoms/Spinner");
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var sdk = require('matrix-react-sdk')
@ -47,6 +46,7 @@ module.exports = React.createClass({
}
if (this.state.creatingRoom) {
var Loader = sdk.getComponent("atoms.Spinner");
spinner = <Loader imgClassName="mx_ContextualMenu_spinner"/>;
}

View File

@ -61,7 +61,7 @@ module.exports = React.createClass({
<MemberAvatar member={me} width={24} height={24} />
</div>
<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 className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
<img src="img/upload.png" width="17" height="22"/>

View File

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

View File

@ -29,10 +29,16 @@ module.exports = React.createClass({
showHelpPopup: function() {
var ErrorDialog = sdk.getComponent('organisms.ErrorDialog');
Modal.createDialog(ErrorDialog, {
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.",
button: "Dismiss",
focus: true
title: 'Custom Server Options',
description: <span>
You can use the custom server options to log into other Matrix servers by specifying a different Home server URL.<br/>
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 (
<div className="mx_ServerConfig">
<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>
<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>
</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 Loader = require("react-loader");
module.exports = React.createClass({
displayName: 'CreateRoom',
mixins: [CreateRoomController],
@ -122,6 +119,7 @@ module.exports = React.createClass({
render: function() {
var curr_phase = this.state.phase;
if (curr_phase == this.phases.CREATING) {
var Loader = sdk.getComponent("atoms.Spinner");
return (
<Loader/>
);

View File

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

View File

@ -23,8 +23,6 @@ var Modal = require('matrix-react-sdk/lib/Modal');
var sdk = require('matrix-react-sdk')
var dis = require('matrix-react-sdk/lib/dispatcher');
var Loader = require("react-loader");
module.exports = React.createClass({
displayName: 'RoomDirectory',
@ -121,6 +119,7 @@ module.exports = React.createClass({
render: function() {
if (this.state.loading) {
var Loader = sdk.getComponent("atoms.Spinner");
return (
<div className="mx_RoomDirectory">
<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 }/>
<div className="mx_RoomDirectory_tableWrapper">
<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) }
</table>
</div>

View File

@ -224,7 +224,7 @@ var RoomSubList = React.createClass({
room={ room }
roomSubList={ self }
key={ room.roomId }
collapsed={ self.props.collapsed }
collapsed={ self.props.collapsed || false}
selected={ selected }
unread={ self.props.activityMap[room.roomId] === 1 }
highlight={ self.props.activityMap[room.roomId] === 2 }
@ -265,7 +265,7 @@ var RoomSubList = React.createClass({
return connectDropTarget(
<div>
<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>
{ subList }
</div>

View File

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

View File

@ -17,12 +17,11 @@ limitations under the License.
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
var sdk = require('matrix-react-sdk')
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
var Loader = require("react-loader");
var LoginController = require('matrix-react-sdk/lib/controllers/templates/Login')
var config = require('../../../../../config.json');
@ -32,20 +31,37 @@ module.exports = React.createClass({
mixins: [LoginController],
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 {
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.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() {
if (this.state.serverConfigVisible) {
return this.customHsUrl;
return this.state.customHsUrl;
} else {
return config.default_hs_url;
}
@ -53,7 +69,7 @@ module.exports = React.createClass({
getIsUrl: function() {
if (this.state.serverConfigVisible) {
return this.customIsUrl;
return this.state.customIsUrl;
} else {
return config.default_is_url;
}
@ -62,7 +78,7 @@ module.exports = React.createClass({
onServerConfigVisibleChange: function(ev) {
this.setState({
serverConfigVisible: ev.target.checked
}, this.onHsUrlChanged);
}, this.onHSChosen);
},
/**
@ -79,16 +95,22 @@ module.exports = React.createClass({
var newHsUrl = this.refs.serverConfig.getHsUrl().trim();
var newIsUrl = this.refs.serverConfig.getIsUrl().trim();
if (newHsUrl == this.customHsUrl &&
newIsUrl == this.customIsUrl)
if (newHsUrl == this.state.customHsUrl &&
newIsUrl == this.state.customIsUrl)
{
return;
}
else {
this.customHsUrl = newHsUrl;
this.customIsUrl = newIsUrl;
this.setState({
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(
this.getHsUrl(),
this.getIsUrl()
@ -97,6 +119,8 @@ module.exports = React.createClass({
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.
@ -123,7 +147,7 @@ module.exports = React.createClass({
<label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label>
<div style={serverConfigStyle}>
<ServerConfig ref="serverConfig"
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl}
defaultHsUrl={this.state.customHsUrl} defaultIsUrl={this.state.customIsUrl}
onHsUrlChanged={this.onHsUrlChanged}
/>
</div>
@ -158,6 +182,7 @@ module.exports = React.createClass({
},
loginContent: function() {
var Loader = sdk.getComponent("atoms.Spinner");
var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
return (
<div>

View File

@ -21,8 +21,6 @@ var React = require('react');
var sdk = require('matrix-react-sdk')
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg')
var Loader = require("react-loader");
var RegisterController = require('../../../../controllers/templates/Register')
var config = require('../../../../../config.json');
@ -32,14 +30,28 @@ module.exports = React.createClass({
mixins: [RegisterController],
getInitialState: function() {
return {
serverConfigVisible: false
};
},
// 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");
}
componentWillMount: function() {
this.customHsUrl = config.default_hs_url;
this.customIsUrl = config.default_is_url;
// make sure we have our MatrixClient set up whatever
// Useful for debugging only.
// 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() {
@ -53,7 +65,7 @@ module.exports = React.createClass({
getHsUrl: function() {
if (this.state.serverConfigVisible) {
return this.customHsUrl;
return this.state.customHsUrl;
} else {
return config.default_hs_url;
}
@ -61,7 +73,7 @@ module.exports = React.createClass({
getIsUrl: function() {
if (this.state.serverConfigVisible) {
return this.customIsUrl;
return this.state.customIsUrl;
} else {
return config.default_is_url;
}
@ -74,8 +86,10 @@ module.exports = React.createClass({
},
onServerUrlChanged: function(newUrl) {
this.customHsUrl = this.refs.serverConfig.getHsUrl();
this.customIsUrl = this.refs.serverConfig.getIsUrl();
this.setState({
customHsUrl: this.refs.serverConfig.getHsUrl(),
customIsUrl: this.refs.serverConfig.getIsUrl(),
});
this.forceUpdate();
},
@ -92,16 +106,16 @@ module.exports = React.createClass({
return (
<div>
<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="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_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>
<div style={serverConfigStyle}>
<ServerConfig ref="serverConfig"
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl}
defaultHsUrl={this.state.customHsUrl} defaultIsUrl={this.state.customIsUrl}
onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} />
</div>
<br />
@ -128,6 +142,7 @@ module.exports = React.createClass({
registerContent: function() {
if (this.state.busy) {
var Loader = sdk.getComponent("atoms.Spinner");
return (
<Loader />
);