From 18a797bc3fe2fd32c4cdaa50479684c6067c887b Mon Sep 17 00:00:00 2001 From: Adrien Date: Sat, 11 May 2024 15:24:49 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20Account,=20Room=20an?= =?UTF-8?q?d=20Space=20UI=20store=20structs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 + src/base.rs | 183 +++--------------- src/domain/model/account.rs | 18 +- src/domain/model/messaging_interface.rs | 4 +- src/domain/model/room.rs | 10 +- src/domain/model/space.rs | 8 +- src/domain/model/store_interface.rs | 10 +- .../messaging/matrix/requester.rs | 4 + src/ui/mod.rs | 1 + src/ui/store/mod.rs | 50 +++++ src/ui/store/room.rs | 21 ++ src/ui/store/space.rs | 24 +++ 12 files changed, 152 insertions(+), 183 deletions(-) create mode 100644 src/ui/store/mod.rs create mode 100644 src/ui/store/room.rs create mode 100644 src/ui/store/space.rs diff --git a/Cargo.toml b/Cargo.toml index a00b679..30bc930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ web = ["dioxus/web"] dioxus = "0.5.*" dioxus-sdk = { version = "0.5.*", features = ["utils"] } dioxus-free-icons = { version = "0.8.*", features = ["ionicons", "font-awesome-solid", "material-design-icons-navigation"] } +# modx = "0.1" +modx = { git = "https://github.com/ASR-ASU/modx.git", branch = "asr/fix-props-visibility" } # matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", branch = "main", default-features = false, features = ["js", "rustls-tls"] } matrix-sdk = { version = "0.7.*", default-features = false, features = ["js", "rustls-tls"] } diff --git a/src/base.rs b/src/base.rs index 515beca..c906966 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,185 +1,60 @@ -// Cf. https://dioxuslabs.com/learn/0.4/reference/use_coroutine -// In order to use/run the rx.next().await statement you will need to extend the [Stream] trait -// (used by [UnboundedReceiver]) by adding 'futures_util' as a dependency to your project -// and adding the use futures_util::stream::StreamExt; -use std::cell::RefCell; +use std::rc::Rc; use dioxus::prelude::*; use futures_util::stream::StreamExt; use log::{debug, error, warn}; -use matrix_sdk::ruma::OwnedRoomId; -use tokio::select; -use crate::domain::model::room::{ByIdRooms, Room}; +use crate::domain::model::account::Account; +use crate::domain::model::messaging_interface::AccountMessagingProviderInterface; use crate::domain::model::session::Session; -use crate::infrastructure::messaging::matrix::client::{Client, RoomEvent}; -use crate::infrastructure::messaging::matrix::requester::{Receivers, Requester}; +use crate::infrastructure::messaging::matrix::client::Client; use crate::infrastructure::messaging::matrix::worker_tasks::LoginStyle; -use crate::ui::components::chats_window::interface::Interface as ChatsWinInterface; +use crate::ui::store::Store; -// #[derive(Clone, Debug)] -// pub struct UserInfo { -// pub avatar_url: Option, -// pub display_name: Option, -// pub blurhash: Option, -// } - -// impl UserInfo { -// pub fn new( -// avatar_url: Option, -// display_name: Option, -// blurhash: Option, -// ) -> Self { -// Self { -// avatar_url, -// display_name, -// blurhash, -// } -// } -// } - -// pub type ByIdUserInfos = HashMap; - -// #[derive(Clone)] -// pub struct Store { -// pub is_logged: bool, -// pub rooms: ByIdRooms, -// pub user_infos: ByIdUserInfos, -// pub user_id: Option, -// } - -// impl Store { -// pub fn new() -> Self { -// Self { -// is_logged: false, -// rooms: HashMap::new(), -// user_infos: HashMap::new(), -// user_id: None, -// } -// } -// } - -// impl PartialEq for Store { -// fn eq(&self, other: &Self) -> bool { -// self.is_logged == other.is_logged -// && self.user_id == other.user_id -// && self.user_infos.len() == other.user_infos.len() -// && self -// .user_infos -// .keys() -// .all(|k| other.user_infos.contains_key(k)) -// && self.rooms.len() == other.rooms.len() -// && self.rooms.keys().all(|k| other.rooms.contains_key(k)) -// } -// } - -// impl Eq for Store {} - -pub struct AppSettings { - pub requester: Option>, -} - -impl AppSettings { - pub fn new() -> Self { - Self { requester: None } - } - - pub fn set_requester(&mut self, requester: RefCell) { - self.requester = Some(requester); - } -} - -async fn on_room(room_id: OwnedRoomId, room: Room, by_id_rooms: &GlobalSignal) { - // TODO: Update rooms - by_id_rooms - .write() - .insert(room_id, RefCell::::new(room)); -} - -async fn on_joining_invitation( - room_id: OwnedRoomId, - room: Room, - by_id_rooms: &GlobalSignal, -) { - debug!("You're invited to join the \"{}\" room", room.id()); - // TODO: Update rooms - by_id_rooms - .write() - .insert(room_id, RefCell::::new(room)); -} - -async fn on_room_topic(room_id: OwnedRoomId, topic: String, by_id_rooms: &GlobalSignal) { - if let Some(room) = by_id_rooms.read().get(&room_id) { - let mut room = room.borrow_mut(); - room.set_topic(Some(topic)); - } else { - warn!("No room found with the \"{}\" id", room_id); - } -} - -pub async fn sync_messages(by_id_rooms: &GlobalSignal, room_id: OwnedRoomId) { - error!("== sync_messages =="); - -} - -pub async fn sync_rooms( - mut rx: UnboundedReceiver, - receivers: Receivers, - by_id_rooms: &GlobalSignal, -) { - if let Some(_is_logged) = rx.next().await { - let mut rooms_receiver = receivers.room_receiver.borrow_mut(); - - loop { - // TODO: Remove select if no more receivers will be used. - select! { - res = rooms_receiver.recv() => { - if let Ok(room_event) = res { - match room_event { - RoomEvent::MemberEvent(room_id, room) => on_room(room_id, room, &by_id_rooms).await, - RoomEvent::InviteEvent(room_id, room) => on_joining_invitation(room_id, room, &by_id_rooms).await, - RoomEvent::TopicEvent(room_id, topic) => on_room_topic(room_id, topic, &by_id_rooms).await, - }; - } - }, - } - } - } -} - -pub async fn login( - mut rx: UnboundedReceiver, - app_settings: &GlobalSignal, - session: &GlobalSignal, -) { +pub async fn login(mut rx: UnboundedReceiver, session: &GlobalSignal) { while let Some(is_logged) = rx.next().await { + error!("is_logged={is_logged}"); if !is_logged { let homeserver_url = session.read().homeserver_url.clone(); let username = session.read().username.clone(); let password = session.read().password.clone(); if homeserver_url.is_some() && username.is_some() && password.is_some() { - let client = Client::spawn(homeserver_url.unwrap()).await; + let (requester, account_events_receiver) = + Client::spawn(homeserver_url.unwrap()).await; - if let Err(err) = client.init().await { + if let Err(err) = requester.init().await { error!("Following error occureds during client init: {}", err); } - match client + error!("Before login"); + + match requester .login(LoginStyle::Password(username.unwrap(), password.unwrap())) .await { Ok(_) => { debug!("successfully logged"); session.write().is_logged = true; + + let requester = Rc::new(requester); + + dioxus::prelude::spawn(async move { + // ACCOUNT.write().set_messaging_provider(requester.clone()); + ACCOUNT.write().set_messaging_provider(requester.clone()); + + let _ = requester + .run_forever(&*ACCOUNT.read(), account_events_receiver) + .await; + }); } Err(err) => { error!("Error during login: {err}"); // TODO: Handle invalid login // invalid_login.modify(|_| true); + return; } } - app_settings.write().set_requester(RefCell::new(client)); } else { warn!("At least one of the following values is/are invalid: homeserver, username or password"); } @@ -190,8 +65,8 @@ pub async fn login( error!("=== LOGIN END ==="); } -pub static APP_SETTINGS: GlobalSignal = Signal::global(AppSettings::new); -pub static ROOMS: GlobalSignal = Signal::global(ByIdRooms::new); +pub static STORE: GlobalSignal = Signal::global(Store::new); + +// TODO: Merge ACCOUNT and SESSION +pub static ACCOUNT: GlobalSignal = Signal::global(|| Account::new(&STORE)); pub static SESSION: GlobalSignal = Signal::global(Session::new); -pub static CHATS_WIN_INTERFACE: GlobalSignal = - Signal::global(ChatsWinInterface::new); diff --git a/src/domain/model/account.rs b/src/domain/model/account.rs index ebd4299..c1b81f5 100644 --- a/src/domain/model/account.rs +++ b/src/domain/model/account.rs @@ -1,6 +1,4 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use async_trait::async_trait; use tracing::error; @@ -95,32 +93,30 @@ impl Account { #[async_trait(?Send)] impl AccountMessagingConsumerInterface for Account { - async fn on_new_room(&self, room: Room) -> Rc { + async fn on_new_room(&self, room: Rc) -> Rc { let room_id = room.id().clone(); - let room = Rc::new(room); self.by_id_rooms .borrow_mut() .insert(room_id, Rc::clone(&room)); - let room_store = Box::new(self.store.on_new_room(Rc::clone(&room))); + let room_store = self.store.on_new_room(Rc::clone(&room)); - room.set_store(Some(room_store)); + room.set_store(room_store); room } - async fn on_new_space(&self, space: Space) -> Rc { + async fn on_new_space(&self, space: Rc) -> Rc { let space_id = space.id().clone(); - let space = Rc::new(space); self.by_id_spaces .borrow_mut() .insert(space_id, Rc::clone(&space)); - let space_store = Box::new(self.store.on_new_space(Rc::clone(&space))); + let space_store = self.store.on_new_space(Rc::clone(&space)); - space.set_store(Some(space_store)); + space.set_store(space_store); space } diff --git a/src/domain/model/messaging_interface.rs b/src/domain/model/messaging_interface.rs index fd8183b..9a1fb60 100644 --- a/src/domain/model/messaging_interface.rs +++ b/src/domain/model/messaging_interface.rs @@ -13,8 +13,8 @@ use crate::infrastructure::messaging::matrix::account_event::AccountEvent; #[async_trait(?Send)] pub trait AccountMessagingConsumerInterface { - async fn on_new_room(&self, room: Room) -> Rc; - async fn on_new_space(&self, space: Space) -> Rc; + async fn on_new_room(&self, room: Rc) -> Rc; + async fn on_new_space(&self, space: Rc) -> Rc; } #[async_trait(?Send)] diff --git a/src/domain/model/room.rs b/src/domain/model/room.rs index d7c6de9..bd16e76 100644 --- a/src/domain/model/room.rs +++ b/src/domain/model/room.rs @@ -1,6 +1,4 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use async_trait::async_trait; use futures::future::{join, join_all}; @@ -34,7 +32,7 @@ pub struct Room { members: RefCell>, messaging_provider: Option>, - store: RefCell>>, + store: RefCell>>, } impl PartialEq for Room { @@ -75,8 +73,8 @@ impl Room { self.messaging_provider = Some(messaging_provider); } - pub fn set_store(&self, store: Option>) { - *self.store.borrow_mut() = store; + pub fn set_store(&self, store: Rc) { + *self.store.borrow_mut() = Some(store); } pub fn id(&self) -> &RoomId { diff --git a/src/domain/model/space.rs b/src/domain/model/space.rs index 034fac5..3c8a4c1 100644 --- a/src/domain/model/space.rs +++ b/src/domain/model/space.rs @@ -26,7 +26,7 @@ pub struct Space { children: RefCell>, // We don“t expect to manage nested spaces messaging_provider: Option>, - store: RefCell>>, + store: RefCell>>, } impl PartialEq for Space { @@ -57,8 +57,8 @@ impl Space { self.messaging_provider = Some(provider); } - pub fn set_store(&self, store: Option>) { - *self.store.borrow_mut() = store; + pub fn set_store(&self, store: Rc) { + *self.store.borrow_mut() = Some(store); } pub fn id(&self) -> &SpaceId { @@ -84,7 +84,7 @@ impl SpaceMessagingConsumerInterface for Space { error!("Space::on_new_name({:?})", name); self.name.borrow_mut().clone_from(&name); - if let Some(store) = self.store.borrow_mut().as_mut() { + if let Some(store) = self.store.borrow().as_ref() { store.set_name(name); } } diff --git a/src/domain/model/store_interface.rs b/src/domain/model/store_interface.rs index 7a097a9..856d4b4 100644 --- a/src/domain/model/store_interface.rs +++ b/src/domain/model/store_interface.rs @@ -1,15 +1,13 @@ use std::rc::Rc; -use super::room::Room; -use super::space::Space; -use crate::base::{StoreRoom, StoreSpace}; +use super::{room::Room, space::Space}; #[allow(dead_code)] pub trait AccountStoreConsumerInterface {} pub trait AccountStoreProviderInterface { - fn on_new_room(&self, room: Rc) -> StoreRoom; - fn on_new_space(&self, space: Rc) -> StoreSpace; + fn on_new_room(&self, room: Rc) -> Rc; + fn on_new_space(&self, space: Rc) -> Rc; } #[allow(dead_code)] @@ -19,5 +17,5 @@ pub trait RoomStoreProviderInterface {} #[allow(dead_code)] pub trait SpaceStoreConsumerInterface {} pub trait SpaceStoreProviderInterface { - fn set_name(&mut self, _name: Option) {} + fn set_name(&self, _name: Option) {} } diff --git a/src/infrastructure/messaging/matrix/requester.rs b/src/infrastructure/messaging/matrix/requester.rs index 6e934de..a1307cd 100644 --- a/src/infrastructure/messaging/matrix/requester.rs +++ b/src/infrastructure/messaging/matrix/requester.rs @@ -147,6 +147,8 @@ impl AccountMessagingProviderInterface for Requester { room.set_messaging_provider(client.clone()); + let room = Rc::new(room); + let stream = BroadcastStream::new(receiver.into()); rooms_events_streams.insert(room_id.clone(), stream); @@ -162,6 +164,8 @@ impl AccountMessagingProviderInterface for Requester { space.set_messaging_provider(client.clone()); + let space = Rc::new(space); + let stream = BroadcastStream::new(receiver.into()); spaces_events_streams.insert(space_id.clone(), stream); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 1cccdfe..3d84e88 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod components; +pub(crate) mod store; pub(crate) mod views; diff --git a/src/ui/store/mod.rs b/src/ui/store/mod.rs new file mode 100644 index 0000000..5771d96 --- /dev/null +++ b/src/ui/store/mod.rs @@ -0,0 +1,50 @@ +pub(crate) mod room; +pub(crate) mod space; + +use std::cell::RefCell; +use std::{collections::HashMap, rc::Rc}; + +use async_trait::async_trait; +use dioxus::prelude::*; + +use crate::domain::model::room::{Room as DomainRoom, RoomId}; +use crate::domain::model::space::{Space as DomainSpace, SpaceId}; +use crate::domain::model::store_interface::{ + AccountStoreProviderInterface, RoomStoreProviderInterface, SpaceStoreProviderInterface, +}; + +use room::{Room, RoomProps}; +use space::{Space, SpaceProps}; + +#[modx::store] +pub struct Store { + rooms: HashMap, + spaces: HashMap, +} + +#[async_trait(?Send)] +impl AccountStoreProviderInterface for GlobalSignal { + fn on_new_room(&self, domain_room: Rc) -> Rc { + let room_id = domain_room.id(); + + let props = RoomProps::new(room_id.clone(), domain_room.name()); + let room = Room::new(props); + + let mut rooms = self.read().rooms; + rooms.write().insert(room_id.clone(), room); + + Rc::new(room.clone()) + } + + fn on_new_space(&self, domain_space: Rc) -> Rc { + let space_id = domain_space.id(); + + let props = SpaceProps::new(space_id.clone(), domain_space.name()); + let space = Space::new(props); + + let mut spaces = self.read().spaces; + spaces.write().insert(space_id.clone(), space); + + Rc::new(RefCell::new(space.clone())) + } +} diff --git a/src/ui/store/room.rs b/src/ui/store/room.rs new file mode 100644 index 0000000..40bad22 --- /dev/null +++ b/src/ui/store/room.rs @@ -0,0 +1,21 @@ +use dioxus::prelude::*; + +use crate::domain::model::{ + room::RoomId, room_member::RoomMember, store_interface::RoomStoreProviderInterface, +}; + +#[modx::props(id, name)] +#[modx::store] +pub struct Room { + id: RoomId, + name: Option, + members: Vec, +} + +impl PartialEq for Room { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl RoomStoreProviderInterface for Room {} diff --git a/src/ui/store/space.rs b/src/ui/store/space.rs new file mode 100644 index 0000000..8ebaeb8 --- /dev/null +++ b/src/ui/store/space.rs @@ -0,0 +1,24 @@ +use std::cell::RefCell; + +use dioxus::prelude::*; + +use crate::domain::model::{space::SpaceId, store_interface::SpaceStoreProviderInterface}; + +#[modx::props(id, name)] +#[modx::store] +pub struct Space { + id: SpaceId, + name: Option, +} + +impl PartialEq for Space { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl SpaceStoreProviderInterface for RefCell { + fn set_name(&self, name: Option) { + self.borrow_mut().name.set(name); + } +}