Fix row height recomputation in Timeline
This commit is contained in:
parent
f647c4e053
commit
254c87a1ac
1 changed files with 85 additions and 61 deletions
|
@ -1084,15 +1084,24 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resizeStartRow: number | undefined;
|
||||||
|
|
||||||
|
if (isNumber(messageHeightChangeIndex)) {
|
||||||
|
resizeStartRow = this.fromItemIndexToRow(messageHeightChangeIndex);
|
||||||
|
clearChangedMessages(id);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
items &&
|
items !== prevProps.items ||
|
||||||
items.length > 0 &&
|
oldestUnreadIndex !== prevProps.oldestUnreadIndex ||
|
||||||
prevProps.items &&
|
Boolean(typingContact) !== Boolean(prevProps.typingContact)
|
||||||
prevProps.items.length > 0 &&
|
|
||||||
items !== prevProps.items
|
|
||||||
) {
|
) {
|
||||||
const { atTop } = this.state;
|
const { atTop } = this.state;
|
||||||
|
|
||||||
|
// This clause handles prepended messages when user scrolls up. New
|
||||||
|
// messages are added to `items`, but we want to keep the scroll position
|
||||||
|
// at the first previously visible message even though the row numbers
|
||||||
|
// have now changed.
|
||||||
if (atTop) {
|
if (atTop) {
|
||||||
const oldFirstIndex = 0;
|
const oldFirstIndex = 0;
|
||||||
const oldFirstId = prevProps.items[oldFirstIndex];
|
const oldFirstId = prevProps.items[oldFirstIndex];
|
||||||
|
@ -1117,39 +1126,55 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We continue on after our atTop check; because if we're not loading new messages
|
// Compare current rows against previous rows to identify the number of
|
||||||
// we still have to check for all the other situations which might require a
|
// consecutive rows (from start of the list) the are the same in both
|
||||||
// resize.
|
// lists.
|
||||||
|
const rowsIterator = Timeline.getEphemeralRows({
|
||||||
|
items,
|
||||||
|
oldestUnreadIndex,
|
||||||
|
typingContact: Boolean(typingContact),
|
||||||
|
haveOldest,
|
||||||
|
});
|
||||||
|
const prevRowsIterator = Timeline.getEphemeralRows({
|
||||||
|
items: prevProps.items,
|
||||||
|
oldestUnreadIndex: prevProps.oldestUnreadIndex,
|
||||||
|
typingContact: Boolean(prevProps.typingContact),
|
||||||
|
haveOldest: prevProps.haveOldest,
|
||||||
|
});
|
||||||
|
|
||||||
const oldLastIndex = prevProps.items.length - 1;
|
let firstChangedRow = 0;
|
||||||
const oldLastId = prevProps.items[oldLastIndex];
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
const newLastIndex = items.findIndex(item => item === oldLastId);
|
const row = rowsIterator.next();
|
||||||
if (newLastIndex < 0) {
|
if (row.done) {
|
||||||
this.resize();
|
break;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexDelta = newLastIndex - oldLastIndex;
|
|
||||||
|
|
||||||
// If we've just added to the end of the list, then the index of the last id's
|
|
||||||
// index won't have changed, and we can rely on List's detection that items is
|
|
||||||
// different for the necessary re-render.
|
|
||||||
if (indexDelta === 0) {
|
|
||||||
if (typingContact || prevProps.typingContact) {
|
|
||||||
// The last row will be off, because it was previously the typing indicator
|
|
||||||
const rowCount = this.getRowCount();
|
|
||||||
this.resize(rowCount - 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// no resize because we just add to the end
|
const prevRow = prevRowsIterator.next();
|
||||||
return;
|
if (prevRow.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRow.value !== row.value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstChangedRow += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resize();
|
// If either:
|
||||||
|
//
|
||||||
return;
|
// - Row count has changed after props update
|
||||||
|
// - There are some different rows (and the loop above was interrupted)
|
||||||
|
//
|
||||||
|
// Recompute heights of all rows starting from the first changed row or
|
||||||
|
// the last row in the previous row list.
|
||||||
|
if (!rowsIterator.next().done || !prevRowsIterator.next().done) {
|
||||||
|
resizeStartRow = Math.min(
|
||||||
|
resizeStartRow ?? firstChangedRow,
|
||||||
|
firstChangedRow
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.resizeFlag) {
|
if (this.resizeFlag) {
|
||||||
|
@ -1158,34 +1183,8 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldestUnreadIndex !== prevProps.oldestUnreadIndex) {
|
if (resizeStartRow !== undefined) {
|
||||||
const prevRow = this.getLastSeenIndicatorRow(prevProps);
|
this.resize(resizeStartRow);
|
||||||
const newRow = this.getLastSeenIndicatorRow();
|
|
||||||
const rowCount = this.getRowCount();
|
|
||||||
const lastRow = rowCount - 1;
|
|
||||||
|
|
||||||
const targetRow = Math.min(
|
|
||||||
isNumber(prevRow) ? prevRow : lastRow,
|
|
||||||
isNumber(newRow) ? newRow : lastRow
|
|
||||||
);
|
|
||||||
this.resize(targetRow);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNumber(messageHeightChangeIndex)) {
|
|
||||||
const rowIndex = this.fromItemIndexToRow(messageHeightChangeIndex);
|
|
||||||
this.resize(rowIndex);
|
|
||||||
clearChangedMessages(id);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Boolean(typingContact) !== Boolean(prevProps.typingContact)) {
|
|
||||||
const rowCount = this.getRowCount();
|
|
||||||
this.resize(rowCount - 2);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateWithVisibleRows();
|
this.updateWithVisibleRows();
|
||||||
|
@ -1575,6 +1574,31 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static *getEphemeralRows({
|
||||||
|
items,
|
||||||
|
typingContact,
|
||||||
|
oldestUnreadIndex,
|
||||||
|
haveOldest,
|
||||||
|
}: {
|
||||||
|
items: ReadonlyArray<string>;
|
||||||
|
typingContact: boolean;
|
||||||
|
oldestUnreadIndex?: number;
|
||||||
|
haveOldest: boolean;
|
||||||
|
}): Iterator<string> {
|
||||||
|
yield haveOldest ? 'hero' : 'loading';
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i += 1) {
|
||||||
|
if (i === oldestUnreadIndex) {
|
||||||
|
yield 'oldest-unread';
|
||||||
|
}
|
||||||
|
yield `item:${items[i]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typingContact) {
|
||||||
|
yield 'typing-contact';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static getWarning(
|
private static getWarning(
|
||||||
{ warning }: PropsType,
|
{ warning }: PropsType,
|
||||||
state: StateType
|
state: StateType
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue