base: Add a method to get all the user ids that use a certain display name

master
Damir Jelić 2021-01-26 13:22:06 +01:00
parent 6cb2c8b468
commit b4a916b797
5 changed files with 182 additions and 32 deletions

View File

@ -33,6 +33,7 @@ pub struct RoomMember {
pub(crate) power_levles: Arc<Option<SyncStateEvent<PowerLevelsEventContent>>>, pub(crate) power_levles: Arc<Option<SyncStateEvent<PowerLevelsEventContent>>>,
pub(crate) max_power_level: i64, pub(crate) max_power_level: i64,
pub(crate) is_room_creator: bool, pub(crate) is_room_creator: bool,
pub(crate) display_name_ambiguous: bool,
} }
impl RoomMember { impl RoomMember {

View File

@ -312,6 +312,13 @@ impl Room {
} }
pub async fn get_member(&self, user_id: &UserId) -> StoreResult<Option<RoomMember>> { pub async fn get_member(&self, user_id: &UserId) -> StoreResult<Option<RoomMember>> {
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 presence = self.store.get_presence_event(user_id).await?;
let profile = self.store.get_profile(self.room_id(), user_id).await?; let profile = self.store.get_profile(self.room_id(), user_id).await?;
let max_power_level = self.max_power_level(); let max_power_level = self.max_power_level();
@ -338,17 +345,28 @@ impl Room {
}) })
.flatten(); .flatten();
Ok(self let ambiguous = self
.store .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? .await?
.map(|e| RoomMember { .len()
event: e.into(), > 1;
Ok(Some(RoomMember {
event: member_event.into(),
profile: profile.into(), profile: profile.into(),
presence: presence.into(), presence: presence.into(),
power_levles: power.into(), power_levles: power.into(),
max_power_level, max_power_level,
is_room_creator, is_room_creator,
display_name_ambiguous: ambiguous,
})) }))
} }
} }

View File

@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::sync::{Arc, RwLock}; use std::{
collections::BTreeSet,
sync::{Arc, RwLock},
};
use dashmap::{DashMap, DashSet}; use dashmap::{DashMap, DashSet};
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -345,4 +348,8 @@ impl StateStore for MemoryStore {
async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>> { async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>> {
Ok(self.get_stripped_room_infos()) Ok(self.get_stripped_room_infos())
} }
async fn get_users_with_display_name(&self, _: &RoomId, _: &str) -> Result<BTreeSet<UserId>> {
Ok(BTreeSet::new())
}
} }

View File

@ -12,7 +12,12 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // 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 dashmap::DashMap;
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -101,6 +106,12 @@ pub trait StateStore: AsyncTraitDeps {
async fn get_room_infos(&self) -> Result<Vec<RoomInfo>>; async fn get_room_infos(&self) -> Result<Vec<RoomInfo>>;
async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>>; async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>>;
async fn get_users_with_display_name(
&self,
room_id: &RoomId,
display_name: &str,
) -> Result<BTreeSet<UserId>>;
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -14,7 +14,7 @@
mod store_key; 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::{ use futures::{
stream::{self, Stream}, stream::{self, Stream},
@ -79,6 +79,43 @@ impl From<SerializationError> for StoreError {
} }
} }
trait EncodeKey {
const SEPARATOR: u8 = 0xff;
fn encode(&self) -> Vec<u8>;
}
impl EncodeKey for &str {
fn encode(&self) -> Vec<u8> {
[self.as_bytes(), &[Self::SEPARATOR]].concat()
}
}
impl EncodeKey for (&str, &str) {
fn encode(&self) -> Vec<u8> {
[
self.0.as_bytes(),
&[Self::SEPARATOR],
self.1.as_bytes(),
&[Self::SEPARATOR],
]
.concat()
}
}
impl EncodeKey for (&str, &str, &str) {
fn encode(&self) -> Vec<u8> {
[
self.0.as_bytes(),
&[Self::SEPARATOR],
self.1.as_bytes(),
&[Self::SEPARATOR],
self.2.as_bytes(),
&[Self::SEPARATOR],
]
.concat()
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SledStore { pub struct SledStore {
pub(crate) inner: Db, pub(crate) inner: Db,
@ -87,6 +124,7 @@ pub struct SledStore {
account_data: Tree, account_data: Tree,
members: Tree, members: Tree,
profiles: Tree, profiles: Tree,
display_names: Tree,
joined_user_ids: Tree, joined_user_ids: Tree,
invited_user_ids: Tree, invited_user_ids: Tree,
room_info: Tree, room_info: Tree,
@ -105,6 +143,7 @@ impl SledStore {
let members = db.open_tree("members")?; let members = db.open_tree("members")?;
let profiles = db.open_tree("profiles")?; 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 joined_user_ids = db.open_tree("joined_user_ids")?;
let invited_user_ids = db.open_tree("invited_user_ids")?; let invited_user_ids = db.open_tree("invited_user_ids")?;
@ -124,6 +163,7 @@ impl SledStore {
account_data, account_data,
members, members,
profiles, profiles,
display_names,
joined_user_ids, joined_user_ids,
invited_user_ids, invited_user_ids,
room_account_data, room_account_data,
@ -230,6 +270,7 @@ impl SledStore {
&self.account_data, &self.account_data,
&self.members, &self.members,
&self.profiles, &self.profiles,
&self.display_names,
&self.joined_user_ids, &self.joined_user_ids,
&self.invited_user_ids, &self.invited_user_ids,
&self.room_info, &self.room_info,
@ -246,6 +287,7 @@ impl SledStore {
account_data, account_data,
members, members,
profiles, profiles,
display_names,
joined, joined,
invited, invited,
rooms, rooms,
@ -261,41 +303,91 @@ impl SledStore {
} }
for (room, events) in &changes.members { for (room, events) in &changes.members {
let profile_changes = changes.profiles.get(room);
for event in events.values() { for event in events.values() {
let key = format!("{}{}", room.as_str(), event.state_key.as_str()); let key = format!("{}{}", room.as_str(), event.state_key.as_str());
let old_profile: Option<MemberEventContent> = 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::<MemberEvent>(&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 { match event.content.membership {
MembershipState::Join => { MembershipState::Join => {
joined.insert(key.as_str(), event.state_key.as_str())?; joined.insert(key.as_str(), event.state_key.as_str())?;
invited.remove(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 => { MembershipState::Invite => {
invited.insert(key.as_str(), event.state_key.as_str())?; invited.insert(key.as_str(), event.state_key.as_str())?;
joined.remove(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())?; joined.remove(key.as_str())?;
invited.remove(key.as_str())?; invited.remove(key.as_str())?;
} }
} }
members.insert( members.insert(
format!("{}{}", room.as_str(), &event.state_key).as_str(), key.as_str(),
self.serialize_event(&event) self.serialize_event(&event)
.map_err(ConflictableTransactionError::Abort)?, .map_err(ConflictableTransactionError::Abort)?,
)?; )?;
}
}
for (room, users) in &changes.profiles { if let Some(profile) =
for (user_id, profile) in users { profile_changes.map(|p| p.get(&event.state_key)).flatten()
{
profiles.insert( profiles.insert(
format!("{}{}", room.as_str(), user_id.as_str()).as_str(), key.as_str(),
self.serialize_event(&profile) self.serialize_event(&profile)
.map_err(ConflictableTransactionError::Abort)?, .map_err(ConflictableTransactionError::Abort)?,
)?; )?;
} }
} }
}
for (event_type, event) in &changes.account_data { for (event_type, event) in &changes.account_data {
account_data.insert( account_data.insert(
@ -449,7 +541,7 @@ impl SledStore {
) -> impl Stream<Item = Result<UserId>> { ) -> impl Stream<Item = Result<UserId>> {
stream::iter( stream::iter(
self.invited_user_ids self.invited_user_ids
.scan_prefix(room_id.as_bytes()) .scan_prefix(room_id.as_str())
.map(|u| { .map(|u| {
UserId::try_from(String::from_utf8_lossy(&u?.1).to_string()) UserId::try_from(String::from_utf8_lossy(&u?.1).to_string())
.map_err(StoreError::Identifier) .map_err(StoreError::Identifier)
@ -461,14 +553,10 @@ impl SledStore {
&self, &self,
room_id: &RoomId, room_id: &RoomId,
) -> impl Stream<Item = Result<UserId>> { ) -> impl Stream<Item = Result<UserId>> {
stream::iter( stream::iter(self.joined_user_ids.scan_prefix(room_id.as_str()).map(|u| {
self.joined_user_ids
.scan_prefix(room_id.as_bytes())
.map(|u| {
UserId::try_from(String::from_utf8_lossy(&u?.1).to_string()) UserId::try_from(String::from_utf8_lossy(&u?.1).to_string())
.map_err(StoreError::Identifier) .map_err(StoreError::Identifier)
}), }))
)
} }
pub async fn get_room_infos(&self) -> impl Stream<Item = Result<RoomInfo>> { pub async fn get_room_infos(&self) -> impl Stream<Item = Result<RoomInfo>> {
@ -488,6 +576,22 @@ impl SledStore {
.map(move |r| db.deserialize_event(&r?.1).map_err(|e| e.into())), .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<BTreeSet<UserId>> {
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] #[async_trait]
@ -552,6 +656,15 @@ impl StateStore for SledStore {
async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>> { async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>> {
self.get_stripped_room_infos().await.try_collect().await self.get_stripped_room_infos().await.try_collect().await
} }
async fn get_users_with_display_name(
&self,
room_id: &RoomId,
display_name: &str,
) -> Result<BTreeSet<UserId>> {
self.get_users_with_display_name(room_id, display_name)
.await
}
} }
#[cfg(test)] #[cfg(test)]