From 6611815d4ebfe5d32dc230ae0aeb13ca0f9e9dfe Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 26 Aug 2024 12:20:58 +0200 Subject: [PATCH] WIP --- .../src/timeline/event_item/mod.rs | 6 +- .../matrix-sdk-ui/src/timeline/inner/mod.rs | 75 ++++++++++++++----- .../src/timeline/tests/reactions.rs | 4 +- 3 files changed, 65 insertions(+), 20 deletions(-) diff --git a/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs b/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs index 52abb9766e0..3a8e7033d10 100644 --- a/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/event_item/mod.rs @@ -21,7 +21,7 @@ use as_variant::as_variant; use indexmap::IndexMap; use matrix_sdk::{ deserialized_responses::{EncryptionInfo, ShieldState}, - send_queue::SendHandle, + send_queue::{SendHandle, SendReactionHandle}, Client, Error, }; use matrix_sdk_base::{ @@ -625,6 +625,10 @@ pub enum EventItemOrigin { /// What's the status of a reaction? #[derive(Clone, Debug)] pub enum ReactionStatus { + /// It's a local reaction to a local echo. + /// + /// The handle is missing only in testing contexts. + LocalToLocal(Option), /// It's a local reaction to a remote event. /// /// The handle is missing only in testing contexts. diff --git a/crates/matrix-sdk-ui/src/timeline/inner/mod.rs b/crates/matrix-sdk-ui/src/timeline/inner/mod.rs index 1b1e81262d5..7cfdf4bbfd7 100644 --- a/crates/matrix-sdk-ui/src/timeline/inner/mod.rs +++ b/crates/matrix-sdk-ui/src/timeline/inner/mod.rs @@ -26,7 +26,9 @@ use matrix_sdk::crypto::OlmMachine; use matrix_sdk::{ deserialized_responses::SyncTimelineEvent, event_cache::{paginator::Paginator, RoomEventCache}, - send_queue::{LocalEcho, LocalEchoContent, RoomSendQueueUpdate, SendHandle}, + send_queue::{ + LocalEcho, LocalEchoContent, RoomSendQueueUpdate, SendHandle, SendReactionHandle, + }, Result, Room, }; #[cfg(test)] @@ -45,7 +47,8 @@ use ruma::{ AnySyncTimelineEvent, MessageLikeEventType, }, serde::Raw, - EventId, OwnedEventId, OwnedTransactionId, RoomVersionId, TransactionId, UserId, + EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, RoomVersionId, + TransactionId, UserId, }; use tokio::sync::{RwLock, RwLockWriteGuard}; use tracing::{debug, error, field::debug, info, instrument, trace, warn}; @@ -64,8 +67,8 @@ use super::{ traits::RoomDataProvider, util::{rfind_event_by_id, rfind_event_item, RelativePosition}, Error, EventSendState, EventTimelineItem, InReplyToDetails, Message, PaginationError, Profile, - RepliedToEvent, TimelineDetails, TimelineEventItemId, TimelineFocus, TimelineItem, - TimelineItemContent, TimelineItemKind, + ReactionInfo, RepliedToEvent, TimelineDetails, TimelineEventItemId, TimelineFocus, + TimelineItem, TimelineItemContent, TimelineItemKind, }; use crate::{ timeline::{ @@ -478,19 +481,24 @@ impl TimelineInner

{ trace!("removing a previous reaction"); match prev_status { - ReactionStatus::LocalToRemote(send_handle) => { - // No need to keep the lock. - drop(state); + ReactionStatus::LocalToLocal(send_reaction_handle) => { + if let Some(handle) = send_reaction_handle { + if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? { + // Impossible state: the reaction has moved from local to echo under our + // feet, but the timeline was supposed to be locked! + warn!("unexpectedly unable to abort sending of local reaction"); + } + } else { + warn!("no send reaction handle (this should only happen in testing contexts)"); + } + } + ReactionStatus::LocalToRemote(send_handle) => { // No need to reflect the change ourselves, since handling the discard of the // local echo will take care of it. trace!("aborting send of the previous reaction that was a local echo"); - if let Some(send_handle) = send_handle { - if !send_handle - .abort() - .await - .map_err(|err| Error::SendQueueError(err.into()))? - { + if let Some(handle) = send_handle { + if !handle.abort().await.map_err(|err| Error::SendQueueError(err.into()))? { // Impossible state: the reaction has moved from local to echo under our // feet, but the timeline was supposed to be locked! warn!("unexpectedly unable to abort sending of local reaction"); @@ -790,10 +798,8 @@ impl TimelineInner

{ state.meta.reactions.map.remove(&TimelineEventItemId::TransactionId(txn_id.to_owned())) { let item = match &full_key.item { - TimelineEventItemId::TransactionId(_) => { - // TODO(bnjbvr): reactions on local echoes - warn!("reactions on local echoes are NYI"); - return false; + TimelineEventItemId::TransactionId(txn_id) => { + rfind_event_item(&state.items, |item| item.transaction_id() == Some(&txn_id)) } TimelineEventItemId::EventId(event_id) => rfind_event_by_id(&state.items, event_id), }; @@ -1181,11 +1187,44 @@ impl TimelineInner

{ } LocalEchoContent::React { key, send_handle, applies_to } => { - todo!(); + self.handle_local_reaction(key, send_handle, applies_to).await; } } } + /// Adds a reaction (local echo) to a local echo. + #[instrument(skip(self, send_handle))] + async fn handle_local_reaction( + &self, + reaction_key: String, + send_handle: SendReactionHandle, + applies_to: OwnedTransactionId, + ) { + let mut state = self.state.write().await; + + let Some((item_pos, item)) = + rfind_event_item(&state.items, |item| item.transaction_id() == Some(&applies_to)) + else { + warn!("Local item not found anymore."); + return; + }; + + let user_id = self.room_data_provider.own_user_id(); + + let reaction_info = ReactionInfo { + timestamp: MilliSecondsSinceUnixEpoch::now(), + status: ReactionStatus::LocalToLocal(Some(send_handle)), + }; + + let mut reactions = item.reactions().clone(); + let by_user = reactions.entry(reaction_key).or_default(); + by_user.insert(user_id.to_owned(), reaction_info); + + trace!("Adding local reaction to local echo"); + let new_item = item.with_reactions(reactions); + state.items.set(item_pos, new_item); + } + /// Handle a single room send queue update. pub(crate) async fn handle_room_send_queue_update(&self, update: RoomSendQueueUpdate) { match update { diff --git a/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs b/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs index 1a6d4e4de60..6351505a664 100644 --- a/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs +++ b/crates/matrix-sdk-ui/src/timeline/tests/reactions.rs @@ -70,7 +70,9 @@ macro_rules! assert_reaction_is_updated { let reactions = event.reactions().get(&REACTION_KEY.to_owned()).unwrap(); let reaction = reactions.get(*ALICE).unwrap(); match reaction.status { - ReactionStatus::LocalToRemote(_) => assert!(!$is_remote_echo), + ReactionStatus::LocalToRemote(_) | ReactionStatus::LocalToLocal(_) => { + assert!(!$is_remote_echo) + } ReactionStatus::RemoteToRemote(_) => assert!($is_remote_echo), }; event