Merge branch 'store-room'
commit
9b38033cec
|
@ -3,7 +3,7 @@ use std::{env, process::exit};
|
|||
use matrix_sdk::{
|
||||
self,
|
||||
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
||||
Client, ClientConfig, EventEmitter, JsonStore, RoomState, SyncSettings,
|
||||
Client, ClientConfig, EventEmitter, JsonStore, SyncRoom, SyncSettings,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
|
@ -21,8 +21,8 @@ impl CommandBot {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl EventEmitter for CommandBot {
|
||||
async fn on_room_message(&self, room: RoomState, event: &MessageEvent) {
|
||||
if let RoomState::Joined(room) = room {
|
||||
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
||||
if let SyncRoom::Joined(room) = room {
|
||||
let msg_body = if let MessageEvent {
|
||||
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
||||
..
|
||||
|
|
|
@ -4,15 +4,15 @@ use url::Url;
|
|||
use matrix_sdk::{
|
||||
self,
|
||||
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
||||
Client, ClientConfig, EventEmitter, RoomState, SyncSettings,
|
||||
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
|
||||
};
|
||||
|
||||
struct EventCallback;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EventEmitter for EventCallback {
|
||||
async fn on_room_message(&self, room: RoomState, event: &MessageEvent) {
|
||||
if let RoomState::Joined(room) = room {
|
||||
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
||||
if let SyncRoom::Joined(room) = room {
|
||||
if let MessageEvent {
|
||||
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
||||
sender,
|
||||
|
|
|
@ -404,6 +404,17 @@ impl Client {
|
|||
Ok(self.base_client.sync_with_state_store().await?)
|
||||
}
|
||||
|
||||
/// This allows `AsyncClient` to manually store `Room`
|
||||
/// state with the provided `StateStore`.
|
||||
///
|
||||
/// Returns Ok when a successful `Room` store occurs.
|
||||
pub async fn store_room_state(&self, room_id: &RoomId) -> Result<()> {
|
||||
self.base_client
|
||||
.store_room_state(room_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Login to the server.
|
||||
///
|
||||
/// # Arguments
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use matrix_sdk_base::JsonStore;
|
||||
pub use matrix_sdk_base::{EventEmitter, Room, Session};
|
||||
pub use matrix_sdk_base::{EventEmitter, Room, Session, SyncRoom};
|
||||
pub use matrix_sdk_base::{RoomState, StateStore};
|
||||
pub use matrix_sdk_common::*;
|
||||
pub use reqwest::header::InvalidHeaderValue;
|
||||
|
|
|
@ -36,7 +36,7 @@ use crate::events::EventJson;
|
|||
use crate::identifiers::{RoomId, UserId};
|
||||
use crate::models::Room;
|
||||
use crate::session::Session;
|
||||
use crate::state::{ClientState, StateStore};
|
||||
use crate::state::{AllRooms, ClientState, StateStore};
|
||||
use crate::EventEmitter;
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
|
@ -73,14 +73,15 @@ pub enum RoomStateType {
|
|||
/// 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.
|
||||
pub enum RoomState {
|
||||
/// 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>>`
|
||||
pub enum RoomState<R> {
|
||||
/// A room from the `join` section of a sync response.
|
||||
Joined(Arc<RwLock<Room>>),
|
||||
Joined(R),
|
||||
/// A room from the `leave` section of a sync response.
|
||||
Left(Arc<RwLock<Room>>),
|
||||
Left(R),
|
||||
/// A room from the `invite` section of a sync response.
|
||||
Invited(Arc<RwLock<Room>>),
|
||||
Invited(R),
|
||||
}
|
||||
|
||||
/// A no IO Client implementation.
|
||||
|
@ -229,8 +230,20 @@ impl BaseClient {
|
|||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut rooms = store.load_all_rooms().await?;
|
||||
*self.joined_rooms.write().await = rooms
|
||||
let AllRooms {
|
||||
mut joined,
|
||||
mut invited,
|
||||
mut left,
|
||||
} = store.load_all_rooms().await?;
|
||||
*self.joined_rooms.write().await = joined
|
||||
.drain()
|
||||
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
||||
.collect();
|
||||
*self.invited_rooms.write().await = invited
|
||||
.drain()
|
||||
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
||||
.collect();
|
||||
*self.left_rooms.write().await = left
|
||||
.drain()
|
||||
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
|
||||
.collect();
|
||||
|
@ -241,6 +254,33 @@ impl BaseClient {
|
|||
Ok(!self.needs_state_store_sync.load(Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// When a client is provided the state store will load state from the `StateStore`.
|
||||
///
|
||||
/// Returns `true` when a state store sync has successfully completed.
|
||||
pub async fn store_room_state(&self, room_id: &RoomId) -> Result<()> {
|
||||
if let Some(store) = self.state_store.read().await.as_ref() {
|
||||
if let Some(room) = self.get_joined_room(room_id).await {
|
||||
let room = room.read().await;
|
||||
store
|
||||
.store_room_state(RoomState::Joined(room.deref()))
|
||||
.await?;
|
||||
}
|
||||
if let Some(room) = self.get_invited_room(room_id).await {
|
||||
let room = room.read().await;
|
||||
store
|
||||
.store_room_state(RoomState::Invited(room.deref()))
|
||||
.await?;
|
||||
}
|
||||
if let Some(room) = self.get_left_room(room_id).await {
|
||||
let room = room.read().await;
|
||||
store
|
||||
.store_room_state(RoomState::Left(room.deref()))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive a login response and update the session of the client.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -619,7 +659,7 @@ impl BaseClient {
|
|||
// TODO do we want to move the rooms to the appropriate HashMaps when the corresponding
|
||||
// event comes in e.g. move a joined room to a left room when leave event comes?
|
||||
|
||||
// when events change state, updated signals to StateStore to update database
|
||||
// when events change state, updated_* signals to StateStore to update database
|
||||
let updated_joined = self.iter_joined_rooms(response).await?;
|
||||
let updated_invited = self.iter_invited_rooms(&response).await?;
|
||||
let updated_left = self.iter_left_rooms(response).await?;
|
||||
|
@ -741,7 +781,7 @@ impl BaseClient {
|
|||
if updated {
|
||||
if let Some(store) = self.state_store.read().await.as_ref() {
|
||||
store
|
||||
.store_room_state(matrix_room.read().await.deref())
|
||||
.store_room_state(RoomState::Joined(matrix_room.read().await.deref()))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
@ -788,7 +828,7 @@ impl BaseClient {
|
|||
if updated {
|
||||
if let Some(store) = self.state_store.read().await.as_ref() {
|
||||
store
|
||||
.store_room_state(matrix_room.read().await.deref())
|
||||
.store_room_state(RoomState::Left(matrix_room.read().await.deref()))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
@ -824,7 +864,7 @@ impl BaseClient {
|
|||
if updated {
|
||||
if let Some(store) = self.state_store.read().await.as_ref() {
|
||||
store
|
||||
.store_room_state(matrix_room.read().await.deref())
|
||||
.store_room_state(RoomState::Invited(matrix_room.read().await.deref()))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::events::{
|
||||
fully_read::FullyReadEvent,
|
||||
|
@ -37,7 +40,10 @@ use crate::events::{
|
|||
},
|
||||
typing::TypingEvent,
|
||||
};
|
||||
use crate::RoomState;
|
||||
use crate::{Room, RoomState};
|
||||
|
||||
/// Type alias for `RoomState` enum when passed to `EventEmitter` methods.
|
||||
pub type SyncRoom = RoomState<Arc<RwLock<Room>>>;
|
||||
|
||||
/// This trait allows any type implementing `EventEmitter` to specify event callbacks for each event.
|
||||
/// The `AsyncClient` calls each method when the corresponding event is received.
|
||||
|
@ -52,7 +58,7 @@ use crate::RoomState;
|
|||
/// # events::{
|
||||
/// # room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
|
||||
/// # },
|
||||
/// # EventEmitter, RoomState
|
||||
/// # EventEmitter, SyncRoom
|
||||
/// # };
|
||||
/// use tokio::sync::RwLock;
|
||||
///
|
||||
|
@ -60,8 +66,8 @@ use crate::RoomState;
|
|||
///
|
||||
/// #[async_trait::async_trait]
|
||||
/// impl EventEmitter for EventCallback {
|
||||
/// async fn on_room_message(&self, room: RoomState, event: &MessageEvent) {
|
||||
/// if let RoomState::Joined(room) = room {
|
||||
/// async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
|
||||
/// if let SyncRoom::Joined(room) = room {
|
||||
/// if let MessageEvent {
|
||||
/// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
|
||||
/// sender,
|
||||
|
@ -87,82 +93,78 @@ use crate::RoomState;
|
|||
pub trait EventEmitter: Send + Sync {
|
||||
// ROOM EVENTS from `IncomingTimeline`
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomMember` event.
|
||||
async fn on_room_member(&self, _: RoomState, _: &MemberEvent) {}
|
||||
async fn on_room_member(&self, _: SyncRoom, _: &MemberEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomName` event.
|
||||
async fn on_room_name(&self, _: RoomState, _: &NameEvent) {}
|
||||
async fn on_room_name(&self, _: SyncRoom, _: &NameEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomCanonicalAlias` event.
|
||||
async fn on_room_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) {}
|
||||
async fn on_room_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomAliases` event.
|
||||
async fn on_room_aliases(&self, _: RoomState, _: &AliasesEvent) {}
|
||||
async fn on_room_aliases(&self, _: SyncRoom, _: &AliasesEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomAvatar` event.
|
||||
async fn on_room_avatar(&self, _: RoomState, _: &AvatarEvent) {}
|
||||
async fn on_room_avatar(&self, _: SyncRoom, _: &AvatarEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomMessage` event.
|
||||
async fn on_room_message(&self, _: RoomState, _: &MessageEvent) {}
|
||||
async fn on_room_message(&self, _: SyncRoom, _: &MessageEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomMessageFeedback` event.
|
||||
async fn on_room_message_feedback(&self, _: RoomState, _: &FeedbackEvent) {}
|
||||
async fn on_room_message_feedback(&self, _: SyncRoom, _: &FeedbackEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomRedaction` event.
|
||||
async fn on_room_redaction(&self, _: RoomState, _: &RedactionEvent) {}
|
||||
async fn on_room_redaction(&self, _: SyncRoom, _: &RedactionEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::RoomPowerLevels` event.
|
||||
async fn on_room_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) {}
|
||||
async fn on_room_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `RoomEvent::Tombstone` event.
|
||||
async fn on_room_tombstone(&self, _: RoomState, _: &TombstoneEvent) {}
|
||||
async fn on_room_tombstone(&self, _: SyncRoom, _: &TombstoneEvent) {}
|
||||
|
||||
// `RoomEvent`s from `IncomingState`
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomMember` event.
|
||||
async fn on_state_member(&self, _: RoomState, _: &MemberEvent) {}
|
||||
async fn on_state_member(&self, _: SyncRoom, _: &MemberEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomName` event.
|
||||
async fn on_state_name(&self, _: RoomState, _: &NameEvent) {}
|
||||
async fn on_state_name(&self, _: SyncRoom, _: &NameEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomCanonicalAlias` event.
|
||||
async fn on_state_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) {}
|
||||
async fn on_state_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomAliases` event.
|
||||
async fn on_state_aliases(&self, _: RoomState, _: &AliasesEvent) {}
|
||||
async fn on_state_aliases(&self, _: SyncRoom, _: &AliasesEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomAvatar` event.
|
||||
async fn on_state_avatar(&self, _: RoomState, _: &AvatarEvent) {}
|
||||
async fn on_state_avatar(&self, _: SyncRoom, _: &AvatarEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomPowerLevels` event.
|
||||
async fn on_state_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) {}
|
||||
async fn on_state_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `StateEvent::RoomJoinRules` event.
|
||||
async fn on_state_join_rules(&self, _: RoomState, _: &JoinRulesEvent) {}
|
||||
async fn on_state_join_rules(&self, _: SyncRoom, _: &JoinRulesEvent) {}
|
||||
|
||||
// `AnyStrippedStateEvent`s
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomMember` event.
|
||||
async fn on_stripped_state_member(&self, _: RoomState, _: &StrippedRoomMember) {}
|
||||
async fn on_stripped_state_member(&self, _: SyncRoom, _: &StrippedRoomMember) {}
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomName` event.
|
||||
async fn on_stripped_state_name(&self, _: RoomState, _: &StrippedRoomName) {}
|
||||
async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedRoomName) {}
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event.
|
||||
async fn on_stripped_state_canonical_alias(
|
||||
&self,
|
||||
_: RoomState,
|
||||
_: &StrippedRoomCanonicalAlias,
|
||||
) {
|
||||
async fn on_stripped_state_canonical_alias(&self, _: SyncRoom, _: &StrippedRoomCanonicalAlias) {
|
||||
}
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event.
|
||||
async fn on_stripped_state_aliases(&self, _: RoomState, _: &StrippedRoomAliases) {}
|
||||
async fn on_stripped_state_aliases(&self, _: SyncRoom, _: &StrippedRoomAliases) {}
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event.
|
||||
async fn on_stripped_state_avatar(&self, _: RoomState, _: &StrippedRoomAvatar) {}
|
||||
async fn on_stripped_state_avatar(&self, _: SyncRoom, _: &StrippedRoomAvatar) {}
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event.
|
||||
async fn on_stripped_state_power_levels(&self, _: RoomState, _: &StrippedRoomPowerLevels) {}
|
||||
async fn on_stripped_state_power_levels(&self, _: SyncRoom, _: &StrippedRoomPowerLevels) {}
|
||||
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event.
|
||||
async fn on_stripped_state_join_rules(&self, _: RoomState, _: &StrippedRoomJoinRules) {}
|
||||
async fn on_stripped_state_join_rules(&self, _: SyncRoom, _: &StrippedRoomJoinRules) {}
|
||||
|
||||
// `NonRoomEvent` (this is a type alias from ruma_events)
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomMember` event.
|
||||
async fn on_account_presence(&self, _: RoomState, _: &PresenceEvent) {}
|
||||
async fn on_account_presence(&self, _: SyncRoom, _: &PresenceEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomName` event.
|
||||
async fn on_account_ignored_users(&self, _: RoomState, _: &IgnoredUserListEvent) {}
|
||||
async fn on_account_ignored_users(&self, _: SyncRoom, _: &IgnoredUserListEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomCanonicalAlias` event.
|
||||
async fn on_account_push_rules(&self, _: RoomState, _: &PushRulesEvent) {}
|
||||
async fn on_account_push_rules(&self, _: SyncRoom, _: &PushRulesEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event.
|
||||
async fn on_account_data_fully_read(&self, _: RoomState, _: &FullyReadEvent) {}
|
||||
async fn on_account_data_fully_read(&self, _: SyncRoom, _: &FullyReadEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::Typing` event.
|
||||
async fn on_account_data_typing(&self, _: RoomState, _: &TypingEvent) {}
|
||||
async fn on_account_data_typing(&self, _: SyncRoom, _: &TypingEvent) {}
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::Receipt` event.
|
||||
///
|
||||
/// This is always a read receipt.
|
||||
async fn on_account_data_receipt(&self, _: RoomState, _: &ReceiptEvent) {}
|
||||
async fn on_account_data_receipt(&self, _: SyncRoom, _: &ReceiptEvent) {}
|
||||
|
||||
// `PresenceEvent` is a struct so there is only the one method
|
||||
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event.
|
||||
async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {}
|
||||
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -180,71 +182,71 @@ mod test {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl EventEmitter for EvEmitterTest {
|
||||
async fn on_room_member(&self, _: RoomState, _: &MemberEvent) {
|
||||
async fn on_room_member(&self, _: SyncRoom, _: &MemberEvent) {
|
||||
self.0.lock().await.push("member".to_string())
|
||||
}
|
||||
async fn on_room_name(&self, _: RoomState, _: &NameEvent) {
|
||||
async fn on_room_name(&self, _: SyncRoom, _: &NameEvent) {
|
||||
self.0.lock().await.push("name".to_string())
|
||||
}
|
||||
async fn on_room_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) {
|
||||
async fn on_room_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {
|
||||
self.0.lock().await.push("canonical".to_string())
|
||||
}
|
||||
async fn on_room_aliases(&self, _: RoomState, _: &AliasesEvent) {
|
||||
async fn on_room_aliases(&self, _: SyncRoom, _: &AliasesEvent) {
|
||||
self.0.lock().await.push("aliases".to_string())
|
||||
}
|
||||
async fn on_room_avatar(&self, _: RoomState, _: &AvatarEvent) {
|
||||
async fn on_room_avatar(&self, _: SyncRoom, _: &AvatarEvent) {
|
||||
self.0.lock().await.push("avatar".to_string())
|
||||
}
|
||||
async fn on_room_message(&self, _: RoomState, _: &MessageEvent) {
|
||||
async fn on_room_message(&self, _: SyncRoom, _: &MessageEvent) {
|
||||
self.0.lock().await.push("message".to_string())
|
||||
}
|
||||
async fn on_room_message_feedback(&self, _: RoomState, _: &FeedbackEvent) {
|
||||
async fn on_room_message_feedback(&self, _: SyncRoom, _: &FeedbackEvent) {
|
||||
self.0.lock().await.push("feedback".to_string())
|
||||
}
|
||||
async fn on_room_redaction(&self, _: RoomState, _: &RedactionEvent) {
|
||||
async fn on_room_redaction(&self, _: SyncRoom, _: &RedactionEvent) {
|
||||
self.0.lock().await.push("redaction".to_string())
|
||||
}
|
||||
async fn on_room_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) {
|
||||
async fn on_room_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {
|
||||
self.0.lock().await.push("power".to_string())
|
||||
}
|
||||
async fn on_room_tombstone(&self, _: RoomState, _: &TombstoneEvent) {
|
||||
async fn on_room_tombstone(&self, _: SyncRoom, _: &TombstoneEvent) {
|
||||
self.0.lock().await.push("tombstone".to_string())
|
||||
}
|
||||
|
||||
async fn on_state_member(&self, _: RoomState, _: &MemberEvent) {
|
||||
async fn on_state_member(&self, _: SyncRoom, _: &MemberEvent) {
|
||||
self.0.lock().await.push("state member".to_string())
|
||||
}
|
||||
async fn on_state_name(&self, _: RoomState, _: &NameEvent) {
|
||||
async fn on_state_name(&self, _: SyncRoom, _: &NameEvent) {
|
||||
self.0.lock().await.push("state name".to_string())
|
||||
}
|
||||
async fn on_state_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) {
|
||||
async fn on_state_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {
|
||||
self.0.lock().await.push("state canonical".to_string())
|
||||
}
|
||||
async fn on_state_aliases(&self, _: RoomState, _: &AliasesEvent) {
|
||||
async fn on_state_aliases(&self, _: SyncRoom, _: &AliasesEvent) {
|
||||
self.0.lock().await.push("state aliases".to_string())
|
||||
}
|
||||
async fn on_state_avatar(&self, _: RoomState, _: &AvatarEvent) {
|
||||
async fn on_state_avatar(&self, _: SyncRoom, _: &AvatarEvent) {
|
||||
self.0.lock().await.push("state avatar".to_string())
|
||||
}
|
||||
async fn on_state_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) {
|
||||
async fn on_state_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {
|
||||
self.0.lock().await.push("state power".to_string())
|
||||
}
|
||||
async fn on_state_join_rules(&self, _: RoomState, _: &JoinRulesEvent) {
|
||||
async fn on_state_join_rules(&self, _: SyncRoom, _: &JoinRulesEvent) {
|
||||
self.0.lock().await.push("state rules".to_string())
|
||||
}
|
||||
|
||||
async fn on_stripped_state_member(&self, _: RoomState, _: &StrippedRoomMember) {
|
||||
async fn on_stripped_state_member(&self, _: SyncRoom, _: &StrippedRoomMember) {
|
||||
self.0
|
||||
.lock()
|
||||
.await
|
||||
.push("stripped state member".to_string())
|
||||
}
|
||||
async fn on_stripped_state_name(&self, _: RoomState, _: &StrippedRoomName) {
|
||||
async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedRoomName) {
|
||||
self.0.lock().await.push("stripped state name".to_string())
|
||||
}
|
||||
async fn on_stripped_state_canonical_alias(
|
||||
&self,
|
||||
_: RoomState,
|
||||
_: SyncRoom,
|
||||
_: &StrippedRoomCanonicalAlias,
|
||||
) {
|
||||
self.0
|
||||
|
@ -252,38 +254,38 @@ mod test {
|
|||
.await
|
||||
.push("stripped state canonical".to_string())
|
||||
}
|
||||
async fn on_stripped_state_aliases(&self, _: RoomState, _: &StrippedRoomAliases) {
|
||||
async fn on_stripped_state_aliases(&self, _: SyncRoom, _: &StrippedRoomAliases) {
|
||||
self.0
|
||||
.lock()
|
||||
.await
|
||||
.push("stripped state aliases".to_string())
|
||||
}
|
||||
async fn on_stripped_state_avatar(&self, _: RoomState, _: &StrippedRoomAvatar) {
|
||||
async fn on_stripped_state_avatar(&self, _: SyncRoom, _: &StrippedRoomAvatar) {
|
||||
self.0
|
||||
.lock()
|
||||
.await
|
||||
.push("stripped state avatar".to_string())
|
||||
}
|
||||
async fn on_stripped_state_power_levels(&self, _: RoomState, _: &StrippedRoomPowerLevels) {
|
||||
async fn on_stripped_state_power_levels(&self, _: SyncRoom, _: &StrippedRoomPowerLevels) {
|
||||
self.0.lock().await.push("stripped state power".to_string())
|
||||
}
|
||||
async fn on_stripped_state_join_rules(&self, _: RoomState, _: &StrippedRoomJoinRules) {
|
||||
async fn on_stripped_state_join_rules(&self, _: SyncRoom, _: &StrippedRoomJoinRules) {
|
||||
self.0.lock().await.push("stripped state rules".to_string())
|
||||
}
|
||||
|
||||
async fn on_account_presence(&self, _: RoomState, _: &PresenceEvent) {
|
||||
async fn on_account_presence(&self, _: SyncRoom, _: &PresenceEvent) {
|
||||
self.0.lock().await.push("account presence".to_string())
|
||||
}
|
||||
async fn on_account_ignored_users(&self, _: RoomState, _: &IgnoredUserListEvent) {
|
||||
async fn on_account_ignored_users(&self, _: SyncRoom, _: &IgnoredUserListEvent) {
|
||||
self.0.lock().await.push("account ignore".to_string())
|
||||
}
|
||||
async fn on_account_push_rules(&self, _: RoomState, _: &PushRulesEvent) {
|
||||
async fn on_account_push_rules(&self, _: SyncRoom, _: &PushRulesEvent) {
|
||||
self.0.lock().await.push("account push rules".to_string())
|
||||
}
|
||||
async fn on_account_data_fully_read(&self, _: RoomState, _: &FullyReadEvent) {
|
||||
async fn on_account_data_fully_read(&self, _: SyncRoom, _: &FullyReadEvent) {
|
||||
self.0.lock().await.push("account read".to_string())
|
||||
}
|
||||
async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {
|
||||
async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {
|
||||
self.0.lock().await.push("presence event".to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ mod session;
|
|||
mod state;
|
||||
|
||||
pub use client::{BaseClient, RoomState, RoomStateType};
|
||||
pub use event_emitter::EventEmitter;
|
||||
pub use event_emitter::{EventEmitter, SyncRoom};
|
||||
#[cfg(feature = "encryption")]
|
||||
pub use matrix_sdk_crypto::{Device, TrustState};
|
||||
pub use models::Room;
|
||||
|
|
|
@ -170,7 +170,9 @@ mod test {
|
|||
|
||||
let mut joined_rooms = HashMap::new();
|
||||
joined_rooms.insert(id, room);
|
||||
println!("{}", serde_json::to_string_pretty(&joined_rooms).unwrap());
|
||||
|
||||
// println!("{}", serde_json::to_string_pretty(&joined_rooms).unwrap());
|
||||
|
||||
// this is the correct JSON string changes to `ruma-events` have not been released
|
||||
// that would fix the doubling of fields
|
||||
// TODO uncomment when fixed
|
||||
|
|
|
@ -14,17 +14,17 @@
|
|||
// limitations under the License.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod state_store;
|
||||
pub use state_store::AllRooms;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use state_store::JsonStore;
|
||||
|
||||
use crate::client::{BaseClient, Token};
|
||||
use crate::events::push_rules::Ruleset;
|
||||
use crate::identifiers::{RoomId, UserId};
|
||||
use crate::{Result, Room, Session};
|
||||
use crate::identifiers::UserId;
|
||||
use crate::{Result, Room, RoomState, Session};
|
||||
|
||||
/// `ClientState` holds all the information to restore a `BaseClient`
|
||||
/// except the `access_token` as the default store is not secure.
|
||||
|
@ -75,11 +75,11 @@ pub trait StateStore: Send + Sync {
|
|||
/// Load the state of all `Room`s.
|
||||
///
|
||||
/// This will be mapped over in the client in order to store `Room`s in an async safe way.
|
||||
async fn load_all_rooms(&self) -> Result<HashMap<RoomId, Room>>;
|
||||
async fn load_all_rooms(&self) -> Result<AllRooms>;
|
||||
/// Save the current state of the `BaseClient` using the `StateStore::Store` type.
|
||||
async fn store_client_state(&self, _: ClientState) -> Result<()>;
|
||||
/// Save the state a single `Room`.
|
||||
async fn store_room_state(&self, _: &Room) -> Result<()>;
|
||||
async fn store_room_state(&self, _: RoomState<&Room>) -> Result<()>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -89,6 +89,8 @@ mod test {
|
|||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::identifiers::RoomId;
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let id = RoomId::try_from("!roomid:example.com").unwrap();
|
||||
|
|
|
@ -12,7 +12,20 @@ use tokio::io::AsyncWriteExt;
|
|||
|
||||
use super::{ClientState, StateStore};
|
||||
use crate::identifiers::RoomId;
|
||||
use crate::{Error, Result, Room, Session};
|
||||
use crate::{Error, Result, Room, RoomState, Session};
|
||||
|
||||
/// `JsonStore::load_all_rooms` returns `AllRooms`.
|
||||
///
|
||||
/// `AllRooms` is made of the `joined`, `invited` and `left` room maps.
|
||||
pub struct AllRooms {
|
||||
/// The joined room mapping of `RoomId` to `Room`.
|
||||
pub joined: HashMap<RoomId, Room>,
|
||||
/// The invited room mapping of `RoomId` to `Room`.
|
||||
pub invited: HashMap<RoomId, Room>,
|
||||
/// The left room mapping of `RoomId` to `Room`.
|
||||
pub left: HashMap<RoomId, Room>,
|
||||
}
|
||||
|
||||
/// A default `StateStore` implementation that serializes state as json
|
||||
/// and saves it to disk.
|
||||
///
|
||||
|
@ -60,34 +73,55 @@ impl StateStore for JsonStore {
|
|||
}
|
||||
}
|
||||
|
||||
async fn load_all_rooms(&self) -> Result<HashMap<RoomId, Room>> {
|
||||
async fn load_all_rooms(&self) -> Result<AllRooms> {
|
||||
let mut path = self.path.read().await.clone();
|
||||
path.push("rooms");
|
||||
|
||||
let mut rooms_map = HashMap::new();
|
||||
for file in fs::read_dir(&path)? {
|
||||
let file = file?.path();
|
||||
|
||||
if file.is_dir() {
|
||||
let mut joined = HashMap::new();
|
||||
let mut left = HashMap::new();
|
||||
let mut invited = HashMap::new();
|
||||
for room_state_type in &["joined", "invited", "left"] {
|
||||
path.push(room_state_type);
|
||||
// don't load rooms that aren't saved yet
|
||||
if !path.exists() {
|
||||
path.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
let json = async_fs::read_to_string(&file).await?;
|
||||
for file in fs::read_dir(&path)? {
|
||||
let file = file?.path();
|
||||
|
||||
let room = serde_json::from_str::<Room>(&json).map_err(Error::from)?;
|
||||
let room_id = room.room_id.clone();
|
||||
if file.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
rooms_map.insert(room_id, room);
|
||||
let json = async_fs::read_to_string(&file).await?;
|
||||
|
||||
let room = serde_json::from_str::<Room>(&json).map_err(Error::from)?;
|
||||
let room_id = room.room_id.clone();
|
||||
|
||||
match *room_state_type {
|
||||
"joined" => joined.insert(room_id, room),
|
||||
"invited" => invited.insert(room_id, room),
|
||||
"left" => left.insert(room_id, room),
|
||||
_ => unreachable!("an array with 3 const elements was altered in JsonStore"),
|
||||
};
|
||||
}
|
||||
path.pop();
|
||||
}
|
||||
|
||||
Ok(rooms_map)
|
||||
Ok(AllRooms {
|
||||
joined,
|
||||
left,
|
||||
invited,
|
||||
})
|
||||
}
|
||||
|
||||
async fn store_client_state(&self, state: ClientState) -> Result<()> {
|
||||
let mut path = self.path.read().await.clone();
|
||||
path.push("client.json");
|
||||
|
||||
if !Path::new(&path).exists() {
|
||||
if !path.exists() {
|
||||
let mut dir = path.clone();
|
||||
dir.pop();
|
||||
async_fs::create_dir_all(dir).await?;
|
||||
|
@ -104,16 +138,23 @@ impl StateStore for JsonStore {
|
|||
file.write_all(json.as_bytes()).await.map_err(Error::from)
|
||||
}
|
||||
|
||||
async fn store_room_state(&self, room: &Room) -> Result<()> {
|
||||
async fn store_room_state(&self, room: RoomState<&Room>) -> Result<()> {
|
||||
let (room, room_state) = match room {
|
||||
RoomState::Joined(room) => (room, "joined"),
|
||||
RoomState::Invited(room) => (room, "invited"),
|
||||
RoomState::Left(room) => (room, "left"),
|
||||
};
|
||||
|
||||
if !self.user_path_set.load(Ordering::SeqCst) {
|
||||
self.user_path_set.swap(true, Ordering::SeqCst);
|
||||
self.path.write().await.push(room.own_user_id.localpart())
|
||||
}
|
||||
|
||||
let mut path = self.path.read().await.clone();
|
||||
path.push(&format!("rooms/{}.json", room.room_id));
|
||||
path.push("rooms");
|
||||
path.push(&format!("{}/{}.json", room_state, room.room_id));
|
||||
|
||||
if !Path::new(&path).exists() {
|
||||
if !path.exists() {
|
||||
let mut dir = path.clone();
|
||||
dir.pop();
|
||||
async_fs::create_dir_all(dir).await?;
|
||||
|
@ -187,7 +228,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_store_room_state() {
|
||||
async fn test_store_load_joined_room_state() {
|
||||
let dir = tempdir().unwrap();
|
||||
let path: &Path = dir.path();
|
||||
let store = JsonStore::open(path).unwrap();
|
||||
|
@ -196,13 +237,16 @@ mod test {
|
|||
let user = UserId::try_from("@example:example.com").unwrap();
|
||||
|
||||
let room = Room::new(&id, &user);
|
||||
store.store_room_state(&room).await.unwrap();
|
||||
let loaded = store.load_all_rooms().await.unwrap();
|
||||
assert_eq!(loaded.get(&id), Some(&Room::new(&id, &user)));
|
||||
store
|
||||
.store_room_state(RoomState::Joined(&room))
|
||||
.await
|
||||
.unwrap();
|
||||
let AllRooms { joined, .. } = store.load_all_rooms().await.unwrap();
|
||||
assert_eq!(joined.get(&id), Some(&Room::new(&id, &user)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_load_rooms() {
|
||||
async fn test_store_load_left_room_state() {
|
||||
let dir = tempdir().unwrap();
|
||||
let path: &Path = dir.path();
|
||||
let store = JsonStore::open(path).unwrap();
|
||||
|
@ -211,9 +255,30 @@ mod test {
|
|||
let user = UserId::try_from("@example:example.com").unwrap();
|
||||
|
||||
let room = Room::new(&id, &user);
|
||||
store.store_room_state(&room).await.unwrap();
|
||||
let loaded = store.load_all_rooms().await.unwrap();
|
||||
assert_eq!(&room, loaded.get(&id).unwrap());
|
||||
store
|
||||
.store_room_state(RoomState::Left(&room))
|
||||
.await
|
||||
.unwrap();
|
||||
let AllRooms { left, .. } = store.load_all_rooms().await.unwrap();
|
||||
assert_eq!(left.get(&id), Some(&Room::new(&id, &user)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_store_load_invited_room_state() {
|
||||
let dir = tempdir().unwrap();
|
||||
let path: &Path = dir.path();
|
||||
let store = JsonStore::open(path).unwrap();
|
||||
|
||||
let id = RoomId::try_from("!roomid:example.com").unwrap();
|
||||
let user = UserId::try_from("@example:example.com").unwrap();
|
||||
|
||||
let room = Room::new(&id, &user);
|
||||
store
|
||||
.store_room_state(RoomState::Invited(&room))
|
||||
.await
|
||||
.unwrap();
|
||||
let AllRooms { invited, .. } = store.load_all_rooms().await.unwrap();
|
||||
assert_eq!(invited.get(&id), Some(&Room::new(&id, &user)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
Loading…
Reference in New Issue