Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions crates/matrix-sdk-base/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ use crate::{
Room, RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons, RoomMembersUpdate, RoomState,
},
store::{
BaseStateStore, DynStateStore, MemoryStore, Result as StoreResult, RoomLoadSettings,
StateChanges, StateStoreDataKey, StateStoreDataValue, StateStoreExt, StoreConfig,
ambiguity_map::AmbiguityCache,
AvatarCache, BaseStateStore, DynStateStore, MemoryStore, Result as StoreResult,
RoomLoadSettings, StateChanges, StateStoreDataKey, StateStoreDataValue, StateStoreExt,
StoreConfig, ambiguity_map::AmbiguityCache,
},
sync::{RoomUpdates, SyncResponse},
};
Expand Down Expand Up @@ -644,6 +644,7 @@ impl BaseClient {
.collect();

let mut ambiguity_cache = AmbiguityCache::new(self.state_store.inner.clone());
let mut avatar_cache = AvatarCache::new(self.state_store.inner.clone());

let global_account_data_processor =
processors::account_data::global(&response.account_data.events);
Expand All @@ -670,6 +671,7 @@ impl BaseClient {
&room_id,
requested_required_states,
&mut ambiguity_cache,
&mut avatar_cache,
),
joined_room,
&mut updated_members_in_room,
Expand All @@ -693,6 +695,7 @@ impl BaseClient {
&room_id,
requested_required_states,
&mut ambiguity_cache,
&mut avatar_cache,
),
left_room,
processors::notification::Notification::new(
Expand Down
9 changes: 7 additions & 2 deletions crates/matrix-sdk-base/src/response_processors/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

use ruma::RoomId;

use crate::{RequestedRequiredStates, store::ambiguity_map::AmbiguityCache};
use crate::{
RequestedRequiredStates,
store::{AvatarCache, ambiguity_map::AmbiguityCache},
};

pub mod display_name;
pub mod msc4186;
Expand All @@ -25,14 +28,16 @@ pub struct RoomCreationData<'a> {
room_id: &'a RoomId,
requested_required_states: &'a RequestedRequiredStates,
ambiguity_cache: &'a mut AmbiguityCache,
avatar_cache: &'a mut AvatarCache,
}

impl<'a> RoomCreationData<'a> {
pub fn new(
room_id: &'a RoomId,
requested_required_states: &'a RequestedRequiredStates,
ambiguity_cache: &'a mut AmbiguityCache,
avatar_cache: &'a mut AvatarCache,
) -> Self {
Self { room_id, requested_required_states, ambiguity_cache }
Self { room_id, requested_required_states, ambiguity_cache, avatar_cache }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub async fn update_any_room(
) -> Result<Option<(RoomInfo, RoomUpdateKind)>> {
let _timer = timer!(tracing::Level::TRACE, "update_any_room");

let RoomCreationData { room_id, requested_required_states, ambiguity_cache } =
let RoomCreationData { room_id, requested_required_states, ambiguity_cache, avatar_cache } =
room_creation_data;

// Read state events from the `required_state` field.
Expand Down Expand Up @@ -118,6 +118,7 @@ pub async fn update_any_room(
raw_state_events,
&mut room_info,
ambiguity_cache,
avatar_cache,
&mut new_user_ids,
state_store,
#[cfg(feature = "experimental-encrypted-state-events")]
Expand Down Expand Up @@ -170,6 +171,7 @@ pub async fn update_any_room(
room_info.update_notification_count(notification_count);

let ambiguity_changes = ambiguity_cache.changes.remove(room_id).unwrap_or_default();
let avatar_changes = avatar_cache.remove_changes(room_id);
let room_account_data = rooms_account_data.get(room_id);

match (room_info.state(), maybe_room_update_kind) {
Expand All @@ -188,6 +190,7 @@ pub async fn update_any_room(
ephemeral,
notification_count,
ambiguity_changes,
avatar_changes,
)),
)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub async fn update_joined_room(
notification: notification::Notification<'_>,
#[cfg(feature = "e2e-encryption")] e2ee: &e2ee::E2EE<'_>,
) -> Result<JoinedRoomUpdate> {
let RoomCreationData { room_id, requested_required_states, ambiguity_cache } =
let RoomCreationData { room_id, requested_required_states, ambiguity_cache, avatar_cache } =
room_creation_data;

let state_store = notification.state_store;
Expand All @@ -68,6 +68,7 @@ pub async fn update_joined_room(
raw_state_events,
&mut room_info,
ambiguity_cache,
avatar_cache,
&mut new_user_ids,
state_store,
#[cfg(feature = "experimental-encrypted-state-events")]
Expand Down Expand Up @@ -137,6 +138,7 @@ pub async fn update_joined_room(
joined_room.ephemeral.events,
notification_count,
ambiguity_cache.changes.remove(room_id).unwrap_or_default(),
avatar_cache.remove_changes(room_id),
))
}

Expand All @@ -149,7 +151,7 @@ pub async fn update_left_room(
notification: notification::Notification<'_>,
#[cfg(feature = "e2e-encryption")] e2ee: &e2ee::E2EE<'_>,
) -> Result<LeftRoomUpdate> {
let RoomCreationData { room_id, requested_required_states, ambiguity_cache } =
let RoomCreationData { room_id, requested_required_states, ambiguity_cache, avatar_cache } =
room_creation_data;

#[cfg(feature = "e2e-encryption")]
Expand All @@ -172,6 +174,7 @@ pub async fn update_left_room(
raw_state_events,
&mut room_info,
ambiguity_cache,
avatar_cache,
&mut (),
state_store,
#[cfg(feature = "experimental-encrypted-state-events")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ pub mod sync {
use crate::response_processors::e2ee;
use crate::{
RoomInfo, RoomInfoNotableUpdateReasons, RoomState,
store::{BaseStateStore, Result as StoreResult, ambiguity_map::AmbiguityCache},
store::{
AvatarCache, BaseStateStore, Result as StoreResult, ambiguity_map::AmbiguityCache,
},
sync::State,
utils::RawStateEventWithKeys,
};
Expand Down Expand Up @@ -94,6 +96,7 @@ pub mod sync {
raw_events: Vec<RawStateEventWithKeys<AnySyncStateEvent>>,
room_info: &mut RoomInfo,
ambiguity_cache: &mut AmbiguityCache,
avatar_cache: &mut AvatarCache,
new_users: &mut U,
state_store: &BaseStateStore,
#[cfg(feature = "experimental-encrypted-state-events")] e2ee: &e2ee::E2EE<'_>,
Expand All @@ -111,6 +114,7 @@ pub mod sync {
&room_info.room_id,
&mut raw_event,
ambiguity_cache,
avatar_cache,
new_users,
)
.await?;
Expand Down Expand Up @@ -177,6 +181,7 @@ pub mod sync {
room_id: &RoomId,
raw_event: &mut RawStateEventWithKeys<AnySyncStateEvent>,
ambiguity_cache: &mut AmbiguityCache,
avatar_cache: &mut AvatarCache,
new_users: &mut U,
) -> StoreResult<()>
where
Expand All @@ -189,6 +194,7 @@ pub mod sync {
};

ambiguity_cache.handle_event(&context.state_changes, room_id, event).await?;
avatar_cache.handle_event(&context.state_changes, room_id, event).await?;

match event.membership() {
MembershipState::Join | MembershipState::Invite => {
Expand Down
4 changes: 3 additions & 1 deletion crates/matrix-sdk-base/src/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::{
RequestedRequiredStates,
error::Result,
response_processors as processors,
store::ambiguity_map::AmbiguityCache,
store::{AvatarCache, ambiguity_map::AmbiguityCache},
sync::{RoomUpdates, SyncResponse},
};

Expand Down Expand Up @@ -120,6 +120,7 @@ impl BaseClient {

let state_store = self.state_store.clone();
let mut ambiguity_cache = AmbiguityCache::new(state_store.inner.clone());
let mut avatar_cache = AvatarCache::new(state_store.inner.clone());

let global_account_data_processor =
processors::account_data::global(&extensions.account_data.global);
Expand All @@ -142,6 +143,7 @@ impl BaseClient {
room_id,
requested_required_states,
&mut ambiguity_cache,
&mut avatar_cache,
),
room_response,
&extensions.account_data.rooms,
Expand Down
101 changes: 101 additions & 0 deletions crates/matrix-sdk-base/src/store/avatar_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::collections::BTreeMap;

use ruma::{
MxcUri, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UserId,
events::room::member::SyncRoomMemberEvent,
};
use tracing::trace;

use crate::{StateChanges, StateStore, StoreError, store::SaveLockedStateStore};

/// A cache for keeping track of avatar changes in sync responses.
#[derive(Debug)]
pub struct AvatarCache {
store: SaveLockedStateStore,
changes: BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
}

impl AvatarCache {
/// Creates a new [`AvatarCache`].
pub fn new(store: SaveLockedStateStore) -> Self {
Self { store, changes: BTreeMap::new() }
}

/// Processes the room member event and checks if there was any change in
/// the avatar URL for the room member.
pub async fn handle_event(
&mut self,
state_changes: &StateChanges,
room_id: &RoomId,
member_event: &SyncRoomMemberEvent,
) -> Result<(), StoreError> {
let user_id = member_event.sender();
if self.changes.get(room_id).is_some_and(|user_ids| user_ids.contains_key(user_id)) {
return Ok(());
}
match member_event {
SyncRoomMemberEvent::Original(original_event) => {
let avatar_url = original_event.content.avatar_url.clone();
self.add_to_changes_if_needed(state_changes, room_id, user_id, avatar_url).await;
}
SyncRoomMemberEvent::Redacted(_) => {
trace!("Redacted event, discarding avatar change for {:?}", user_id);
}
}
Ok(())
}

async fn add_to_changes_if_needed(
&mut self,
state_changes: &StateChanges,
room_id: &RoomId,
user_id: &UserId,
avatar: Option<OwnedMxcUri>,
) {
if !self.is_same_avatar(state_changes, room_id, user_id, avatar.as_deref()).await {
trace!("Avatar for {} is different, saving to changes", user_id);
let change = self.changes.entry(room_id.to_owned()).or_default();
change.insert(user_id.to_owned(), avatar);
} else {
trace!("Avatar for {} is the same, not saving", user_id);
}
}

async fn is_same_avatar(
&self,
state_changes: &StateChanges,
room_id: &RoomId,
user_id: &UserId,
avatar: Option<&MxcUri>,
) -> bool {
let current_avatar = if let Some(event) = state_changes.member(room_id, user_id) {
event.content.avatar_url
} else {
match self.store.get_profile(room_id, user_id).await {
Ok(Some(profile)) => profile.content.avatar_url,
Ok(None) => None,
Err(_) => None,
}
};

trace!(
"Current avatar for {} in {} is: {:?}, new avatar is: {:?}",
user_id, room_id, current_avatar, avatar
);

match (current_avatar, avatar) {
(Some(current_avatar), Some(avatar)) => current_avatar == avatar,
(None, None) => true,
_ => false,
}
}

/// Removes and returns the avatar changes associated with the [`RoomId`],
/// if any.
pub fn remove_changes(
&mut self,
room_id: &RoomId,
) -> Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>> {
self.changes.remove(room_id)
}
}
3 changes: 3 additions & 0 deletions crates/matrix-sdk-base/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,13 @@ use crate::{
};

pub(crate) mod ambiguity_map;
mod avatar_cache;
mod memory_store;
pub mod migration_helpers;
mod send_queue;

pub use avatar_cache::AvatarCache;

#[cfg(any(test, feature = "testing"))]
pub use self::integration_tests::StateStoreIntegrationTests;
#[cfg(feature = "unstable-msc4274")]
Expand Down
15 changes: 13 additions & 2 deletions crates/matrix-sdk-base/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub use ruma::api::client::sync::sync_events::v3::{
InvitedRoom as InvitedRoomUpdate, KnockedRoom as KnockedRoomUpdate,
};
use ruma::{
OwnedEventId, OwnedRoomId,
OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId,
api::client::sync::sync_events::UnreadNotificationsCount as RumaUnreadNotificationsCount,
events::{
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncEphemeralRoomEvent,
Expand Down Expand Up @@ -219,6 +219,8 @@ pub struct JoinedRoomUpdate {
/// This is a map of event ID of the `m.room.member` event to the
/// details of the ambiguity change.
pub ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
/// Collection of avatar changes that room member events trigger.
pub avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
}

#[cfg(not(tarpaulin_include))]
Expand All @@ -243,8 +245,17 @@ impl JoinedRoomUpdate {
ephemeral: Vec<Raw<AnySyncEphemeralRoomEvent>>,
unread_notifications: UnreadNotificationsCount,
ambiguity_changes: BTreeMap<OwnedEventId, AmbiguityChange>,
avatar_changes: Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
) -> Self {
Self { unread_notifications, timeline, state, account_data, ephemeral, ambiguity_changes }
Self {
unread_notifications,
timeline,
state,
account_data,
ephemeral,
ambiguity_changes,
avatar_changes,
}
}
}

Expand Down
23 changes: 18 additions & 5 deletions crates/matrix-sdk-ui/src/timeline/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,28 @@ pub(in crate::timeline) async fn room_event_cache_updates_task(
timeline_controller.handle_ephemeral_events(events).await;
}

RoomEventCacheUpdate::UpdateMembers { ambiguity_changes } => {
if !ambiguity_changes.is_empty() {
RoomEventCacheUpdate::UpdateMembers { ambiguity_changes, avatar_changes } => {
if !ambiguity_changes.is_empty()
|| !avatar_changes.as_ref().is_none_or(|avatars| avatars.is_empty())
{
let member_ambiguity_changes = ambiguity_changes
.values()
.flat_map(|change| change.user_ids())
.collect::<BTreeSet<_>>();
timeline_controller
.force_update_sender_profiles(&member_ambiguity_changes)
.await;

let mut user_ids_to_update = member_ambiguity_changes;

if let Some(avatar_changes) = &avatar_changes {
let mut user_ids =
avatar_changes.keys().map(|u| u.as_ref()).collect::<BTreeSet<_>>();
user_ids_to_update.append(&mut user_ids)
} else {
warn!(
"No avatar changes to update for {}, ignoring",
room_event_cache.room_id()
);
}
timeline_controller.force_update_sender_profiles(&user_ids_to_update).await;
}
}
}
Expand Down
Loading
Loading