Fix timeline crash when deleting the oldest visible message

This commit is contained in:
Evan Hahn 2022-02-07 12:54:15 -06:00 committed by GitHub
parent 6de2710841
commit 45393b1ca5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 23 deletions

View file

@ -115,7 +115,7 @@ type PropsHousekeepingType = {
warning?: WarningType; warning?: WarningType;
contactSpoofingReview?: ContactSpoofingReviewPropType; contactSpoofingReview?: ContactSpoofingReviewPropType;
getTimestampForMessage: (_: string) => number; getTimestampForMessage: (messageId: string) => undefined | number;
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType; theme: ThemeType;
@ -220,7 +220,7 @@ type StateType = {
oneTimeScrollRow?: number; oneTimeScrollRow?: number;
visibleRows?: { visibleRows?: {
newestFullyVisible?: VisibleRowType; newestFullyVisible?: VisibleRowType;
oldestPartiallyVisible?: VisibleRowType; oldestPartiallyVisibleMessageId?: string;
oldestFullyVisible?: VisibleRowType; oldestFullyVisible?: VisibleRowType;
}; };
@ -612,7 +612,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
} }
let newestFullyVisible: undefined | VisibleRowType; let newestFullyVisible: undefined | VisibleRowType;
let oldestPartiallyVisible: undefined | VisibleRowType; let oldestPartiallyVisibleMessageId: undefined | string;
let oldestFullyVisible: undefined | VisibleRowType; let oldestFullyVisible: undefined | VisibleRowType;
const { children } = innerScrollContainer; const { children } = innerScrollContainer;
@ -646,20 +646,18 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
continue; continue;
} }
const thisRow = { const bottom = offsetTop + offsetHeight;
if (bottom >= visibleTop && !oldestPartiallyVisibleMessageId) {
oldestPartiallyVisibleMessageId = id;
}
if (offsetTop + AT_TOP_THRESHOLD >= visibleTop) {
oldestFullyVisible = {
offsetTop, offsetTop,
row: parseInt(child.getAttribute('data-row') || '-1', 10), row: parseInt(child.getAttribute('data-row') || '-1', 10),
id, id,
}; };
const bottom = offsetTop + offsetHeight;
if (bottom >= visibleTop && !oldestPartiallyVisible) {
oldestPartiallyVisible = thisRow;
}
if (offsetTop + AT_TOP_THRESHOLD >= visibleTop) {
oldestFullyVisible = thisRow;
break; break;
} }
} }
@ -667,7 +665,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
this.setState(oldState => { this.setState(oldState => {
const visibleRows = { const visibleRows = {
newestFullyVisible, newestFullyVisible,
oldestPartiallyVisible, oldestPartiallyVisibleMessageId,
oldestFullyVisible, oldestFullyVisible,
}; };
@ -1289,8 +1287,15 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
} }
let floatingHeader: ReactNode; let floatingHeader: ReactNode;
const oldestPartiallyVisibleRow = visibleRows?.oldestPartiallyVisible; const oldestPartiallyVisibleMessageId =
if (oldestPartiallyVisibleRow) { visibleRows?.oldestPartiallyVisibleMessageId;
// It's possible that a message was removed from `items` but we still have its ID in
// state. `getTimestampForMessage` might return undefined in that case.
const oldestPartiallyVisibleMessageTimestamp =
oldestPartiallyVisibleMessageId
? getTimestampForMessage(oldestPartiallyVisibleMessageId)
: undefined;
if (oldestPartiallyVisibleMessageTimestamp) {
floatingHeader = ( floatingHeader = (
<TimelineFloatingHeader <TimelineFloatingHeader
i18n={i18n} i18n={i18n}
@ -1300,10 +1305,10 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
? { marginTop: lastMeasuredWarningHeight } ? { marginTop: lastMeasuredWarningHeight }
: undefined : undefined
} }
timestamp={getTimestampForMessage(oldestPartiallyVisibleRow.id)} timestamp={oldestPartiallyVisibleMessageTimestamp}
visible={ visible={
(hasRecentlyScrolled || isLoadingMessages) && (hasRecentlyScrolled || isLoadingMessages) &&
(!haveOldest || oldestPartiallyVisibleRow.id !== items[0]) (!haveOldest || oldestPartiallyVisibleMessageId !== items[0])
} }
/> />
); );

View file

@ -38,7 +38,7 @@ import { renderEmojiPicker } from './renderEmojiPicker';
import { renderReactionPicker } from './renderReactionPicker'; import { renderReactionPicker } from './renderReactionPicker';
import { getOwn } from '../../util/getOwn'; import { getOwn } from '../../util/getOwn';
import { assert, strictAssert } from '../../util/assert'; import { assert } from '../../util/assert';
import { missingCaseError } from '../../util/missingCaseError'; import { missingCaseError } from '../../util/missingCaseError';
import { getGroupMemberships } from '../../util/getGroupMemberships'; import { getGroupMemberships } from '../../util/getGroupMemberships';
import { import {
@ -295,11 +295,8 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
const selectedMessage = getSelectedMessage(state); const selectedMessage = getSelectedMessage(state);
const messageSelector = getMessageSelector(state); const messageSelector = getMessageSelector(state);
const getTimestampForMessage = (messageId: string): number => { const getTimestampForMessage = (messageId: string): undefined | number =>
const result = messageSelector(messageId)?.timestamp; messageSelector(messageId)?.timestamp;
strictAssert(result, 'Expected a message');
return result;
};
return { return {
id, id,