Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide pre-join UTDs #3881

Merged
merged 16 commits into from Jan 28, 2020
Merged

Hide pre-join UTDs #3881

merged 16 commits into from Jan 28, 2020

Conversation

uhoreg
Copy link
Member

@uhoreg uhoreg commented Jan 20, 2020

When a user joins an encrypted room, any undecryptable messages before the join are hidden. (More precisely, any events before the most recent undecryptable message before the join.)

(I plan on squash-merging this, since the commit history is a bit convoluted.)

@uhoreg uhoreg requested a review from a team January 23, 2020 19:00
@uhoreg uhoreg marked this pull request as ready for review January 23, 2020 19:00
@uhoreg
Copy link
Member Author

uhoreg commented Jan 23, 2020

fixes element-hq/element-web#5642

@foldleft
Copy link
Contributor

foldleft commented Jan 24, 2020

I think I like this, but I'm going to leave this to Dave or someone who understands event/DAG structure a little better to approve.

A nit, or a suggestion, or, only bother touching this if you happen to agree – I found it took me a moment to understand the logic where you're looking for a timeline. It might be more readable to run a tight loop to find the timeline, then do the stuff in the if/break block afterwards?

Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it's probably okay overall, but I'd very much like to avoid O(n^2) work here if at all possible.

Also curious if there are opportunities to break out earlier in the process... Wouldn't it be faster for the happy path (where everything is visible) if you instead go forward through the events? The first time you see a decryptable message, you can stop, and in the happy path, that's the first one, so there's almost nothing to do, right? (I have probably missed something important here... 😅)

src/components/structures/MessagePanel.js Outdated Show resolved Hide resolved
@uhoreg
Copy link
Member Author

uhoreg commented Jan 24, 2020

It's not O(n^2) work. Although I have a loop within a loop, the inner loop only runs once. On the other hand, I'm not too happy about running the loop every time render is called -- I don't know how often that happens in React world -- so maybe we can pull it to somewhere better? Suggestions welcome.

Regarding only running the loop when we find an undecryptable message, we could do that, but then we'd be running through all the events anyways, so I don't think that buys us much.

@uhoreg uhoreg requested a review from jryans January 24, 2020 19:10
Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I think this would be more naturally accomplished in TimelinePanel. However, this is a tricky area to work in, so I'll also flag @dbkr to ensure I am not leading you astray.

src/components/structures/MessagePanel.js Outdated Show resolved Hide resolved
src/components/structures/MessagePanel.js Outdated Show resolved Hide resolved
src/components/structures/MessagePanel.js Outdated Show resolved Hide resolved
src/components/structures/MessagePanel.js Outdated Show resolved Hide resolved
@jryans jryans requested a review from dbkr January 24, 2020 21:29
@dbkr
Copy link
Member

dbkr commented Jan 27, 2020

Yep, although we also do things like MemberEventListSummary on render in MessagePanel so there is some precedent for this, for better or worse.

const prevContent = event.getPrevContent();
userMembership = prevContent.membership || "leave";
} else if (userMembership === "leave" &&
(event.isDecryptionFailure() || event.isBeingDecrypted())) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One result of moving this to TimelinePanel is that I had to make it also hide things that are in the process of being decrypted (which is probably a wrong thing to do), rather than just things that failed to decrypt, presumably because it hasn't had enough time to finish trying to decrypt. It would be nice if there was a better way to do this, but I can't see anything better.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure I follow why that's only an issue up here in TimelinePanel, I would have assumed it would apply in either component...

What is the user experience with and without including events being decrypted here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure why it only happens when it's in TimelinePanel and not in MessagePanel. My best guess is that it's a timing issue -- by the time MessagePanel gets called, the message decryption has completed, whereas in TimelinePanel, it's still trying to decrypt.

If I don't include isBeingDecrypted, then it will just show all the events, without dropping the pre-join UISIs.

@uhoreg uhoreg requested a review from jryans January 27, 2020 21:42
// 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());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I was hoping that moving up to TimelinePanel would also allow removing the outer loop and timeline check by only looking at liveEvents in state (instead of plain events). Is it possible to make that simplification, or does it break down somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, we don't need to use events. I've changed it to use liveEvents instead of events. (liveEvents vs events confused me.) I'm not sure if the outer loop is needed, but if it isn't, then it will exit after the first iteration of the loop.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

liveEvents is the set we know are already in a timeline:

const events = this._timelineWindow.getEvents();
// 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];

events is liveEvents plus the pending / unsent events as well. (Maybe not the best naming, open to suggestions...!) 😅

Given that then, let's remove the outer loop and update the comment since we know the last event of liveEvents is always the one we'll pick. (This is a somewhat complex function, so I'd like to simplify it where possible like this.)

@uhoreg uhoreg requested a review from jryans January 28, 2020 03:10
Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for going ahead with the move, overall I think it's easier to follow here. 😄 A few more comments before landing.

// 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());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

liveEvents is the set we know are already in a timeline:

const events = this._timelineWindow.getEvents();
// 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];

events is liveEvents plus the pending / unsent events as well. (Maybe not the best naming, open to suggestions...!) 😅

Given that then, let's remove the outer loop and update the comment since we know the last event of liveEvents is always the one we'll pick. (This is a somewhat complex function, so I'd like to simplify it where possible like this.)

@@ -330,10 +333,12 @@ const TimelinePanel = createReactClass({
// We can now paginate in the unpaginated direction
const canPaginateKey = (backwards) ? 'canBackPaginate' : 'canForwardPaginate';
const { events, liveEvents } = this._getEvents();
const firstVisibleEventIndex = this._checkForPreJoinUISI(liveEvents);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense that we'd need to run this every time we get events, so I think it would be more natural to call _checkForPreJoinUISI inside _getEvents, and then firstVisibleEventIndex would be another key returned by _getEvents.

@@ -390,7 +397,7 @@ const TimelinePanel = createReactClass({
// itself into the right place
return new Promise((resolve) => {
this.setState(newState, () => {
resolve(r);
resolve(r && (!backwards || firstVisibleEventIndex === 0));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment to explain this.

const prevContent = event.getPrevContent();
userMembership = prevContent.membership || "leave";
} else if (userMembership === "leave" &&
(event.isDecryptionFailure() || event.isBeingDecrypted())) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure I follow why that's only an issue up here in TimelinePanel, I would have assumed it would apply in either component...

What is the user experience with and without including events being decrypted here?

@uhoreg uhoreg requested a review from jryans January 28, 2020 16:53
Copy link
Collaborator

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks for working on this, I think it's looking good to go. 😄

@jryans
Copy link
Collaborator

jryans commented Jan 28, 2020

@uhoreg, for the test issues, it looks maybe related to decorator changes on develop...? I'd suggest merging the latest changes from develop into your branch.

@uhoreg uhoreg merged commit 793ff2c into develop Jan 28, 2020
@uhoreg uhoreg mentioned this pull request Apr 30, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants