matrix_sdk_base/store/
avatar_cache.rs1use std::collections::BTreeMap;
2
3use ruma::{
4 MxcUri, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UserId,
5 events::room::member::SyncRoomMemberEvent,
6};
7use tracing::trace;
8
9use crate::{StateChanges, StateStore, StoreError, store::SaveLockedStateStore};
10
11#[derive(Debug)]
13pub struct AvatarCache {
14 store: SaveLockedStateStore,
15 changes: BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, Option<OwnedMxcUri>>>,
16}
17
18impl AvatarCache {
19 pub fn new(store: SaveLockedStateStore) -> Self {
21 Self { store, changes: BTreeMap::new() }
22 }
23
24 pub async fn handle_event(
27 &mut self,
28 state_changes: &StateChanges,
29 room_id: &RoomId,
30 member_event: &SyncRoomMemberEvent,
31 ) -> Result<(), StoreError> {
32 let user_id = member_event.sender();
33 if self.changes.get(room_id).is_some_and(|user_ids| user_ids.contains_key(user_id)) {
34 return Ok(());
35 }
36 match member_event {
37 SyncRoomMemberEvent::Original(original_event) => {
38 let avatar_url = original_event.content.avatar_url.clone();
39 self.add_to_changes_if_needed(state_changes, room_id, user_id, avatar_url).await;
40 }
41 SyncRoomMemberEvent::Redacted(_) => {
42 trace!("Redacted event, discarding avatar change for {:?}", user_id);
43 }
44 }
45 Ok(())
46 }
47
48 async fn add_to_changes_if_needed(
49 &mut self,
50 state_changes: &StateChanges,
51 room_id: &RoomId,
52 user_id: &UserId,
53 avatar: Option<OwnedMxcUri>,
54 ) {
55 if !self.is_same_avatar(state_changes, room_id, user_id, avatar.as_deref()).await {
56 trace!("Avatar for {} is different, saving to changes", user_id);
57 let change = self.changes.entry(room_id.to_owned()).or_default();
58 change.insert(user_id.to_owned(), avatar);
59 } else {
60 trace!("Avatar for {} is the same, not saving", user_id);
61 }
62 }
63
64 async fn is_same_avatar(
65 &self,
66 state_changes: &StateChanges,
67 room_id: &RoomId,
68 user_id: &UserId,
69 avatar: Option<&MxcUri>,
70 ) -> bool {
71 let current_avatar = if let Some(event) = state_changes.member(room_id, user_id) {
72 event.content.avatar_url
73 } else {
74 match self.store.get_profile(room_id, user_id).await {
75 Ok(Some(profile)) => profile.content.avatar_url,
76 Ok(None) => None,
77 Err(_) => None,
78 }
79 };
80
81 trace!(
82 "Current avatar for {} in {} is: {:?}, new avatar is: {:?}",
83 user_id, room_id, current_avatar, avatar
84 );
85
86 match (current_avatar, avatar) {
87 (Some(current_avatar), Some(avatar)) => current_avatar == avatar,
88 (None, None) => true,
89 _ => false,
90 }
91 }
92
93 pub fn remove_changes(
96 &mut self,
97 room_id: &RoomId,
98 ) -> Option<BTreeMap<OwnedUserId, Option<OwnedMxcUri>>> {
99 self.changes.remove(room_id)
100 }
101}