Skip to content

Commit c54dbfe

Browse files
committed
refactor(timeline): apply aggregations in a single place
1 parent 4fb6989 commit c54dbfe

File tree

3 files changed

+77
-127
lines changed

3 files changed

+77
-127
lines changed

crates/matrix-sdk-ui/src/timeline/controller/aggregations.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@
4040
use std::collections::HashMap;
4141

4242
use ruma::{MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId};
43-
use tracing::warn;
43+
use tracing::{trace, warn};
4444

45+
use super::{rfind_event_by_item_id, ObservableItemsTransaction};
4546
use crate::timeline::{
46-
PollState, ReactionInfo, ReactionStatus, TimelineEventItemId, TimelineItemContent,
47+
PollState, ReactionInfo, ReactionStatus, TimelineEventItemId, TimelineItem, TimelineItemContent,
4748
};
4849

4950
/// Which kind of aggregation (related event) is this?
@@ -369,6 +370,41 @@ impl Aggregations {
369370
}
370371
}
371372

373+
/// Find an item identified by the target identifier, and apply the aggregation
374+
/// onto it.
375+
///
376+
/// Returns whether the aggregation has been applied or not (so as to increment
377+
/// a number of updated result, for instance).
378+
pub(crate) fn find_item_and_apply_aggregation(
379+
items: &mut ObservableItemsTransaction<'_>,
380+
target: &TimelineEventItemId,
381+
aggregation: Aggregation,
382+
) -> bool {
383+
let Some((idx, event_item)) = rfind_event_by_item_id(items, &target) else {
384+
warn!("couldn't find aggregation's target {target:?}");
385+
return false;
386+
};
387+
388+
let mut new_content = event_item.content().clone();
389+
390+
match aggregation.apply(&mut new_content) {
391+
Ok(true) => {
392+
trace!("applied aggregation");
393+
let new_item = event_item.with_content(new_content);
394+
items.replace(idx, TimelineItem::new(new_item, event_item.internal_id.to_owned()));
395+
true
396+
}
397+
Ok(false) => {
398+
trace!("applying the aggregation had no effect");
399+
false
400+
}
401+
Err(err) => {
402+
warn!("error when applying aggregation: {err}");
403+
false
404+
}
405+
}
406+
}
407+
372408
/// The result of marking an aggregation as sent.
373409
pub(crate) enum MarkAggregationSentResult {
374410
/// The aggregation has been found, and marked as sent.

crates/matrix-sdk-ui/src/timeline/controller/mod.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ impl<P: RoomDataProvider> TimelineController<P> {
13571357
applies_to: OwnedTransactionId,
13581358
) {
13591359
let mut state = self.state.write().await;
1360+
let mut tr = state.transaction();
13601361

13611362
let target = TimelineEventItemId::TransactionId(applies_to);
13621363

@@ -1372,26 +1373,10 @@ impl<P: RoomDataProvider> TimelineController<P> {
13721373
},
13731374
);
13741375

1375-
state.meta.aggregations.add(target.clone(), aggregation.clone());
1376+
tr.meta.aggregations.add(target.clone(), aggregation.clone());
1377+
find_item_and_apply_aggregation(&mut tr.items, &target, aggregation);
13761378

1377-
let Some((item_pos, item)) = rfind_event_by_item_id(&state.items, &target) else {
1378-
warn!("Local item not found anymore.");
1379-
return;
1380-
};
1381-
1382-
let mut content = item.content().clone();
1383-
match aggregation.apply(&mut content) {
1384-
Ok(true) => {
1385-
trace!("added local reaction to local echo");
1386-
let internal_id = item.internal_id.clone();
1387-
let new_item = item.with_content(content);
1388-
state.items.replace(item_pos, TimelineItem::new(new_item, internal_id));
1389-
}
1390-
Ok(false) => {}
1391-
Err(err) => {
1392-
warn!("when applying local reaction to local echo: {err}");
1393-
}
1394-
}
1379+
tr.commit();
13951380
}
13961381

13971382
/// Handle a single room send queue update.

crates/matrix-sdk-ui/src/timeline/event_handler.rs

Lines changed: 35 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ use tracing::{debug, error, field::debug, info, instrument, trace, warn};
5353
use super::{
5454
algorithms::{rfind_event_by_id, rfind_event_by_item_id},
5555
controller::{
56-
Aggregation, AggregationKind, ObservableItemsTransaction, ObservableItemsTransactionEntry,
57-
PendingEdit, PendingEditKind, TimelineMetadata, TimelineStateTransaction,
56+
find_item_and_apply_aggregation, Aggregation, AggregationKind, ObservableItemsTransaction,
57+
ObservableItemsTransactionEntry, PendingEdit, PendingEditKind, TimelineMetadata,
58+
TimelineStateTransaction,
5859
},
5960
date_dividers::DateDividerAdjuster,
6061
event_item::{
@@ -411,23 +412,13 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
411412

412413
AnyMessageLikeEventContent::Sticker(content) => {
413414
if should_add {
414-
let mut content = TimelineItemContent::Sticker(Sticker {
415-
content,
416-
reactions: Default::default(),
417-
});
418-
419-
if let Some(event_id) = self.ctx.flow.event_id() {
420-
// Applying the cache to remote events only because local echoes
421-
// don't have an event ID that could be referenced by responses yet.
422-
if let Err(err) = self.meta.aggregations.apply(
423-
&TimelineEventItemId::EventId(event_id.to_owned()),
424-
&mut content,
425-
) {
426-
warn!("discarding sticker aggregations: {err}");
427-
}
428-
}
429-
430-
self.add_item(content, None);
415+
self.add_item(
416+
TimelineItemContent::Sticker(Sticker {
417+
content,
418+
reactions: Default::default(),
419+
}),
420+
None,
421+
);
431422
}
432423
}
433424

@@ -587,15 +578,10 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
587578

588579
let edit_json = edit_json.flatten();
589580

590-
let mut content =
591-
TimelineItemContent::message(msg, edit_content, self.items, Default::default());
592-
if let Err(err) =
593-
self.meta.aggregations.apply(&self.ctx.flow.timeline_item_id(), &mut content)
594-
{
595-
warn!("discarding message aggregations: {err}");
596-
}
597-
598-
self.add_item(content, edit_json);
581+
self.add_item(
582+
TimelineItemContent::message(msg, edit_content, self.items, Default::default()),
583+
edit_json,
584+
);
599585
}
600586

601587
#[instrument(skip_all, fields(replacement_event_id = ?replacement.event_id))]
@@ -732,7 +718,7 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
732718
/// [`crate::timeline::TimelineController::handle_local_echo`].
733719
#[instrument(skip_all, fields(relates_to_event_id = ?c.relates_to.event_id))]
734720
fn handle_reaction(&mut self, c: ReactionEventContent) {
735-
let reacted_to_event_id = &c.relates_to.event_id;
721+
let target = TimelineEventItemId::EventId(c.relates_to.event_id);
736722

737723
// Add the aggregation to the manager.
738724
let reaction_status = match &self.ctx.flow {
@@ -754,28 +740,10 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
754740
reaction_status,
755741
},
756742
);
757-
self.meta
758-
.aggregations
759-
.add(TimelineEventItemId::EventId(reacted_to_event_id.clone()), aggregation.clone());
760743

761-
let Some((idx, event_item)) = rfind_event_by_id(self.items, reacted_to_event_id) else {
762-
warn!("couldn't find reaction's target {reacted_to_event_id:?}");
763-
return;
764-
};
765-
766-
let mut new_content = event_item.content().clone();
767-
match aggregation.apply(&mut new_content) {
768-
Ok(true) => {
769-
trace!("added reaction");
770-
let new_item = event_item.with_content(new_content);
771-
self.items
772-
.replace(idx, TimelineItem::new(new_item, event_item.internal_id.to_owned()));
773-
self.result.items_updated += 1;
774-
}
775-
Ok(false) => {}
776-
Err(err) => {
777-
warn!("error when applying reaction aggregation: {err}");
778-
}
744+
self.meta.aggregations.add(target.clone(), aggregation.clone());
745+
if find_item_and_apply_aggregation(self.items, &target, aggregation) {
746+
self.result.items_updated += 1;
779747
}
780748
}
781749

@@ -872,21 +840,14 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
872840
.unzip();
873841

874842
let poll_state = PollState::new(c, edit_content, Default::default());
875-
let mut content = TimelineItemContent::Poll(poll_state);
876-
if let Err(err) =
877-
self.meta.aggregations.apply(&self.ctx.flow.timeline_item_id(), &mut content)
878-
{
879-
warn!("discarding poll aggregations: {err}");
880-
}
881843

882844
let edit_json = edit_json.flatten();
883845

884-
self.add_item(content, edit_json);
846+
self.add_item(TimelineItemContent::Poll(poll_state), edit_json);
885847
}
886848

887849
fn handle_poll_response(&mut self, c: UnstablePollResponseEventContent) {
888-
let start_event_id = c.relates_to.event_id;
889-
850+
let target = TimelineEventItemId::EventId(c.relates_to.event_id);
890851
let aggregation = Aggregation::new(
891852
self.ctx.flow.timeline_item_id(),
892853
AggregationKind::PollResponse {
@@ -895,60 +856,21 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
895856
answers: c.poll_response.answers,
896857
},
897858
);
898-
899-
self.meta
900-
.aggregations
901-
.add(TimelineEventItemId::EventId(start_event_id.clone()), aggregation.clone());
902-
903-
let Some((item_pos, item)) = rfind_event_by_id(self.items, &start_event_id) else {
904-
return;
905-
};
906-
907-
let mut new_content = item.content().clone();
908-
match aggregation.apply(&mut new_content) {
909-
Ok(true) => {
910-
trace!("adding poll response.");
911-
self.items.replace(
912-
item_pos,
913-
TimelineItem::new(item.with_content(new_content), item.internal_id.clone()),
914-
);
915-
self.result.items_updated += 1;
916-
}
917-
Ok(false) => {}
918-
Err(err) => {
919-
warn!("discarding poll response: {err}");
920-
}
859+
self.meta.aggregations.add(target.clone(), aggregation.clone());
860+
if find_item_and_apply_aggregation(self.items, &target, aggregation) {
861+
self.result.items_updated += 1;
921862
}
922863
}
923864

924865
fn handle_poll_end(&mut self, c: UnstablePollEndEventContent) {
925-
let start_event_id = c.relates_to.event_id;
926-
866+
let target = TimelineEventItemId::EventId(c.relates_to.event_id);
927867
let aggregation = Aggregation::new(
928868
self.ctx.flow.timeline_item_id(),
929869
AggregationKind::PollEnd { end_date: self.ctx.timestamp },
930870
);
931-
self.meta
932-
.aggregations
933-
.add(TimelineEventItemId::EventId(start_event_id.clone()), aggregation.clone());
934-
935-
let Some((item_pos, item)) = rfind_event_by_id(self.items, &start_event_id) else {
936-
return;
937-
};
938-
939-
let mut new_content = item.content().clone();
940-
match aggregation.apply(&mut new_content) {
941-
Ok(true) => {
942-
trace!("Ending poll.");
943-
let new_item = item.with_content(new_content);
944-
self.items
945-
.replace(item_pos, TimelineItem::new(new_item, item.internal_id.to_owned()));
946-
self.result.items_updated += 1;
947-
}
948-
Ok(false) => {}
949-
Err(err) => {
950-
warn!("discarding poll end: {err}");
951-
}
871+
self.meta.aggregations.add(target.clone(), aggregation.clone());
872+
if find_item_and_apply_aggregation(self.items, &target, aggregation) {
873+
self.result.items_updated += 1;
952874
}
953875
}
954876

@@ -1044,11 +966,18 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
1044966
/// timeline item being added here.
1045967
fn add_item(
1046968
&mut self,
1047-
content: TimelineItemContent,
969+
mut content: TimelineItemContent,
1048970
edit_json: Option<Raw<AnySyncTimelineEvent>>,
1049971
) {
1050972
self.result.item_added = true;
1051973

974+
// Apply any pending or stashed aggregations.
975+
if let Err(err) =
976+
self.meta.aggregations.apply(&self.ctx.flow.timeline_item_id(), &mut content)
977+
{
978+
warn!("discarding aggregations: {err}");
979+
}
980+
1052981
let sender = self.ctx.sender.to_owned();
1053982
let sender_profile = TimelineDetails::from_initial_value(self.ctx.sender_profile.clone());
1054983
let timestamp = self.ctx.timestamp;

0 commit comments

Comments
 (0)