🏗️ Rearchitecting the interface with the MatrixClient

- Replace RwStore with channels.
- Use of fermi to handle application data.
- Use of tracing.
This commit is contained in:
2023-12-10 22:01:05 +01:00
parent 4988054dae
commit ae8dba86f6
11 changed files with 472 additions and 450 deletions

View File

@@ -1,4 +1,5 @@
use dioxus::prelude::*;
use tracing::debug;
use crate::components::avatar_selector::AvatarSelector;
use crate::components::chats_window::edit_section::EditSection;
@@ -7,6 +8,8 @@ use crate::components::icons::DownArrowIcon;
turf::style_sheet!("src/components/chats_window/chats_window.scss");
pub fn ChatsWindow(cx: Scope) -> Element {
debug!("ChatsWindow rendering");
let room_names = vec![
"MON POTE",
"Second room",

View File

@@ -1,37 +1,47 @@
use std::cell::RefCell;
use std::time::Duration;
use dioxus::prelude::*;
use dioxus_std::utils::rw::UseRw;
use fermi::*;
use tokio::time::sleep;
use tracing::debug;
use crate::base::Room;
use crate::base::Store;
use crate::components::contacts_window::contacts_section::ContactsSection;
use crate::base::AppSettings;
use crate::base::{ByIdRooms, Room, APP_SETTINGS, ROOMS};
use crate::components::contacts_window::contacts_section::{
filter_people_conversations, filter_room_conversations, ContactsSection,
};
turf::style_sheet!("src/components/contacts_window/contacts.scss");
#[inline_props]
pub fn Contacts<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
println!("Contacts rendering");
pub fn Contacts(cx: Scope) -> Element {
debug!("Contacts rendering");
let store = rw_store.read().unwrap();
let app_settings_atom = use_atom_ref(cx, &APP_SETTINGS);
let rooms_atom = use_atom_ref(cx, &ROOMS);
let rooms = &store.rooms;
async fn sync_rooms(app_settings: UseAtomRef<AppSettings>, rooms_atom: UseAtomRef<ByIdRooms>) {
loop {
let requester = &app_settings.read().requester;
let rooms_len = rooms.len();
let mut groups = Vec::<Room>::with_capacity(rooms_len);
let mut directs = Vec::<Room>::with_capacity(rooms_len);
for arc_room in rooms.values() {
let room = arc_room.read().unwrap().to_owned();
let is_direct = room.is_direct.unwrap();
if is_direct {
directs.push(room);
} else {
groups.push(room);
if requester.is_some() {
let rooms_receiver = &requester.as_ref().unwrap().rooms_receiver;
let room = rooms_receiver.recv_async().await.unwrap();
let room_id = room.id();
dbg!(&room_id);
rooms_atom
.write()
.insert(room_id, RefCell::<Room>::new(room));
} else {
sleep(Duration::from_millis(1000)).await;
}
}
}
use_coroutine(cx, |_: UnboundedReceiver<()>| {
sync_rooms(app_settings_atom.clone(), rooms_atom.clone())
});
// TODO: Test overflow
// TODO: Add offline users ?
cx.render(rsx! {
@@ -40,8 +50,8 @@ pub fn Contacts<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
div {
class: ClassName::CONTACTS,
ContactsSection {name: "Groups", contacts: RefCell::new(groups)},
ContactsSection {name: "Available", contacts: RefCell::new(directs)},
ContactsSection {name: "Groups", filter: &filter_room_conversations},
ContactsSection {name: "Available", filter: &filter_people_conversations},
},
})
}

View File

@@ -3,9 +3,11 @@ use std::cell::RefCell;
use dioxus::prelude::*;
use dioxus_free_icons::icons::io_icons::IoChevronDown;
use dioxus_free_icons::Icon;
use fermi::prelude::*;
use matrix_sdk::RoomState;
use tracing::{debug, warn};
use crate::base::Room;
use crate::base::{ByIdRooms, Room, ROOMS};
turf::style_sheet!("src/components/contacts_window/contacts_section.scss");
@@ -19,11 +21,46 @@ fn ContactsArrow(cx: Scope) -> Element {
})
}
static NO_NAME_REPR: &str = "No name";
static NO_SUBJECT_REPR: &str = "No subject";
#[inline_props]
pub fn ContactsSection<'a>(cx: Scope, name: &'a str, contacts: RefCell<Vec<Room>>) -> Element {
println!("ContactsSection rendering");
pub fn filter_people_conversations(rooms_atom: UseAtomRef<ByIdRooms>) -> Vec<RefCell<Room>> {
let rooms = rooms_atom.read();
let mut filtered_rooms = Vec::<RefCell<Room>>::with_capacity(rooms.len());
for room in rooms.values() {
let is_direct = room.borrow().is_direct.unwrap();
if !is_direct {
filtered_rooms.push(room.to_owned());
}
}
filtered_rooms
}
pub fn filter_room_conversations(rooms_atom: UseAtomRef<ByIdRooms>) -> Vec<RefCell<Room>> {
let rooms = rooms_atom.read();
let mut filtered_rooms = Vec::<RefCell<Room>>::with_capacity(rooms.len());
for room in rooms.values() {
let is_direct = room.borrow().is_direct.unwrap();
if is_direct {
filtered_rooms.push(room.to_owned());
}
}
filtered_rooms
}
#[component]
pub fn ContactsSection<'a>(
cx: Scope,
name: &'a str,
filter: &'a dyn Fn(UseAtomRef<ByIdRooms>) -> Vec<RefCell<Room>>,
) -> Element {
debug!("ContactsSection rendering");
let rooms_atom = use_atom_ref(cx, &ROOMS);
let contacts = filter(rooms_atom.clone());
let contacts_len = contacts.len();
let show = use_state(cx, || false);
@@ -33,11 +70,19 @@ pub fn ContactsSection<'a>(cx: Scope, name: &'a str, contacts: RefCell<Vec<Room>
]
.join(" ");
let contacts_len = contacts.borrow().len();
let rendered_contacts = contacts.into_iter().map(|room_ref| {
let room = room_ref.borrow();
let room_topic = room
.topic
.as_ref()
.unwrap_or(&RefCell::new(NO_SUBJECT_REPR.to_string()))
.borrow()
.to_owned();
let room_name = room.name().unwrap_or(NO_NAME_REPR.to_string());
let rendered_contacts = contacts.borrow_mut().clone().into_iter().map(|room| {
let room_name = room.name().unwrap();
let is_invited = room.matrix_room.state() == RoomState::Invited;
let formatted = format!(
"{room_name} - {}",
if is_invited {
@@ -47,8 +92,6 @@ pub fn ContactsSection<'a>(cx: Scope, name: &'a str, contacts: RefCell<Vec<Room>
}
);
let room_topic = room.topic.unwrap_or(NO_SUBJECT_REPR.to_string()).to_owned();
rsx!(li {
img {
src: "./images/status_online.png",
@@ -60,8 +103,7 @@ pub fn ContactsSection<'a>(cx: Scope, name: &'a str, contacts: RefCell<Vec<Room>
style: "color: darkgrey;",
room_topic,
},
},
)
})
});
cx.render(rsx! {

View File

@@ -1,14 +1,14 @@
use dioxus::prelude::*;
use dioxus_std::utils::rw::UseRw;
use tracing::debug;
use crate::base::Store;
use crate::components::contacts_window::contacts::Contacts;
use crate::components::contacts_window::user_infos::UserInfos;
turf::style_sheet!("src/components/contacts_window/contacts_window.scss");
#[inline_props]
pub fn ContactsWindow<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
pub fn ContactsWindow(cx: Scope) -> Element {
debug!("ContactsWindow rendering");
cx.render(rsx! {
style { STYLE_SHEET },
@@ -26,7 +26,7 @@ pub fn ContactsWindow<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
class: ClassName::USER_INFO,
},
UserInfos {rw_store: rw_store},
UserInfos {},
},
div {
@@ -84,7 +84,7 @@ pub fn ContactsWindow<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
},
},
Contacts {rw_store: rw_store},
Contacts {},
div {
class: ClassName::FOOTER,

View File

@@ -1,7 +1,8 @@
use dioxus::prelude::*;
use dioxus_std::utils::rw::UseRw;
use fermi::*;
use tracing::debug;
use crate::base::Store;
use crate::base::APP_SETTINGS;
use crate::components::avatar_selector::AvatarSelector;
use crate::components::icons::DownArrowIcon;
@@ -9,15 +10,31 @@ turf::style_sheet!("src/components/contacts_window/user_infos.scss");
static MESSAGE_PLACEHOLDER: &str = "<Enter a personal message>";
#[inline_props]
pub fn UserInfos<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
pub fn UserInfos(cx: Scope) -> Element {
debug!("UserInfos rendering");
let app_settings = use_atom_ref(cx, &APP_SETTINGS);
let store = &app_settings.read().store;
println!("----------------------------------");
println!("UserInfos rendering");
// println!("store={:?}", &store);
dbg!(&store.user_id);
println!("----------------------------------");
let store = rw_store.read().unwrap().clone();
// let user_id = store.user_id..as_ref().unwrap();
let user_id = store.user_id.unwrap();
let user_info = store.user_infos.get(&user_id).unwrap();
let user_display_name = user_info.display_name.as_ref().unwrap();
// let mut user_info_option = None;
let mut user_display_name_option = None;
let user_id_option = &store.user_id;
if user_id_option.is_some() {
let user_id = user_id_option.as_ref().unwrap();
let user_info_option = store.user_infos.get(user_id);
if user_info_option.is_some() {
user_display_name_option = user_info_option.unwrap().display_name.as_ref();
}
}
cx.render(rsx! {
style { STYLE_SHEET },
@@ -37,7 +54,7 @@ pub fn UserInfos<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
class: ClassName::USER_ID,
p {
class: ClassName::USER_NAME,
"{user_display_name}",
if user_display_name_option.is_some() { "{user_display_name}" } else { "AIE" },
},
p {
class: ClassName::USER_STATUS,

View File

@@ -1,10 +1,10 @@
use std::str::FromStr;
use std::sync::Arc;
use dioxus::prelude::*;
use dioxus_std::utils::rw::UseRw;
use fermi::*;
use tracing::{debug, error};
use crate::base::{AppSettings, Store};
use crate::base::APP_SETTINGS;
use crate::components::avatar_selector::AvatarSelector;
use crate::components::header::Header;
use crate::matrix_client::{LoginStyle, MatrixClient};
@@ -13,14 +13,14 @@ turf::style_sheet!("src/components/login.scss");
static EMPTY_PLACEHOLDER: &str = "Tmp placeholder";
#[inline_props]
pub fn Login<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
let app_context = use_shared_state::<AppSettings>(cx).unwrap();
pub fn Login(cx: Scope) -> Element {
debug!("Login rendering");
let app_settings = use_atom_ref(cx, &APP_SETTINGS);
let invalid_login = use_state(cx, || false);
let login = use_ref(cx, || Login::new());
let arc_store = Arc::new(rw_store.to_owned().clone());
let password_class = if **invalid_login {
ClassName::INVALID_INPUT
} else {
@@ -29,7 +29,7 @@ pub fn Login<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
let run_matrix_client = move |_| {
cx.spawn({
to_owned![app_context, invalid_login, login, arc_store];
to_owned![invalid_login, login, app_settings];
let login_ref = login.read();
let homeserver_url = login_ref.homeserver_url.clone().unwrap();
@@ -37,20 +37,22 @@ pub fn Login<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
let password = login_ref.password.clone().unwrap();
async move {
let requester = MatrixClient::spawn(homeserver_url, arc_store.clone()).await;
requester.init();
let new_matrix_client = MatrixClient::spawn(homeserver_url).await;
match requester.login(LoginStyle::Password(username, password)) {
new_matrix_client.init();
match new_matrix_client.login(LoginStyle::Password(username, password)) {
Ok(_) => {
println!("successfully logged");
debug!("successfully logged");
app_settings.write().store.is_logged = true;
}
Err(err) => {
println!("Error during login: {err}");
error!("Error during login: {err}");
invalid_login.modify(|_| true);
}
}
app_context.write().requester = Some(Arc::new(requester));
app_settings.write().requester = Some(new_matrix_client);
}
});
};

View File

@@ -1,20 +1,23 @@
use dioxus::prelude::*;
use dioxus_std::utils::rw::UseRw;
use fermi::*;
use tracing::debug;
use crate::base::Store;
use crate::base::APP_SETTINGS;
use crate::components::contacts_window::contacts_window::ContactsWindow;
use crate::components::login::Login;
#[inline_props]
pub fn MainWindow<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
let is_logged = rw_store.read().unwrap().is_logged;
pub fn MainWindow(cx: Scope) -> Element {
debug!("MainWindow rendering");
let app_settings = use_atom_ref(cx, &APP_SETTINGS);
let is_logged = app_settings.read().store.is_logged;
cx.render(rsx! {
if is_logged {
rsx!(ContactsWindow {rw_store: rw_store})
rsx!(ContactsWindow {})
}
else {
rsx!(Login {rw_store: rw_store})
rsx!(Login {})
}
})
}