mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-07 07:27:45 -04:00
chore: Merge remote-tracking branch 'origin/main' into b-featureflag-timeline
This commit is contained in:
@@ -55,7 +55,10 @@ ruma = { version = "0.6.1", features = ["client-api-c", "signatures"] }
|
||||
|
||||
[dev-dependencies]
|
||||
futures = { version = "0.3.21", default-features = false, features = ["executor"] }
|
||||
tracing = { version = "0.1.26", features = ["log"] }
|
||||
http = "0.2.6"
|
||||
assign = "1.1.1"
|
||||
env_logger = "0.9.0"
|
||||
matrix-sdk-test = { version = "0.4.0", path = "../matrix-sdk-test" }
|
||||
tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
|
||||
@@ -1301,12 +1301,15 @@ impl Default for BaseClient {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use matrix_sdk_test::{async_test, EventBuilder};
|
||||
use ruma::{room_id, user_id};
|
||||
use matrix_sdk_test::{async_test, response_from_file, EventBuilder};
|
||||
use ruma::{
|
||||
api::{client as api, IncomingResponse},
|
||||
room_id, user_id,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use super::BaseClient;
|
||||
use crate::{RoomType, Session};
|
||||
use crate::{DisplayName, RoomType, Session};
|
||||
|
||||
#[async_test]
|
||||
async fn invite_after_leaving() {
|
||||
@@ -1363,4 +1366,100 @@ mod tests {
|
||||
client.receive_sync_response(response).await.unwrap();
|
||||
assert_eq!(client.get_room(room_id).unwrap().room_type(), RoomType::Invited);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn invite_displayname_integration_test() {
|
||||
let user_id = user_id!("@alice:example.org");
|
||||
let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
|
||||
|
||||
let client = BaseClient::new();
|
||||
client
|
||||
.restore_login(Session {
|
||||
access_token: "token".to_owned(),
|
||||
user_id: user_id.to_owned(),
|
||||
device_id: "FOOBAR".into(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let response = api::sync::sync_events::v3::Response::try_from_http_response(response_from_file(&json!({
|
||||
"next_batch": "asdkl;fjasdkl;fj;asdkl;f",
|
||||
"device_one_time_keys_count": {
|
||||
"signed_curve25519": 50u64
|
||||
},
|
||||
"device_unused_fallback_key_types": [
|
||||
"signed_curve25519"
|
||||
],
|
||||
"rooms": {
|
||||
"invite": {
|
||||
"!ithpyNKDtmhneaTQja:example.org": {
|
||||
"invite_state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"creator": "@test:example.org",
|
||||
"room_version": "9"
|
||||
},
|
||||
"sender": "@test:example.org",
|
||||
"state_key": "",
|
||||
"type": "m.room.create"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"join_rule": "invite"
|
||||
},
|
||||
"sender": "@test:example.org",
|
||||
"state_key": "",
|
||||
"type": "m.room.join_rules"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"algorithm": "m.megolm.v1.aes-sha2"
|
||||
},
|
||||
"sender": "@test:example.org",
|
||||
"state_key": "",
|
||||
"type": "m.room.encryption"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/dcBBDwuWEUrjfrOchvkirUST",
|
||||
"displayname": "Kyra",
|
||||
"membership": "join"
|
||||
},
|
||||
"sender": "@test:example.org",
|
||||
"state_key": "@test:example.org",
|
||||
"type": "m.room.member"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/ABFEXSDrESxovWwEnCYdNcHT",
|
||||
"displayname": "alice",
|
||||
"is_direct": true,
|
||||
"membership": "invite"
|
||||
},
|
||||
"origin_server_ts": 1650878657984u64,
|
||||
"sender": "@test:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 14u64
|
||||
},
|
||||
"event_id": "$fLDqltg9Puj-kWItLSFVHPGN4YkgpYQf2qImPzdmgrE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))).expect("static json doesn't fail to parse");
|
||||
|
||||
client.receive_sync_response(response).await.unwrap();
|
||||
|
||||
let room = client.get_room(room_id).expect("Room not found");
|
||||
assert_eq!(room.room_type(), RoomType::Invited);
|
||||
assert_eq!(
|
||||
room.display_name().await.expect("fetching display name failed"),
|
||||
DisplayName::Calculated("Kyra".to_owned())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,5 +40,5 @@ pub use client::BaseClient;
|
||||
pub use http;
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
pub use matrix_sdk_crypto as crypto;
|
||||
pub use rooms::{Room, RoomInfo, RoomMember, RoomType};
|
||||
pub use rooms::{DisplayName, Room, RoomInfo, RoomMember, RoomType};
|
||||
pub use store::{StateChanges, StateStore, Store, StoreError};
|
||||
|
||||
@@ -18,17 +18,19 @@ use ruma::{
|
||||
events::{
|
||||
presence::PresenceEvent,
|
||||
room::{
|
||||
member::{MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
power_levels::SyncRoomPowerLevelsEvent,
|
||||
},
|
||||
},
|
||||
MxcUri, UserId,
|
||||
};
|
||||
|
||||
use crate::deserialized_responses::MemberEvent;
|
||||
|
||||
/// A member of a room.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoomMember {
|
||||
pub(crate) event: Arc<OriginalSyncRoomMemberEvent>,
|
||||
pub(crate) event: Arc<MemberEvent>,
|
||||
pub(crate) profile: Arc<Option<RoomMemberEventContent>>,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) presence: Arc<Option<PresenceEvent>>,
|
||||
@@ -41,7 +43,7 @@ pub struct RoomMember {
|
||||
impl RoomMember {
|
||||
/// Get the unique user id of this member.
|
||||
pub fn user_id(&self) -> &UserId {
|
||||
&self.event.state_key
|
||||
self.event.user_id()
|
||||
}
|
||||
|
||||
/// Get the display name of the member if there is one.
|
||||
@@ -49,7 +51,7 @@ impl RoomMember {
|
||||
if let Some(p) = self.profile.as_ref() {
|
||||
p.displayname.as_deref()
|
||||
} else {
|
||||
self.event.content.displayname.as_deref()
|
||||
self.event.content().displayname.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +71,7 @@ impl RoomMember {
|
||||
pub fn avatar_url(&self) -> Option<&MxcUri> {
|
||||
match self.profile.as_ref() {
|
||||
Some(p) => p.avatar_url.as_deref(),
|
||||
None => self.event.content.avatar_url.as_deref(),
|
||||
None => self.event.content().avatar_url.as_deref(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +114,7 @@ impl RoomMember {
|
||||
if let Some(p) = self.profile.as_ref() {
|
||||
&p.membership
|
||||
} else {
|
||||
&self.event.content.membership
|
||||
&self.event.content().membership
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod members;
|
||||
mod normal;
|
||||
|
||||
use std::{cmp::max, collections::HashSet};
|
||||
use std::{cmp::max, collections::HashSet, fmt};
|
||||
|
||||
pub use members::RoomMember;
|
||||
pub use normal::{Room, RoomInfo, RoomType};
|
||||
@@ -18,6 +18,36 @@ use ruma::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The name of the room, either from the metadata or calculaetd
|
||||
/// according to [matrix specification](https://matrix.org/docs/spec/client_server/latest#calculating-the-display-name-for-a-room)
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DisplayName {
|
||||
/// The room has been named explicitly as
|
||||
Named(String),
|
||||
/// The room has a canonical alias that should be used
|
||||
Aliased(String),
|
||||
/// The room has not given an explicit name but a name could be
|
||||
/// calculated
|
||||
Calculated(String),
|
||||
/// The room doesn't have a name right now, but used to have one
|
||||
/// e.g. because it was a DM and everyone has left the room
|
||||
EmptyWas(String),
|
||||
/// No useful name could be calculated or ever found
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl fmt::Display for DisplayName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DisplayName::Named(s) | DisplayName::Calculated(s) | DisplayName::Aliased(s) => {
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
DisplayName::EmptyWas(s) => write!(f, "Empty Room (was {})", s),
|
||||
DisplayName::Empty => write!(f, "Empty Room"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A base room info struct that is the backbone of normal as well as stripped
|
||||
/// rooms. Holds all the state events that are important to present a room to
|
||||
/// users.
|
||||
@@ -61,7 +91,7 @@ impl BaseRoomInfo {
|
||||
joined_member_count: u64,
|
||||
invited_member_count: u64,
|
||||
heroes: Vec<RoomMember>,
|
||||
) -> String {
|
||||
) -> DisplayName {
|
||||
calculate_room_name(
|
||||
joined_member_count,
|
||||
invited_member_count,
|
||||
@@ -190,14 +220,12 @@ impl Default for BaseRoomInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate room name according to step 3 of the [naming algorithm.][spec]
|
||||
///
|
||||
/// [spec]: <https://matrix.org/docs/spec/client_server/latest#calculating-the-display-name-for-a-room>
|
||||
/// Calculate room name according to step 3 of the [naming algorithm.]
|
||||
fn calculate_room_name(
|
||||
joined_member_count: u64,
|
||||
invited_member_count: u64,
|
||||
heroes: Vec<&str>,
|
||||
) -> String {
|
||||
) -> DisplayName {
|
||||
let heroes_count = heroes.len() as u64;
|
||||
let invited_joined = invited_member_count + joined_member_count;
|
||||
let invited_joined_minus_one = invited_joined.saturating_sub(1);
|
||||
@@ -221,12 +249,12 @@ fn calculate_room_name(
|
||||
// User is alone.
|
||||
if invited_joined <= 1 {
|
||||
if names.is_empty() {
|
||||
"Empty room".to_owned()
|
||||
DisplayName::Empty
|
||||
} else {
|
||||
format!("Empty room (was {})", names)
|
||||
DisplayName::EmptyWas(names)
|
||||
}
|
||||
} else {
|
||||
names
|
||||
DisplayName::Calculated(names)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,33 +265,33 @@ mod tests {
|
||||
|
||||
fn test_calculate_room_name() {
|
||||
let mut actual = calculate_room_name(2, 0, vec!["a"]);
|
||||
assert_eq!("a", actual);
|
||||
assert_eq!(DisplayName::Calculated("a".to_owned()), actual);
|
||||
|
||||
actual = calculate_room_name(3, 0, vec!["a", "b"]);
|
||||
assert_eq!("a, b", actual);
|
||||
assert_eq!(DisplayName::Calculated("a, b".to_owned()), actual);
|
||||
|
||||
actual = calculate_room_name(4, 0, vec!["a", "b", "c"]);
|
||||
assert_eq!("a, b, c", actual);
|
||||
assert_eq!(DisplayName::Calculated("a, b, c".to_owned()), actual);
|
||||
|
||||
actual = calculate_room_name(5, 0, vec!["a", "b", "c"]);
|
||||
assert_eq!("a, b, c, and 2 others", actual);
|
||||
assert_eq!(DisplayName::Calculated("a, b, c, and 2 others".to_owned()), actual);
|
||||
|
||||
actual = calculate_room_name(0, 0, vec![]);
|
||||
assert_eq!("Empty room", actual);
|
||||
assert_eq!(DisplayName::Empty, actual);
|
||||
|
||||
actual = calculate_room_name(1, 0, vec![]);
|
||||
assert_eq!("Empty room", actual);
|
||||
assert_eq!(DisplayName::Empty, actual);
|
||||
|
||||
actual = calculate_room_name(0, 1, vec![]);
|
||||
assert_eq!("Empty room", actual);
|
||||
assert_eq!(DisplayName::Empty, actual);
|
||||
|
||||
actual = calculate_room_name(1, 0, vec!["a"]);
|
||||
assert_eq!("Empty room (was a)", actual);
|
||||
assert_eq!(DisplayName::EmptyWas("a".to_owned()), actual);
|
||||
|
||||
actual = calculate_room_name(1, 0, vec!["a", "b"]);
|
||||
assert_eq!("Empty room (was a, b)", actual);
|
||||
assert_eq!(DisplayName::EmptyWas("a, b".to_owned()), actual);
|
||||
|
||||
actual = calculate_room_name(1, 0, vec!["a", "b", "c"]);
|
||||
assert_eq!("Empty room (was a, b, c)", actual);
|
||||
assert_eq!(DisplayName::EmptyWas("a, b, c".to_owned()), actual);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ use ruma::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{BaseRoomInfo, RoomMember};
|
||||
use super::{BaseRoomInfo, DisplayName, RoomMember};
|
||||
use crate::{
|
||||
deserialized_responses::UnreadNotificationsCount,
|
||||
store::{Result as StoreResult, StateStore},
|
||||
@@ -276,7 +276,7 @@ impl Room {
|
||||
/// The display name is calculated according to [this algorithm][spec].
|
||||
///
|
||||
/// [spec]: <https://matrix.org/docs/spec/client_server/latest#calculating-the-display-name-for-a-room>
|
||||
pub async fn display_name(&self) -> StoreResult<String> {
|
||||
pub async fn display_name(&self) -> StoreResult<DisplayName> {
|
||||
self.calculate_name().await
|
||||
}
|
||||
|
||||
@@ -338,24 +338,19 @@ impl Room {
|
||||
Ok(members)
|
||||
}
|
||||
|
||||
async fn calculate_name(&self) -> StoreResult<String> {
|
||||
async fn calculate_name(&self) -> StoreResult<DisplayName> {
|
||||
let summary = {
|
||||
let inner = self.inner.read().unwrap();
|
||||
|
||||
if let Some(name) = &inner.base_info.name {
|
||||
let name = name.trim();
|
||||
return Ok(name.to_owned());
|
||||
return Ok(DisplayName::Named(name.to_owned()));
|
||||
} else if let Some(alias) = &inner.base_info.canonical_alias {
|
||||
let alias = alias.alias().trim();
|
||||
return Ok(alias.to_owned());
|
||||
return Ok(DisplayName::Aliased(alias.to_owned()));
|
||||
}
|
||||
inner.summary.clone()
|
||||
};
|
||||
// TODO what should we do here? We have correct counts only if lazy
|
||||
// loading is used.
|
||||
let joined = summary.joined_member_count;
|
||||
let invited = summary.invited_member_count;
|
||||
let heroes_count = summary.heroes.len() as u64;
|
||||
|
||||
let is_own_member = |m: &RoomMember| m.user_id() == &*self.own_user_id;
|
||||
let is_own_user_id = |u: &str| u == self.own_user_id().as_str();
|
||||
@@ -377,11 +372,27 @@ impl Room {
|
||||
members?
|
||||
};
|
||||
|
||||
let (joined, invited) = match self.room_type() {
|
||||
RoomType::Invited => {
|
||||
// when we were invited we don't have a proper summary, we have to do best
|
||||
// guessing
|
||||
(members.len() as u64, 1u64)
|
||||
}
|
||||
RoomType::Joined if summary.joined_member_count == 0 => {
|
||||
// joined but the summary is not completed yet
|
||||
(
|
||||
(members.len() as u64) + 1, // we've taken ourselves out of the count
|
||||
summary.invited_member_count,
|
||||
)
|
||||
}
|
||||
_ => (summary.joined_member_count, summary.invited_member_count),
|
||||
};
|
||||
|
||||
tracing::debug!(
|
||||
room_id = self.room_id().as_str(),
|
||||
own_user = self.own_user_id.as_str(),
|
||||
heroes_count = heroes_count,
|
||||
heroes = ?summary.heroes,
|
||||
joined, invited,
|
||||
heroes = ?members,
|
||||
"Calculating name for a room",
|
||||
);
|
||||
|
||||
@@ -444,14 +455,18 @@ impl Room {
|
||||
.store
|
||||
.get_users_with_display_name(
|
||||
self.room_id(),
|
||||
member_event.content.displayname.as_deref().unwrap_or_else(|| user_id.localpart()),
|
||||
member_event
|
||||
.content()
|
||||
.displayname
|
||||
.as_deref()
|
||||
.unwrap_or_else(|| user_id.localpart()),
|
||||
)
|
||||
.await?
|
||||
.len()
|
||||
> 1;
|
||||
|
||||
Ok(Some(RoomMember {
|
||||
event: member_event.into(),
|
||||
event: Arc::new(member_event),
|
||||
profile: profile.into(),
|
||||
presence: presence.into(),
|
||||
power_levels: power.into(),
|
||||
@@ -735,3 +750,212 @@ impl RoomInfo {
|
||||
self.base_info.create.as_ref().map(|c| &c.room_version)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
||||
use assign::assign;
|
||||
use ruma::{
|
||||
event_id,
|
||||
events::{
|
||||
room::member::{
|
||||
MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent,
|
||||
StrippedRoomMemberEvent,
|
||||
},
|
||||
StateUnsigned,
|
||||
},
|
||||
room_id, user_id, MilliSecondsSinceUnixEpoch, RoomAliasId,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::store::{MemoryStore, StateChanges};
|
||||
|
||||
fn make_room(room_type: RoomType) -> (Arc<MemoryStore>, Room) {
|
||||
let store = Arc::new(MemoryStore::new());
|
||||
let user_id = user_id!("@me:example.org");
|
||||
let room_id = room_id!("!test:localhost");
|
||||
|
||||
(store.clone(), Room::new(user_id, store, room_id, room_type))
|
||||
}
|
||||
|
||||
fn make_stripped_member_event(user_id: &UserId, name: &str) -> StrippedRoomMemberEvent {
|
||||
StrippedRoomMemberEvent {
|
||||
content: assign!(RoomMemberEventContent::new(MembershipState::Join), {
|
||||
displayname: Some(name.to_owned())
|
||||
}),
|
||||
sender: user_id.to_owned(),
|
||||
state_key: user_id.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_member_event(user_id: &UserId, name: &str) -> OriginalSyncRoomMemberEvent {
|
||||
OriginalSyncRoomMemberEvent {
|
||||
content: assign!(RoomMemberEventContent::new(MembershipState::Join), {
|
||||
displayname: Some(name.to_owned())
|
||||
}),
|
||||
sender: user_id.to_owned(),
|
||||
state_key: user_id.to_owned(),
|
||||
event_id: event_id!("$h29iv0s1:example.com").to_owned(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch(208u32.into()),
|
||||
unsigned: StateUnsigned::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_display_name_default() {
|
||||
let _ = env_logger::try_init();
|
||||
let (_, room) = make_room(RoomType::Joined);
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::Empty);
|
||||
|
||||
// has precedence
|
||||
room.inner.write().unwrap().base_info.canonical_alias =
|
||||
Some(RoomAliasId::parse("#test:example.com").unwrap());
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::Aliased("test".to_owned()));
|
||||
|
||||
// has precedence
|
||||
room.inner.write().unwrap().base_info.name = Some("Test Room".to_owned());
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::Named("Test Room".to_owned()));
|
||||
|
||||
let (_, room) = make_room(RoomType::Invited);
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::Empty);
|
||||
|
||||
// has precedence
|
||||
room.inner.write().unwrap().base_info.canonical_alias =
|
||||
Some(RoomAliasId::parse("#test:example.com").unwrap());
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::Aliased("test".to_owned()));
|
||||
|
||||
// has precedence
|
||||
room.inner.write().unwrap().base_info.name = Some("Test Room".to_owned());
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::Named("Test Room".to_owned()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_display_name_dm_invited() {
|
||||
let _ = env_logger::try_init();
|
||||
let (store, room) = make_room(RoomType::Invited);
|
||||
let room_id = room_id!("!test:localhost");
|
||||
let matthew = user_id!("@matthew:example.org");
|
||||
let me = user_id!("@me:example.org");
|
||||
let mut changes = StateChanges::new("".to_owned());
|
||||
let summary = assign!(RumaSummary::new(), {
|
||||
heroes: vec![me.to_string(), matthew.to_string()],
|
||||
});
|
||||
|
||||
changes.add_stripped_member(room_id, make_stripped_member_event(matthew, "Matthew"));
|
||||
changes.add_stripped_member(room_id, make_stripped_member_event(me, "Me"));
|
||||
store.save_changes(&changes).await.unwrap();
|
||||
|
||||
room.inner.write().unwrap().update_summary(&summary);
|
||||
assert_eq!(
|
||||
room.display_name().await.unwrap(),
|
||||
DisplayName::Calculated("Matthew".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_display_name_dm_invited_no_heroes() {
|
||||
let _ = env_logger::try_init();
|
||||
let (store, room) = make_room(RoomType::Invited);
|
||||
let room_id = room_id!("!test:localhost");
|
||||
let matthew = user_id!("@matthew:example.org");
|
||||
let me = user_id!("@me:example.org");
|
||||
let mut changes = StateChanges::new("".to_owned());
|
||||
|
||||
changes.add_stripped_member(room_id, make_stripped_member_event(matthew, "Matthew"));
|
||||
changes.add_stripped_member(room_id, make_stripped_member_event(me, "Me"));
|
||||
store.save_changes(&changes).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
room.display_name().await.unwrap(),
|
||||
DisplayName::Calculated("Matthew".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_display_name_dm_joined() {
|
||||
let _ = env_logger::try_init();
|
||||
let (store, room) = make_room(RoomType::Joined);
|
||||
let room_id = room_id!("!test:localhost");
|
||||
let matthew = user_id!("@matthew:example.org");
|
||||
let me = user_id!("@me:example.org");
|
||||
let mut changes = StateChanges::new("".to_owned());
|
||||
let summary = assign!(RumaSummary::new(), {
|
||||
joined_member_count: Some(2u32.into()),
|
||||
heroes: vec![me.to_string(), matthew.to_string()],
|
||||
});
|
||||
|
||||
changes
|
||||
.members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(matthew.to_owned(), make_member_event(matthew, "Matthew"));
|
||||
changes
|
||||
.members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(me.to_owned(), make_member_event(me, "Me"));
|
||||
store.save_changes(&changes).await.unwrap();
|
||||
|
||||
room.inner.write().unwrap().update_summary(&summary);
|
||||
assert_eq!(
|
||||
room.display_name().await.unwrap(),
|
||||
DisplayName::Calculated("Matthew".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_display_name_dm_joined_no_heroes() {
|
||||
let _ = env_logger::try_init();
|
||||
let (store, room) = make_room(RoomType::Joined);
|
||||
let room_id = room_id!("!test:localhost");
|
||||
let matthew = user_id!("@matthew:example.org");
|
||||
let me = user_id!("@me:example.org");
|
||||
let mut changes = StateChanges::new("".to_owned());
|
||||
|
||||
changes
|
||||
.members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(matthew.to_owned(), make_member_event(matthew, "Matthew"));
|
||||
changes
|
||||
.members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(me.to_owned(), make_member_event(me, "Me"));
|
||||
store.save_changes(&changes).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
room.display_name().await.unwrap(),
|
||||
DisplayName::Calculated("Matthew".to_owned())
|
||||
);
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn test_display_name_dm_alone() {
|
||||
let _ = env_logger::try_init();
|
||||
let (store, room) = make_room(RoomType::Joined);
|
||||
let room_id = room_id!("!test:localhost");
|
||||
let matthew = user_id!("@matthew:example.org");
|
||||
let me = user_id!("@me:example.org");
|
||||
let mut changes = StateChanges::new("".to_owned());
|
||||
let summary = assign!(RumaSummary::new(), {
|
||||
joined_member_count: Some(1u32.into()),
|
||||
heroes: vec![me.to_string(), matthew.to_string()],
|
||||
});
|
||||
|
||||
changes
|
||||
.members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(matthew.to_owned(), make_member_event(matthew, "Matthew"));
|
||||
changes
|
||||
.members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(me.to_owned(), make_member_event(me, "Me"));
|
||||
store.save_changes(&changes).await.unwrap();
|
||||
|
||||
room.inner.write().unwrap().update_summary(&summary);
|
||||
assert_eq!(room.display_name().await.unwrap(), DisplayName::EmptyWas("Matthew".to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use matrix_sdk_common::deserialized_responses::AmbiguityChange;
|
||||
use matrix_sdk_common::deserialized_responses::{AmbiguityChange, MemberEvent};
|
||||
use ruma::{
|
||||
events::room::member::{MembershipState, OriginalSyncRoomMemberEvent},
|
||||
OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||
@@ -158,13 +158,13 @@ impl AmbiguityCache {
|
||||
let old_event = if let Some(m) =
|
||||
changes.members.get(room_id).and_then(|m| m.get(&member_event.state_key))
|
||||
{
|
||||
Some(m.clone())
|
||||
Some(MemberEvent::Original(m.clone()))
|
||||
} else {
|
||||
self.store.get_member_event(room_id, &member_event.state_key).await?
|
||||
};
|
||||
|
||||
let old_display_name = if let Some(event) = old_event {
|
||||
if matches!(event.content.membership, Join | Invite) {
|
||||
if matches!(event.content().membership, Join | Invite) {
|
||||
let display_name = if let Some(d) = changes
|
||||
.profiles
|
||||
.get(room_id)
|
||||
@@ -180,10 +180,10 @@ impl AmbiguityCache {
|
||||
{
|
||||
Some(d)
|
||||
} else {
|
||||
event.content.displayname.clone()
|
||||
event.content().displayname.clone()
|
||||
};
|
||||
|
||||
Some(display_name.unwrap_or_else(|| event.state_key.localpart().to_owned()))
|
||||
Some(display_name.unwrap_or_else(|| event.user_id().localpart().to_owned()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -265,6 +265,20 @@ macro_rules! statestore_integration_tests {
|
||||
serde_json::from_value(event).unwrap()
|
||||
}
|
||||
|
||||
fn stripped_membership_event() -> StrippedRoomMemberEvent {
|
||||
custom_stripped_membership_event(user_id())
|
||||
}
|
||||
|
||||
fn custom_stripped_membership_event(
|
||||
user_id: &UserId,
|
||||
) -> StrippedRoomMemberEvent {
|
||||
StrippedRoomMemberEvent {
|
||||
content: RoomMemberEventContent::new(MembershipState::Join),
|
||||
sender: user_id.to_owned(),
|
||||
state_key: user_id.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn membership_event() -> OriginalSyncRoomMemberEvent {
|
||||
custom_membership_event(user_id(), event_id!("$h29iv0s8:example.com").to_owned())
|
||||
}
|
||||
@@ -281,7 +295,6 @@ macro_rules! statestore_integration_tests {
|
||||
state_key: user_id.to_owned(),
|
||||
unsigned: StateUnsigned::default(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
@@ -344,6 +357,27 @@ macro_rules! statestore_integration_tests {
|
||||
assert!(!members.is_empty(), "We expected to find members for the room")
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_stripped_member_saving() {
|
||||
let store = get_store().await.unwrap();
|
||||
let room_id = room_id!("!test_stripped_member_saving:localhost");
|
||||
let user_id = user_id();
|
||||
|
||||
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_none());
|
||||
let mut changes = StateChanges::default();
|
||||
changes
|
||||
.stripped_members
|
||||
.entry(room_id.to_owned())
|
||||
.or_default()
|
||||
.insert(user_id.to_owned(), stripped_membership_event());
|
||||
|
||||
store.save_changes(&changes).await.unwrap();
|
||||
assert!(store.get_member_event(room_id, user_id).await.unwrap().is_some());
|
||||
|
||||
let members = store.get_user_ids(room_id).await.unwrap();
|
||||
assert!(!members.is_empty(), "We expected to find members for the room")
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_power_level_saving() {
|
||||
let store = get_store().await.unwrap();
|
||||
|
||||
@@ -51,7 +51,10 @@ use ruma::{
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use super::BoxStream;
|
||||
use super::{Result, RoomInfo, StateChanges, StateStore};
|
||||
use crate::media::{MediaRequest, UniqueKey};
|
||||
use crate::{
|
||||
deserialized_responses::MemberEvent,
|
||||
media::{MediaRequest, UniqueKey},
|
||||
};
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use crate::{deserialized_responses::SyncRoomEvent, StoreError};
|
||||
|
||||
@@ -79,6 +82,8 @@ pub struct MemoryStore {
|
||||
DashMap<OwnedRoomId, DashMap<StateEventType, DashMap<String, Raw<AnyStrippedStateEvent>>>>,
|
||||
>,
|
||||
stripped_members: Arc<DashMap<OwnedRoomId, DashMap<OwnedUserId, StrippedRoomMemberEvent>>>,
|
||||
stripped_joined_user_ids: Arc<DashMap<OwnedRoomId, DashSet<OwnedUserId>>>,
|
||||
stripped_invited_user_ids: Arc<DashMap<OwnedRoomId, DashSet<OwnedUserId>>>,
|
||||
presence: Arc<DashMap<OwnedUserId, Raw<PresenceEvent>>>,
|
||||
room_user_receipts:
|
||||
Arc<DashMap<OwnedRoomId, DashMap<String, DashMap<OwnedUserId, (OwnedEventId, Receipt)>>>>,
|
||||
@@ -116,6 +121,8 @@ impl MemoryStore {
|
||||
stripped_room_infos: Default::default(),
|
||||
stripped_room_state: Default::default(),
|
||||
stripped_members: Default::default(),
|
||||
stripped_joined_user_ids: Default::default(),
|
||||
stripped_invited_user_ids: Default::default(),
|
||||
presence: Default::default(),
|
||||
room_user_receipts: Default::default(),
|
||||
room_event_receipts: Default::default(),
|
||||
@@ -247,6 +254,39 @@ impl MemoryStore {
|
||||
|
||||
for (room, events) in &changes.stripped_members {
|
||||
for event in events.values() {
|
||||
match event.content.membership {
|
||||
MembershipState::Join => {
|
||||
self.stripped_joined_user_ids
|
||||
.entry(room.clone())
|
||||
.or_insert_with(DashSet::new)
|
||||
.insert(event.state_key.clone());
|
||||
self.stripped_invited_user_ids
|
||||
.entry(room.clone())
|
||||
.or_insert_with(DashSet::new)
|
||||
.remove(&event.state_key);
|
||||
}
|
||||
MembershipState::Invite => {
|
||||
self.stripped_invited_user_ids
|
||||
.entry(room.clone())
|
||||
.or_insert_with(DashSet::new)
|
||||
.insert(event.state_key.clone());
|
||||
self.stripped_joined_user_ids
|
||||
.entry(room.clone())
|
||||
.or_insert_with(DashSet::new)
|
||||
.remove(&event.state_key);
|
||||
}
|
||||
_ => {
|
||||
self.stripped_joined_user_ids
|
||||
.entry(room.clone())
|
||||
.or_insert_with(DashSet::new)
|
||||
.remove(&event.state_key);
|
||||
self.stripped_invited_user_ids
|
||||
.entry(room.clone())
|
||||
.or_insert_with(DashSet::new)
|
||||
.remove(&event.state_key);
|
||||
}
|
||||
}
|
||||
|
||||
self.stripped_members
|
||||
.entry(room.clone())
|
||||
.or_default()
|
||||
@@ -467,15 +507,28 @@ impl MemoryStore {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> Result<Option<OriginalSyncRoomMemberEvent>> {
|
||||
Ok(self.members.get(room_id).and_then(|m| m.get(state_key).map(|m| m.clone())))
|
||||
) -> Result<Option<MemberEvent>> {
|
||||
if let Some(e) = self.members.get(room_id).and_then(|m| m.get(state_key).map(|m| m.clone()))
|
||||
{
|
||||
Ok(Some(e.into()))
|
||||
} else if let Some(e) =
|
||||
self.stripped_members.get(room_id).and_then(|m| m.get(state_key).map(|m| m.clone()))
|
||||
{
|
||||
Ok(Some(e.into()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_user_ids(&self, room_id: &RoomId) -> Vec<OwnedUserId> {
|
||||
self.members
|
||||
.get(room_id)
|
||||
.map(|u| u.iter().map(|u| u.key().clone()).collect())
|
||||
.unwrap_or_default()
|
||||
if let Some(u) = self.members.get(room_id) {
|
||||
u.iter().map(|u| u.key().clone()).collect()
|
||||
} else {
|
||||
self.stripped_members
|
||||
.get(room_id)
|
||||
.map(|u| u.iter().map(|u| u.key().clone()).collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_invited_user_ids(&self, room_id: &RoomId) -> Vec<OwnedUserId> {
|
||||
@@ -492,6 +545,20 @@ impl MemoryStore {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn get_stripped_invited_user_ids(&self, room_id: &RoomId) -> Vec<OwnedUserId> {
|
||||
self.stripped_invited_user_ids
|
||||
.get(room_id)
|
||||
.map(|u| u.iter().map(|u| u.clone()).collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn get_stripped_joined_user_ids(&self, room_id: &RoomId) -> Vec<OwnedUserId> {
|
||||
self.stripped_joined_user_ids
|
||||
.get(room_id)
|
||||
.map(|u| u.iter().map(|u| u.clone()).collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn get_room_infos(&self) -> Vec<RoomInfo> {
|
||||
self.room_info.iter().map(|r| r.clone()).collect()
|
||||
}
|
||||
@@ -686,7 +753,7 @@ impl StateStore for MemoryStore {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> Result<Option<OriginalSyncRoomMemberEvent>> {
|
||||
) -> Result<Option<MemberEvent>> {
|
||||
self.get_member_event(room_id, state_key).await
|
||||
}
|
||||
|
||||
@@ -695,11 +762,19 @@ impl StateStore for MemoryStore {
|
||||
}
|
||||
|
||||
async fn get_invited_user_ids(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>> {
|
||||
Ok(self.get_invited_user_ids(room_id))
|
||||
let v = self.get_invited_user_ids(room_id);
|
||||
if !v.is_empty() {
|
||||
return Ok(v);
|
||||
}
|
||||
Ok(self.get_stripped_invited_user_ids(room_id))
|
||||
}
|
||||
|
||||
async fn get_joined_user_ids(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>> {
|
||||
Ok(self.get_joined_user_ids(room_id))
|
||||
let v = self.get_joined_user_ids(room_id);
|
||||
if !v.is_empty() {
|
||||
return Ok(v);
|
||||
}
|
||||
Ok(self.get_stripped_joined_user_ids(room_id))
|
||||
}
|
||||
|
||||
async fn get_room_infos(&self) -> Result<Vec<RoomInfo>> {
|
||||
|
||||
@@ -59,6 +59,7 @@ pub type BoxStream<T> = Pin<Box<dyn futures_util::Stream<Item = T> + Send>>;
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use crate::deserialized_responses::{SyncRoomEvent, TimelineSlice};
|
||||
use crate::{
|
||||
deserialized_responses::MemberEvent,
|
||||
media::MediaRequest,
|
||||
rooms::{RoomInfo, RoomType},
|
||||
Room, Session,
|
||||
@@ -179,7 +180,7 @@ pub trait StateStore: AsyncTraitDeps {
|
||||
user_id: &UserId,
|
||||
) -> Result<Option<RoomMemberEventContent>>;
|
||||
|
||||
/// Get a raw `MemberEvent` for the given state key in the given room id.
|
||||
/// Get the `MemberEvent` for the given state key in the given room id.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@@ -190,17 +191,18 @@ pub trait StateStore: AsyncTraitDeps {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> Result<Option<OriginalSyncRoomMemberEvent>>;
|
||||
) -> Result<Option<MemberEvent>>;
|
||||
|
||||
/// Get all the user ids of members for a given room.
|
||||
/// Get all the user ids of members for a given room, for stripped and
|
||||
/// regular rooms alike.
|
||||
async fn get_user_ids(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>>;
|
||||
|
||||
/// Get all the user ids of members that are in the invited state for a
|
||||
/// given room.
|
||||
/// given room, for stripped and regular rooms alike.
|
||||
async fn get_invited_user_ids(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>>;
|
||||
|
||||
/// Get all the user ids of members that are in the joined state for a
|
||||
/// given room.
|
||||
/// given room, for stripped and regular rooms alike.
|
||||
async fn get_joined_user_ids(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>>;
|
||||
|
||||
/// Get all the pure `RoomInfo`s the store knows about.
|
||||
|
||||
@@ -8,9 +8,15 @@ use ruma::{
|
||||
State, ToDevice, UnreadNotificationsCount as RumaUnreadNotificationsCount,
|
||||
},
|
||||
},
|
||||
events::{room::member::OriginalRoomMemberEvent, AnyRoomEvent, AnySyncRoomEvent},
|
||||
events::{
|
||||
room::member::{
|
||||
OriginalRoomMemberEvent, OriginalSyncRoomMemberEvent, RoomMemberEventContent,
|
||||
StrippedRoomMemberEvent,
|
||||
},
|
||||
AnyRoomEvent, AnySyncRoomEvent,
|
||||
},
|
||||
serde::Raw,
|
||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedUserId,
|
||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedRoomId, OwnedUserId, UserId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -290,6 +296,43 @@ impl TimelineSlice {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around both MemberEvent-Types
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum MemberEvent {
|
||||
Stripped(StrippedRoomMemberEvent),
|
||||
Original(OriginalSyncRoomMemberEvent),
|
||||
}
|
||||
|
||||
impl MemberEvent {
|
||||
/// The inner Content of the wrapped Event
|
||||
pub fn content(&self) -> &RoomMemberEventContent {
|
||||
match &*self {
|
||||
MemberEvent::Stripped(e) => &e.content,
|
||||
MemberEvent::Original(e) => &e.content,
|
||||
}
|
||||
}
|
||||
|
||||
/// The user id associated to this member event
|
||||
pub fn user_id(&self) -> &UserId {
|
||||
match &*self {
|
||||
MemberEvent::Stripped(e) => &e.state_key,
|
||||
MemberEvent::Original(e) => &e.state_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StrippedRoomMemberEvent> for MemberEvent {
|
||||
fn from(other: StrippedRoomMemberEvent) -> Self {
|
||||
MemberEvent::Stripped(other)
|
||||
}
|
||||
}
|
||||
impl From<OriginalSyncRoomMemberEvent> for MemberEvent {
|
||||
fn from(other: OriginalSyncRoomMemberEvent) -> Self {
|
||||
MemberEvent::Original(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// A deserialized response for the rooms members API call.
|
||||
///
|
||||
/// [GET /_matrix/client/r0/rooms/{roomId}/members](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-members)
|
||||
|
||||
@@ -19,19 +19,20 @@ use async_trait::async_trait;
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use futures_util::stream;
|
||||
use indexed_db_futures::prelude::*;
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use matrix_sdk_base::{deserialized_responses::SyncRoomEvent, store::BoxStream};
|
||||
use matrix_sdk_base::{
|
||||
deserialized_responses::MemberEvent,
|
||||
media::{MediaRequest, UniqueKey},
|
||||
store::{Result as StoreResult, StateChanges, StateStore, StoreError},
|
||||
RoomInfo,
|
||||
};
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use matrix_sdk_base::{deserialized_responses::SyncRoomEvent, store::BoxStream};
|
||||
use matrix_sdk_store_encryption::{Error as EncryptionError, StoreCipher};
|
||||
use ruma::{
|
||||
events::{
|
||||
presence::PresenceEvent,
|
||||
receipt::Receipt,
|
||||
room::member::{MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent},
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncStateEvent,
|
||||
GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
|
||||
},
|
||||
@@ -120,6 +121,8 @@ mod KEYS {
|
||||
pub const STRIPPED_ROOM_INFOS: &str = "stripped_room_infos";
|
||||
pub const STRIPPED_MEMBERS: &str = "stripped_members";
|
||||
pub const STRIPPED_ROOM_STATE: &str = "stripped_room_state";
|
||||
pub const STRIPPED_JOINED_USER_IDS: &str = "stripped_joined_user_ids";
|
||||
pub const STRIPPED_INVITED_USER_IDS: &str = "stripped_invited_user_ids";
|
||||
|
||||
pub const ROOM_USER_RECEIPTS: &str = "room_user_receipts";
|
||||
pub const ROOM_EVENT_RECEIPTS: &str = "room_event_receipts";
|
||||
@@ -183,6 +186,8 @@ impl IndexeddbStore {
|
||||
db.create_object_store(KEYS::STRIPPED_ROOM_INFOS)?;
|
||||
db.create_object_store(KEYS::STRIPPED_MEMBERS)?;
|
||||
db.create_object_store(KEYS::STRIPPED_ROOM_STATE)?;
|
||||
db.create_object_store(KEYS::STRIPPED_JOINED_USER_IDS)?;
|
||||
db.create_object_store(KEYS::STRIPPED_INVITED_USER_IDS)?;
|
||||
|
||||
db.create_object_store(KEYS::ROOM_USER_RECEIPTS)?;
|
||||
db.create_object_store(KEYS::ROOM_EVENT_RECEIPTS)?;
|
||||
@@ -369,7 +374,6 @@ impl IndexeddbStore {
|
||||
(!changes.room_infos.is_empty(), KEYS::ROOM_INFOS),
|
||||
(!changes.receipts.is_empty(), KEYS::ROOM_EVENT_RECEIPTS),
|
||||
(!changes.stripped_state.is_empty(), KEYS::STRIPPED_ROOM_STATE),
|
||||
(!changes.stripped_members.is_empty(), KEYS::STRIPPED_MEMBERS),
|
||||
(!changes.stripped_room_infos.is_empty(), KEYS::STRIPPED_ROOM_INFOS),
|
||||
]
|
||||
.iter()
|
||||
@@ -385,6 +389,14 @@ impl IndexeddbStore {
|
||||
])
|
||||
}
|
||||
|
||||
if !changes.stripped_members.is_empty() {
|
||||
stores.extend([
|
||||
KEYS::STRIPPED_MEMBERS,
|
||||
KEYS::STRIPPED_INVITED_USER_IDS,
|
||||
KEYS::STRIPPED_JOINED_USER_IDS,
|
||||
])
|
||||
}
|
||||
|
||||
if !changes.receipts.is_empty() {
|
||||
stores.extend([KEYS::ROOM_EVENT_RECEIPTS, KEYS::ROOM_USER_RECEIPTS])
|
||||
}
|
||||
@@ -486,10 +498,38 @@ impl IndexeddbStore {
|
||||
|
||||
if !changes.stripped_members.is_empty() {
|
||||
let store = tx.object_store(KEYS::STRIPPED_MEMBERS)?;
|
||||
let joined = tx.object_store(KEYS::STRIPPED_JOINED_USER_IDS)?;
|
||||
let invited = tx.object_store(KEYS::STRIPPED_INVITED_USER_IDS)?;
|
||||
for (room, events) in &changes.stripped_members {
|
||||
for event in events.values() {
|
||||
let key = self.encode_key(KEYS::STRIPPED_MEMBERS, (room, &event.state_key));
|
||||
store.put_key_val(&key, &self.serialize_event(&event)?)?;
|
||||
let key = (room, &event.state_key);
|
||||
|
||||
match event.content.membership {
|
||||
MembershipState::Join => {
|
||||
joined.put_key_val_owned(
|
||||
&self.encode_key(KEYS::STRIPPED_JOINED_USER_IDS, key),
|
||||
&self.serialize_event(&event.state_key)?,
|
||||
)?;
|
||||
invited
|
||||
.delete(&self.encode_key(KEYS::STRIPPED_INVITED_USER_IDS, key))?;
|
||||
}
|
||||
MembershipState::Invite => {
|
||||
invited.put_key_val_owned(
|
||||
&self.encode_key(KEYS::STRIPPED_INVITED_USER_IDS, key),
|
||||
&self.serialize_event(&event.state_key)?,
|
||||
)?;
|
||||
joined.delete(&self.encode_key(KEYS::STRIPPED_JOINED_USER_IDS, key))?;
|
||||
}
|
||||
_ => {
|
||||
joined.delete(&self.encode_key(KEYS::STRIPPED_JOINED_USER_IDS, key))?;
|
||||
invited
|
||||
.delete(&self.encode_key(KEYS::STRIPPED_INVITED_USER_IDS, key))?;
|
||||
}
|
||||
}
|
||||
store.put_key_val(
|
||||
&self.encode_key(KEYS::STRIPPED_MEMBERS, key),
|
||||
&self.serialize_event(&event)?,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -508,14 +548,14 @@ impl IndexeddbStore {
|
||||
}
|
||||
|
||||
if !changes.members.is_empty() {
|
||||
let profiles_store = tx.object_store(KEYS::PROFILES)?;
|
||||
let joined = tx.object_store(KEYS::JOINED_USER_IDS)?;
|
||||
let invited = tx.object_store(KEYS::INVITED_USER_IDS)?;
|
||||
let members = tx.object_store(KEYS::MEMBERS)?;
|
||||
|
||||
for (room, events) in &changes.members {
|
||||
let profile_changes = changes.profiles.get(room);
|
||||
|
||||
let profiles_store = tx.object_store(KEYS::PROFILES)?;
|
||||
let joined = tx.object_store(KEYS::JOINED_USER_IDS)?;
|
||||
let invited = tx.object_store(KEYS::INVITED_USER_IDS)?;
|
||||
let members = tx.object_store(KEYS::MEMBERS)?;
|
||||
|
||||
for event in events.values() {
|
||||
let key = (room, &event.state_key);
|
||||
|
||||
@@ -862,14 +902,30 @@ impl IndexeddbStore {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> Result<Option<OriginalSyncRoomMemberEvent>> {
|
||||
self.inner
|
||||
) -> Result<Option<MemberEvent>> {
|
||||
if let Some(e) = self
|
||||
.inner
|
||||
.transaction_on_one_with_mode(KEYS::MEMBERS, IdbTransactionMode::Readonly)?
|
||||
.object_store(KEYS::MEMBERS)?
|
||||
.get(&self.encode_key(KEYS::MEMBERS, (room_id, state_key)))?
|
||||
.await?
|
||||
.map(|f| self.deserialize_event(f))
|
||||
.transpose()
|
||||
.transpose()?
|
||||
{
|
||||
Ok(Some(MemberEvent::Original(e)))
|
||||
} else if let Some(e) = self
|
||||
.inner
|
||||
.transaction_on_one_with_mode(KEYS::STRIPPED_MEMBERS, IdbTransactionMode::Readonly)?
|
||||
.object_store(KEYS::STRIPPED_MEMBERS)?
|
||||
.get(&self.encode_key(KEYS::STRIPPED_MEMBERS, (room_id, state_key)))?
|
||||
.await?
|
||||
.map(|f| self.deserialize_event(f))
|
||||
.transpose()?
|
||||
{
|
||||
Ok(Some(MemberEvent::Stripped(e)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_user_ids_stream(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>> {
|
||||
@@ -905,6 +961,51 @@ impl IndexeddbStore {
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
pub async fn get_stripped_user_ids_stream(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>> {
|
||||
Ok([
|
||||
self.get_stripped_invited_user_ids(room_id).await?,
|
||||
self.get_stripped_joined_user_ids(room_id).await?,
|
||||
]
|
||||
.concat())
|
||||
}
|
||||
|
||||
pub async fn get_stripped_invited_user_ids(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> Result<Vec<OwnedUserId>> {
|
||||
let range = self.encode_to_range(KEYS::STRIPPED_INVITED_USER_IDS, room_id)?;
|
||||
let entries = self
|
||||
.inner
|
||||
.transaction_on_one_with_mode(
|
||||
KEYS::STRIPPED_INVITED_USER_IDS,
|
||||
IdbTransactionMode::Readonly,
|
||||
)?
|
||||
.object_store(KEYS::STRIPPED_INVITED_USER_IDS)?
|
||||
.get_all_with_key(&range)?
|
||||
.await?
|
||||
.iter()
|
||||
.filter_map(|f| self.deserialize_event::<OwnedUserId>(f).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
pub async fn get_stripped_joined_user_ids(&self, room_id: &RoomId) -> Result<Vec<OwnedUserId>> {
|
||||
let range = self.encode_to_range(KEYS::STRIPPED_JOINED_USER_IDS, room_id)?;
|
||||
Ok(self
|
||||
.inner
|
||||
.transaction_on_one_with_mode(
|
||||
KEYS::STRIPPED_JOINED_USER_IDS,
|
||||
IdbTransactionMode::Readonly,
|
||||
)?
|
||||
.object_store(KEYS::STRIPPED_JOINED_USER_IDS)?
|
||||
.get_all_with_key(&range)?
|
||||
.await?
|
||||
.iter()
|
||||
.filter_map(|f| self.deserialize_event::<OwnedUserId>(f).ok())
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
pub async fn get_room_infos(&self) -> Result<Vec<RoomInfo>> {
|
||||
let entries: Vec<_> = self
|
||||
.inner
|
||||
@@ -1242,20 +1343,32 @@ impl StateStore for IndexeddbStore {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> StoreResult<Option<OriginalSyncRoomMemberEvent>> {
|
||||
) -> StoreResult<Option<MemberEvent>> {
|
||||
self.get_member_event(room_id, state_key).await.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
async fn get_user_ids(&self, room_id: &RoomId) -> StoreResult<Vec<OwnedUserId>> {
|
||||
self.get_user_ids_stream(room_id).await.map_err(|e| e.into())
|
||||
let ids: Vec<OwnedUserId> = self.get_user_ids_stream(room_id).await?;
|
||||
if !ids.is_empty() {
|
||||
return Ok(ids);
|
||||
}
|
||||
self.get_stripped_user_ids_stream(room_id).await.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
async fn get_invited_user_ids(&self, room_id: &RoomId) -> StoreResult<Vec<OwnedUserId>> {
|
||||
self.get_invited_user_ids(room_id).await.map_err(|e| e.into())
|
||||
let ids: Vec<OwnedUserId> = self.get_invited_user_ids(room_id).await?;
|
||||
if !ids.is_empty() {
|
||||
return Ok(ids);
|
||||
}
|
||||
self.get_stripped_invited_user_ids(room_id).await.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
async fn get_joined_user_ids(&self, room_id: &RoomId) -> StoreResult<Vec<OwnedUserId>> {
|
||||
self.get_joined_user_ids(room_id).await.map_err(|e| e.into())
|
||||
let ids: Vec<OwnedUserId> = self.get_joined_user_ids(room_id).await?;
|
||||
if !ids.is_empty() {
|
||||
return Ok(ids);
|
||||
}
|
||||
self.get_stripped_joined_user_ids(room_id).await.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
async fn get_room_infos(&self) -> StoreResult<Vec<RoomInfo>> {
|
||||
|
||||
@@ -25,19 +25,20 @@ use async_stream::stream;
|
||||
use async_trait::async_trait;
|
||||
use futures_core::stream::Stream;
|
||||
use futures_util::stream::{self, StreamExt, TryStreamExt};
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use matrix_sdk_base::{deserialized_responses::SyncRoomEvent, store::BoxStream};
|
||||
use matrix_sdk_base::{
|
||||
deserialized_responses::MemberEvent,
|
||||
media::{MediaRequest, UniqueKey},
|
||||
store::{Result as StoreResult, StateChanges, StateStore, StoreError},
|
||||
RoomInfo,
|
||||
};
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use matrix_sdk_base::{deserialized_responses::SyncRoomEvent, store::BoxStream};
|
||||
use matrix_sdk_store_encryption::{Error as KeyEncryptionError, StoreCipher};
|
||||
use ruma::{
|
||||
events::{
|
||||
presence::PresenceEvent,
|
||||
receipt::Receipt,
|
||||
room::member::{MembershipState, OriginalSyncRoomMemberEvent, RoomMemberEventContent},
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnySyncStateEvent,
|
||||
GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType,
|
||||
},
|
||||
@@ -134,6 +135,8 @@ const ROOM_STATE: &str = "room-state";
|
||||
const ROOM_USER_RECEIPT: &str = "room-user-receipt";
|
||||
const ROOM: &str = "room";
|
||||
const SESSION: &str = "session";
|
||||
const STRIPPED_INVITED_USER_ID: &str = "stripped-invited-user-id";
|
||||
const STRIPPED_JOINED_USER_ID: &str = "stripped-joined-user-id";
|
||||
const STRIPPED_ROOM_INFO: &str = "stripped-room-info";
|
||||
const STRIPPED_ROOM_MEMBER: &str = "stripped-room-member";
|
||||
const STRIPPED_ROOM_STATE: &str = "stripped-room-state";
|
||||
@@ -159,6 +162,8 @@ pub struct SledStore {
|
||||
room_info: Tree,
|
||||
room_state: Tree,
|
||||
room_account_data: Tree,
|
||||
stripped_joined_user_ids: Tree,
|
||||
stripped_invited_user_ids: Tree,
|
||||
stripped_room_infos: Tree,
|
||||
stripped_room_state: Tree,
|
||||
stripped_members: Tree,
|
||||
@@ -205,6 +210,8 @@ impl SledStore {
|
||||
let presence = db.open_tree(PRESENCE)?;
|
||||
let room_account_data = db.open_tree(ROOM_ACCOUNT_DATA)?;
|
||||
|
||||
let stripped_joined_user_ids = db.open_tree(STRIPPED_JOINED_USER_ID)?;
|
||||
let stripped_invited_user_ids = db.open_tree(STRIPPED_INVITED_USER_ID)?;
|
||||
let stripped_room_infos = db.open_tree(STRIPPED_ROOM_INFO)?;
|
||||
let stripped_members = db.open_tree(STRIPPED_ROOM_MEMBER)?;
|
||||
let stripped_room_state = db.open_tree(STRIPPED_ROOM_STATE)?;
|
||||
@@ -238,6 +245,8 @@ impl SledStore {
|
||||
presence,
|
||||
room_state,
|
||||
room_info,
|
||||
stripped_joined_user_ids,
|
||||
stripped_invited_user_ids,
|
||||
stripped_room_infos,
|
||||
stripped_members,
|
||||
stripped_room_state,
|
||||
@@ -374,9 +383,8 @@ impl SledStore {
|
||||
pub async fn save_changes(&self, changes: &StateChanges) -> Result<()> {
|
||||
let now = Instant::now();
|
||||
|
||||
// room state & memberships
|
||||
let ret: Result<(), TransactionError<SledStoreError>> = (
|
||||
&self.session,
|
||||
&self.account_data,
|
||||
&self.members,
|
||||
&self.profiles,
|
||||
&self.display_names,
|
||||
@@ -385,15 +393,14 @@ impl SledStore {
|
||||
&self.room_info,
|
||||
&self.room_state,
|
||||
&self.room_account_data,
|
||||
&self.presence,
|
||||
&self.stripped_joined_user_ids,
|
||||
&self.stripped_invited_user_ids,
|
||||
&self.stripped_room_infos,
|
||||
&self.stripped_members,
|
||||
&self.stripped_room_state,
|
||||
)
|
||||
.transaction(
|
||||
|(
|
||||
session,
|
||||
account_data,
|
||||
members,
|
||||
profiles,
|
||||
display_names,
|
||||
@@ -402,15 +409,12 @@ impl SledStore {
|
||||
rooms,
|
||||
state,
|
||||
room_account_data,
|
||||
presence,
|
||||
stripped_joined,
|
||||
stripped_invited,
|
||||
striped_rooms,
|
||||
stripped_members,
|
||||
stripped_state,
|
||||
)| {
|
||||
if let Some(s) = &changes.sync_token {
|
||||
session.insert("sync_token".encode(), s.as_str())?;
|
||||
}
|
||||
|
||||
for (room, events) in &changes.members {
|
||||
let profile_changes = changes.profiles.get(room);
|
||||
|
||||
@@ -466,14 +470,6 @@ impl SledStore {
|
||||
}
|
||||
}
|
||||
|
||||
for (event_type, event) in &changes.account_data {
|
||||
account_data.insert(
|
||||
self.encode_key(ACCOUNT_DATA, event_type),
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (room, events) in &changes.room_account_data {
|
||||
for (event_type, event) in events {
|
||||
room_account_data.insert(
|
||||
@@ -504,14 +500,6 @@ impl SledStore {
|
||||
)?;
|
||||
}
|
||||
|
||||
for (sender, event) in &changes.presence {
|
||||
presence.insert(
|
||||
self.encode_key(PRESENCE, sender),
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
for (room_id, info) in &changes.stripped_room_infos {
|
||||
striped_rooms.insert(
|
||||
self.encode_key(STRIPPED_ROOM_INFO, room_id),
|
||||
@@ -522,11 +510,34 @@ impl SledStore {
|
||||
|
||||
for (room, events) in &changes.stripped_members {
|
||||
for event in events.values() {
|
||||
let key = (room, &event.state_key);
|
||||
|
||||
match event.content.membership {
|
||||
MembershipState::Join => {
|
||||
stripped_joined.insert(
|
||||
self.encode_key(STRIPPED_JOINED_USER_ID, &key),
|
||||
event.state_key.as_str(),
|
||||
)?;
|
||||
stripped_invited
|
||||
.remove(self.encode_key(STRIPPED_INVITED_USER_ID, &key))?;
|
||||
}
|
||||
MembershipState::Invite => {
|
||||
stripped_invited.insert(
|
||||
self.encode_key(STRIPPED_INVITED_USER_ID, &key),
|
||||
event.state_key.as_str(),
|
||||
)?;
|
||||
stripped_joined
|
||||
.remove(self.encode_key(STRIPPED_JOINED_USER_ID, &key))?;
|
||||
}
|
||||
_ => {
|
||||
stripped_joined
|
||||
.remove(self.encode_key(STRIPPED_JOINED_USER_ID, &key))?;
|
||||
stripped_invited
|
||||
.remove(self.encode_key(STRIPPED_INVITED_USER_ID, &key))?;
|
||||
}
|
||||
}
|
||||
stripped_members.insert(
|
||||
self.encode_key(
|
||||
STRIPPED_ROOM_MEMBER,
|
||||
(room, event.state_key.to_string()),
|
||||
),
|
||||
self.encode_key(STRIPPED_ROOM_MEMBER, &key),
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
@@ -555,8 +566,8 @@ impl SledStore {
|
||||
ret?;
|
||||
|
||||
let ret: Result<(), TransactionError<SledStoreError>> =
|
||||
(&self.room_user_receipts, &self.room_event_receipts).transaction(
|
||||
|(room_user_receipts, room_event_receipts)| {
|
||||
(&self.room_user_receipts, &self.room_event_receipts, &self.presence).transaction(
|
||||
|(room_user_receipts, room_event_receipts, presence)| {
|
||||
for (room, content) in &changes.receipts {
|
||||
for (event_id, receipts) in &content.0 {
|
||||
for (receipt_type, receipts) in receipts {
|
||||
@@ -594,6 +605,14 @@ impl SledStore {
|
||||
}
|
||||
}
|
||||
|
||||
for (sender, event) in &changes.presence {
|
||||
presence.insert(
|
||||
self.encode_key(PRESENCE, sender),
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
@@ -603,6 +622,26 @@ impl SledStore {
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
self.save_room_timeline(changes).await?;
|
||||
|
||||
// user state
|
||||
let ret: Result<(), TransactionError<SledStoreError>> = (&self.session, &self.account_data)
|
||||
.transaction(|(session, account_data)| {
|
||||
if let Some(s) = &changes.sync_token {
|
||||
session.insert("sync_token".encode(), s.as_str())?;
|
||||
}
|
||||
|
||||
for (event_type, event) in &changes.account_data {
|
||||
account_data.insert(
|
||||
self.encode_key(ACCOUNT_DATA, event_type),
|
||||
self.serialize_event(&event)
|
||||
.map_err(ConflictableTransactionError::Abort)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
ret?;
|
||||
|
||||
self.inner.flush_async().await?;
|
||||
|
||||
tracing::info!("Saved changes in {:?}", now.elapsed());
|
||||
@@ -662,11 +701,25 @@ impl SledStore {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> Result<Option<OriginalSyncRoomMemberEvent>> {
|
||||
) -> Result<Option<MemberEvent>> {
|
||||
let db = self.clone();
|
||||
let key = self.encode_key(MEMBER, (room_id, state_key));
|
||||
spawn_blocking(move || db.members.get(key)?.map(|v| db.deserialize_event(&v)).transpose())
|
||||
.await?
|
||||
let stripped_key = self.encode_key(STRIPPED_ROOM_MEMBER, (room_id, state_key));
|
||||
spawn_blocking(move || {
|
||||
if let Some(e) = db.members.get(key)?.map(|v| db.deserialize_event(&v)).transpose()? {
|
||||
Ok(Some(MemberEvent::Original(e)))
|
||||
} else if let Some(e) = db
|
||||
.stripped_members
|
||||
.get(stripped_key)?
|
||||
.map(|v| db.deserialize_event(&v))
|
||||
.transpose()?
|
||||
{
|
||||
Ok(Some(MemberEvent::Stripped(e)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
pub async fn get_user_ids_stream(
|
||||
@@ -678,6 +731,15 @@ impl SledStore {
|
||||
.await?
|
||||
.chain(self.get_invited_user_ids(room_id).await?))
|
||||
}
|
||||
pub async fn get_stripped_user_ids_stream(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> StoreResult<impl Stream<Item = StoreResult<OwnedUserId>>> {
|
||||
Ok(self
|
||||
.get_stripped_joined_user_ids(room_id)
|
||||
.await?
|
||||
.chain(self.get_stripped_invited_user_ids(room_id).await?))
|
||||
}
|
||||
|
||||
pub async fn get_invited_user_ids(
|
||||
&self,
|
||||
@@ -715,6 +777,42 @@ impl SledStore {
|
||||
.map_err(|e| StoreError::Backend(anyhow!(e)))
|
||||
}
|
||||
|
||||
pub async fn get_stripped_invited_user_ids(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> StoreResult<impl Stream<Item = StoreResult<OwnedUserId>>> {
|
||||
let db = self.clone();
|
||||
let key = self.encode_key(STRIPPED_INVITED_USER_ID, room_id);
|
||||
spawn_blocking(move || {
|
||||
stream::iter(db.stripped_invited_user_ids.scan_prefix(key).map(|u| {
|
||||
UserId::parse(String::from_utf8_lossy(
|
||||
&u.map_err(|e| StoreError::Backend(anyhow!(e)))?.1,
|
||||
))
|
||||
.map_err(StoreError::Identifier)
|
||||
}))
|
||||
})
|
||||
.await
|
||||
.map_err(|e| StoreError::Backend(anyhow!(e)))
|
||||
}
|
||||
|
||||
pub async fn get_stripped_joined_user_ids(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> StoreResult<impl Stream<Item = StoreResult<OwnedUserId>>> {
|
||||
let db = self.clone();
|
||||
let key = self.encode_key(STRIPPED_JOINED_USER_ID, room_id);
|
||||
spawn_blocking(move || {
|
||||
stream::iter(db.stripped_joined_user_ids.scan_prefix(key).map(|u| {
|
||||
UserId::parse(String::from_utf8_lossy(
|
||||
&u.map_err(|e| StoreError::Backend(anyhow!(e)))?.1,
|
||||
))
|
||||
.map_err(StoreError::Identifier)
|
||||
}))
|
||||
})
|
||||
.await
|
||||
.map_err(|e| StoreError::Backend(anyhow!(e)))
|
||||
}
|
||||
|
||||
pub async fn get_room_infos(&self) -> Result<impl Stream<Item = Result<RoomInfo>>> {
|
||||
let db = self.clone();
|
||||
spawn_blocking(move || {
|
||||
@@ -1303,20 +1401,32 @@ impl StateStore for SledStore {
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
state_key: &UserId,
|
||||
) -> StoreResult<Option<OriginalSyncRoomMemberEvent>> {
|
||||
) -> StoreResult<Option<MemberEvent>> {
|
||||
self.get_member_event(room_id, state_key).await.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_user_ids(&self, room_id: &RoomId) -> StoreResult<Vec<OwnedUserId>> {
|
||||
self.get_user_ids_stream(room_id).await?.try_collect().await
|
||||
let v: Vec<OwnedUserId> = self.get_user_ids_stream(room_id).await?.try_collect().await?;
|
||||
if !v.is_empty() {
|
||||
return Ok(v);
|
||||
}
|
||||
self.get_stripped_user_ids_stream(room_id).await?.try_collect().await
|
||||
}
|
||||
|
||||
async fn get_invited_user_ids(&self, room_id: &RoomId) -> StoreResult<Vec<OwnedUserId>> {
|
||||
self.get_invited_user_ids(room_id).await?.try_collect().await
|
||||
let v: Vec<OwnedUserId> = self.get_invited_user_ids(room_id).await?.try_collect().await?;
|
||||
if !v.is_empty() {
|
||||
return Ok(v);
|
||||
}
|
||||
self.get_stripped_invited_user_ids(room_id).await?.try_collect().await
|
||||
}
|
||||
|
||||
async fn get_joined_user_ids(&self, room_id: &RoomId) -> StoreResult<Vec<OwnedUserId>> {
|
||||
self.get_joined_user_ids(room_id).await?.try_collect().await
|
||||
let v: Vec<OwnedUserId> = self.get_joined_user_ids(room_id).await?.try_collect().await?;
|
||||
if !v.is_empty() {
|
||||
return Ok(v);
|
||||
}
|
||||
self.get_stripped_joined_user_ids(room_id).await?.try_collect().await
|
||||
}
|
||||
|
||||
async fn get_room_infos(&self) -> StoreResult<Vec<RoomInfo>> {
|
||||
|
||||
@@ -2249,7 +2249,10 @@ pub(crate) mod tests {
|
||||
|
||||
use std::{collections::BTreeMap, convert::TryInto, io::Cursor, str::FromStr, time::Duration};
|
||||
|
||||
use matrix_sdk_base::media::{MediaFormat, MediaRequest, MediaThumbnailSize};
|
||||
use matrix_sdk_base::{
|
||||
media::{MediaFormat, MediaRequest, MediaThumbnailSize},
|
||||
DisplayName,
|
||||
};
|
||||
#[cfg(feature = "experimental-timeline")]
|
||||
use matrix_sdk_common::deserialized_responses::SyncRoomEvent;
|
||||
use matrix_sdk_test::{test_json, EventBuilder, EventsJson};
|
||||
@@ -3366,7 +3369,10 @@ pub(crate) mod tests {
|
||||
let _response = client.sync_once(sync_settings).await.unwrap();
|
||||
let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap();
|
||||
|
||||
assert_eq!("example2", room.display_name().await.unwrap());
|
||||
assert_eq!(
|
||||
DisplayName::Calculated("example2".to_owned()),
|
||||
room.display_name().await.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
@@ -3444,7 +3450,7 @@ pub(crate) mod tests {
|
||||
assert_eq!(client.rooms().len(), 1);
|
||||
let room = client.get_joined_room(room_id!("!SVkFJHzfwvuaIEawgC:localhost")).unwrap();
|
||||
|
||||
assert_eq!("tutorial".to_owned(), room.display_name().await.unwrap());
|
||||
assert_eq!(DisplayName::Aliased("tutorial".to_owned()), room.display_name().await.unwrap());
|
||||
|
||||
let _m = mock("GET", Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_owned()))
|
||||
.with_status(200)
|
||||
@@ -3458,7 +3464,10 @@ pub(crate) mod tests {
|
||||
assert_eq!(client.rooms().len(), 1);
|
||||
let invited_room = client.get_invited_room(room_id!("!696r7674:example.com")).unwrap();
|
||||
|
||||
assert_eq!("My Room Name".to_owned(), invited_room.display_name().await.unwrap());
|
||||
assert_eq!(
|
||||
DisplayName::Named("My Room Name".to_owned()),
|
||||
invited_room.display_name().await.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
|
||||
Reference in New Issue
Block a user