Merge up from develop
This commit is contained in:
commit
524eeaa315
27 changed files with 780 additions and 171 deletions
121
src/components/structures/FilePanel.js
Normal file
121
src/components/structures/FilePanel.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
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 ReactDOM = require("react-dom");
|
||||
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var sdk = require('../../index');
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var dis = require("../../dispatcher");
|
||||
|
||||
/*
|
||||
* Component which shows the filtered file using a TimelinePanel
|
||||
*/
|
||||
var FilePanel = React.createClass({
|
||||
displayName: 'FilePanel',
|
||||
|
||||
propTypes: {
|
||||
roomId: React.PropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
timelineSet: null,
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.updateTimelineSet(this.props.roomId);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
if (nextProps.roomId !== this.props.roomId) {
|
||||
// otherwise we race between re-rendering the TimelinePanel and setting the new timelineSet.
|
||||
//
|
||||
// FIXME: this race only happens because of the promise returned by getOrCreateFilter().
|
||||
// We should only need to create the containsUrl filter once per login session, so in practice
|
||||
// it shouldn't be being done here at all. Then we could just update the timelineSet directly
|
||||
// without resetting it first, and speed up room-change.
|
||||
this.setState({ timelineSet: null });
|
||||
this.updateTimelineSet(nextProps.roomId);
|
||||
}
|
||||
},
|
||||
|
||||
updateTimelineSet: function(roomId) {
|
||||
var client = MatrixClientPeg.get();
|
||||
var room = client.getRoom(roomId);
|
||||
|
||||
if (room) {
|
||||
var filter = new Matrix.Filter(client.credentials.userId);
|
||||
filter.setDefinition(
|
||||
{
|
||||
"room": {
|
||||
"timeline": {
|
||||
"contains_url": true
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// FIXME: we shouldn't be doing this every time we change room - see comment above.
|
||||
client.getOrCreateFilter("FILTER_FILES_" + client.credentials.userId, filter).then(
|
||||
(filterId)=>{
|
||||
filter.filterId = filterId;
|
||||
var timelineSet = room.getOrCreateFilteredTimelineSet(filter);
|
||||
this.setState({ timelineSet: timelineSet });
|
||||
},
|
||||
(error)=>{
|
||||
console.error("Failed to get or create file panel filter", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// wrap a TimelinePanel with the jump-to-event bits turned off.
|
||||
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
if (this.state.timelineSet) {
|
||||
// console.log("rendering TimelinePanel for timelineSet " + this.state.timelineSet.room.roomId + " " +
|
||||
// "(" + this.state.timelineSet._timelines.join(", ") + ")" + " with key " + this.props.roomId);
|
||||
return (
|
||||
<TimelinePanel key={"filepanel_" + this.props.roomId}
|
||||
className="mx_FilePanel"
|
||||
manageReadReceipts={false}
|
||||
manageReadMarkers={false}
|
||||
timelineSet={this.state.timelineSet}
|
||||
showUrlPreview = { false }
|
||||
tileShape="file_grid"
|
||||
opacity={ this.props.opacity }
|
||||
/>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="mx_FilePanel">
|
||||
<Loader/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = FilePanel;
|
|
@ -60,6 +60,9 @@ module.exports = React.createClass({
|
|||
// true to suppress the date at the start of the timeline
|
||||
suppressFirstDateSeparator: React.PropTypes.bool,
|
||||
|
||||
// whether to show read receipts
|
||||
manageReadReceipts: 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.
|
||||
|
@ -73,6 +76,12 @@ module.exports = React.createClass({
|
|||
|
||||
// opacity for dynamic UI fading effects
|
||||
opacity: React.PropTypes.number,
|
||||
|
||||
// className for the panel
|
||||
className: React.PropTypes.string.isRequired,
|
||||
|
||||
// shape parameter to be passed to EventTiles
|
||||
tileShape: React.PropTypes.string,
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -337,6 +346,7 @@ module.exports = React.createClass({
|
|||
continuation = true;
|
||||
}
|
||||
|
||||
/*
|
||||
// Work out if this is still a continuation, as we are now showing commands
|
||||
// and /me messages with their own little avatar. The case of a change of
|
||||
// event type (commands) is handled above, but we need to handle the /me
|
||||
|
@ -348,6 +358,7 @@ module.exports = React.createClass({
|
|||
&& prevEvent.getContent().msgtype === 'm.emote') {
|
||||
continuation = false;
|
||||
}
|
||||
*/
|
||||
|
||||
// local echoes have a fake date, which could even be yesterday. Treat them
|
||||
// as 'today' for the date separators.
|
||||
|
@ -370,7 +381,10 @@ module.exports = React.createClass({
|
|||
// Local echos have a send "status".
|
||||
var scrollToken = mxEv.status ? undefined : eventId;
|
||||
|
||||
var readReceipts = this._getReadReceiptsForEvent(mxEv);
|
||||
var readReceipts;
|
||||
if (this.props.manageReadReceipts) {
|
||||
readReceipts = this._getReadReceiptsForEvent(mxEv);
|
||||
}
|
||||
|
||||
ret.push(
|
||||
<li key={eventId}
|
||||
|
@ -383,6 +397,7 @@ module.exports = React.createClass({
|
|||
showUrlPreview={this.props.showUrlPreview}
|
||||
checkUnmounting={this._isUnmounting}
|
||||
eventSendStatus={mxEv.status}
|
||||
tileShape={this.props.tileShape}
|
||||
last={last} isSelectedEvent={highlight}/>
|
||||
</li>
|
||||
);
|
||||
|
@ -503,7 +518,7 @@ module.exports = React.createClass({
|
|||
style.opacity = this.props.opacity;
|
||||
|
||||
return (
|
||||
<ScrollPanel ref="scrollPanel" className="mx_RoomView_messagePanel mx_fadable"
|
||||
<ScrollPanel ref="scrollPanel" className={ this.props.className + " mx_fadable" }
|
||||
onScroll={ this.props.onScroll }
|
||||
onResize={ this.onResize }
|
||||
onFillRequest={ this.props.onFillRequest }
|
||||
|
|
65
src/components/structures/NotificationPanel.js
Normal file
65
src/components/structures/NotificationPanel.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
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 ReactDOM = require("react-dom");
|
||||
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var sdk = require('../../index');
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
var dis = require("../../dispatcher");
|
||||
|
||||
/*
|
||||
* Component which shows the global notification list using a TimelinePanel
|
||||
*/
|
||||
var NotificationPanel = React.createClass({
|
||||
displayName: 'NotificationPanel',
|
||||
|
||||
propTypes: {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// wrap a TimelinePanel with the jump-to-event bits turned off.
|
||||
var TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
var timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
|
||||
|
||||
if (timelineSet) {
|
||||
return (
|
||||
<TimelinePanel key={"NotificationPanel_" + this.props.roomId}
|
||||
className="mx_NotificationPanel"
|
||||
manageReadReceipts={false}
|
||||
manageReadMarkers={false}
|
||||
timelineSet={timelineSet}
|
||||
showUrlPreview = { false }
|
||||
opacity={ this.props.opacity }
|
||||
tileShape="notif"
|
||||
/>
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.error("No notifTimelineSet available!");
|
||||
return (
|
||||
<div className="mx_NotificationPanel">
|
||||
<Loader/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NotificationPanel;
|
|
@ -340,8 +340,12 @@ module.exports = React.createClass({
|
|||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (!room) return;
|
||||
if (!this.state.room || room.roomId != this.state.room.roomId) return;
|
||||
|
||||
// ignore events from filtered timelines
|
||||
if (data.timeline.getTimelineSet() !== room.getUnfilteredTimelineSet()) return;
|
||||
|
||||
if (ev.getType() === "org.matrix.room.preview_urls") {
|
||||
this._updatePreviewUrlVisibility(room);
|
||||
}
|
||||
|
@ -1570,7 +1574,9 @@ module.exports = React.createClass({
|
|||
|
||||
var messagePanel = (
|
||||
<TimelinePanel ref={this._gatherTimelinePanelRef}
|
||||
room={this.state.room}
|
||||
timelineSet={this.state.room.getUnfilteredTimelineSet()}
|
||||
manageReadReceipts={true}
|
||||
manageReadMarkers={true}
|
||||
hidden={hideMessagePanel}
|
||||
highlightedEventId={this.props.highlightedEventId}
|
||||
eventId={this.props.eventId}
|
||||
|
@ -1579,6 +1585,7 @@ module.exports = React.createClass({
|
|||
onReadMarkerUpdated={ this._updateTopUnreadMessagesBar }
|
||||
showUrlPreview = { this.state.showUrlPreview }
|
||||
opacity={ this.props.opacity }
|
||||
className="mx_RoomView_messagePanel"
|
||||
/>);
|
||||
|
||||
var topUnreadMessagesBar = null;
|
||||
|
|
|
@ -50,9 +50,15 @@ var TimelinePanel = React.createClass({
|
|||
displayName: 'TimelinePanel',
|
||||
|
||||
propTypes: {
|
||||
// The js-sdk Room object for the room whose timeline we are
|
||||
// representing.
|
||||
room: React.PropTypes.object.isRequired,
|
||||
// The js-sdk EventTimelineSet object for the timeline sequence we are
|
||||
// representing. This may or may not have a room, depending on what it's
|
||||
// a timeline representing. If it has a room, we maintain RRs etc for
|
||||
// that room.
|
||||
timelineSet: React.PropTypes.object.isRequired,
|
||||
|
||||
// Enable managing RRs and RMs. These require the timelineSet to have a room.
|
||||
manageReadReceipts: React.PropTypes.bool,
|
||||
manageReadMarkers: React.PropTypes.bool,
|
||||
|
||||
// true to give the component a 'display: none' style.
|
||||
hidden: React.PropTypes.bool,
|
||||
|
@ -84,6 +90,12 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// maximum number of events to show in a timeline
|
||||
timelineCap: React.PropTypes.number,
|
||||
|
||||
// classname to use for the messagepanel
|
||||
className: React.PropTypes.string,
|
||||
|
||||
// shape property to be passed to EventTiles
|
||||
tileShape: React.PropTypes.string,
|
||||
},
|
||||
|
||||
statics: {
|
||||
|
@ -97,13 +109,18 @@ var TimelinePanel = React.createClass({
|
|||
getDefaultProps: function() {
|
||||
return {
|
||||
timelineCap: 250,
|
||||
className: 'mx_RoomView_messagePanel',
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var initialReadMarker =
|
||||
TimelinePanel.roomReadMarkerMap[this.props.room.roomId]
|
||||
|| this._getCurrentReadReceipt();
|
||||
// XXX: we could track RM per TimelineSet rather than per Room.
|
||||
// but for now we just do it per room for simplicity.
|
||||
if (this.props.manageReadMarkers) {
|
||||
var initialReadMarker =
|
||||
TimelinePanel.roomReadMarkerMap[this.props.timelineSet.room.roomId]
|
||||
|| this._getCurrentReadReceipt();
|
||||
}
|
||||
|
||||
return {
|
||||
events: [],
|
||||
|
@ -137,7 +154,7 @@ var TimelinePanel = React.createClass({
|
|||
canForwardPaginate: false,
|
||||
|
||||
// start with the read-marker visible, so that we see its animated
|
||||
// disappearance when swtitching into the room.
|
||||
// disappearance when switching into the room.
|
||||
readMarkerVisible: true,
|
||||
|
||||
readMarkerEventId: initialReadMarker,
|
||||
|
@ -163,8 +180,8 @@ var TimelinePanel = React.createClass({
|
|||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
if (newProps.room !== this.props.room) {
|
||||
// throw new Error("changing room on a TimelinePanel is not supported");
|
||||
if (newProps.timelineSet !== this.props.timelineSet) {
|
||||
// throw new Error("changing timelineSet on a TimelinePanel is not supported");
|
||||
|
||||
// regrettably, this does happen; in particular, when joining a
|
||||
// room with /join. In that case, there are two Rooms in
|
||||
|
@ -175,7 +192,7 @@ var TimelinePanel = React.createClass({
|
|||
//
|
||||
// for now, just warn about this. But we're going to end up paginating
|
||||
// both rooms separately, and it's all bad.
|
||||
console.warn("Replacing room on a TimelinePanel - confusion may ensue");
|
||||
console.warn("Replacing timelineSet on a TimelinePanel - confusion may ensue");
|
||||
}
|
||||
|
||||
if (newProps.eventId != this.props.eventId) {
|
||||
|
@ -280,11 +297,13 @@ var TimelinePanel = React.createClass({
|
|||
this.props.onScroll();
|
||||
}
|
||||
|
||||
// we hide the read marker when it first comes onto the screen, but if
|
||||
// it goes back off the top of the screen (presumably because the user
|
||||
// clicks on the 'jump to bottom' button), we need to re-enable it.
|
||||
if (this.getReadMarkerPosition() < 0) {
|
||||
this.setState({readMarkerVisible: true});
|
||||
if (this.props.manageReadMarkers) {
|
||||
// we hide the read marker when it first comes onto the screen, but if
|
||||
// it goes back off the top of the screen (presumably because the user
|
||||
// clicks on the 'jump to bottom' button), we need to re-enable it.
|
||||
if (this.getReadMarkerPosition() < 0) {
|
||||
this.setState({readMarkerVisible: true});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -304,8 +323,8 @@ var TimelinePanel = React.createClass({
|
|||
},
|
||||
|
||||
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
|
||||
// ignore events for other rooms
|
||||
if (room !== this.props.room) return;
|
||||
// ignore events for other timeline sets
|
||||
if (data.timeline.getTimelineSet() !== this.props.timelineSet) return;
|
||||
|
||||
// ignore anything but real-time updates at the end of the room:
|
||||
// updates from pagination will happen when the paginate completes.
|
||||
|
@ -337,40 +356,42 @@ var TimelinePanel = React.createClass({
|
|||
var lastEv = events[events.length-1];
|
||||
|
||||
// if we're at the end of the live timeline, append the pending events
|
||||
if (!this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
|
||||
events.push(... this.props.room.getPendingEvents());
|
||||
if (this.props.timelineSet.room && !this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
|
||||
events.push(... this.props.timelineSet.room.getPendingEvents());
|
||||
}
|
||||
|
||||
var updatedState = {events: events};
|
||||
|
||||
// when a new event arrives when the user is not watching the
|
||||
// window, but the window is in its auto-scroll mode, make sure the
|
||||
// read marker is visible.
|
||||
//
|
||||
// We ignore events we have sent ourselves; we don't want to see the
|
||||
// read-marker when a remote echo of an event we have just sent takes
|
||||
// more than the timeout on userCurrentlyActive.
|
||||
//
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var sender = ev.sender ? ev.sender.userId : null;
|
||||
var callback = null;
|
||||
if (sender != myUserId && !UserActivity.userCurrentlyActive()) {
|
||||
updatedState.readMarkerVisible = true;
|
||||
} else if(lastEv && this.getReadMarkerPosition() === 0) {
|
||||
// we know we're stuckAtBottom, so we can advance the RM
|
||||
// immediately, to save a later render cycle
|
||||
this._setReadMarker(lastEv.getId(), lastEv.getTs(), true);
|
||||
updatedState.readMarkerVisible = false;
|
||||
updatedState.readMarkerEventId = lastEv.getId();
|
||||
callback = this.props.onReadMarkerUpdated;
|
||||
if (this.props.manageReadMarkers) {
|
||||
// when a new event arrives when the user is not watching the
|
||||
// window, but the window is in its auto-scroll mode, make sure the
|
||||
// read marker is visible.
|
||||
//
|
||||
// We ignore events we have sent ourselves; we don't want to see the
|
||||
// read-marker when a remote echo of an event we have just sent takes
|
||||
// more than the timeout on userCurrentlyActive.
|
||||
//
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var sender = ev.sender ? ev.sender.userId : null;
|
||||
var callback = null;
|
||||
if (sender != myUserId && !UserActivity.userCurrentlyActive()) {
|
||||
updatedState.readMarkerVisible = true;
|
||||
} else if(lastEv && this.getReadMarkerPosition() === 0) {
|
||||
// we know we're stuckAtBottom, so we can advance the RM
|
||||
// immediately, to save a later render cycle
|
||||
this._setReadMarker(lastEv.getId(), lastEv.getTs(), true);
|
||||
updatedState.readMarkerVisible = false;
|
||||
updatedState.readMarkerEventId = lastEv.getId();
|
||||
callback = this.props.onReadMarkerUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(updatedState, callback);
|
||||
});
|
||||
},
|
||||
|
||||
onRoomTimelineReset: function(room) {
|
||||
if (room !== this.props.room) return;
|
||||
onRoomTimelineReset: function(room, timelineSet) {
|
||||
if (timelineSet !== this.props.timelineSet) return;
|
||||
|
||||
if (this.refs.messagePanel && this.refs.messagePanel.isAtBottom()) {
|
||||
this._loadTimeline();
|
||||
|
@ -381,7 +402,7 @@ var TimelinePanel = React.createClass({
|
|||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (room !== this.props.room) return;
|
||||
if (room !== this.props.timelineSet.room) return;
|
||||
|
||||
// we could skip an update if the event isn't in our timeline,
|
||||
// but that's probably an early optimisation.
|
||||
|
@ -392,7 +413,7 @@ var TimelinePanel = React.createClass({
|
|||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (room !== this.props.room) return;
|
||||
if (room !== this.props.timelineSet.room) return;
|
||||
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
@ -401,7 +422,7 @@ var TimelinePanel = React.createClass({
|
|||
if (this.unmounted) return;
|
||||
|
||||
// ignore events for other rooms
|
||||
if (room !== this.props.room) return;
|
||||
if (room !== this.props.timelineSet.room) return;
|
||||
|
||||
this._reloadEvents();
|
||||
},
|
||||
|
@ -409,12 +430,13 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
sendReadReceipt: function() {
|
||||
if (!this.refs.messagePanel) return;
|
||||
if (!this.props.manageReadReceipts) return;
|
||||
|
||||
// if we are scrolled to the bottom, do a quick-reset of our unreadNotificationCount
|
||||
// to avoid having to wait from the remote echo from the homeserver.
|
||||
if (this.isAtEndOfLiveTimeline()) {
|
||||
this.props.room.setUnreadNotificationCount('total', 0);
|
||||
this.props.room.setUnreadNotificationCount('highlight', 0);
|
||||
this.props.timelineSet.room.setUnreadNotificationCount('total', 0);
|
||||
this.props.timelineSet.room.setUnreadNotificationCount('highlight', 0);
|
||||
// XXX: i'm a bit surprised we don't have to emit an event or dispatch to get this picked up
|
||||
}
|
||||
|
||||
|
@ -461,6 +483,7 @@ var TimelinePanel = React.createClass({
|
|||
// if the read marker is on the screen, we can now assume we've caught up to the end
|
||||
// of the screen, so move the marker down to the bottom of the screen.
|
||||
updateReadMarker: function() {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
if (this.getReadMarkerPosition() !== 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -498,6 +521,8 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// advance the read marker past any events we sent ourselves.
|
||||
_advanceReadMarkerPastMyEvents: function() {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
|
||||
// we call _timelineWindow.getEvents() rather than using
|
||||
// this.state.events, because react batches the update to the latter, so it
|
||||
// may not have been updated yet.
|
||||
|
@ -548,11 +573,9 @@ var TimelinePanel = React.createClass({
|
|||
* the container.
|
||||
*/
|
||||
jumpToReadMarker: function() {
|
||||
if (!this.refs.messagePanel)
|
||||
return;
|
||||
|
||||
if (!this.state.readMarkerEventId)
|
||||
return;
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
if (!this.refs.messagePanel) return;
|
||||
if (!this.state.readMarkerEventId) return;
|
||||
|
||||
// we may not have loaded the event corresponding to the read-marker
|
||||
// into the _timelineWindow. In that case, attempts to scroll to it
|
||||
|
@ -579,10 +602,12 @@ var TimelinePanel = React.createClass({
|
|||
/* update the read-up-to marker to match the read receipt
|
||||
*/
|
||||
forgetReadMarker: function() {
|
||||
if (!this.props.manageReadMarkers) return;
|
||||
|
||||
var rmId = this._getCurrentReadReceipt();
|
||||
|
||||
// see if we know the timestamp for the rr event
|
||||
var tl = this.props.room.getTimelineForEvent(rmId);
|
||||
var tl = this.props.timelineSet.getTimelineForEvent(rmId);
|
||||
var rmTs;
|
||||
if (tl) {
|
||||
var event = tl.getEvents().find((e) => { return e.getId() == rmId });
|
||||
|
@ -622,7 +647,9 @@ var TimelinePanel = React.createClass({
|
|||
// 0: read marker is visible
|
||||
// +1: read marker is below the window
|
||||
getReadMarkerPosition: function() {
|
||||
if (!this.refs.messagePanel) { return null; }
|
||||
if (!this.props.manageReadMarkers) return null;
|
||||
if (!this.refs.messagePanel) return null;
|
||||
|
||||
var ret = this.refs.messagePanel.getReadMarkerPosition();
|
||||
if (ret !== null) {
|
||||
return ret;
|
||||
|
@ -630,7 +657,7 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// the messagePanel doesn't know where the read marker is.
|
||||
// if we know the timestamp of the read marker, make a guess based on that.
|
||||
var rmTs = TimelinePanel.roomReadMarkerTsMap[this.props.room.roomId];
|
||||
var rmTs = TimelinePanel.roomReadMarkerTsMap[this.props.timelineSet.roomId];
|
||||
if (rmTs && this.state.events.length > 0) {
|
||||
if (rmTs < this.state.events[0].getTs()) {
|
||||
return -1;
|
||||
|
@ -691,7 +718,7 @@ var TimelinePanel = React.createClass({
|
|||
*/
|
||||
_loadTimeline: function(eventId, pixelOffset, offsetBase) {
|
||||
this._timelineWindow = new Matrix.TimelineWindow(
|
||||
MatrixClientPeg.get(), this.props.room,
|
||||
MatrixClientPeg.get(), this.props.timelineSet,
|
||||
{windowLimit: this.props.timelineCap});
|
||||
|
||||
var onLoaded = () => {
|
||||
|
@ -745,7 +772,7 @@ var TimelinePanel = React.createClass({
|
|||
// go via the dispatcher so that the URL is updated
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: this.props.room.roomId,
|
||||
room_id: this.props.timelineSet.roomId,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -807,7 +834,7 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// if we're at the end of the live timeline, append the pending events
|
||||
if (!this._timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
|
||||
events.push(... this.props.room.getPendingEvents());
|
||||
events.push(... this.props.timelineSet.getPendingEvents());
|
||||
}
|
||||
|
||||
return events;
|
||||
|
@ -873,11 +900,13 @@ var TimelinePanel = React.createClass({
|
|||
return null;
|
||||
|
||||
var myUserId = client.credentials.userId;
|
||||
return this.props.room.getEventReadUpTo(myUserId, ignoreSynthesized);
|
||||
return this.props.timelineSet.room.getEventReadUpTo(myUserId, ignoreSynthesized);
|
||||
},
|
||||
|
||||
_setReadMarker: function(eventId, eventTs, inhibitSetState) {
|
||||
if (TimelinePanel.roomReadMarkerMap[this.props.room.roomId] == eventId) {
|
||||
var roomId = this.props.timelineSet.room.roomId;
|
||||
|
||||
if (TimelinePanel.roomReadMarkerMap[roomId] == eventId) {
|
||||
// don't update the state (and cause a re-render) if there is
|
||||
// no change to the RM.
|
||||
return;
|
||||
|
@ -885,11 +914,11 @@ var TimelinePanel = React.createClass({
|
|||
|
||||
// ideally we'd sync these via the server, but for now just stash them
|
||||
// in a map.
|
||||
TimelinePanel.roomReadMarkerMap[this.props.room.roomId] = eventId;
|
||||
TimelinePanel.roomReadMarkerMap[roomId] = eventId;
|
||||
|
||||
// in order to later figure out if the read marker is
|
||||
// above or below the visible timeline, we stash the timestamp.
|
||||
TimelinePanel.roomReadMarkerTsMap[this.props.room.roomId] = eventTs;
|
||||
TimelinePanel.roomReadMarkerTsMap[roomId] = eventTs;
|
||||
|
||||
if (inhibitSetState) {
|
||||
return;
|
||||
|
@ -919,7 +948,7 @@ var TimelinePanel = React.createClass({
|
|||
// exist.
|
||||
if (this.state.timelineLoading) {
|
||||
return (
|
||||
<div className="mx_RoomView_messagePanel mx_RoomView_messageListWrapper">
|
||||
<div className={ this.props.className + " mx_RoomView_messageListWrapper" }>
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
|
@ -946,11 +975,14 @@ var TimelinePanel = React.createClass({
|
|||
readMarkerVisible={ this.state.readMarkerVisible }
|
||||
suppressFirstDateSeparator={ this.state.canBackPaginate }
|
||||
showUrlPreview = { this.props.showUrlPreview }
|
||||
manageReadReceipts = { this.props.manageReadReceipts }
|
||||
ourUserId={ MatrixClientPeg.get().credentials.userId }
|
||||
stickyBottom={ stickyBottom }
|
||||
onScroll={ this.onMessageListScroll }
|
||||
onFillRequest={ this.onMessageListFillRequest }
|
||||
opacity={ this.props.opacity }
|
||||
className={ this.props.className }
|
||||
tileShape={ this.props.tileShape }
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue