take adjacent no-tile events in combination with ignored events into account when determining the last displayed event

pull/21833/head
Bruno Windels 2019-07-12 17:40:51 +02:00 committed by David Baker
parent f05a7b2195
commit e66ebec083
1 changed files with 54 additions and 38 deletions

View File

@ -36,6 +36,7 @@ const Modal = require("../../Modal");
const UserActivity = require("../../UserActivity"); const UserActivity = require("../../UserActivity");
import { KeyCode } from '../../Keyboard'; import { KeyCode } from '../../Keyboard';
import Timer from '../../utils/Timer'; import Timer from '../../utils/Timer';
import shouldHideEvent from '../../shouldHideEvent';
import EditorStateTransfer from '../../utils/EditorStateTransfer'; import EditorStateTransfer from '../../utils/EditorStateTransfer';
const PAGINATE_SIZE = 20; const PAGINATE_SIZE = 20;
@ -1140,55 +1141,70 @@ const TimelinePanel = React.createClass({
const wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect(); const wrapperRect = ReactDOM.findDOMNode(messagePanel).getBoundingClientRect();
const myUserId = MatrixClientPeg.get().credentials.userId; const myUserId = MatrixClientPeg.get().credentials.userId;
let lastDisplayedIndex = null; const isNodeInView = (node) => {
if (node) {
const boundingRect = node.getBoundingClientRect();
if ((allowPartial && boundingRect.top < wrapperRect.bottom) ||
(!allowPartial && boundingRect.bottom < wrapperRect.bottom)) {
return true;
}
}
return false;
};
// if allowEventsWithoutTiles is enabled, we keep track
// of how many of the adjacent events were invisible (because no tile)
// but should have the read receipt moved past, so
// we can include those once we find the last displayed (visible) event.
// The counter is cleared when we ignore an event we don't want
// to send a read receipt for (our own events, local echos)
let adjacentInvisibleEventCount = 0;
// Use `liveEvents` here because we don't want the read marker or read // Use `liveEvents` here because we don't want the read marker or read
// receipt to advance into pending events. // receipt to advance into pending events.
for (let i = this.state.liveEvents.length - 1; i >= 0; --i) { for (let i = this.state.liveEvents.length - 1; i >= 0; --i) {
const ev = this.state.liveEvents[i]; const ev = this.state.liveEvents[i];
if (ignoreOwn && ev.sender && ev.sender.userId == myUserId) {
continue;
}
// local echoes have a fake event ID
if (ignoreEchoes && ev.status) {
continue;
}
const node = messagePanel.getNodeForEventId(ev.getId()); const node = messagePanel.getNodeForEventId(ev.getId());
if (!node) continue; const isInView = isNodeInView(node);
const boundingRect = node.getBoundingClientRect(); // the event at i + adjacentInvisibleEventCount should
if ((allowPartial && boundingRect.top < wrapperRect.bottom) || // not be ignored, and it considered the first in view (at the bottom)
(!allowPartial && boundingRect.bottom < wrapperRect.bottom)) { // because i is the first one in view and the adjacent ones are invisible,
lastDisplayedIndex = i; // so return this without further ado.
break; if (isInView && adjacentInvisibleEventCount !== 0) {
} return i + adjacentInvisibleEventCount;
} } else {
if (node && !isInView) {
if (lastDisplayedIndex === null) { // has node but not in view, so adjacent invisible events don't count
return null; adjacentInvisibleEventCount = 0;
} }
// If events without tiles are allowed, then we walk forward from the const shouldIgnore = (ignoreEchoes && ev.status) || // local echo
// the last displayed event and advance the index for any events without (ignoreOwn && ev.sender && ev.sender.userId == myUserId); // own message
// tiles that immediately follow it. const isWithoutTile = !EventTile.haveTileForEvent(ev) || shouldHideEvent(ev);
// XXX: We could track the last event without a tile after the last
// displayed event in the loop above so that we only do a single pass if (allowEventsWithoutTiles && (isWithoutTile || !node)) {
// through the loop, which would be more efficient. Using two passes is // don't start counting if the event should be ignored,
// easier to reason about, so let's start there and optimise later if // but continue counting if we were already so the offset
// needed. // to the previous invisble event that didn't need to be ignored
if (allowEventsWithoutTiles) { // doesn't get messed up
for (let i = lastDisplayedIndex + 1; i < this.state.liveEvents.length; i++) { if (!shouldIgnore || (shouldIgnore && adjacentInvisibleEventCount !== 0)) {
const ev = this.state.liveEvents[i]; ++adjacentInvisibleEventCount;
if (EventTile.haveTileForEvent(ev)) { }
break; continue;
}
if (shouldIgnore) {
continue;
}
if (isInView) {
return i;
} }
lastDisplayedIndex = i;
} }
} }
return lastDisplayedIndex; return null;
}, },
/** /**