-
-
Notifications
You must be signed in to change notification settings - Fork 827
Conversation
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 |
There was a problem hiding this 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... 😅)
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 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. |
There was a problem hiding this 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.
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())) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
// 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()); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
matrix-react-sdk/src/components/structures/TimelinePanel.js
Lines 1116 to 1120 in 380a81b
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.)
There was a problem hiding this 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()); |
There was a problem hiding this comment.
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:
matrix-react-sdk/src/components/structures/TimelinePanel.js
Lines 1116 to 1120 in 380a81b
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); |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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())) { |
There was a problem hiding this comment.
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?
There was a problem hiding this 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. 😄
@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. |
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.)