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) max_power_level: i64,
pub(crate) is_room_creator: bool,
pub(crate) display_name_ambiguous: bool,
}
impl RoomMember {

View File

@ -312,6 +312,13 @@ impl Room {
}
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 profile = self.store.get_profile(self.room_id(), user_id).await?;
let max_power_level = self.max_power_level();
@ -338,17 +345,28 @@ 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(),
.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,
}))
}
}

View File

@ -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<Vec<StrippedRoomInfo>> {
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
// 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<Vec<RoomInfo>>;
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)]

View File

@ -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<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)]
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,41 +303,91 @@ 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<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 {
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 {
if let Some(profile) =
profile_changes.map(|p| p.get(&event.state_key)).flatten()
{
profiles.insert(
format!("{}{}", room.as_str(), user_id.as_str()).as_str(),
key.as_str(),
self.serialize_event(&profile)
.map_err(ConflictableTransactionError::Abort)?,
)?;
}
}
}
for (event_type, event) in &changes.account_data {
account_data.insert(
@ -449,7 +541,7 @@ impl SledStore {
) -> impl Stream<Item = Result<UserId>> {
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<Item = Result<UserId>> {
stream::iter(
self.joined_user_ids
.scan_prefix(room_id.as_bytes())
.map(|u| {
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<Item = Result<RoomInfo>> {
@ -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<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]
@ -552,6 +656,15 @@ impl StateStore for SledStore {
async fn get_stripped_room_infos(&self) -> Result<Vec<StrippedRoomInfo>> {
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)]