base: Upcast the bare rooms based on the membership state

master
Damir Jelić 2020-12-19 16:37:35 +01:00
parent 7abf0c8805
commit f9af880176
7 changed files with 252 additions and 136 deletions

View File

@ -41,7 +41,7 @@ use tracing::{debug, warn};
use tracing::{error, info, instrument}; use tracing::{error, info, instrument};
use matrix_sdk_base::{ use matrix_sdk_base::{
responses::SyncResponse, BaseClient, BaseClientConfig, Room, Session, Store, responses::SyncResponse, BaseClient, BaseClientConfig, JoinedRoom, Room, Session, Store,
}; };
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
@ -575,8 +575,8 @@ impl Client {
/// # Arguments /// # Arguments
/// ///
/// `room_id` - The unique id of the room that should be fetched. /// `room_id` - The unique id of the room that should be fetched.
pub fn get_joined_room(&self, room_id: &RoomId) -> Option<Room> { pub fn get_joined_room(&self, room_id: &RoomId) -> Option<JoinedRoom> {
self.base_client.get_room(room_id) self.base_client.get_joined_room(room_id)
} }
///// Get an invited room with the given room id. ///// Get an invited room with the given room id.
@ -1150,7 +1150,7 @@ impl Client {
let _guard = mutex.lock().await; let _guard = mutex.lock().await;
{ {
let room = self.base_client.get_room(room_id).unwrap(); let room = self.get_joined_room(room_id).unwrap();
let members = room.joined_user_ids().await; let members = room.joined_user_ids().await;
// TODO don't collect here. // TODO don't collect here.
let members_iter: Vec<UserId> = members.collect().await; let members_iter: Vec<UserId> = members.collect().await;
@ -1252,17 +1252,17 @@ impl Client {
/// Returns true if a room with the given id was found and the room is /// Returns true if a room with the given id was found and the room is
/// encrypted, false if the room wasn't found or isn't encrypted. /// encrypted, false if the room wasn't found or isn't encrypted.
async fn is_room_encrypted(&self, room_id: &RoomId) -> bool { async fn is_room_encrypted(&self, room_id: &RoomId) -> bool {
match self.base_client.get_room(room_id) { self.base_client
Some(r) => r.is_encrypted(), .get_room(room_id)
None => false, .map(|r| r.is_encrypted())
} .unwrap_or(false)
} }
async fn are_members_synced(&self, room_id: &RoomId) -> bool { async fn are_members_synced(&self, room_id: &RoomId) -> bool {
match self.base_client.get_room(room_id) { self.base_client
Some(r) => r.are_members_synced(), .get_room(room_id)
None => true, .map(|r| r.are_members_synced())
} .unwrap_or(true)
} }
/// Send an attachment to a room. /// Send an attachment to a room.

View File

@ -66,7 +66,10 @@ compile_error!("only one of 'native-tls' or 'rustls-tls' features can be enabled
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub use matrix_sdk_base::crypto::LocalTrust; pub use matrix_sdk_base::crypto::LocalTrust;
pub use matrix_sdk_base::{Error as BaseError, Room, RoomInfo, RoomMember, Session}; pub use matrix_sdk_base::{
Error as BaseError, EventEmitter, InvitedRoom, JoinedRoom, LeftRoom, RoomInfo, RoomMember,
Session,
};
pub use matrix_sdk_common::*; pub use matrix_sdk_common::*;
pub use reqwest; pub use reqwest;

View File

@ -59,12 +59,14 @@ use zeroize::Zeroizing;
use crate::{ use crate::{
error::Result, error::Result,
responses::{ responses::{
AccountData, Ephemeral, InviteState, InvitedRoom, JoinedRoom, LeftRoom, MemberEvent, AccountData, Ephemeral, InviteState, InvitedRoom as InvitedRoomResponse,
Presence, Rooms, State, StrippedMemberEvent, SyncResponse, Timeline, JoinedRoom as JoinedRoomResponse, LeftRoom as LeftRoomResponse, MemberEvent, Presence,
Rooms, State, StrippedMemberEvent, SyncResponse, Timeline,
}, },
rooms::{Room, RoomInfo, RoomType, StrippedRoom, StrippedRoomInfo}, rooms::{Room, RoomInfo, RoomType, StrippedRoom, StrippedRoomInfo},
session::Session, session::Session,
store::{StateChanges, Store}, store::{StateChanges, Store},
EventEmitter, InvitedRoom, JoinedRoom, LeftRoom, RoomState,
}; };
pub type Token = String; pub type Token = String;
@ -163,21 +165,6 @@ pub enum RoomStateType {
Invited, Invited,
} }
/// An enum that represents the state of the given `Room`.
///
/// If the event came from the `join`, `invite` or `leave` rooms map from the server
/// the variant that holds the corresponding room is used. `RoomState` is generic
/// so it can be used to represent a `Room` or an `Arc<RwLock<Room>>`
#[derive(Debug)]
pub enum RoomState<R> {
/// A room from the `join` section of a sync response.
Joined(R),
/// A room from the `leave` section of a sync response.
Left(R),
/// A room from the `invite` section of a sync response.
Invited(R),
}
/// A no IO Client implementation. /// A no IO Client implementation.
/// ///
/// This Client is a state machine that receives responses and events and /// This Client is a state machine that receives responses and events and
@ -199,6 +186,9 @@ pub struct BaseClient {
cryptostore: Arc<Mutex<Option<Box<dyn CryptoStore>>>>, cryptostore: Arc<Mutex<Option<Box<dyn CryptoStore>>>>,
store_path: Arc<Option<PathBuf>>, store_path: Arc<Option<PathBuf>>,
store_passphrase: Arc<Zeroizing<String>>, store_passphrase: Arc<Zeroizing<String>>,
/// Any implementor of EventEmitter will act as the callbacks for various
/// events.
event_emitter: Arc<RwLock<Option<Box<dyn EventEmitter>>>>,
} }
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -303,21 +293,21 @@ impl BaseClient {
}; };
Ok(BaseClient { Ok(BaseClient {
session: Arc::new(RwLock::new(None)), session: RwLock::new(None).into(),
sync_token: Arc::new(RwLock::new(None)), sync_token: RwLock::new(None).into(),
store, store,
rooms: Arc::new(DashMap::new()), rooms: DashMap::new().into(),
stripped_rooms: Arc::new(DashMap::new()), stripped_rooms: DashMap::new().into(),
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
olm: Arc::new(Mutex::new(None)), olm: Mutex::new(None).into(),
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
cryptostore: Arc::new(Mutex::new(config.crypto_store)), cryptostore: Mutex::new(config.crypto_store).into(),
store_path: Arc::new(config.store_path), store_path: config.store_path.into(),
store_passphrase: Arc::new( store_passphrase: config
config .passphrase
.passphrase .unwrap_or_else(|| Zeroizing::new("DEFAULT_PASSPHRASE".to_owned()))
.unwrap_or_else(|| Zeroizing::new("DEFAULT_PASSPHRASE".to_owned())), .into(),
), event_emitter: RwLock::new(None).into(),
}) })
} }
@ -425,6 +415,13 @@ impl BaseClient {
self.sync_token.read().await.clone() self.sync_token.read().await.clone()
} }
/// Add `EventEmitter` to `Client`.
///
/// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
pub async fn add_event_emitter(&self, emitter: Box<dyn EventEmitter>) {
*self.event_emitter.write().await = Some(emitter);
}
async fn get_or_create_stripped_room(&self, room_id: &RoomId) -> StrippedRoom { async fn get_or_create_stripped_room(&self, room_id: &RoomId) -> StrippedRoom {
let session = self.session.read().await; let session = self.session.read().await;
let user_id = &session let user_id = &session
@ -738,7 +735,13 @@ impl BaseClient {
rooms.join.insert( rooms.join.insert(
room_id, room_id,
JoinedRoom::new(timeline, state, account_data, ephemeral, notification_count), JoinedRoomResponse::new(
timeline,
state,
account_data,
ephemeral,
notification_count,
),
); );
changes.add_room(room_info); changes.add_room(room_info);
@ -769,9 +772,10 @@ impl BaseClient {
.handle_room_account_data(&room_id, &new_info.account_data.events, &mut changes) .handle_room_account_data(&room_id, &new_info.account_data.events, &mut changes)
.await; .await;
rooms rooms.leave.insert(
.leave room_id,
.insert(room_id, LeftRoom::new(timeline, state, account_data)); LeftRoomResponse::new(timeline, state, account_data),
);
} }
for (room_id, new_info) in response.rooms.invite { for (room_id, new_info) in response.rooms.invite {
@ -791,7 +795,7 @@ impl BaseClient {
changes.stripped_members.insert(room_id.clone(), members); changes.stripped_members.insert(room_id.clone(), members);
changes.stripped_state.insert(room_id.clone(), state_events); changes.stripped_state.insert(room_id.clone(), state_events);
let room = InvitedRoom { let room = InvitedRoomResponse {
invite_state: state, invite_state: state,
}; };
@ -851,7 +855,7 @@ impl BaseClient {
async fn apply_changes(&self, changes: &StateChanges) { async fn apply_changes(&self, changes: &StateChanges) {
// TODO emit room changes here // TODO emit room changes here
for (room_id, room_info) in &changes.room_infos { for (room_id, room_info) in &changes.room_infos {
if let Some(room) = self.get_room(&room_id) { if let Some(room) = self.get_bare_room(&room_id) {
room.update_summary(room_info.clone()) room.update_summary(room_info.clone())
} }
} }
@ -862,7 +866,7 @@ impl BaseClient {
room_id: &RoomId, room_id: &RoomId,
response: &api::membership::get_member_events::Response, response: &api::membership::get_member_events::Response,
) -> Result<()> { ) -> Result<()> {
if let Some(room) = self.get_room(room_id) { if let Some(room) = self.get_bare_room(room_id) {
let mut room_info = room.clone_info(); let mut room_info = room.clone_info();
room_info.mark_members_synced(); room_info.mark_members_synced();
@ -1027,12 +1031,28 @@ impl BaseClient {
} }
} }
pub fn get_room(&self, room_id: &RoomId) -> Option<Room> { fn get_bare_room(&self, room_id: &RoomId) -> Option<Room> {
#[allow(clippy::map_clone)] #[allow(clippy::map_clone)]
self.rooms.get(room_id).map(|r| r.clone()) self.rooms.get(room_id).map(|r| r.clone())
} }
pub fn get_stripped_room(&self, room_id: &RoomId) -> Option<StrippedRoom> { pub fn get_joined_room(&self, room_id: &RoomId) -> Option<JoinedRoom> {
self.get_room(room_id).map(|r| r.joined()).flatten()
}
pub fn get_room(&self, room_id: &RoomId) -> Option<RoomState> {
self.get_bare_room(room_id)
.map(|r| match r.room_type() {
RoomType::Joined => Some(RoomState::Joined(JoinedRoom { inner: r })),
RoomType::Left => Some(RoomState::Left(LeftRoom { inner: r })),
RoomType::Invited => self
.get_stripped_room(room_id)
.map(|r| RoomState::Invited(InvitedRoom { inner: r })),
})
.flatten()
}
fn get_stripped_room(&self, room_id: &RoomId) -> Option<StrippedRoom> {
#[allow(clippy::map_clone)] #[allow(clippy::map_clone)]
self.stripped_rooms.get(room_id).map(|r| r.clone()) self.stripped_rooms.get(room_id).map(|r| r.clone())
} }

View File

@ -40,13 +40,11 @@ use crate::{
typing::TypingEventContent, typing::TypingEventContent,
BasicEvent, StrippedStateEvent, SyncEphemeralRoomEvent, SyncMessageEvent, SyncStateEvent, BasicEvent, StrippedStateEvent, SyncEphemeralRoomEvent, SyncMessageEvent, SyncStateEvent,
}, },
Room, RoomState, rooms::RoomState,
Room,
}; };
use matrix_sdk_common_macros::async_trait; use matrix_sdk_common_macros::async_trait;
/// Type alias for `RoomState` enum when passed to `EventEmitter` methods.
pub type SyncRoom = RoomState<Arc<RwLock<Room>>>;
/// This represents the various "unrecognized" events. /// This represents the various "unrecognized" events.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum CustomEvent<'c> { pub enum CustomEvent<'c> {
@ -76,7 +74,7 @@ pub enum CustomEvent<'c> {
/// # room::message::{MessageEventContent, TextMessageEventContent}, /// # room::message::{MessageEventContent, TextMessageEventContent},
/// # SyncMessageEvent /// # SyncMessageEvent
/// # }, /// # },
/// # EventEmitter, SyncRoom /// # EventEmitter, RoomState
/// # }; /// # };
/// # use matrix_sdk_common::locks::RwLock; /// # use matrix_sdk_common::locks::RwLock;
/// # use matrix_sdk_common_macros::async_trait; /// # use matrix_sdk_common_macros::async_trait;
@ -85,8 +83,8 @@ pub enum CustomEvent<'c> {
/// ///
/// #[async_trait] /// #[async_trait]
/// impl EventEmitter for EventCallback { /// impl EventEmitter for EventCallback {
/// async fn on_room_message(&self, room: SyncRoom, event: &SyncMessageEvent<MessageEventContent>) { /// async fn on_room_message(&self, room: RoomState, event: &SyncMessageEvent<MessageEventContent>) {
/// if let SyncRoom::Joined(room) = room { /// if let RoomState::Joined(room) = room {
/// if let SyncMessageEvent { /// if let SyncMessageEvent {
/// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }), /// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
/// sender, /// sender,
@ -112,135 +110,140 @@ pub enum CustomEvent<'c> {
pub trait EventEmitter: Send + Sync { pub trait EventEmitter: Send + Sync {
// ROOM EVENTS from `IncomingTimeline` // ROOM EVENTS from `IncomingTimeline`
/// Fires when `Client` receives a `RoomEvent::RoomMember` event. /// Fires when `Client` receives a `RoomEvent::RoomMember` event.
async fn on_room_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) {} async fn on_room_member(&self, _: RoomState, _: &SyncStateEvent<MemberEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::RoomName` event. /// Fires when `Client` receives a `RoomEvent::RoomName` event.
async fn on_room_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) {} async fn on_room_name(&self, _: RoomState, _: &SyncStateEvent<NameEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::RoomCanonicalAlias` event. /// Fires when `Client` receives a `RoomEvent::RoomCanonicalAlias` event.
async fn on_room_canonical_alias( async fn on_room_canonical_alias(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<CanonicalAliasEventContent>, _: &SyncStateEvent<CanonicalAliasEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `RoomEvent::RoomAliases` event. /// Fires when `Client` receives a `RoomEvent::RoomAliases` event.
async fn on_room_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) {} async fn on_room_aliases(&self, _: RoomState, _: &SyncStateEvent<AliasesEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::RoomAvatar` event. /// Fires when `Client` receives a `RoomEvent::RoomAvatar` event.
async fn on_room_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) {} async fn on_room_avatar(&self, _: RoomState, _: &SyncStateEvent<AvatarEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::RoomMessage` event. /// Fires when `Client` receives a `RoomEvent::RoomMessage` event.
async fn on_room_message(&self, _: SyncRoom, _: &SyncMessageEvent<MsgEventContent>) {} async fn on_room_message(&self, _: RoomState, _: &SyncMessageEvent<MsgEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::RoomMessageFeedback` event. /// Fires when `Client` receives a `RoomEvent::RoomMessageFeedback` event.
async fn on_room_message_feedback( async fn on_room_message_feedback(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncMessageEvent<FeedbackEventContent>, _: &SyncMessageEvent<FeedbackEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `RoomEvent::RoomRedaction` event. /// Fires when `Client` receives a `RoomEvent::RoomRedaction` event.
async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) {} async fn on_room_redaction(&self, _: RoomState, _: &SyncRedactionEvent) {}
/// Fires when `Client` receives a `RoomEvent::RoomPowerLevels` event. /// Fires when `Client` receives a `RoomEvent::RoomPowerLevels` event.
async fn on_room_power_levels(&self, _: SyncRoom, _: &SyncStateEvent<PowerLevelsEventContent>) { async fn on_room_power_levels(
&self,
_: RoomState,
_: &SyncStateEvent<PowerLevelsEventContent>,
) {
} }
/// Fires when `Client` receives a `RoomEvent::Tombstone` event. /// Fires when `Client` receives a `RoomEvent::Tombstone` event.
async fn on_room_join_rules(&self, _: SyncRoom, _: &SyncStateEvent<JoinRulesEventContent>) {} async fn on_room_join_rules(&self, _: RoomState, _: &SyncStateEvent<JoinRulesEventContent>) {}
/// Fires when `Client` receives a `RoomEvent::Tombstone` event. /// Fires when `Client` receives a `RoomEvent::Tombstone` event.
async fn on_room_tombstone(&self, _: SyncRoom, _: &SyncStateEvent<TombstoneEventContent>) {} async fn on_room_tombstone(&self, _: RoomState, _: &SyncStateEvent<TombstoneEventContent>) {}
// `RoomEvent`s from `IncomingState` // `RoomEvent`s from `IncomingState`
/// Fires when `Client` receives a `StateEvent::RoomMember` event. /// Fires when `Client` receives a `StateEvent::RoomMember` event.
async fn on_state_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) {} async fn on_state_member(&self, _: RoomState, _: &SyncStateEvent<MemberEventContent>) {}
/// Fires when `Client` receives a `StateEvent::RoomName` event. /// Fires when `Client` receives a `StateEvent::RoomName` event.
async fn on_state_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) {} async fn on_state_name(&self, _: RoomState, _: &SyncStateEvent<NameEventContent>) {}
/// Fires when `Client` receives a `StateEvent::RoomCanonicalAlias` event. /// Fires when `Client` receives a `StateEvent::RoomCanonicalAlias` event.
async fn on_state_canonical_alias( async fn on_state_canonical_alias(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<CanonicalAliasEventContent>, _: &SyncStateEvent<CanonicalAliasEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `StateEvent::RoomAliases` event. /// Fires when `Client` receives a `StateEvent::RoomAliases` event.
async fn on_state_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) {} async fn on_state_aliases(&self, _: RoomState, _: &SyncStateEvent<AliasesEventContent>) {}
/// Fires when `Client` receives a `StateEvent::RoomAvatar` event. /// Fires when `Client` receives a `StateEvent::RoomAvatar` event.
async fn on_state_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) {} async fn on_state_avatar(&self, _: RoomState, _: &SyncStateEvent<AvatarEventContent>) {}
/// Fires when `Client` receives a `StateEvent::RoomPowerLevels` event. /// Fires when `Client` receives a `StateEvent::RoomPowerLevels` event.
async fn on_state_power_levels( async fn on_state_power_levels(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<PowerLevelsEventContent>, _: &SyncStateEvent<PowerLevelsEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `StateEvent::RoomJoinRules` event. /// Fires when `Client` receives a `StateEvent::RoomJoinRules` event.
async fn on_state_join_rules(&self, _: SyncRoom, _: &SyncStateEvent<JoinRulesEventContent>) {} async fn on_state_join_rules(&self, _: RoomState, _: &SyncStateEvent<JoinRulesEventContent>) {}
// `AnyStrippedStateEvent`s // `AnyStrippedStateEvent`s
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomMember` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomMember` event.
async fn on_stripped_state_member( async fn on_stripped_state_member(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<MemberEventContent>, _: &StrippedStateEvent<MemberEventContent>,
_: Option<MemberEventContent>, _: Option<MemberEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomName` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomName` event.
async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedStateEvent<NameEventContent>) {} async fn on_stripped_state_name(&self, _: RoomState, _: &StrippedStateEvent<NameEventContent>) {
}
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event.
async fn on_stripped_state_canonical_alias( async fn on_stripped_state_canonical_alias(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<CanonicalAliasEventContent>, _: &StrippedStateEvent<CanonicalAliasEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event.
async fn on_stripped_state_aliases( async fn on_stripped_state_aliases(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<AliasesEventContent>, _: &StrippedStateEvent<AliasesEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event.
async fn on_stripped_state_avatar( async fn on_stripped_state_avatar(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<AvatarEventContent>, _: &StrippedStateEvent<AvatarEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event.
async fn on_stripped_state_power_levels( async fn on_stripped_state_power_levels(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<PowerLevelsEventContent>, _: &StrippedStateEvent<PowerLevelsEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event.
async fn on_stripped_state_join_rules( async fn on_stripped_state_join_rules(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<JoinRulesEventContent>, _: &StrippedStateEvent<JoinRulesEventContent>,
) { ) {
} }
// `NonRoomEvent` (this is a type alias from ruma_events) // `NonRoomEvent` (this is a type alias from ruma_events)
/// Fires when `Client` receives a `NonRoomEvent::RoomPresence` event. /// Fires when `Client` receives a `NonRoomEvent::RoomPresence` event.
async fn on_non_room_presence(&self, _: SyncRoom, _: &PresenceEvent) {} async fn on_non_room_presence(&self, _: RoomState, _: &PresenceEvent) {}
/// Fires when `Client` receives a `NonRoomEvent::RoomName` event. /// Fires when `Client` receives a `NonRoomEvent::RoomName` event.
async fn on_non_room_ignored_users( async fn on_non_room_ignored_users(
&self, &self,
_: SyncRoom, _: RoomState,
_: &BasicEvent<IgnoredUserListEventContent>, _: &BasicEvent<IgnoredUserListEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `NonRoomEvent::RoomCanonicalAlias` event. /// Fires when `Client` receives a `NonRoomEvent::RoomCanonicalAlias` event.
async fn on_non_room_push_rules(&self, _: SyncRoom, _: &BasicEvent<PushRulesEventContent>) {} async fn on_non_room_push_rules(&self, _: RoomState, _: &BasicEvent<PushRulesEventContent>) {}
/// Fires when `Client` receives a `NonRoomEvent::RoomAliases` event. /// Fires when `Client` receives a `NonRoomEvent::RoomAliases` event.
async fn on_non_room_fully_read( async fn on_non_room_fully_read(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncEphemeralRoomEvent<FullyReadEventContent>, _: &SyncEphemeralRoomEvent<FullyReadEventContent>,
) { ) {
} }
/// Fires when `Client` receives a `NonRoomEvent::Typing` event. /// Fires when `Client` receives a `NonRoomEvent::Typing` event.
async fn on_non_room_typing( async fn on_non_room_typing(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncEphemeralRoomEvent<TypingEventContent>, _: &SyncEphemeralRoomEvent<TypingEventContent>,
) { ) {
} }
@ -249,27 +252,27 @@ pub trait EventEmitter: Send + Sync {
/// This is always a read receipt. /// This is always a read receipt.
async fn on_non_room_receipt( async fn on_non_room_receipt(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncEphemeralRoomEvent<ReceiptEventContent>, _: &SyncEphemeralRoomEvent<ReceiptEventContent>,
) { ) {
} }
// `PresenceEvent` is a struct so there is only the one method // `PresenceEvent` is a struct so there is only the one method
/// Fires when `Client` receives a `NonRoomEvent::RoomAliases` event. /// Fires when `Client` receives a `NonRoomEvent::RoomAliases` event.
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {} async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {}
/// Fires when `Client` receives a `Event::Custom` event or if deserialization fails /// Fires when `Client` receives a `Event::Custom` event or if deserialization fails
/// because the event was unknown to ruma. /// because the event was unknown to ruma.
/// ///
/// The only guarantee this method can give about the event is that it is valid JSON. /// The only guarantee this method can give about the event is that it is valid JSON.
async fn on_unrecognized_event(&self, _: SyncRoom, _: &RawJsonValue) {} async fn on_unrecognized_event(&self, _: RoomState, _: &RawJsonValue) {}
/// Fires when `Client` receives a `Event::Custom` event or if deserialization fails /// Fires when `Client` receives a `Event::Custom` event or if deserialization fails
/// because the event was unknown to ruma. /// because the event was unknown to ruma.
/// ///
/// The only guarantee this method can give about the event is that it is in the /// The only guarantee this method can give about the event is that it is in the
/// shape of a valid matrix event. /// shape of a valid matrix event.
async fn on_custom_event(&self, _: SyncRoom, _: &CustomEvent<'_>) {} async fn on_custom_event(&self, _: RoomState, _: &CustomEvent<'_>) {}
} }
#[cfg(test)] #[cfg(test)]
@ -288,78 +291,78 @@ mod test {
#[async_trait] #[async_trait]
impl EventEmitter for EvEmitterTest { impl EventEmitter for EvEmitterTest {
async fn on_room_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) { async fn on_room_member(&self, _: RoomState, _: &SyncStateEvent<MemberEventContent>) {
self.0.lock().await.push("member".to_string()) self.0.lock().await.push("member".to_string())
} }
async fn on_room_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) { async fn on_room_name(&self, _: RoomState, _: &SyncStateEvent<NameEventContent>) {
self.0.lock().await.push("name".to_string()) self.0.lock().await.push("name".to_string())
} }
async fn on_room_canonical_alias( async fn on_room_canonical_alias(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<CanonicalAliasEventContent>, _: &SyncStateEvent<CanonicalAliasEventContent>,
) { ) {
self.0.lock().await.push("canonical".to_string()) self.0.lock().await.push("canonical".to_string())
} }
async fn on_room_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) { async fn on_room_aliases(&self, _: RoomState, _: &SyncStateEvent<AliasesEventContent>) {
self.0.lock().await.push("aliases".to_string()) self.0.lock().await.push("aliases".to_string())
} }
async fn on_room_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) { async fn on_room_avatar(&self, _: RoomState, _: &SyncStateEvent<AvatarEventContent>) {
self.0.lock().await.push("avatar".to_string()) self.0.lock().await.push("avatar".to_string())
} }
async fn on_room_message(&self, _: SyncRoom, _: &SyncMessageEvent<MsgEventContent>) { async fn on_room_message(&self, _: RoomState, _: &SyncMessageEvent<MsgEventContent>) {
self.0.lock().await.push("message".to_string()) self.0.lock().await.push("message".to_string())
} }
async fn on_room_message_feedback( async fn on_room_message_feedback(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncMessageEvent<FeedbackEventContent>, _: &SyncMessageEvent<FeedbackEventContent>,
) { ) {
self.0.lock().await.push("feedback".to_string()) self.0.lock().await.push("feedback".to_string())
} }
async fn on_room_redaction(&self, _: SyncRoom, _: &SyncRedactionEvent) { async fn on_room_redaction(&self, _: RoomState, _: &SyncRedactionEvent) {
self.0.lock().await.push("redaction".to_string()) self.0.lock().await.push("redaction".to_string())
} }
async fn on_room_power_levels( async fn on_room_power_levels(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<PowerLevelsEventContent>, _: &SyncStateEvent<PowerLevelsEventContent>,
) { ) {
self.0.lock().await.push("power".to_string()) self.0.lock().await.push("power".to_string())
} }
async fn on_room_tombstone(&self, _: SyncRoom, _: &SyncStateEvent<TombstoneEventContent>) { async fn on_room_tombstone(&self, _: RoomState, _: &SyncStateEvent<TombstoneEventContent>) {
self.0.lock().await.push("tombstone".to_string()) self.0.lock().await.push("tombstone".to_string())
} }
async fn on_state_member(&self, _: SyncRoom, _: &SyncStateEvent<MemberEventContent>) { async fn on_state_member(&self, _: RoomState, _: &SyncStateEvent<MemberEventContent>) {
self.0.lock().await.push("state member".to_string()) self.0.lock().await.push("state member".to_string())
} }
async fn on_state_name(&self, _: SyncRoom, _: &SyncStateEvent<NameEventContent>) { async fn on_state_name(&self, _: RoomState, _: &SyncStateEvent<NameEventContent>) {
self.0.lock().await.push("state name".to_string()) self.0.lock().await.push("state name".to_string())
} }
async fn on_state_canonical_alias( async fn on_state_canonical_alias(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<CanonicalAliasEventContent>, _: &SyncStateEvent<CanonicalAliasEventContent>,
) { ) {
self.0.lock().await.push("state canonical".to_string()) self.0.lock().await.push("state canonical".to_string())
} }
async fn on_state_aliases(&self, _: SyncRoom, _: &SyncStateEvent<AliasesEventContent>) { async fn on_state_aliases(&self, _: RoomState, _: &SyncStateEvent<AliasesEventContent>) {
self.0.lock().await.push("state aliases".to_string()) self.0.lock().await.push("state aliases".to_string())
} }
async fn on_state_avatar(&self, _: SyncRoom, _: &SyncStateEvent<AvatarEventContent>) { async fn on_state_avatar(&self, _: RoomState, _: &SyncStateEvent<AvatarEventContent>) {
self.0.lock().await.push("state avatar".to_string()) self.0.lock().await.push("state avatar".to_string())
} }
async fn on_state_power_levels( async fn on_state_power_levels(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<PowerLevelsEventContent>, _: &SyncStateEvent<PowerLevelsEventContent>,
) { ) {
self.0.lock().await.push("state power".to_string()) self.0.lock().await.push("state power".to_string())
} }
async fn on_state_join_rules( async fn on_state_join_rules(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncStateEvent<JoinRulesEventContent>, _: &SyncStateEvent<JoinRulesEventContent>,
) { ) {
self.0.lock().await.push("state rules".to_string()) self.0.lock().await.push("state rules".to_string())
@ -369,7 +372,7 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomMember` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomMember` event.
async fn on_stripped_state_member( async fn on_stripped_state_member(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<MemberEventContent>, _: &StrippedStateEvent<MemberEventContent>,
_: Option<MemberEventContent>, _: Option<MemberEventContent>,
) { ) {
@ -381,7 +384,7 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomName` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomName` event.
async fn on_stripped_state_name( async fn on_stripped_state_name(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<NameEventContent>, _: &StrippedStateEvent<NameEventContent>,
) { ) {
self.0.lock().await.push("stripped state name".to_string()) self.0.lock().await.push("stripped state name".to_string())
@ -389,7 +392,7 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event.
async fn on_stripped_state_canonical_alias( async fn on_stripped_state_canonical_alias(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<CanonicalAliasEventContent>, _: &StrippedStateEvent<CanonicalAliasEventContent>,
) { ) {
self.0 self.0
@ -400,7 +403,7 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event.
async fn on_stripped_state_aliases( async fn on_stripped_state_aliases(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<AliasesEventContent>, _: &StrippedStateEvent<AliasesEventContent>,
) { ) {
self.0 self.0
@ -411,7 +414,7 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event.
async fn on_stripped_state_avatar( async fn on_stripped_state_avatar(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<AvatarEventContent>, _: &StrippedStateEvent<AvatarEventContent>,
) { ) {
self.0 self.0
@ -422,7 +425,7 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event.
async fn on_stripped_state_power_levels( async fn on_stripped_state_power_levels(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<PowerLevelsEventContent>, _: &StrippedStateEvent<PowerLevelsEventContent>,
) { ) {
self.0.lock().await.push("stripped state power".to_string()) self.0.lock().await.push("stripped state power".to_string())
@ -430,53 +433,57 @@ mod test {
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event. /// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event.
async fn on_stripped_state_join_rules( async fn on_stripped_state_join_rules(
&self, &self,
_: SyncRoom, _: RoomState,
_: &StrippedStateEvent<JoinRulesEventContent>, _: &StrippedStateEvent<JoinRulesEventContent>,
) { ) {
self.0.lock().await.push("stripped state rules".to_string()) self.0.lock().await.push("stripped state rules".to_string())
} }
async fn on_non_room_presence(&self, _: SyncRoom, _: &PresenceEvent) { async fn on_non_room_presence(&self, _: RoomState, _: &PresenceEvent) {
self.0.lock().await.push("presence".to_string()) self.0.lock().await.push("presence".to_string())
} }
async fn on_non_room_ignored_users( async fn on_non_room_ignored_users(
&self, &self,
_: SyncRoom, _: RoomState,
_: &BasicEvent<IgnoredUserListEventContent>, _: &BasicEvent<IgnoredUserListEventContent>,
) { ) {
self.0.lock().await.push("account ignore".to_string()) self.0.lock().await.push("account ignore".to_string())
} }
async fn on_non_room_push_rules(&self, _: SyncRoom, _: &BasicEvent<PushRulesEventContent>) { async fn on_non_room_push_rules(
&self,
_: RoomState,
_: &BasicEvent<PushRulesEventContent>,
) {
self.0.lock().await.push("account push rules".to_string()) self.0.lock().await.push("account push rules".to_string())
} }
async fn on_non_room_fully_read( async fn on_non_room_fully_read(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncEphemeralRoomEvent<FullyReadEventContent>, _: &SyncEphemeralRoomEvent<FullyReadEventContent>,
) { ) {
self.0.lock().await.push("account read".to_string()) self.0.lock().await.push("account read".to_string())
} }
async fn on_non_room_typing( async fn on_non_room_typing(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncEphemeralRoomEvent<TypingEventContent>, _: &SyncEphemeralRoomEvent<TypingEventContent>,
) { ) {
self.0.lock().await.push("typing event".to_string()) self.0.lock().await.push("typing event".to_string())
} }
async fn on_non_room_receipt( async fn on_non_room_receipt(
&self, &self,
_: SyncRoom, _: RoomState,
_: &SyncEphemeralRoomEvent<ReceiptEventContent>, _: &SyncEphemeralRoomEvent<ReceiptEventContent>,
) { ) {
self.0.lock().await.push("receipt event".to_string()) self.0.lock().await.push("receipt event".to_string())
} }
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) { async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {
self.0.lock().await.push("presence event".to_string()) self.0.lock().await.push("presence event".to_string())
} }
async fn on_unrecognized_event(&self, _: SyncRoom, _: &RawJsonValue) { async fn on_unrecognized_event(&self, _: RoomState, _: &RawJsonValue) {
self.0.lock().await.push("unrecognized event".to_string()) self.0.lock().await.push("unrecognized event".to_string())
} }
async fn on_custom_event(&self, _: SyncRoom, _: &CustomEvent<'_>) { async fn on_custom_event(&self, _: RoomState, _: &CustomEvent<'_>) {
self.0.lock().await.push("custom event".to_string()) self.0.lock().await.push("custom event".to_string())
} }
} }
@ -503,8 +510,8 @@ mod test {
let client = get_client().await; let client = get_client().await;
client.add_event_emitter(emitter).await; client.add_event_emitter(emitter).await;
let mut response = sync_response(SyncResponseFile::Default); let response = sync_response(SyncResponseFile::Default);
client.receive_sync_response(&mut response).await.unwrap(); client.receive_sync_response(response).await.unwrap();
let v = test_vec.lock().await; let v = test_vec.lock().await;
assert_eq!( assert_eq!(
@ -535,8 +542,8 @@ mod test {
let client = get_client().await; let client = get_client().await;
client.add_event_emitter(emitter).await; client.add_event_emitter(emitter).await;
let mut response = sync_response(SyncResponseFile::Invite); let response = sync_response(SyncResponseFile::Invite);
client.receive_sync_response(&mut response).await.unwrap(); client.receive_sync_response(response).await.unwrap();
let v = test_vec.lock().await; let v = test_vec.lock().await;
assert_eq!( assert_eq!(
@ -554,8 +561,8 @@ mod test {
let client = get_client().await; let client = get_client().await;
client.add_event_emitter(emitter).await; client.add_event_emitter(emitter).await;
let mut response = sync_response(SyncResponseFile::Leave); let response = sync_response(SyncResponseFile::Leave);
client.receive_sync_response(&mut response).await.unwrap(); client.receive_sync_response(response).await.unwrap();
let v = test_vec.lock().await; let v = test_vec.lock().await;
assert_eq!( assert_eq!(
@ -582,8 +589,8 @@ mod test {
let client = get_client().await; let client = get_client().await;
client.add_event_emitter(emitter).await; client.add_event_emitter(emitter).await;
let mut response = sync_response(SyncResponseFile::All); let response = sync_response(SyncResponseFile::All);
client.receive_sync_response(&mut response).await.unwrap(); client.receive_sync_response(response).await.unwrap();
let v = test_vec.lock().await; let v = test_vec.lock().await;
assert_eq!( assert_eq!(

View File

@ -44,15 +44,17 @@ pub use matrix_sdk_common::*;
mod client; mod client;
mod error; mod error;
mod event_emitter;
pub mod responses; pub mod responses;
mod rooms; mod rooms;
mod session; mod session;
mod store; mod store;
pub use rooms::{Room, RoomInfo, RoomMember}; pub use event_emitter::EventEmitter;
pub use rooms::{InvitedRoom, JoinedRoom, LeftRoom, Room, RoomInfo, RoomMember, RoomState};
pub use store::Store; pub use store::Store;
pub use client::{BaseClient, BaseClientConfig, RoomState, RoomStateType}; pub use client::{BaseClient, BaseClientConfig, RoomStateType};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]

View File

@ -8,7 +8,7 @@ pub use stripped::{StrippedRoom, StrippedRoomInfo};
pub use members::RoomMember; pub use members::RoomMember;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::max; use std::{cmp::max, ops::Deref};
use matrix_sdk_common::{ use matrix_sdk_common::{
events::{ events::{
@ -18,6 +18,86 @@ use matrix_sdk_common::{
identifiers::RoomAliasId, identifiers::RoomAliasId,
}; };
/// An enum that represents the state of the given `Room`.
///
/// If the event came from the `join`, `invite` or `leave` rooms map from the server
/// the variant that holds the corresponding room is used. `RoomState` is generic
/// so it can be used to represent a `Room` or an `Arc<RwLock<Room>>`
#[derive(Debug, Clone)]
pub enum RoomState {
/// A room from the `join` section of a sync response.
Joined(JoinedRoom),
/// A room from the `leave` section of a sync response.
Left(LeftRoom),
/// A room from the `invite` section of a sync response.
Invited(InvitedRoom),
}
impl RoomState {
pub fn joined(self) -> Option<JoinedRoom> {
if let RoomState::Joined(r) = self {
Some(r)
} else {
None
}
}
pub fn is_encrypted(&self) -> bool {
match self {
RoomState::Joined(r) => r.inner.is_encrypted(),
RoomState::Left(r) => r.inner.is_encrypted(),
RoomState::Invited(r) => r.inner.is_encrypted(),
}
}
pub fn are_members_synced(&self) -> bool {
if let RoomState::Joined(r) = self {
r.inner.are_members_synced()
} else {
true
}
}
}
#[derive(Debug, Clone)]
pub struct JoinedRoom {
pub(crate) inner: Room,
}
impl Deref for JoinedRoom {
type Target = Room;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Debug, Clone)]
pub struct LeftRoom {
pub(crate) inner: Room,
}
impl Deref for LeftRoom {
type Target = Room;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Debug, Clone)]
pub struct InvitedRoom {
pub(crate) inner: StrippedRoom,
}
impl Deref for InvitedRoom {
type Target = StrippedRoom;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BaseRoomInfo { pub struct BaseRoomInfo {
pub name: Option<String>, pub name: Option<String>,

View File

@ -83,6 +83,10 @@ impl Room {
self.inner.lock().unwrap().members_synced self.inner.lock().unwrap().members_synced
} }
pub fn room_type(&self) -> RoomType {
self.inner.lock().unwrap().room_type
}
pub async fn get_active_members(&self) -> impl Stream<Item = RoomMember> + '_ { pub async fn get_active_members(&self) -> impl Stream<Item = RoomMember> + '_ {
let joined = self.store.get_joined_user_ids(self.room_id()).await; let joined = self.store.get_joined_user_ids(self.room_id()).await;
let invited = self.store.get_invited_user_ids(self.room_id()).await; let invited = self.store.get_invited_user_ids(self.room_id()).await;