Skip to content

Commit

Permalink
refactor(timeline): apply aggregation to TimelineItemContent [CONSI…
Browse files Browse the repository at this point in the history
…DER SQUASHING]
  • Loading branch information
bnjbvr committed Jan 30, 2025
1 parent 5562aec commit 10952bb
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 41 deletions.
38 changes: 29 additions & 9 deletions crates/matrix-sdk-ui/src/timeline/controller/aggregations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::collections::HashMap;

use ruma::{EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId};

use crate::timeline::PollState;
use crate::timeline::{PollState, TimelineItemContent};

#[derive(Clone, Debug)]
pub(crate) enum Aggregation {
Expand All @@ -31,13 +31,30 @@ pub(crate) enum Aggregation {
},
}

fn poll_state_from_item(
content: &mut TimelineItemContent,
) -> Result<&mut PollState, AggregationError> {
match content {
TimelineItemContent::Poll(state) => Ok(state),
c => Err(AggregationError::InvalidType {
expected: "a poll".to_owned(),
actual: c.debug_string().to_owned(),
}),
}
}

impl Aggregation {
pub fn apply_poll(&self, poll_state: &mut PollState) -> Result<(), AggregationError> {
pub fn apply(&self, content: &mut TimelineItemContent) -> Result<(), AggregationError> {
match self {
Aggregation::PollResponse { sender, timestamp, answers } => {
poll_state.add_response(sender.clone(), *timestamp, answers.clone());
poll_state_from_item(content)?.add_response(
sender.clone(),
*timestamp,
answers.clone(),
);
}
Aggregation::PollEnd { end_date } => {
let poll_state = poll_state_from_item(content)?;
if !poll_state.end(*end_date) {
return Err(AggregationError::PollAlreadyEnded);
}
Expand All @@ -61,23 +78,26 @@ impl Aggregations {
self.stashed.entry(event_id).or_default().push(aggregation);
}

pub fn apply_poll(
pub fn apply(
&self,
event_id: &EventId,
poll_state: &mut PollState,
) -> Result<(), AggregationError> {
content: &mut TimelineItemContent,
) -> Result<bool, AggregationError> {
let Some(aggregations) = self.stashed.get(event_id) else {
return Ok(());
return Ok(false);
};
for a in aggregations {
a.apply_poll(poll_state)?;
a.apply(content)?;
}
Ok(())
Ok(true)
}
}

#[derive(Debug, thiserror::Error)]
pub(crate) enum AggregationError {
#[error("trying to end a poll twice")]
PollAlreadyEnded,

#[error("trying to apply an aggregation of one type to an invalid target: expected {expected}, actual {actual}")]
InvalidType { expected: String, actual: String },
}
62 changes: 30 additions & 32 deletions crates/matrix-sdk-ui/src/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,49 +865,53 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
.or(pending_edit)
.unzip();

let mut poll_state = PollState::new(c, edit_content);
let poll_state = PollState::new(c, edit_content);
let mut content = TimelineItemContent::Poll(poll_state);

if let Some(event_id) = self.ctx.flow.event_id() {
// Applying the cache to remote events only because local echoes
// don't have an event ID that could be referenced by responses yet.
if let Err(err) = self.meta.aggregations.apply_poll(event_id, &mut poll_state) {
if let Err(err) = self.meta.aggregations.apply(event_id, &mut content) {
warn!("discarding poll aggregations: {err}");
}
}

let edit_json = edit_json.flatten();

self.add_item(TimelineItemContent::Poll(poll_state), edit_json);
self.add_item(content, edit_json);
}

fn handle_poll_response(&mut self, c: UnstablePollResponseEventContent) {
let start_event_id = c.relates_to.event_id;

self.meta.aggregations.add(
start_event_id.clone(),
Aggregation::PollResponse {
sender: self.ctx.sender.clone(),
timestamp: self.ctx.timestamp,
answers: c.poll_response.answers.clone(),
},
);

let Some((item_pos, item)) = rfind_event_by_id(self.items, &start_event_id) else {
return;
let aggregation = Aggregation::PollResponse {
sender: self.ctx.sender.clone(),
timestamp: self.ctx.timestamp,
answers: c.poll_response.answers,
};
self.meta.aggregations.add(start_event_id.clone(), aggregation.clone());

let TimelineItemContent::Poll(poll_state) = item.content() else {
let Some((item_pos, item)) = rfind_event_by_id(self.items, &start_event_id) else {
return;
};

let mut new_poll = poll_state.clone();
new_poll.add_response(self.ctx.sender.clone(), self.ctx.timestamp, c.poll_response.answers);

let new_item = item.with_content(TimelineItemContent::Poll(new_poll), None);

trace!("Adding poll response.");
self.items.replace(item_pos, TimelineItem::new(new_item, item.internal_id.to_owned()));
self.result.items_updated += 1;
let mut new_content = item.content().clone();
match aggregation.apply(&mut new_content) {
Ok(()) => {
trace!("adding poll response.");
self.items.replace(
item_pos,
TimelineItem::new(
item.with_content(new_content, None),
item.internal_id.clone(),
),
);
self.result.items_updated += 1;
}
Err(err) => {
warn!("discarding poll response: {err}");
}
}
}

fn handle_poll_end(&mut self, c: UnstablePollEndEventContent) {
Expand All @@ -920,21 +924,15 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
return;
};

let TimelineItemContent::Poll(poll_state) = item.content() else {
return;
};

let mut poll_state = poll_state.clone();

match aggregation.apply_poll(&mut poll_state) {
let mut new_content = item.content().clone();
match aggregation.apply(&mut new_content) {
Ok(()) => {
trace!("Ending poll.");
let new_item = item.with_content(TimelineItemContent::Poll(poll_state), None);
let new_item = item.with_content(new_content, None);
self.items
.replace(item_pos, TimelineItem::new(new_item, item.internal_id.to_owned()));
self.result.items_updated += 1;
}

Err(err) => {
warn!("discarding poll end: {err}");
}
Expand Down

0 comments on commit 10952bb

Please sign in to comment.