From b4a916b7977910373ec6e3192965816753a7117d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 26 Jan 2021 13:22:06 +0100 Subject: [PATCH] base: Add a method to get all the user ids that use a certain display name --- matrix_sdk_base/src/rooms/members.rs | 1 + matrix_sdk_base/src/rooms/normal.rs | 38 +++-- matrix_sdk_base/src/store/memory_store.rs | 9 +- matrix_sdk_base/src/store/mod.rs | 13 +- matrix_sdk_base/src/store/sled_store/mod.rs | 153 +++++++++++++++++--- 5 files changed, 182 insertions(+), 32 deletions(-) diff --git a/matrix_sdk_base/src/rooms/members.rs b/matrix_sdk_base/src/rooms/members.rs index ef6042b0..68c8ae51 100644 --- a/matrix_sdk_base/src/rooms/members.rs +++ b/matrix_sdk_base/src/rooms/members.rs @@ -33,6 +33,7 @@ pub struct RoomMember { pub(crate) power_levles: Arc>>, pub(crate) max_power_level: i64, pub(crate) is_room_creator: bool, + pub(crate) display_name_ambiguous: bool, } impl RoomMember { diff --git a/matrix_sdk_base/src/rooms/normal.rs b/matrix_sdk_base/src/rooms/normal.rs index 481386c8..603caec0 100644 --- a/matrix_sdk_base/src/rooms/normal.rs +++ b/matrix_sdk_base/src/rooms/normal.rs @@ -312,6 +312,13 @@ impl Room { } pub async fn get_member(&self, user_id: &UserId) -> StoreResult> { + let member_event = + if let Some(m) = self.store.get_member_event(self.room_id(), user_id).await? { + m + } else { + return Ok(None); + }; + let presence = self.store.get_presence_event(user_id).await?; let profile = self.store.get_profile(self.room_id(), user_id).await?; let max_power_level = self.max_power_level(); @@ -338,18 +345,29 @@ impl Room { }) .flatten(); - Ok(self + let ambiguous = self .store - .get_member_event(&self.room_id, user_id) + .get_users_with_display_name( + self.room_id(), + member_event + .content + .displayname + .as_deref() + .unwrap_or_else(|| user_id.localpart()), + ) .await? - .map(|e| RoomMember { - event: e.into(), - profile: profile.into(), - presence: presence.into(), - power_levles: power.into(), - max_power_level, - is_room_creator, - })) + .len() + > 1; + + Ok(Some(RoomMember { + event: member_event.into(), + profile: profile.into(), + presence: presence.into(), + power_levles: power.into(), + max_power_level, + is_room_creator, + display_name_ambiguous: ambiguous, + })) } } diff --git a/matrix_sdk_base/src/store/memory_store.rs b/matrix_sdk_base/src/store/memory_store.rs index e69745bc..2653eb55 100644 --- a/matrix_sdk_base/src/store/memory_store.rs +++ b/matrix_sdk_base/src/store/memory_store.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::{Arc, RwLock}; +use std::{ + collections::BTreeSet, + sync::{Arc, RwLock}, +}; use dashmap::{DashMap, DashSet}; use matrix_sdk_common::{ @@ -345,4 +348,8 @@ impl StateStore for MemoryStore { async fn get_stripped_room_infos(&self) -> Result> { Ok(self.get_stripped_room_infos()) } + + async fn get_users_with_display_name(&self, _: &RoomId, _: &str) -> Result> { + Ok(BTreeSet::new()) + } } diff --git a/matrix_sdk_base/src/store/mod.rs b/matrix_sdk_base/src/store/mod.rs index 3e414c13..bb27e483 100644 --- a/matrix_sdk_base/src/store/mod.rs +++ b/matrix_sdk_base/src/store/mod.rs @@ -12,7 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{collections::BTreeMap, ops::Deref, path::Path, sync::Arc}; +use std::{ + collections::{BTreeMap, BTreeSet}, + ops::Deref, + path::Path, + sync::Arc, +}; use dashmap::DashMap; use matrix_sdk_common::{ @@ -101,6 +106,12 @@ pub trait StateStore: AsyncTraitDeps { async fn get_room_infos(&self) -> Result>; async fn get_stripped_room_infos(&self) -> Result>; + + async fn get_users_with_display_name( + &self, + room_id: &RoomId, + display_name: &str, + ) -> Result>; } #[derive(Debug, Clone)] diff --git a/matrix_sdk_base/src/store/sled_store/mod.rs b/matrix_sdk_base/src/store/sled_store/mod.rs index 33707952..5b0ee8f4 100644 --- a/matrix_sdk_base/src/store/sled_store/mod.rs +++ b/matrix_sdk_base/src/store/sled_store/mod.rs @@ -14,7 +14,7 @@ mod store_key; -use std::{convert::TryFrom, path::Path, sync::Arc, time::SystemTime}; +use std::{collections::BTreeSet, convert::TryFrom, path::Path, sync::Arc, time::SystemTime}; use futures::{ stream::{self, Stream}, @@ -79,6 +79,43 @@ impl From for StoreError { } } +trait EncodeKey { + const SEPARATOR: u8 = 0xff; + fn encode(&self) -> Vec; +} + +impl EncodeKey for &str { + fn encode(&self) -> Vec { + [self.as_bytes(), &[Self::SEPARATOR]].concat() + } +} + +impl EncodeKey for (&str, &str) { + fn encode(&self) -> Vec { + [ + self.0.as_bytes(), + &[Self::SEPARATOR], + self.1.as_bytes(), + &[Self::SEPARATOR], + ] + .concat() + } +} + +impl EncodeKey for (&str, &str, &str) { + fn encode(&self) -> Vec { + [ + self.0.as_bytes(), + &[Self::SEPARATOR], + self.1.as_bytes(), + &[Self::SEPARATOR], + self.2.as_bytes(), + &[Self::SEPARATOR], + ] + .concat() + } +} + #[derive(Debug, Clone)] pub struct SledStore { pub(crate) inner: Db, @@ -87,6 +124,7 @@ pub struct SledStore { account_data: Tree, members: Tree, profiles: Tree, + display_names: Tree, joined_user_ids: Tree, invited_user_ids: Tree, room_info: Tree, @@ -105,6 +143,7 @@ impl SledStore { let members = db.open_tree("members")?; let profiles = db.open_tree("profiles")?; + let display_names = db.open_tree("display_names")?; let joined_user_ids = db.open_tree("joined_user_ids")?; let invited_user_ids = db.open_tree("invited_user_ids")?; @@ -124,6 +163,7 @@ impl SledStore { account_data, members, profiles, + display_names, joined_user_ids, invited_user_ids, room_account_data, @@ -230,6 +270,7 @@ impl SledStore { &self.account_data, &self.members, &self.profiles, + &self.display_names, &self.joined_user_ids, &self.invited_user_ids, &self.room_info, @@ -246,6 +287,7 @@ impl SledStore { account_data, members, profiles, + display_names, joined, invited, rooms, @@ -261,39 +303,89 @@ impl SledStore { } for (room, events) in &changes.members { + let profile_changes = changes.profiles.get(room); + for event in events.values() { let key = format!("{}{}", room.as_str(), event.state_key.as_str()); + let old_profile: Option = if let Some(p) = profiles + .get(key.as_str())? + .map(|p| self.deserialize_event(&p)) + .transpose() + .map_err(ConflictableTransactionError::Abort)? + { + p + } else { + members + .get(key.as_str())? + .map(|m| self.deserialize_event::(&m)) + .transpose() + .map_err(ConflictableTransactionError::Abort)? + .map(|m| m.content) + }; + + let old_display_name = old_profile + .map(|m| { + m.displayname + .unwrap_or_else(|| event.state_key.localpart().to_string()) + }) + .unwrap_or_else(|| event.state_key.localpart().to_string()); + + let old_display_name_key = ( + room.as_str(), + old_display_name.as_str(), + event.state_key.as_str(), + ) + .encode(); + + let display_name = profile_changes + .map(|p| p.get(&event.state_key)) + .flatten() + .as_ref() + .map(|m| m.displayname.as_deref()) + .unwrap_or_else(|| Some(event.state_key.localpart())) + .unwrap_or_else(|| event.state_key.localpart()); + + let display_name_key = + (room.as_str(), display_name, event.state_key.as_str()).encode(); + match event.content.membership { MembershipState::Join => { joined.insert(key.as_str(), event.state_key.as_str())?; invited.remove(key.as_str())?; + display_names.remove(old_display_name_key)?; + display_names + .insert(display_name_key, event.state_key.as_str())?; } MembershipState::Invite => { invited.insert(key.as_str(), event.state_key.as_str())?; joined.remove(key.as_str())?; + display_names.remove(old_display_name_key)?; + display_names + .insert(display_name_key, event.state_key.as_str())?; } _ => { + display_names.remove(old_display_name_key)?; joined.remove(key.as_str())?; invited.remove(key.as_str())?; } } members.insert( - format!("{}{}", room.as_str(), &event.state_key).as_str(), + key.as_str(), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; - } - } - for (room, users) in &changes.profiles { - for (user_id, profile) in users { - profiles.insert( - format!("{}{}", room.as_str(), user_id.as_str()).as_str(), - self.serialize_event(&profile) - .map_err(ConflictableTransactionError::Abort)?, - )?; + if let Some(profile) = + profile_changes.map(|p| p.get(&event.state_key)).flatten() + { + profiles.insert( + key.as_str(), + self.serialize_event(&profile) + .map_err(ConflictableTransactionError::Abort)?, + )?; + } } } @@ -449,7 +541,7 @@ impl SledStore { ) -> impl Stream> { stream::iter( self.invited_user_ids - .scan_prefix(room_id.as_bytes()) + .scan_prefix(room_id.as_str()) .map(|u| { UserId::try_from(String::from_utf8_lossy(&u?.1).to_string()) .map_err(StoreError::Identifier) @@ -461,14 +553,10 @@ impl SledStore { &self, room_id: &RoomId, ) -> impl Stream> { - stream::iter( - self.joined_user_ids - .scan_prefix(room_id.as_bytes()) - .map(|u| { - UserId::try_from(String::from_utf8_lossy(&u?.1).to_string()) - .map_err(StoreError::Identifier) - }), - ) + stream::iter(self.joined_user_ids.scan_prefix(room_id.as_str()).map(|u| { + UserId::try_from(String::from_utf8_lossy(&u?.1).to_string()) + .map_err(StoreError::Identifier) + })) } pub async fn get_room_infos(&self) -> impl Stream> { @@ -488,6 +576,22 @@ impl SledStore { .map(move |r| db.deserialize_event(&r?.1).map_err(|e| e.into())), ) } + + pub async fn get_users_with_display_name( + &self, + room_id: &RoomId, + display_name: &str, + ) -> Result> { + let key = (room_id.as_str(), display_name).encode(); + + self.display_names + .scan_prefix(key) + .map(|u| { + UserId::try_from(String::from_utf8_lossy(&u?.1).to_string()) + .map_err(StoreError::Identifier) + }) + .collect() + } } #[async_trait] @@ -552,6 +656,15 @@ impl StateStore for SledStore { async fn get_stripped_room_infos(&self) -> Result> { self.get_stripped_room_infos().await.try_collect().await } + + async fn get_users_with_display_name( + &self, + room_id: &RoomId, + display_name: &str, + ) -> Result> { + self.get_users_with_display_name(room_id, display_name) + .await + } } #[cfg(test)]