mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-04-26 18:20:40 -04:00
feat(sdk): Add num_updates to timeline PaginationOutcome
This commit is contained in:
committed by
Jonas Platte
parent
108950c706
commit
9cba98ae1b
@@ -289,6 +289,8 @@ dictionary MoveData {
|
||||
dictionary PaginationOutcome {
|
||||
// Whether there's more messages to be paginated.
|
||||
boolean more_messages;
|
||||
// The number of updated or added timeline items.
|
||||
u16 num_updates;
|
||||
};
|
||||
|
||||
interface RoomMessageEventContent {};
|
||||
|
||||
@@ -183,6 +183,22 @@ pub(super) struct TimelineEventHandler<'a, 'i> {
|
||||
fully_read_event: &'a mut Option<OwnedEventId>,
|
||||
fully_read_event_in_timeline: &'a mut bool,
|
||||
item_created: bool,
|
||||
items_updated: u16,
|
||||
}
|
||||
|
||||
// This is a macro instead of a method plus free fn so the borrow checker can
|
||||
// "see through" it, allowing borrows of TimelineEventHandler fields other than
|
||||
// `timeline_items` and `items_updated` in the update closure.
|
||||
macro_rules! update_timeline_item {
|
||||
($this:ident, $event_id:expr, $action:expr, $update:expr) => {
|
||||
_update_timeline_item(
|
||||
&mut *$this.timeline_items,
|
||||
&mut $this.items_updated,
|
||||
$event_id,
|
||||
$action,
|
||||
$update,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
@@ -200,10 +216,14 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
fully_read_event: &mut timeline_meta.fully_read_event,
|
||||
fully_read_event_in_timeline: &mut timeline_meta.fully_read_event_in_timeline,
|
||||
item_created: false,
|
||||
items_updated: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_event(mut self, event_kind: TimelineEventKind) {
|
||||
/// Handle an event.
|
||||
///
|
||||
/// Returns the number of timeline updates that were made.
|
||||
pub(super) fn handle_event(mut self, event_kind: TimelineEventKind) -> u16 {
|
||||
match event_kind {
|
||||
TimelineEventKind::Message { content } => match content {
|
||||
AnyMessageLikeEventContent::Reaction(c) => self.handle_reaction(c),
|
||||
@@ -235,6 +255,8 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
if !self.item_created {
|
||||
// TODO: Add event as raw
|
||||
}
|
||||
|
||||
self.item_created as u16 + self.items_updated
|
||||
}
|
||||
|
||||
fn handle_room_message(&mut self, content: RoomMessageEventContent) {
|
||||
@@ -251,7 +273,7 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
fn handle_room_message_edit(&mut self, replacement: Replacement<MessageType>) {
|
||||
let event_id = &replacement.event_id;
|
||||
|
||||
update_timeline_item(self.timeline_items, event_id, "edit", |item| {
|
||||
update_timeline_item!(self, event_id, "edit", |item| {
|
||||
if self.meta.sender != item.sender() {
|
||||
info!(
|
||||
%event_id, original_sender = %item.sender(), edit_sender = %self.meta.sender,
|
||||
@@ -301,8 +323,7 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
fn handle_reaction(&mut self, c: ReactionEventContent) {
|
||||
let event_id: &EventId = &c.relates_to.event_id;
|
||||
|
||||
let items = &mut *self.timeline_items;
|
||||
let did_update = update_timeline_item(items, event_id, "reaction", |item| {
|
||||
update_timeline_item!(self, event_id, "reaction", |item| {
|
||||
// Handling of reactions on redacted events is an open question.
|
||||
// For now, ignore reactions on redacted events like Element does.
|
||||
if let TimelineItemContent::RedactedMessage = item.content {
|
||||
@@ -322,7 +343,7 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
}
|
||||
});
|
||||
|
||||
if did_update {
|
||||
if self.items_updated > 0 {
|
||||
self.reaction_map.insert(self.flow.to_key(), (self.meta.sender.clone(), c.relates_to));
|
||||
}
|
||||
}
|
||||
@@ -341,13 +362,10 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
|
||||
// Redacted redactions are no-ops (unfortunately)
|
||||
fn handle_redaction(&mut self, redacts: OwnedEventId, _content: RoomRedactionEventContent) {
|
||||
let mut did_update = false;
|
||||
|
||||
if let Some((sender, rel)) =
|
||||
self.reaction_map.remove(&TimelineKey::EventId(redacts.clone()))
|
||||
{
|
||||
let items = &mut *self.timeline_items;
|
||||
did_update = update_timeline_item(items, &rel.event_id, "redaction", |item| {
|
||||
update_timeline_item!(self, &rel.event_id, "redaction", |item| {
|
||||
let mut reactions = item.reactions.clone();
|
||||
|
||||
let Entry::Occupied(mut details_entry) = reactions.bundled.entry(rel.key) else {
|
||||
@@ -390,7 +408,7 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
Some(item.with_reactions(reactions))
|
||||
});
|
||||
|
||||
if !did_update {
|
||||
if !self.items_updated > 0 {
|
||||
warn!("reaction_map out of sync with timeline items");
|
||||
}
|
||||
}
|
||||
@@ -398,11 +416,9 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
|
||||
// Even if the event being redacted is a reaction (found in
|
||||
// `reaction_map`), it can still be present in the timeline items
|
||||
// directly with the raw event timeline feature (not yet implemented).
|
||||
let items = &mut *self.timeline_items;
|
||||
did_update |=
|
||||
update_timeline_item(items, &redacts, "redaction", |item| Some(item.to_redacted()));
|
||||
update_timeline_item!(self, &redacts, "redaction", |item| Some(item.to_redacted()));
|
||||
|
||||
if !did_update {
|
||||
if self.items_updated > 0 {
|
||||
// We will want to know this when debugging redaction issues.
|
||||
debug!(redaction_key = ?self.flow.to_key(), %redacts, "redaction affected no event");
|
||||
}
|
||||
@@ -594,23 +610,21 @@ pub(crate) fn update_read_marker(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether an update happened
|
||||
fn update_timeline_item(
|
||||
fn _update_timeline_item(
|
||||
timeline_items: &mut MutableVecLockMut<'_, Arc<TimelineItem>>,
|
||||
items_updated: &mut u16,
|
||||
event_id: &EventId,
|
||||
action: &str,
|
||||
update: impl FnOnce(&EventTimelineItem) -> Option<EventTimelineItem>,
|
||||
) -> bool {
|
||||
) {
|
||||
if let Some((idx, item)) = find_event_by_id(timeline_items, event_id) {
|
||||
if let Some(new_item) = update(item) {
|
||||
timeline_items.set_cloned(idx, Arc::new(TimelineItem::Event(new_item)));
|
||||
return true;
|
||||
*items_updated += 1;
|
||||
}
|
||||
} else {
|
||||
debug!(%event_id, "Timeline item not found, discarding {action}");
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Converts a timestamp to a `(year, month, day)` tuple.
|
||||
|
||||
@@ -91,11 +91,14 @@ impl TimelineInner {
|
||||
.handle_event(kind);
|
||||
}
|
||||
|
||||
/// Handle a back-paginated event.
|
||||
///
|
||||
/// Returns the number of timeline updates that were made.
|
||||
pub(super) async fn handle_back_paginated_event(
|
||||
&self,
|
||||
event: TimelineEvent,
|
||||
own_user_id: &UserId,
|
||||
) {
|
||||
) -> u16 {
|
||||
let mut metadata_lock = self.metadata.lock().await;
|
||||
handle_remote_event(
|
||||
event.event.cast(),
|
||||
@@ -104,7 +107,7 @@ impl TimelineInner {
|
||||
TimelineItemPosition::Start,
|
||||
&mut self.items.lock_mut(),
|
||||
&mut metadata_lock,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn add_event_id(&self, txn_id: &TransactionId, event_id: OwnedEventId) {
|
||||
@@ -233,6 +236,9 @@ impl TimelineInner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a remote event.
|
||||
///
|
||||
/// Returns the number of timeline updates that were made.
|
||||
fn handle_remote_event(
|
||||
raw: Raw<AnySyncTimelineEvent>,
|
||||
own_user_id: &UserId,
|
||||
@@ -240,7 +246,7 @@ fn handle_remote_event(
|
||||
position: TimelineItemPosition,
|
||||
timeline_items: &mut MutableVecLockMut<'_, Arc<TimelineItem>>,
|
||||
timeline_meta: &mut TimelineInnerMetadata,
|
||||
) {
|
||||
) -> u16 {
|
||||
let (event_id, sender, origin_server_ts, txn_id, relations, event_kind) =
|
||||
match raw.deserialize() {
|
||||
Ok(event) => (
|
||||
@@ -262,7 +268,7 @@ fn handle_remote_event(
|
||||
),
|
||||
Err(e) => {
|
||||
warn!("Failed to deserialize timeline event: {e}");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -184,14 +184,15 @@ impl Timeline {
|
||||
}))
|
||||
.await?;
|
||||
|
||||
let outcome = PaginationOutcome { more_messages: messages.end.is_some() };
|
||||
*self.start_token.lock().unwrap() = messages.end;
|
||||
|
||||
let own_user_id = self.room.own_user_id();
|
||||
let mut num_updates = 0;
|
||||
for room_ev in messages.chunk {
|
||||
self.inner.handle_back_paginated_event(room_ev, own_user_id).await;
|
||||
num_updates += self.inner.handle_back_paginated_event(room_ev, own_user_id).await;
|
||||
}
|
||||
|
||||
let outcome = PaginationOutcome { more_messages: messages.end.is_some(), num_updates };
|
||||
*self.start_token.lock().unwrap() = messages.end;
|
||||
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
@@ -344,6 +345,13 @@ impl TimelineItem {
|
||||
pub struct PaginationOutcome {
|
||||
/// Whether there's more messages to be paginated.
|
||||
pub more_messages: bool,
|
||||
|
||||
/// The number of timeline updates to expect from this pagination.
|
||||
///
|
||||
/// Since timeline updates are received asynchronously, you can use this
|
||||
/// number to determine whether all updates have been observed, and whether
|
||||
/// another back pagination request should be made.
|
||||
pub num_updates: u16,
|
||||
}
|
||||
|
||||
// FIXME: Put an upper bound on timeline size or add a separate map to look up
|
||||
|
||||
Reference in New Issue
Block a user