♻️ Add Account, Room and Space UI store structs

This commit is contained in:
2024-05-11 15:24:49 +02:00
parent bc6b02bc34
commit 18a797bc3f
12 changed files with 152 additions and 183 deletions

View File

@@ -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<OwnedMxcUri>,
// pub display_name: Option<String>,
// pub blurhash: Option<String>,
// }
// impl UserInfo {
// pub fn new(
// avatar_url: Option<OwnedMxcUri>,
// display_name: Option<String>,
// blurhash: Option<String>,
// ) -> Self {
// Self {
// avatar_url,
// display_name,
// blurhash,
// }
// }
// }
// pub type ByIdUserInfos = HashMap<OwnedUserId, UserInfo>;
// #[derive(Clone)]
// pub struct Store {
// pub is_logged: bool,
// pub rooms: ByIdRooms,
// pub user_infos: ByIdUserInfos,
// pub user_id: Option<OwnedUserId>,
// }
// 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<RefCell<Requester>>,
}
impl AppSettings {
pub fn new() -> Self {
Self { requester: None }
}
pub fn set_requester(&mut self, requester: RefCell<Requester>) {
self.requester = Some(requester);
}
}
async fn on_room(room_id: OwnedRoomId, room: Room, by_id_rooms: &GlobalSignal<ByIdRooms>) {
// TODO: Update rooms
by_id_rooms
.write()
.insert(room_id, RefCell::<Room>::new(room));
}
async fn on_joining_invitation(
room_id: OwnedRoomId,
room: Room,
by_id_rooms: &GlobalSignal<ByIdRooms>,
) {
debug!("You're invited to join the \"{}\" room", room.id());
// TODO: Update rooms
by_id_rooms
.write()
.insert(room_id, RefCell::<Room>::new(room));
}
async fn on_room_topic(room_id: OwnedRoomId, topic: String, by_id_rooms: &GlobalSignal<ByIdRooms>) {
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<ByIdRooms>, room_id: OwnedRoomId) {
error!("== sync_messages ==");
}
pub async fn sync_rooms(
mut rx: UnboundedReceiver<bool>,
receivers: Receivers,
by_id_rooms: &GlobalSignal<ByIdRooms>,
) {
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<bool>,
app_settings: &GlobalSignal<AppSettings>,
session: &GlobalSignal<Session>,
) {
pub async fn login(mut rx: UnboundedReceiver<bool>, session: &GlobalSignal<Session>) {
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<AppSettings> = Signal::global(AppSettings::new);
pub static ROOMS: GlobalSignal<ByIdRooms> = Signal::global(ByIdRooms::new);
pub static STORE: GlobalSignal<Store> = Signal::global(Store::new);
// TODO: Merge ACCOUNT and SESSION
pub static ACCOUNT: GlobalSignal<Account> = Signal::global(|| Account::new(&STORE));
pub static SESSION: GlobalSignal<Session> = Signal::global(Session::new);
pub static CHATS_WIN_INTERFACE: GlobalSignal<ChatsWinInterface> =
Signal::global(ChatsWinInterface::new);

View File

@@ -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<dyn RoomMessagingConsumerInterface> {
async fn on_new_room(&self, room: Rc<Room>) -> Rc<dyn RoomMessagingConsumerInterface> {
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<dyn SpaceMessagingConsumerInterface> {
async fn on_new_space(&self, space: Rc<Space>) -> Rc<dyn SpaceMessagingConsumerInterface> {
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
}

View File

@@ -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<dyn RoomMessagingConsumerInterface>;
async fn on_new_space(&self, space: Space) -> Rc<dyn SpaceMessagingConsumerInterface>;
async fn on_new_room(&self, room: Rc<Room>) -> Rc<dyn RoomMessagingConsumerInterface>;
async fn on_new_space(&self, space: Rc<Space>) -> Rc<dyn SpaceMessagingConsumerInterface>;
}
#[async_trait(?Send)]

View File

@@ -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<HashMap<UserId, RoomMember>>,
messaging_provider: Option<Rc<dyn RoomMessagingProviderInterface>>,
store: RefCell<Option<Box<dyn RoomStoreProviderInterface>>>,
store: RefCell<Option<Rc<dyn RoomStoreProviderInterface>>>,
}
impl PartialEq for Room {
@@ -75,8 +73,8 @@ impl Room {
self.messaging_provider = Some(messaging_provider);
}
pub fn set_store(&self, store: Option<Box<dyn RoomStoreProviderInterface>>) {
*self.store.borrow_mut() = store;
pub fn set_store(&self, store: Rc<dyn RoomStoreProviderInterface>) {
*self.store.borrow_mut() = Some(store);
}
pub fn id(&self) -> &RoomId {

View File

@@ -26,7 +26,7 @@ pub struct Space {
children: RefCell<HashSet<RoomId>>, // We don´t expect to manage nested spaces
messaging_provider: Option<Rc<dyn SpaceMessagingProviderInterface>>,
store: RefCell<Option<Box<dyn SpaceStoreProviderInterface>>>,
store: RefCell<Option<Rc<dyn SpaceStoreProviderInterface>>>,
}
impl PartialEq for Space {
@@ -57,8 +57,8 @@ impl Space {
self.messaging_provider = Some(provider);
}
pub fn set_store(&self, store: Option<Box<dyn SpaceStoreProviderInterface>>) {
*self.store.borrow_mut() = store;
pub fn set_store(&self, store: Rc<dyn SpaceStoreProviderInterface>) {
*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);
}
}

View File

@@ -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<Room>) -> StoreRoom;
fn on_new_space(&self, space: Rc<Space>) -> StoreSpace;
fn on_new_room(&self, room: Rc<Room>) -> Rc<dyn RoomStoreProviderInterface>;
fn on_new_space(&self, space: Rc<Space>) -> Rc<dyn SpaceStoreProviderInterface>;
}
#[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<String>) {}
fn set_name(&self, _name: Option<String>) {}
}

View File

@@ -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);

View File

@@ -1,2 +1,3 @@
pub(crate) mod components;
pub(crate) mod store;
pub(crate) mod views;

50
src/ui/store/mod.rs Normal file
View File

@@ -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<RoomId, Room>,
spaces: HashMap<SpaceId, Space>,
}
#[async_trait(?Send)]
impl AccountStoreProviderInterface for GlobalSignal<Store> {
fn on_new_room(&self, domain_room: Rc<DomainRoom>) -> Rc<dyn RoomStoreProviderInterface> {
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<DomainSpace>) -> Rc<dyn SpaceStoreProviderInterface> {
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()))
}
}

21
src/ui/store/room.rs Normal file
View File

@@ -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<String>,
members: Vec<RoomMember>,
}
impl PartialEq for Room {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl RoomStoreProviderInterface for Room {}

24
src/ui/store/space.rs Normal file
View File

@@ -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<String>,
}
impl PartialEq for Space {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl SpaceStoreProviderInterface for RefCell<Space> {
fn set_name(&self, name: Option<String>) {
self.borrow_mut().name.set(name);
}
}