Remove old pre-join UTD logic (#12464)

* remove old pre-join UTD logic

and add a playwright test for new pre-join UTD

* remove variable that isn't used any more

* add missing synapse template

* remove unused variable (again) and run prettier

* add test that we can jump to an event before our latest join membership event

* modify default template instead of creating a new template
This commit is contained in:
Hubert Chathi 2024-05-10 16:20:40 -04:00 committed by GitHub
parent 948435ceb9
commit 4ed6da4eba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 232 additions and 126 deletions

View file

@ -39,8 +39,7 @@ import {
ThreadEvent,
ReceiptType,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership, Membership } from "matrix-js-sdk/src/types";
import { debounce, findLastIndex, throttle } from "lodash";
import { debounce, findLastIndex } from "lodash";
import { logger } from "matrix-js-sdk/src/logger";
import SettingsStore from "../../settings/SettingsStore";
@ -176,9 +175,6 @@ interface IState {
// track whether our room timeline is loading
timelineLoading: boolean;
// the index of the first event that is to be shown
firstVisibleEventIndex: number;
// canBackPaginate == false may mean:
//
// * we haven't (successfully) loaded the timeline yet, or:
@ -296,7 +292,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
events: [],
liveEvents: [],
timelineLoading: true,
firstVisibleEventIndex: 0,
canBackPaginate: false,
canForwardPaginate: false,
readMarkerVisible: true,
@ -568,12 +563,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.overlayTimelineWindow!.unpaginate(overlayCount, backwards);
}
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
const { events, liveEvents } = this.getEvents();
this.buildLegacyCallEventGroupers(events);
this.setState({
events,
liveEvents,
firstVisibleEventIndex,
});
// We can now paginate in the unpaginated direction
@ -617,11 +611,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
return Promise.resolve(false);
}
if (backwards && this.state.firstVisibleEventIndex !== 0) {
debuglog("won't", dir, "paginate past first visible event");
return Promise.resolve(false);
}
debuglog("Initiating paginate; backwards:" + backwards);
this.setState({ [paginatingKey]: true } as Pick<IState, PaginatingKey>);
@ -636,15 +625,14 @@ class TimelinePanel extends React.Component<IProps, IState> {
debuglog("paginate complete backwards:" + backwards + "; success:" + r);
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
const { events, liveEvents } = this.getEvents();
this.buildLegacyCallEventGroupers(events);
const newState = {
[paginatingKey]: false,
[canPaginateKey]: r,
events,
liveEvents,
firstVisibleEventIndex,
} as Pick<IState, PaginatingKey | CanPaginateKey | "events" | "liveEvents" | "firstVisibleEventIndex">;
} as Pick<IState, PaginatingKey | CanPaginateKey | "events" | "liveEvents">;
// moving the window in this direction may mean that we can now
// paginate in the other where we previously could not.
@ -662,11 +650,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
// itself into the right place
return new Promise((resolve) => {
this.setState(newState, () => {
// we can continue paginating in the given direction if:
// - timelineWindow.paginate says we can
// - we're paginating forwards, or we won't be trying to
// paginate backwards past the first visible event
resolve(r && (!backwards || firstVisibleEventIndex === 0));
// we can continue paginating in the given direction if
// timelineWindow.paginate says we can
resolve(r);
});
});
});
@ -782,14 +768,13 @@ class TimelinePanel extends React.Component<IProps, IState> {
return;
}
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
const { events, liveEvents } = this.getEvents();
this.buildLegacyCallEventGroupers(events);
const lastLiveEvent = liveEvents[liveEvents.length - 1];
const updatedState: Partial<IState> = {
events,
liveEvents,
firstVisibleEventIndex,
};
let callRMUpdated = false;
@ -967,8 +952,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
if (!this.state.events.includes(ev)) return;
this.recheckFirstVisibleEventIndex();
// Need to update as we don't display event tiles for events that
// haven't yet been decrypted. The event will have just been updated
// in place so we just need to re-render.
@ -984,17 +967,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
this.setState({ clientSyncState });
};
private recheckFirstVisibleEventIndex = throttle(
(): void => {
const firstVisibleEventIndex = this.checkForPreJoinUISI(this.state.events);
if (firstVisibleEventIndex !== this.state.firstVisibleEventIndex) {
this.setState({ firstVisibleEventIndex });
}
},
500,
{ leading: true, trailing: true },
);
private readMarkerTimeout(readMarkerPosition: number | null): number {
return readMarkerPosition === 0
? this.context?.readMarkerInViewThresholdMs ?? this.state.readMarkerInViewThresholdMs
@ -1721,7 +1693,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
}
// get the list of events from the timeline windows and the pending event list
private getEvents(): Pick<IState, "events" | "liveEvents" | "firstVisibleEventIndex"> {
private getEvents(): Pick<IState, "events" | "liveEvents"> {
const mainEvents = this.timelineWindow!.getEvents();
let overlayEvents = this.overlayTimelineWindow?.getEvents() ?? [];
if (this.props.overlayTimelineSetFilter !== undefined) {
@ -1759,8 +1731,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
client.decryptEventIfNeeded(events[i]);
}
const firstVisibleEventIndex = this.checkForPreJoinUISI(events);
// Hold onto the live events separately. The read receipt and read marker
// should use this list, so that they don't advance into pending events.
const liveEvents = [...events];
@ -1788,87 +1758,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
return {
events,
liveEvents,
firstVisibleEventIndex,
};
}
/**
* Check for undecryptable messages that were sent while the user was not in
* the room.
*
* @param {Array<MatrixEvent>} events The timeline events to check
*
* @return {Number} The index within `events` of the event after the most recent
* undecryptable event that was sent while the user was not in the room. If no
* such events were found, then it returns 0.
*/
private checkForPreJoinUISI(events: MatrixEvent[]): number {
const cli = MatrixClientPeg.safeGet();
const room = this.props.timelineSet.room;
const isThreadTimeline = [TimelineRenderingType.Thread, TimelineRenderingType.ThreadsList].includes(
this.context.timelineRenderingType,
);
if (events.length === 0 || !room || !cli.isRoomEncrypted(room.roomId) || isThreadTimeline) {
logger.debug("checkForPreJoinUISI: showing all messages, skipping check");
return 0;
}
const userId = cli.getSafeUserId();
// get the user's membership at the last event by getting the timeline
// that the event belongs to, and traversing the timeline looking for
// that event, while keeping track of the user's membership
let i = events.length - 1;
let userMembership: Membership = KnownMembership.Leave;
for (; i >= 0; i--) {
const timeline = this.props.timelineSet.getTimelineForEvent(events[i].getId()!);
if (!timeline) {
// Somehow, it seems to be possible for live events to not have
// a timeline, even though that should not happen. :(
// https://github.com/vector-im/element-web/issues/12120
logger.warn(
`Event ${events[i].getId()} in room ${room.roomId} is live, ` + `but it does not have a timeline`,
);
continue;
}
userMembership =
timeline.getState(EventTimeline.FORWARDS)?.getMember(userId)?.membership ?? KnownMembership.Leave;
const timelineEvents = timeline.getEvents();
for (let j = timelineEvents.length - 1; j >= 0; j--) {
const event = timelineEvents[j];
if (event.getId() === events[i].getId()) {
break;
} else if (event.getStateKey() === userId && event.getType() === EventType.RoomMember) {
userMembership = event.getPrevContent().membership || KnownMembership.Leave;
}
}
break;
}
// now go through the rest of the events and find the first undecryptable
// one that was sent when the user wasn't in the room
for (; i >= 0; i--) {
const event = events[i];
if (event.getStateKey() === userId && event.getType() === EventType.RoomMember) {
userMembership = event.getPrevContent().membership || KnownMembership.Leave;
} else if (
userMembership === KnownMembership.Leave &&
(event.isDecryptionFailure() || event.isBeingDecrypted())
) {
// reached an undecryptable message when the user wasn't in the room -- don't try to load any more
// Note: for now, we assume that events that are being decrypted are
// not decryptable - we will be called once more when it is decrypted.
logger.debug("checkForPreJoinUISI: reached a pre-join UISI at index ", i);
return i + 1;
}
}
logger.debug("checkForPreJoinUISI: did not find pre-join UISI");
return 0;
}
private indexForEventId(evId: string | null): number | null {
if (evId === null) {
return null;
@ -2119,9 +2011,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
// the HS and fetch the latest events, so we are effectively forward paginating.
const forwardPaginating =
this.state.forwardPaginating || ["PREPARED", "CATCHUP"].includes(this.state.clientSyncState!);
const events = this.state.firstVisibleEventIndex
? this.state.events.slice(this.state.firstVisibleEventIndex)
: this.state.events;
const events = this.state.events;
return (
<MessagePanel
ref={this.messagePanel}
@ -2134,7 +2024,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
highlightedEventId={this.props.highlightedEventId}
readMarkerEventId={this.state.readMarkerEventId}
readMarkerVisible={this.state.readMarkerVisible}
canBackPaginate={this.state.canBackPaginate && this.state.firstVisibleEventIndex === 0}
canBackPaginate={this.state.canBackPaginate}
showUrlPreview={this.props.showUrlPreview}
showReadReceipts={this.props.showReadReceipts}
ourUserId={MatrixClientPeg.safeGet().getSafeUserId()}

View file

@ -25,7 +25,6 @@ const HistoryTile: React.FC = () => {
const { room } = useContext(RoomContext);
const oldState = room?.getLiveTimeline().getState(EventTimeline.BACKWARDS);
const encryptionState = oldState?.getStateEvents("m.room.encryption")[0];
const historyState = oldState?.getStateEvents("m.room.history_visibility")[0]?.getContent().history_visibility;
let subtitle: string | undefined;
@ -33,8 +32,6 @@ const HistoryTile: React.FC = () => {
subtitle = _t("timeline|no_permission_messages_before_invite");
} else if (historyState == "joined") {
subtitle = _t("timeline|no_permission_messages_before_join");
} else if (encryptionState) {
subtitle = _t("timeline|encrypted_historical_messages_unavailable");
}
return (