Factor a stateless messagepanel out from RoomView
This commit is contained in:
parent
f016a327b1
commit
c3692378fa
3 changed files with 287 additions and 158 deletions
|
@ -31,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.MessagePanel'] = require('./components/structures/MessagePanel');
|
||||||
module.exports.components['structures.RoomStatusBar'] = require('./components/structures/RoomStatusBar');
|
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');
|
||||||
|
|
264
src/components/structures/MessagePanel.js
Normal file
264
src/components/structures/MessagePanel.js
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
Copyright 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');
|
||||||
|
|
||||||
|
/* stateless UI component which builds the event tiles in the room timeline.
|
||||||
|
*/
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MessagePanel',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
// true to give the component a 'display: hidden' style.
|
||||||
|
hidden: React.PropTypes.bool,
|
||||||
|
|
||||||
|
// the list of MatrixEvents to display
|
||||||
|
events: React.PropTypes.array.isRequired,
|
||||||
|
|
||||||
|
// ID of an event to highlight. If undefined, no event will be highlighted.
|
||||||
|
highlightedEventId: React.PropTypes.string,
|
||||||
|
|
||||||
|
// event after which we should show a read marker
|
||||||
|
readMarkerEventId: React.PropTypes.string,
|
||||||
|
|
||||||
|
// event after which we should show an animating disappearance of a
|
||||||
|
// read marker
|
||||||
|
readMarkerGhostEventId: React.PropTypes.string,
|
||||||
|
|
||||||
|
// the userid of our user. This is used to suppress the read marker
|
||||||
|
// for pending messages.
|
||||||
|
ourUserId: React.PropTypes.string,
|
||||||
|
|
||||||
|
// true to suppress the date at the start of the timeline
|
||||||
|
suppressFirstDateSeparator: React.PropTypes.bool,
|
||||||
|
|
||||||
|
// true if updates to the event list should cause the scroll panel to
|
||||||
|
// scroll down when we are at the bottom of the window. See ScrollPanel
|
||||||
|
// for more details.
|
||||||
|
stickyBottom: React.PropTypes.bool,
|
||||||
|
|
||||||
|
// callback to determine if a user is the magic freeswitch conference
|
||||||
|
// user. Takes one parameter, which is a user id. Should return true if
|
||||||
|
// the user is the conference user.
|
||||||
|
isConferenceUser: React.PropTypes.func,
|
||||||
|
|
||||||
|
// callback which is called when the panel is scrolled.
|
||||||
|
onScroll: React.PropTypes.func,
|
||||||
|
|
||||||
|
// callback which is called when more content is needed.
|
||||||
|
onFillRequest: React.PropTypes.func,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* get the DOM node representing the given event */
|
||||||
|
getNodeForEventId: function(eventId) {
|
||||||
|
if (!this.eventNodes) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.eventNodes[eventId];
|
||||||
|
},
|
||||||
|
|
||||||
|
/* return true if the content is fully scrolled down right now; else false.
|
||||||
|
*/
|
||||||
|
isAtBottom: function() {
|
||||||
|
return this.refs.scrollPanel
|
||||||
|
&& this.refs.scrollPanel.isAtBottom();
|
||||||
|
},
|
||||||
|
|
||||||
|
/* get the current scroll state. See ScrollPanel.getScrollState for
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* returns null if we are not mounted.
|
||||||
|
*/
|
||||||
|
getScrollState: function() {
|
||||||
|
if (!this.refs.scrollPanel) { return null; }
|
||||||
|
return this.refs.scrollPanel.getScrollState();
|
||||||
|
},
|
||||||
|
|
||||||
|
/* jump to the bottom of the content.
|
||||||
|
*/
|
||||||
|
scrollToBottom: function() {
|
||||||
|
if (this.refs.scrollPanel) {
|
||||||
|
this.refs.scrollPanel.scrollToBottom();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* jump to the given event id.
|
||||||
|
*
|
||||||
|
* pixelOffset gives the number of pixels between the bottom of the node
|
||||||
|
* and the bottom of the container. If undefined, it will put the node
|
||||||
|
* in the middle of the container.
|
||||||
|
*/
|
||||||
|
scrollToEvent: function(eventId, pixelOffset) {
|
||||||
|
if (this.refs.scrollPanel) {
|
||||||
|
this.refs.scrollPanel.scrollToToken(eventId, pixelOffset);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* check the scroll state and send out pagination requests if necessary.
|
||||||
|
*/
|
||||||
|
checkFillState: function() {
|
||||||
|
if (this.refs.scrollPanel) {
|
||||||
|
this.refs.scrollPanel.checkFillState();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||||
|
return (
|
||||||
|
<ScrollPanel ref="scrollPanel" className="mx_RoomView_messagePanel"
|
||||||
|
onScroll={ this.props.onScroll }
|
||||||
|
onFillRequest={ this.props.onFillRequest }
|
||||||
|
style={ this.props.hidden ? { display: 'none' } : {} }
|
||||||
|
stickyBottom={ this.props.stickyBottom }>
|
||||||
|
{this._getEventTiles()}
|
||||||
|
</ScrollPanel>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getEventTiles: function() {
|
||||||
|
var DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||||
|
var EventTile = sdk.getComponent('rooms.EventTile');
|
||||||
|
|
||||||
|
var ret = [];
|
||||||
|
|
||||||
|
var prevEvent = null; // the last event we showed
|
||||||
|
var ghostIndex;
|
||||||
|
var readMarkerIndex;
|
||||||
|
for (var i = 0; i < this.props.events.length; i++) {
|
||||||
|
var mxEv = this.props.events[i];
|
||||||
|
|
||||||
|
if (!EventTile.haveTileForEvent(mxEv)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.isConferenceUser && mxEv.getType() === "m.room.member") {
|
||||||
|
if (this.props.isConferenceUser(mxEv.getSender()) ||
|
||||||
|
this.props.isConferenceUser(mxEv.getStateKey())) {
|
||||||
|
continue; // suppress conf user join/parts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we've decided whether or not to show this message,
|
||||||
|
// add the read up to marker if appropriate
|
||||||
|
// doing this here means we implicitly do not show the marker
|
||||||
|
// if it's at the bottom
|
||||||
|
// NB. it would be better to decide where the read marker was going
|
||||||
|
// when the state changed rather than here in the render method, but
|
||||||
|
// this is where we decide what messages we show so it's the only
|
||||||
|
// place we know whether we're at the bottom or not.
|
||||||
|
var mxEvSender = mxEv.sender ? mxEv.sender.userId : null;
|
||||||
|
if (prevEvent && prevEvent.getId() == this.props.readMarkerEventId) {
|
||||||
|
// suppress the read marker if the next event is sent by us; this
|
||||||
|
// is a nonsensical and temporary situation caused by the delay between
|
||||||
|
// us sending a message and receiving the synthesized receipt.
|
||||||
|
if (mxEvSender != this.props.ourUserId) {
|
||||||
|
var hr;
|
||||||
|
hr = (
|
||||||
|
<hr className="mx_RoomView_myReadMarker"
|
||||||
|
style={{opacity: 1, width: '99%'}}
|
||||||
|
/>);
|
||||||
|
readMarkerIndex = ret.length;
|
||||||
|
ret.push(
|
||||||
|
<li key="_readupto"
|
||||||
|
className="mx_RoomView_myReadMarker_container">
|
||||||
|
{hr}
|
||||||
|
</li>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is this a continuation of the previous message?
|
||||||
|
var continuation = false;
|
||||||
|
if (prevEvent !== null) {
|
||||||
|
if (mxEvSender &&
|
||||||
|
prevEvent.sender &&
|
||||||
|
(mxEvSender === prevEvent.sender.userId) &&
|
||||||
|
(mxEv.getType() == prevEvent.getType())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
continuation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do we need a date separator since the last event?
|
||||||
|
var ts1 = mxEv.getTs();
|
||||||
|
if ((prevEvent == null && !this.props.suppressFirstDateSeparator) ||
|
||||||
|
(prevEvent != null &&
|
||||||
|
new Date(prevEvent.getTs()).toDateString()
|
||||||
|
!== new Date(ts1).toDateString())) {
|
||||||
|
var dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1}/></li>;
|
||||||
|
ret.push(dateSeparator);
|
||||||
|
continuation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var last = false;
|
||||||
|
if (i == this.props.events.length - 1) {
|
||||||
|
// XXX: we might not show a tile for the last event.
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventId = mxEv.getId();
|
||||||
|
var highlight = (eventId == this.props.highlightedEventId);
|
||||||
|
|
||||||
|
// we can't use local echoes as scroll tokens, because their event IDs change.
|
||||||
|
// Local echos have a send "status".
|
||||||
|
var scrollToken = mxEv.status ? undefined : eventId;
|
||||||
|
|
||||||
|
ret.push(
|
||||||
|
<li key={eventId}
|
||||||
|
ref={this._collectEventNode.bind(this, eventId)}
|
||||||
|
data-scroll-token={scrollToken}>
|
||||||
|
<EventTile mxEvent={mxEv} continuation={continuation}
|
||||||
|
last={last} isSelectedEvent={highlight}/>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
// A read up to marker has died and returned as a ghost!
|
||||||
|
// Lives in the dom as the ghost of the previous one while it fades away
|
||||||
|
if (eventId == this.props.readMarkerGhostEventId) {
|
||||||
|
ghostIndex = ret.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevEvent = mxEv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// splice the read marker ghost in now that we know whether the read receipt
|
||||||
|
// is the last element or not, because we only decide as we're going along.
|
||||||
|
if (readMarkerIndex === undefined && ghostIndex && ghostIndex <= ret.length) {
|
||||||
|
var hr;
|
||||||
|
hr = (<hr className="mx_RoomView_myReadMarker"
|
||||||
|
style={{opacity: 1, width: '99%'}}
|
||||||
|
ref={function(n) {
|
||||||
|
Velocity(n, {opacity: '0', width: '10%'},
|
||||||
|
{duration: 400, easing: 'easeInSine', delay: 1000});
|
||||||
|
}} />);
|
||||||
|
ret.splice(ghostIndex, 0, (
|
||||||
|
<li key="_readuptoghost"
|
||||||
|
className="mx_RoomView_myReadMarker_container">
|
||||||
|
{hr}
|
||||||
|
</li>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
|
||||||
|
_collectEventNode: function(eventId, node) {
|
||||||
|
if (this.eventNodes == undefined) this.eventNodes = {};
|
||||||
|
this.eventNodes[eventId] = node;
|
||||||
|
},
|
||||||
|
});
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
// - Search results component
|
// - Search results component
|
||||||
// - Drag and drop
|
// - Drag and drop
|
||||||
// - File uploading - uploadFile()
|
// - File uploading - uploadFile()
|
||||||
// - Timeline component (alllll the logic in getEventTiles())
|
|
||||||
|
|
||||||
var React = require("react");
|
var React = require("react");
|
||||||
var ReactDOM = require("react-dom");
|
var ReactDOM = require("react-dom");
|
||||||
|
@ -77,10 +76,6 @@ module.exports = React.createClass({
|
||||||
highlightedEventId: React.PropTypes.string,
|
highlightedEventId: React.PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
/* properties in RoomView objects include:
|
|
||||||
*
|
|
||||||
* eventNodes: a map from event id to DOM node representing that event
|
|
||||||
*/
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null;
|
var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null;
|
||||||
return {
|
return {
|
||||||
|
@ -227,7 +222,7 @@ module.exports = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (eventId) {
|
if (eventId) {
|
||||||
this.refs.messagePanel.scrollToToken(eventId, pixelOffset);
|
this.refs.messagePanel.scrollToEvent(eventId, pixelOffset);
|
||||||
} else {
|
} else {
|
||||||
this.refs.messagePanel.scrollToBottom();
|
this.refs.messagePanel.scrollToBottom();
|
||||||
}
|
}
|
||||||
|
@ -537,10 +532,6 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
if (this.refs.messagePanel) {
|
|
||||||
this._initialiseMessagePanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
var call = CallHandler.getCallForRoom(this.props.roomId);
|
var call = CallHandler.getCallForRoom(this.props.roomId);
|
||||||
var callState = call ? call.call_state : "ended";
|
var callState = call ? call.call_state : "ended";
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -585,23 +576,6 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}, 500),
|
}, 500),
|
||||||
|
|
||||||
_initialiseMessagePanel: function() {
|
|
||||||
var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel);
|
|
||||||
this.refs.messagePanel.initialised = true;
|
|
||||||
this.updateTint();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
|
||||||
// we need to initialise the messagepanel if we've just joined the
|
|
||||||
// room. TODO: we really really ought to factor out messagepanel to a
|
|
||||||
// separate component to avoid this ridiculous dance.
|
|
||||||
if (!this.refs.messagePanel) return;
|
|
||||||
|
|
||||||
if (!this.refs.messagePanel.initialised) {
|
|
||||||
this._initialiseMessagePanel();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onTimelineUpdated: function(gotResults) {
|
_onTimelineUpdated: function(gotResults) {
|
||||||
// we might have switched rooms since the load started - just bin
|
// we might have switched rooms since the load started - just bin
|
||||||
// the results if so.
|
// the results if so.
|
||||||
|
@ -954,125 +928,6 @@ module.exports = React.createClass({
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
getEventTiles: function() {
|
|
||||||
var DateSeparator = sdk.getComponent('messages.DateSeparator');
|
|
||||||
|
|
||||||
var ret = [];
|
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
var EventTile = sdk.getComponent('rooms.EventTile');
|
|
||||||
|
|
||||||
var prevEvent = null; // the last event we showed
|
|
||||||
var ghostIndex;
|
|
||||||
var readMarkerIndex;
|
|
||||||
for (var i = 0; i < this.state.events.length; i++) {
|
|
||||||
var mxEv = this.state.events[i];
|
|
||||||
|
|
||||||
if (!EventTile.haveTileForEvent(mxEv)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (this.props.ConferenceHandler && mxEv.getType() === "m.room.member") {
|
|
||||||
if (this.props.ConferenceHandler.isConferenceUser(mxEv.getSender()) ||
|
|
||||||
this.props.ConferenceHandler.isConferenceUser(mxEv.getStateKey())) {
|
|
||||||
continue; // suppress conf user join/parts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we've decided whether or not to show this message,
|
|
||||||
// add the read up to marker if appropriate
|
|
||||||
// doing this here means we implicitly do not show the marker
|
|
||||||
// if it's at the bottom
|
|
||||||
// NB. it would be better to decide where the read marker was going
|
|
||||||
// when the state changed rather than here in the render method, but
|
|
||||||
// this is where we decide what messages we show so it's the only
|
|
||||||
// place we know whether we're at the bottom or not.
|
|
||||||
var self = this;
|
|
||||||
var mxEvSender = mxEv.sender ? mxEv.sender.userId : null;
|
|
||||||
if (prevEvent && prevEvent.getId() == this.state.readMarkerEventId && mxEvSender != MatrixClientPeg.get().credentials.userId) {
|
|
||||||
var hr;
|
|
||||||
hr = (<hr className="mx_RoomView_myReadMarker" style={{opacity: 1, width: '99%'}} ref={function(n) {
|
|
||||||
self.readMarkerNode = n;
|
|
||||||
}} />);
|
|
||||||
readMarkerIndex = ret.length;
|
|
||||||
ret.push(<li key="_readupto" className="mx_RoomView_myReadMarker_container">{hr}</li>);
|
|
||||||
}
|
|
||||||
|
|
||||||
// is this a continuation of the previous message?
|
|
||||||
var continuation = false;
|
|
||||||
if (prevEvent !== null) {
|
|
||||||
if (mxEv.sender &&
|
|
||||||
prevEvent.sender &&
|
|
||||||
(mxEv.sender.userId === prevEvent.sender.userId) &&
|
|
||||||
(mxEv.getType() == prevEvent.getType())
|
|
||||||
)
|
|
||||||
{
|
|
||||||
continuation = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do we need a date separator since the last event?
|
|
||||||
var ts1 = mxEv.getTs();
|
|
||||||
if ((prevEvent == null && !this.state.canBackPaginate) ||
|
|
||||||
(prevEvent != null &&
|
|
||||||
new Date(prevEvent.getTs()).toDateString() !== new Date(ts1).toDateString())) {
|
|
||||||
var dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1}/></li>;
|
|
||||||
ret.push(dateSeparator);
|
|
||||||
continuation = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var last = false;
|
|
||||||
if (i == this.state.events.length - 1) {
|
|
||||||
// XXX: we might not show a tile for the last event.
|
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var eventId = mxEv.getId();
|
|
||||||
var highlight = (eventId == this.props.highlightedEventId);
|
|
||||||
|
|
||||||
// we can't use local echoes as scroll tokens, because their event IDs change.
|
|
||||||
// Local echos have a send "status".
|
|
||||||
var scrollToken = mxEv.status ? undefined : eventId;
|
|
||||||
|
|
||||||
ret.push(
|
|
||||||
<li key={eventId}
|
|
||||||
ref={this._collectEventNode.bind(this, eventId)}
|
|
||||||
data-scroll-token={scrollToken}>
|
|
||||||
<EventTile mxEvent={mxEv} continuation={continuation}
|
|
||||||
last={last} isSelectedEvent={highlight}/>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
|
|
||||||
// A read up to marker has died and returned as a ghost!
|
|
||||||
// Lives in the dom as the ghost of the previous one while it fades away
|
|
||||||
if (eventId == this.state.readMarkerGhostEventId) {
|
|
||||||
ghostIndex = ret.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
prevEvent = mxEv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// splice the read marker ghost in now that we know whether the read receipt
|
|
||||||
// is the last element or not, because we only decide as we're going along.
|
|
||||||
if (readMarkerIndex === undefined && ghostIndex && ghostIndex <= ret.length) {
|
|
||||||
var hr;
|
|
||||||
hr = (<hr className="mx_RoomView_myReadMarker" style={{opacity: 1, width: '99%'}} ref={function(n) {
|
|
||||||
Velocity(n, {opacity: '0', width: '10%'}, {duration: 400, easing: 'easeInSine', delay: 1000, complete: function() {
|
|
||||||
if (!self.unmounted) self.setState({readMarkerGhostEventId: undefined});
|
|
||||||
}});
|
|
||||||
}} />);
|
|
||||||
ret.splice(ghostIndex, 0, (
|
|
||||||
<li key="_readuptoghost" className="mx_RoomView_myReadMarker_container">{hr}</li>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
|
|
||||||
_collectEventNode: function(eventId, node) {
|
|
||||||
if (this.eventNodes == undefined) this.eventNodes = {};
|
|
||||||
this.eventNodes[eventId] = node;
|
|
||||||
},
|
|
||||||
|
|
||||||
_indexForEventId(evId) {
|
_indexForEventId(evId) {
|
||||||
for (var i = 0; i < this.state.events.length; ++i) {
|
for (var i = 0; i < this.state.events.length; ++i) {
|
||||||
if (evId == this.state.events[i].getId()) {
|
if (evId == this.state.events[i].getId()) {
|
||||||
|
@ -1130,11 +985,10 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_getLastDisplayedEventIndexIgnoringOwn: function() {
|
_getLastDisplayedEventIndexIgnoringOwn: function() {
|
||||||
if (this.eventNodes === undefined) return null;
|
var messagePanel = this.refs.messagePanel;
|
||||||
|
if (messagePanel === undefined) return null;
|
||||||
|
|
||||||
var messageWrapper = this.refs.messagePanel;
|
var wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect();
|
||||||
if (messageWrapper === undefined) return null;
|
|
||||||
var wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect();
|
|
||||||
|
|
||||||
for (var i = this.state.events.length-1; i >= 0; --i) {
|
for (var i = this.state.events.length-1; i >= 0; --i) {
|
||||||
var ev = this.state.events[i];
|
var ev = this.state.events[i];
|
||||||
|
@ -1143,7 +997,7 @@ module.exports = React.createClass({
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = this.eventNodes[ev.getId()];
|
var node = messagePanel.getNodeForEventId(ev.getId());
|
||||||
if (!node) continue;
|
if (!node) continue;
|
||||||
|
|
||||||
var boundingRect = node.getBoundingClientRect();
|
var boundingRect = node.getBoundingClientRect();
|
||||||
|
@ -1412,10 +1266,10 @@ module.exports = React.createClass({
|
||||||
var CallView = sdk.getComponent("voip.CallView");
|
var CallView = sdk.getComponent("voip.CallView");
|
||||||
var RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
var RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
||||||
var SearchBar = sdk.getComponent("rooms.SearchBar");
|
var SearchBar = sdk.getComponent("rooms.SearchBar");
|
||||||
var ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
|
||||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
|
var RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
|
||||||
var Loader = sdk.getComponent("elements.Spinner");
|
var Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
var MessagePanel = sdk.getComponent("structures.MessagePanel");
|
||||||
|
|
||||||
if (!this._timelineWindow) {
|
if (!this._timelineWindow) {
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
|
@ -1687,14 +1541,24 @@ module.exports = React.createClass({
|
||||||
var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
var stickyBottom = !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
||||||
|
|
||||||
messagePanel = (
|
messagePanel = (
|
||||||
<ScrollPanel ref="messagePanel" className="mx_RoomView_messagePanel"
|
<MessagePanel ref={(r) => {
|
||||||
|
this.refs.messagePanel = r;
|
||||||
|
this.updateTint();
|
||||||
|
}}
|
||||||
|
hidden={ hideMessagePanel }
|
||||||
|
events={ this.state.events }
|
||||||
|
highlightedEventId={ this.props.highlightedEventId }
|
||||||
|
readMarkerEventId={ this.state.readMarkerEventId }
|
||||||
|
readMarkerGhostEventId={ this.state.readMarkerGhostEventId }
|
||||||
|
ourUserId={ MatrixClientPeg.get().credentials.userId }
|
||||||
|
suppressFirstDateSeparator={ this.state.canBackPaginate }
|
||||||
|
stickyBottom={ stickyBottom }
|
||||||
|
isConferenceUser={this.props.ConferenceHandler ?
|
||||||
|
this.props.ConferenceHandler.isConferenceUser :
|
||||||
|
null }
|
||||||
onScroll={ this.onMessageListScroll }
|
onScroll={ this.onMessageListScroll }
|
||||||
onFillRequest={ this.onMessageListFillRequest }
|
onFillRequest={ this.onMessageListFillRequest }
|
||||||
style={ hideMessagePanel ? { display: 'none' } : {} }
|
/>
|
||||||
stickyBottom={ stickyBottom }>
|
|
||||||
<li className={scrollheader_classes}></li>
|
|
||||||
{this.getEventTiles()}
|
|
||||||
</ScrollPanel>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue