-
-
Notifications
You must be signed in to change notification settings - Fork 592
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
Draft: Refresh room timeline when we see a MSC2716 marker event #2282
Draft: Refresh room timeline when we see a MSC2716 marker event #2282
Conversation
See matrix-org/matrix-spec-proposals#2716 (comment) > In a [sync meeting with @ara4n](https://docs.google.com/document/d/1KCEmpnGr4J-I8EeaVQ8QJZKBDu53ViI7V62y5BzfXr0/edit#bookmark=id.67nio1ka8znc), we came up with the idea to make the `marker` events as state events. When the client sees that the `m.room.marker` state changed to a different event ID, it can throw away all of the timeline and re-fetch as needed. > > For homeservers where the [same problem](matrix-org/matrix-spec-proposals#2716 (comment)) can happen, we probably don't want to throw away the whole timeline but it can go up the `unsigned.replaces_state` chain of the `m.room.marker` state events to get them all. > > In terms of state performance, there could be thousands of `marker` events in a room but it's no different than room members joining and leaving over and over like an IRC room.
src/sync.ts
Outdated
) { | ||
// Saw new marker event, refreshing the timeline | ||
console.log('Saw new marker event, refreshing the timeline', new Error().stack); | ||
room.resetLiveTimeline(null, null); |
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.
TODO: Test if this is the function we actually want to use.
Our goal is to throw away the current timeline and re-fetch events for the room from the server.
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 would probably be better if we re-fetched the new events first, then replaced the timeline
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.
Thinking about this more, do we even want to do this automatically? Someone could cause your Element client to constantly refresh the timeline by just sending marker events over and over. Granted, you probably want to leave a room like this 🤷. Perhaps also some sort of DOS vector since everyone will be refreshing and hitting the server at the exact same time.
I think we might want to approach this to be a similar flow to how Element receives app updates. When the client receives a marker event, we first notify the user that history was imported and is available to see after they refresh their timeline (which can be a button action). The UI can probably use something similar to the "Connectivity to the server has been lost." banner.
In most cases, the history won't be in people's current timelines cached in the Element client, so do we even need to worry about refreshing? This is what I kinda leaned to before this PR but it's slightly important from a demo perspective and a personal bridge flow. For example with Beeper, a room is created for your Whatsapp conversation, and as you sit in the room, you probably ideally want the history to appear as it is imported but at least be apparent in some way after the import finishes.
We can detect whether the history imported affects any timelines on the client and only ask to refresh then. To detect, when we receive a marker event, make a request to /context/{insertionEventId}
with the insertionEventId
the marker is pointing to and check if any its prev_events
are in the timeline. /context
doesn't work over federation but the homeserver backfills the insertion event when it receives the marker so it already has it available.
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.
Updated to prompt the user to refresh their timeline: matrix-org/matrix-react-sdk#8303
We can detect whether the history imported affects any timelines on the client and only ask to refresh then. To detect, when we receive a marker event, make a request to
/context/{insertionEventId}
with theinsertionEventId
the marker is pointing to and check if any itsprev_events
are in the timeline./context
doesn't work over federation but the homeserver backfills the insertion event when it receives the marker so it already has it available.
We can't do this because prev_events
aren't available in the client API's 😢. I'm not too keen to add an extra field to the marker or insertion event indicating where the history is imported next to as there isn't a way to enforce that it's true (a homeserver could lie and make it up). I'd rather keep the source of truth in the prev_events
.
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.
review to clear codeowners. When leaving draft it'll need to be re-reviewed.
Associated matrix-js-sdk PR: matrix-org/matrix-js-sdk#2282
* 2. It's the first state we're seeing after joining the room | ||
* 3. Or whether it's coming from `syncFromCache` */ | ||
fromInitialState?: boolean; | ||
} |
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.
Instead of dealing with unwieldy long function signatures with unlabeled and optional arguments in the usage, I've refactored some of these into option objects now that I am adding yet another boolean
here (adding fromInitialState
).
Before:
room.addLiveEvents(mainTimelineEvents, null, fromCache, timelineWasEmpty);
After:
room.addLiveEvents(mainTimelineEvents, {
fromCache,
fromInitialState: timelineWasEmpty
});
If necessary, this refactor can be split out to another PR with some effort.
…msc2716-marker-events Conflicts: src/models/room.ts src/sync.ts
Codecov Report
@@ Coverage Diff @@
## develop #2282 +/- ##
===========================================
- Coverage 59.71% 59.62% -0.10%
===========================================
Files 91 91
Lines 16439 16484 +45
Branches 3794 3808 +14
===========================================
+ Hits 9817 9828 +11
- Misses 6622 6656 +34
|
src/sync.ts
Outdated
room.currentState.on(RoomStateEvent.Marker, async function( | ||
markerEvent, | ||
{ fromInitialState }: ISetStateOptions = {}, | ||
) { |
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.
Calling out one of the main points of interest in the PR (new logic)
@@ -401,6 +414,8 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap> | |||
|
|||
// assume all our sentinels are now out-of-date | |||
this.sentinels = {}; | |||
} else if (event.getType() === EventType.Marker) { | |||
this.emit(RoomStateEvent.Marker, event, setStateOptions); |
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.
When we see a new marker state event, emit an event so we can listen for it in sync.ts
-> registerStateListeners
and see whether we need to refresh the timeline
Closing in favor of #2299 |
Refresh room timeline when we see a MSC2716 marker event.
The "marker" events are being used as state now because this way they can't be lost in the timeline. Regardless of when they were sent, we will still have the latest version of the state to compare against. Any time we see our latest state value change for marker events, refresh the timeline.
Todo
marker
event will causetimelineNeedsRefresh
marker
event already sent in room, user joins room where the/sync
state
includesmarker
should NOT triggertimelineNeedsRefresh
marker
event already sent in room, initial/sync
state
includesmarker
should NOT triggertimelineNeedsRefresh
syncFromCache
where the state includes marker should NOT triggertimelineNeedsRefresh
marker
from creator will causetimelineNeedsRefresh
marker
not from creator should NOT triggertimelineNeedsRefresh
Dev notes
Here's what your changelog entry will look like:
✨ Features