Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Hide pre-join UTDs #3881

Merged
merged 16 commits into from
Jan 28, 2020
93 changes: 90 additions & 3 deletions src/components/structures/MessagePanel.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019-2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -20,6 +20,7 @@ import React, {createRef} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {EventTimeline} from 'matrix-js-sdk/src/models/event-timeline';
import shouldHideEvent from '../../shouldHideEvent';
import {wantsDateSeparator} from '../../DateUtils';
import * as sdk from '../../index';
Expand Down Expand Up @@ -115,6 +116,7 @@ export default class MessagePanel extends React.Component {
// previous positions the read marker has been in, so we can
// display 'ghost' read markers that are animating away
ghostReadMarkers: [],
preventBackPaginating: false,
};

// opaque readreceipt info for each userId; used by ReadReceiptMarker
Expand Down Expand Up @@ -419,6 +421,7 @@ export default class MessagePanel extends React.Component {
let lastShownEvent;

let lastShownNonLocalEchoIndex = -1;

for (i = this.props.events.length-1; i >= 0; i--) {
const mxEv = this.props.events[i];
if (!this._shouldShowEvent(mxEv)) {
Expand All @@ -440,14 +443,32 @@ export default class MessagePanel extends React.Component {

const ret = [];

const firstShownEventIndex = checkForPreJoinUISI(
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
this.props.events, this.props.room, this.props.ourUserId,
);

if (firstShownEventIndex > 0) {
if (!this.state.preventBackPaginating) {
this.setState({
preventBackPaginating: true,
});
}
} else {
if (this.state.preventBackPaginating) {
this.setState({
preventBackPaginating: false,
});
}
}

let prevEvent = null; // the last event we showed

this._readReceiptsByEvent = {};
if (this.props.showReadReceipts) {
this._readReceiptsByEvent = this._getReadReceiptsByShownEvent();
}

for (i = 0; i < this.props.events.length; i++) {
for (i = firstShownEventIndex; i < this.props.events.length; i++) {
const mxEv = this.props.events[i];
const eventId = mxEv.getId();
const last = (mxEv === lastShownEvent);
Expand Down Expand Up @@ -883,6 +904,13 @@ export default class MessagePanel extends React.Component {
}
}

onFillRequest = (backwards) => {
if (this.state.preventBackPaginating) {
return Promise.resolve(false);
}
return this.props.onFillRequest(backwards);
}

render() {
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
const WhoIsTypingTile = sdk.getComponent("rooms.WhoIsTypingTile");
Expand Down Expand Up @@ -921,7 +949,7 @@ export default class MessagePanel extends React.Component {
className={className}
onScroll={this.props.onScroll}
onResize={this.onResize}
onFillRequest={this.props.onFillRequest}
onFillRequest={this.onFillRequest}
onUnfillRequest={this.props.onUnfillRequest}
style={style}
stickyBottom={this.props.stickyBottom}
Expand All @@ -935,3 +963,62 @@ export default class MessagePanel extends React.Component {
);
}
}

/**
* Check for undecryptable messages that were sent while the user was not in
* the room.
*
* @param {Array<MatrixEvent>} events The timeline events to check
* @param {Room} room The room that the events are in
* @param {string} userId The user's ID
*
* @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.
*/
function checkForPreJoinUISI(events, room, userId) {
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
if (events.length === 0 ||
!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
return 0;
}

let i;
let userMembership = "leave";
// find the last event that is in a timeline (events may not be in a
// timeline if they have not been sent yet) and get the user's membership
// at that point.
for (i = events.length - 1; i >= 0; i--) {
const timeline = room.getTimelineForEvent(events[i].getId());
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
if (timeline) {
const userMembershipEvent =
timeline.getState(EventTimeline.FORWARDS).getMember(userId);
userMembership = userMembershipEvent ? userMembershipEvent.membership : "leave";
const timelineEvents = timeline.getEvents();
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
for (let j = timelineEvents.length - 1; j >= 0; j--) {
jryans marked this conversation as resolved.
Show resolved Hide resolved
const event = timelineEvents[j];
if (event.getStateKey() === userId
&& event.getType() === "m.room.member") {
const prevContent = event.getPrevContent();
userMembership = prevContent.membership || "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() === "m.room.member") {
const prevContent = event.getPrevContent();
userMembership = prevContent.membership || "leave";
} else if (userMembership === "leave" && event.isDecryptionFailure()) {
// reached an undecryptable message when the user wasn't in
// the room -- don't try to load any more
return i + 1;
}
}
return 0;
}