Factor the statusbar implementation out of RoomView

... because RoomView must die
This commit is contained in:
Richard van der Hoff 2016-02-08 18:04:54 +00:00
parent 4996164b46
commit 1d9c88698f
4 changed files with 205 additions and 117 deletions

View file

@ -26,6 +26,9 @@ strm.write(" * THIS FILE IS AUTO-GENERATED\n");
strm.write(" * You can edit it you like, but your changes will be overwritten,\n"); strm.write(" * You can edit it you like, but your changes will be overwritten,\n");
strm.write(" * so you'd just be trying to swim upstream like a salmon.\n"); strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
strm.write(" * You are not a salmon.\n"); strm.write(" * You are not a salmon.\n");
strm.write(" *\n");
strm.write(" * To update it, run:\n");
strm.write(" * ./reskindex.js -h header\n");
strm.write(" */\n\n"); strm.write(" */\n\n");
if (packageJson['matrix-react-parent']) { if (packageJson['matrix-react-parent']) {

View file

@ -19,6 +19,9 @@ limitations under the License.
* You can edit it you like, but your changes will be overwritten, * You can edit it you like, but your changes will be overwritten,
* so you'd just be trying to swim upstream like a salmon. * so you'd just be trying to swim upstream like a salmon.
* You are not a salmon. * You are not a salmon.
*
* To update it, run:
* ./reskindex.js -h header
*/ */
module.exports.components = {}; module.exports.components = {};
@ -28,6 +31,7 @@ module.exports.components['structures.login.Login'] = require('./components/stru
module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration');
module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration');
module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat'); module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat');
module.exports.components['structures.RoomStatusBar'] = require('./components/structures/RoomStatusBar');
module.exports.components['structures.RoomView'] = require('./components/structures/RoomView'); module.exports.components['structures.RoomView'] = require('./components/structures/RoomView');
module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel'); module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel');
module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar'); module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar');

View file

@ -0,0 +1,177 @@
/*
Copyright 2015, 2016 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.
*/
var React = require('react');
var sdk = require('../../index');
var dis = require("../../dispatcher");
var WhoIsTyping = require("../../WhoIsTyping");
var MatrixClientPeg = require("../../MatrixClientPeg");
module.exports = React.createClass({
displayName: 'RoomStatusBar',
propTypes: {
// the room this statusbar is representing.
room: React.PropTypes.object.isRequired,
// a list of TabCompleteEntries.Entry objects
tabCompleteEntries: React.PropTypes.array,
// the number of messages which have arrived since we've been scrolled up
numUnreadMessages: React.PropTypes.number,
// true if there are messages in the room which had errors on send
hasUnsentMessages: React.PropTypes.bool,
// callback for when the user clicks on the 'resend all' button in the
// 'unsent messages' bar
onResendAllClick: React.PropTypes.func,
// this is true if we are fully scrolled-down, and are looking at
// the end of the live timeline.
atEndOfLiveTimeline: React.PropTypes.bool,
// true if there is an active call in this room (means we show
// the 'Active Call' text in the status bar if there is nothing
// more interesting)
hasActiveCall: React.PropTypes.bool,
},
getInitialState: function() {
return {
syncState: MatrixClientPeg.get().getSyncState(),
};
},
componentWillMount: function() {
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
},
componentWillUnmount: function() {
MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
},
onSyncStateChange: function(state, prevState) {
if (state === "SYNCING" && prevState === "SYNCING") {
return;
}
this.setState({
syncState: state
});
},
render: function() {
var TabCompleteBar = sdk.getComponent('rooms.TabCompleteBar');
var TintableSvg = sdk.getComponent("elements.TintableSvg");
// no conn bar trumps unread count since you can't get unread messages
// without a connection! (technically may already have some but meh)
// It also trumps the "some not sent" msg since you can't resend without
// a connection!
if (this.state.syncState === "ERROR") {
return (
<div className="mx_RoomView_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<div className="mx_RoomView_connectionLostBar_textArea">
<div className="mx_RoomView_connectionLostBar_title">
Connectivity to the server has been lost.
</div>
<div className="mx_RoomView_connectionLostBar_desc">
Sent messages will be stored until your connection has returned.
</div>
</div>
</div>
);
}
if (this.props.tabCompleteEntries) {
return (
<div className="mx_RoomView_tabCompleteBar">
<div className="mx_RoomView_tabCompleteImage">...</div>
<div className="mx_RoomView_tabCompleteWrapper">
<TabCompleteBar entries={this.props.tabCompleteEntries} />
<div className="mx_RoomView_tabCompleteEol" title="->|">
<TintableSvg src="img/eol.svg" width="22" height="16"/>
Auto-complete
</div>
</div>
</div>
);
}
if (this.props.hasUnsentMessages) {
return (
<div className="mx_RoomView_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<div className="mx_RoomView_connectionLostBar_textArea">
<div className="mx_RoomView_connectionLostBar_title">
Some of your messages have not been sent.
</div>
<div className="mx_RoomView_connectionLostBar_desc">
<a className="mx_RoomView_resend_link"
onClick={ this.props.onResendAllClick }>
Resend all now
</a> or select individual messages to re-send.
</div>
</div>
</div>
);
}
// unread count trumps who is typing since the unread count is only
// set when you've scrolled up
if (this.props.numUnreadMsgs) {
var unreadMsgs = this.props.numUnreadMessages + " new message" +
(this.props.numUnreadMessages > 1 ? "s" : "");
return (
<div className="mx_RoomView_unreadMessagesBar" onClick={ this.jumpToLiveTimeline }>
<img src="img/newmessages.svg" width="24" height="24" alt=""/>
{unreadMsgs}
</div>
);
}
var typingString = WhoIsTyping.whoIsTypingString(this.props.room);
if (typingString) {
return (
<div className="mx_RoomView_typingBar">
<div className="mx_RoomView_typingImage">...</div>
<span className="mx_RoomView_typingText">{typingString}</span>
</div>
);
}
if (!this.props.atEndOfLiveTimeline) {
return (
<div className="mx_RoomView_scrollToBottomBar" onClick={ this.jumpToLiveTimeline }>
<img src="img/scrolldown.svg" width="24" height="24" alt="Scroll to bottom of page" title="Scroll to bottom of page"/>
</div>
);
}
if (this.props.hasActiveCall) {
return (
<div className="mx_RoomView_callBar">
<img src="img/sound-indicator.svg" width="23" height="20"/>
<b>Active call</b>
</div>
);
}
return <div />;
},
});

View file

@ -30,7 +30,6 @@ var EventTimeline = Matrix.EventTimeline;
var MatrixClientPeg = require("../../MatrixClientPeg"); var MatrixClientPeg = require("../../MatrixClientPeg");
var ContentMessages = require("../../ContentMessages"); var ContentMessages = require("../../ContentMessages");
var WhoIsTyping = require("../../WhoIsTyping");
var Modal = require("../../Modal"); var Modal = require("../../Modal");
var sdk = require('../../index'); var sdk = require('../../index');
var CallHandler = require('../../CallHandler'); var CallHandler = require('../../CallHandler');
@ -104,7 +103,6 @@ module.exports = React.createClass({
draggingFile: false, draggingFile: false,
searching: false, searching: false,
searchResults: null, searchResults: null,
syncState: MatrixClientPeg.get().getSyncState(),
hasUnsentMessages: this._hasUnsentMessages(room), hasUnsentMessages: this._hasUnsentMessages(room),
callState: null, callState: null,
timelineLoading: true, // track whether our room timeline is loading timelineLoading: true, // track whether our room timeline is loading
@ -130,7 +128,6 @@ module.exports = React.createClass({
MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt);
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping); MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember); MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember);
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
// xchat-style tab complete, add a colon if tab // xchat-style tab complete, add a colon if tab
// completing at the start of the text // completing at the start of the text
this.tabComplete = new TabComplete({ this.tabComplete = new TabComplete({
@ -286,7 +283,6 @@ module.exports = React.createClass({
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping); MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping);
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange);
} }
window.removeEventListener('resize', this.onResize); window.removeEventListener('resize', this.onResize);
@ -352,15 +348,6 @@ module.exports = React.createClass({
} }
}, },
onSyncStateChange: function(state, prevState) {
if (state === "SYNCING" && prevState === "SYNCING") {
return;
}
this.setState({
syncState: state
});
},
componentWillReceiveProps: function(newProps) { componentWillReceiveProps: function(newProps) {
if (newProps.roomId != this.props.roomId) { if (newProps.roomId != this.props.roomId) {
throw new Error("changing room on a RoomView is not supported"); throw new Error("changing room on a RoomView is not supported");
@ -833,10 +820,6 @@ module.exports = React.createClass({
}); });
}, },
getWhoIsTypingString: function() {
return WhoIsTyping.whoIsTypingString(this.state.room);
},
onSearch: function(term, scope) { onSearch: function(term, scope) {
this.setState({ this.setState({
searchTerm: term, searchTerm: term,
@ -1320,13 +1303,6 @@ module.exports = React.createClass({
}); });
}, },
getUnreadMessagesString: function() {
if (!this.state.numUnreadMessages) {
return "";
}
return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : "");
},
// jump down to the bottom of this room, where new events are arriving // jump down to the bottom of this room, where new events are arriving
jumpToLiveTimeline: function() { jumpToLiveTimeline: function() {
// if we can't forward-paginate the existing timeline, then there // if we can't forward-paginate the existing timeline, then there
@ -1559,6 +1535,12 @@ module.exports = React.createClass({
// We have successfully loaded this room, and are not previewing. // We have successfully loaded this room, and are not previewing.
// Display the "normal" room view. // Display the "normal" room view.
var call = CallHandler.getCallForRoom(this.props.roomId);
var inCall = false;
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
inCall = true;
}
var scrollheader_classes = classNames({ var scrollheader_classes = classNames({
mx_RoomView_scrollheader: true, mx_RoomView_scrollheader: true,
loading: this.state.paginating loading: this.state.paginating
@ -1577,86 +1559,19 @@ module.exports = React.createClass({
var UploadBar = sdk.getComponent('structures.UploadBar'); var UploadBar = sdk.getComponent('structures.UploadBar');
statusBar = <UploadBar room={this.state.room} /> statusBar = <UploadBar room={this.state.room} />
} else if (!this.state.searchResults) { } else if (!this.state.searchResults) {
var typingString = this.getWhoIsTypingString(); var RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
// typingString = "S͚͍̭̪̤͙̱͙̖̥͙̥̤̻̙͕͓͂̌ͬ͐̂k̜̝͎̰̥̻̼̂̌͛͗͊̅̒͂̊̍̍͌̈̈́͌̋̊ͬa͉̯͚̺̗̳̩ͪ̋̑͌̓̆̍̂̉̏̅̆ͧ̌̑v̲̲̪̝ͥ̌ͨͮͭ̊͆̾ͮ̍ͮ͑̚e̮̙͈̱̘͕̼̮͒ͩͨͫ̃͗̇ͩ͒ͣͦ͒̄̍͐ͣ̿ͥṘ̗̺͇̺̺͔̄́̊̓͊̍̃ͨ̚ā̼͎̘̟̼͎̜̪̪͚̋ͨͨͧ̓ͦͯͤ̄͆̋͂ͩ͌ͧͅt̙̙̹̗̦͖̞ͫͪ͑̑̅ͪ̃̚ͅ is typing..."; var tabEntries = this.tabComplete.isTabCompleting() ?
var unreadMsgs = this.getUnreadMessagesString(); this.tabComplete.peek(6) : null;
// no conn bar trumps unread count since you can't get unread messages
// without a connection! (technically may already have some but meh) statusBar = <RoomStatusBar
// It also trumps the "some not sent" msg since you can't resend without room={this.state.room}
// a connection! tabCompleteEntries={tabEntries}
if (this.state.syncState === "ERROR") { numUnreadMessages={this.state.numUnreadMessages}
statusBar = ( hasUnsentMessages={this.state.hasUnsentMessages}
<div className="mx_RoomView_connectionLostBar"> onResendAllClick={this.onResendAllClick}
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/> atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
<div className="mx_RoomView_connectionLostBar_textArea"> hasActiveCall={inCall}
<div className="mx_RoomView_connectionLostBar_title"> />
Connectivity to the server has been lost.
</div>
<div className="mx_RoomView_connectionLostBar_desc">
Sent messages will be stored until your connection has returned.
</div>
</div>
</div>
);
}
else if (this.tabComplete.isTabCompleting()) {
var TabCompleteBar = sdk.getComponent('rooms.TabCompleteBar');
statusBar = (
<div className="mx_RoomView_tabCompleteBar">
<div className="mx_RoomView_tabCompleteImage">...</div>
<div className="mx_RoomView_tabCompleteWrapper">
<TabCompleteBar entries={this.tabComplete.peek(6)} />
<div className="mx_RoomView_tabCompleteEol" title="->|">
<TintableSvg src="img/eol.svg" width="22" height="16"/>
Auto-complete
</div>
</div>
</div>
);
}
else if (this.state.hasUnsentMessages) {
statusBar = (
<div className="mx_RoomView_connectionLostBar">
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ "/>
<div className="mx_RoomView_connectionLostBar_textArea">
<div className="mx_RoomView_connectionLostBar_title">
Some of your messages have not been sent.
</div>
<div className="mx_RoomView_connectionLostBar_desc">
<a className="mx_RoomView_resend_link"
onClick={ this.onResendAllClick }>
Resend all now
</a> or select individual messages to re-send.
</div>
</div>
</div>
);
}
// unread count trumps who is typing since the unread count is only
// set when you've scrolled up
else if (unreadMsgs) {
statusBar = (
<div className="mx_RoomView_unreadMessagesBar" onClick={ this.jumpToLiveTimeline }>
<img src="img/newmessages.svg" width="24" height="24" alt=""/>
{unreadMsgs}
</div>
);
}
else if (typingString) {
statusBar = (
<div className="mx_RoomView_typingBar">
<div className="mx_RoomView_typingImage">...</div>
<span className="mx_RoomView_typingText">{typingString}</span>
</div>
);
}
else if (!this.state.atEndOfLiveTimeline) {
statusBar = (
<div className="mx_RoomView_scrollToBottomBar" onClick={ this.jumpToLiveTimeline }>
<img src="img/scrolldown.svg" width="24" height="24" alt="Scroll to bottom of page" title="Scroll to bottom of page"/>
</div>
);
}
} }
var aux = null; var aux = null;
@ -1727,11 +1642,7 @@ module.exports = React.createClass({
}; };
} }
var call = CallHandler.getCallForRoom(this.props.roomId); if (inCall) {
//var call = CallHandler.getAnyActiveCall();
var inCall = false;
if (call && (this.state.callState !== 'ended' && this.state.callState !== 'ringing')) {
inCall = true;
var zoomButton, voiceMuteButton, videoMuteButton; var zoomButton, voiceMuteButton, videoMuteButton;
if (call.type === "video") { if (call.type === "video") {
@ -1755,14 +1666,7 @@ module.exports = React.createClass({
width="21" height="26"/> width="21" height="26"/>
</div> </div>
if (!statusBar) { // wrap the existing status bar into a 'callStatusBar' which adds more knobs.
statusBar =
<div className="mx_RoomView_callBar">
<img src="img/sound-indicator.svg" width="23" height="20"/>
<b>Active call</b>
</div>;
}
statusBar = statusBar =
<div className="mx_RoomView_callStatusBar"> <div className="mx_RoomView_callStatusBar">
{ voiceMuteButton } { voiceMuteButton }