diff --git a/src/base.rs b/src/base.rs index e43a935..b14c2b4 100644 --- a/src/base.rs +++ b/src/base.rs @@ -3,7 +3,7 @@ // (used by [UnboundedReceiver]) by adding 'futures_util' as a dependency to your project // and adding the use futures_util::stream::StreamExt; use futures_util::stream::StreamExt; -use std::{cell::RefCell, collections::HashMap, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; use dioxus::prelude::*; use fermi::*; @@ -15,9 +15,9 @@ use matrix_sdk::{ use tokio::select; use tracing::{debug, error, warn}; +use crate::components::chats_window::interface::Interface as ChatsWinInterface; use crate::matrix_interface::client::{Client, RoomTopicEvent}; -use crate::matrix_interface::requester::Receivers; -use crate::matrix_interface::requester::Requester; +use crate::matrix_interface::requester::{Receivers, Requester}; use crate::matrix_interface::worker_tasks::LoginStyle; // #[derive(Clone, Debug)] @@ -41,7 +41,7 @@ use crate::matrix_interface::worker_tasks::LoginStyle; // } // } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct Room { pub matrix_room: Arc, pub topic: Option>, @@ -186,10 +186,7 @@ pub async fn login( app_settings_ref: UseAtomRef, session_ref: UseAtomRef, ) { - error!("=== LOGIN BEG ==="); - while let Some(is_logged) = rx.next().await { - error!("State updated"); if !is_logged { let homeserver_url = session_ref.read().homeserver_url.clone(); let username = session_ref.read().username.clone(); @@ -198,9 +195,12 @@ pub async fn login( if homeserver_url.is_some() && username.is_some() && password.is_some() { let client = Client::spawn(homeserver_url.unwrap()).await; - client.init(); + client.init().await; - match client.login(LoginStyle::Password(username.unwrap(), password.unwrap())) { + match client + .login(LoginStyle::Password(username.unwrap(), password.unwrap())) + .await + { Ok(_) => { debug!("successfully logged"); session_ref.write().is_logged = true; @@ -221,8 +221,6 @@ pub async fn login( error!("=== LOGIN END ==="); } -pub static ROOMS: AtomRef = AtomRef(|_| ByIdRooms::new()); - pub struct Session { pub homeserver_url: Option, pub username: Option, @@ -250,4 +248,6 @@ impl Session { } } +pub static ROOMS: AtomRef = AtomRef(|_| ByIdRooms::new()); pub static SESSION: AtomRef = AtomRef(|_| Session::new()); +pub static CHATS_WIN_INTERFACE: AtomRef = AtomRef(|_| ChatsWinInterface::new()); diff --git a/src/components/chats_window/interface.rs b/src/components/chats_window/interface.rs new file mode 100644 index 0000000..b797a04 --- /dev/null +++ b/src/components/chats_window/interface.rs @@ -0,0 +1,32 @@ +use dioxus::prelude::*; +use matrix_sdk::ruma::OwnedRoomId; +use tokio::sync::broadcast::error::SendError; +use tokio::sync::broadcast::{channel, Receiver, Sender}; + +#[derive(Clone)] +pub enum Tasks { + ToggleRoom(OwnedRoomId), +} + +pub struct Interface { + sender: Sender, + receiver: RefCell>, +} + +impl Interface { + pub fn new() -> Self { + let (sender, receiver) = channel::(32); + Self { + sender, + receiver: RefCell::new(receiver), + } + } + + pub(super) fn receiver(&self) -> &RefCell> { + &self.receiver + } + + pub fn toggle_room(&self, room_id: OwnedRoomId) -> Result> { + self.sender.send(Tasks::ToggleRoom(room_id)) + } +} diff --git a/src/components/chats_window/mod.rs b/src/components/chats_window/mod.rs index 2ee6afd..eba1dcd 100644 --- a/src/components/chats_window/mod.rs +++ b/src/components/chats_window/mod.rs @@ -1,56 +1,115 @@ +pub mod interface; + mod edit_section; +use std::collections::{HashMap, HashSet}; + use dioxus::prelude::*; use fermi::*; -use tracing::debug; +use matrix_sdk::ruma::OwnedRoomId; +use tokio::sync::broadcast::Receiver; +use tracing::{debug, error}; -use crate::base::{sync_rooms, ROOMS}; +use crate::base::{sync_rooms, Room, ROOMS}; use crate::components::avatar_selector::AvatarSelector; use crate::components::icons::DownArrowIcon; use crate::matrix_interface::requester::Receivers; + use edit_section::EditSection; +use interface::{Interface, Tasks}; turf::style_sheet!("src/components/chats_window/chats_window.scss"); pub struct ChatsWindowProps { pub receivers: Receivers, + pub interface: UseAtomRef, +} + +fn render_rooms_tabs<'a>( + rooms_atom_ref: &'a UseAtomRef>>, + displayed_room_ids_ref: &'a UseRef>, +) -> Vec> { + let rooms_ref = rooms_atom_ref.read(); + let displayed_room_ids = displayed_room_ids_ref.read(); + rooms_ref + .values() + .filter(|room| displayed_room_ids.contains(&room.borrow().id())) + .map(|room| -> LazyNodes { + let room = room.borrow(); + let room_name = room.name().unwrap_or(room.id().to_string()); + rsx!( + div { + class: ClassName::TAB, + + button { + img { + src: "./images/status_online.png", + }, + + "{room_name}", + }, + }, + ) + }) + .collect() +} + +async fn handle_controls<'a>( + receiver_ref: &'a RefCell>, + displayed_room_ids_ref: &'a UseRef>, +) { + loop { + let result = receiver_ref.borrow_mut().recv().await; + match result { + Ok(task) => match task { + Tasks::ToggleRoom(room_id) => { + error!("ON TOGGLE ROOM {}", room_id); + let mut displayed_room_ids = displayed_room_ids_ref.write(); + match displayed_room_ids.take(&room_id) { + Some(_) => { + error!("Toggle {} already dispayed... close it", room_id); + } + None => { + displayed_room_ids.insert(room_id); + } + } + } + Tasks::Test(msg) => error!("TEST {}", msg), + }, + Err(err) => error!("{}", err), + } + } } pub fn ChatsWindow(cx: Scope) -> Element { debug!("ChatsWindow rendering"); - let receivers = &cx.props.receivers; - use_init_atom_root(cx); + let receivers = &cx.props.receivers; + let interface_ref = &cx.props.interface; + let rooms_ref = use_atom_ref(cx, &ROOMS); + let displayed_room_ids = use_ref(cx, HashSet::::new); + let sync_rooms_coro = use_coroutine(cx, |rx| { to_owned![receivers]; - sync_rooms(rx, receivers, rooms_ref.clone()) }); sync_rooms_coro.send(true); - let rooms = rooms_ref.read(); - let rendered_room_tabs = rooms.values().map(|room| { - let room = room.borrow(); - let room_name = room.name().unwrap_or(room.id().to_string()); - rsx!( - div { - class: ClassName::TAB, - - button { - img { - src: "./images/status_online.png", - }, - - "{room_name}", - }, - }, - ) + let _: &Coroutine<()> = use_coroutine(cx, |_: UnboundedReceiver<_>| { + to_owned![interface_ref, displayed_room_ids]; + async move { + let interface = interface_ref.read(); + let receiver = &interface.receiver(); + handle_controls(receiver, &displayed_room_ids).await + } }); + let rendered_rooms_tabs = render_rooms_tabs(rooms_ref, displayed_room_ids); + cx.render(rsx! { style { STYLE_SHEET }, @@ -60,7 +119,7 @@ pub fn ChatsWindow(cx: Scope) -> Element { div { class: ClassName::TABS, - rendered_room_tabs.into_iter(), + rendered_rooms_tabs.into_iter(), }, div { diff --git a/src/components/contacts_window/contacts_section.rs b/src/components/contacts_window/contacts_section.rs index 2c4aaa3..ce5d1e0 100644 --- a/src/components/contacts_window/contacts_section.rs +++ b/src/components/contacts_window/contacts_section.rs @@ -1,13 +1,12 @@ -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 matrix_sdk::{ruma::OwnedRoomId, RoomState}; use tracing::{debug, warn}; -use crate::base::{ByIdRooms, Room, ROOMS}; +use crate::base::{ByIdRooms, Room, CHATS_WIN_INTERFACE, ROOMS}; +use crate::components::chats_window::interface::Interface as ChatsWindowInterface; turf::style_sheet!("src/components/contacts_window/contacts_section.scss"); @@ -24,7 +23,7 @@ fn ContactsArrow(cx: Scope) -> Element { static NO_NAME_REPR: &str = "No name"; static NO_SUBJECT_REPR: &str = "No subject"; -pub fn filter_people_conversations(rooms_atom: UseAtomRef) -> Vec> { +pub(super) fn filter_people_conversations(rooms_atom: UseAtomRef) -> Vec> { let rooms = rooms_atom.read(); let mut filtered_rooms = Vec::>::with_capacity(rooms.len()); @@ -37,7 +36,7 @@ pub fn filter_people_conversations(rooms_atom: UseAtomRef) -> Vec) -> Vec> { +pub(super) fn filter_room_conversations(rooms_atom: UseAtomRef) -> Vec> { let rooms = rooms_atom.read(); let mut filtered_rooms = Vec::>::with_capacity(rooms.len()); @@ -50,6 +49,14 @@ pub fn filter_room_conversations(rooms_atom: UseAtomRef) -> Vec, +) { + let _ = chats_window_interface.read().toggle_room(room_id.clone()); +} + #[component] pub fn ContactsSection<'a>( cx: Scope, @@ -58,8 +65,10 @@ pub fn ContactsSection<'a>( ) -> Element { debug!("ContactsSection rendering"); - let rooms_atom = use_atom_ref(cx, &ROOMS); - let contacts = filter(rooms_atom.clone()); + let rooms_atom_ref = use_atom_ref(cx, &ROOMS); + let chats_window_interface_ref = use_atom_ref(cx, &CHATS_WIN_INTERFACE); + + let contacts = filter(rooms_atom_ref.clone()); let contacts_len = contacts.len(); let show = use_state(cx, || false); @@ -80,6 +89,7 @@ pub fn ContactsSection<'a>( .borrow() .to_owned(); let room_name = room.name().unwrap_or(NO_NAME_REPR.to_string()); + let room_id = room.id(); let is_invited = room.matrix_room.state() == RoomState::Invited; @@ -93,6 +103,8 @@ pub fn ContactsSection<'a>( ); rsx!(li { + onclick: move |_| on_clicked_room(&room_id, chats_window_interface_ref), + img { src: "./images/status_online.png", }, diff --git a/src/main.rs b/src/main.rs index 7814dc9..f6149db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,15 @@ #![allow(non_snake_case)] +pub mod components; +pub mod matrix_interface; +pub mod utils; + use dioxus::prelude::*; use dioxus_desktop::Config; use fermi::*; use tracing::{debug, Level}; -pub mod components; -pub mod matrix_interface; - -use crate::base::{login, sync_rooms, APP_SETTINGS, ROOMS, SESSION}; +use crate::base::{login, sync_rooms, APP_SETTINGS, CHATS_WIN_INTERFACE, ROOMS, SESSION}; use crate::components::chats_window::{ChatsWindow, ChatsWindowProps}; use crate::components::main_window::MainWindow; @@ -22,6 +23,7 @@ fn App(cx: Scope) -> Element { let app_settings_ref = use_atom_ref(cx, &APP_SETTINGS); let session_ref = use_atom_ref(cx, &SESSION); let rooms_ref = use_atom_ref(cx, &ROOMS); + let chats_win_interface_ref = use_atom_ref(cx, &CHATS_WIN_INTERFACE); let chats_win_state = use_state(cx, || None); @@ -55,7 +57,11 @@ fn App(cx: Scope) -> Element { .borrow() .receivers .clone(); - let chats_props = ChatsWindowProps { receivers }; + + let chats_props = ChatsWindowProps { + receivers, + interface: chats_win_interface_ref.clone(), + }; let chats_dom = VirtualDom::new_with_props(ChatsWindow, chats_props); diff --git a/src/matrix_interface/client.rs b/src/matrix_interface/client.rs index eeea087..ca24a19 100644 --- a/src/matrix_interface/client.rs +++ b/src/matrix_interface/client.rs @@ -1,8 +1,7 @@ -use std::cell::RefCell; use std::sync::Arc; use std::time::Duration; -use dioxus::prelude::to_owned; +use dioxus::prelude::*; use tokio::sync::broadcast::Sender; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use tokio::sync::{broadcast, oneshot}; @@ -157,12 +156,10 @@ impl Client { async fn on_original_sync_room_member_event( _ev: OriginalSyncRoomMemberEvent, - room: MatrixRoom, + _room: MatrixRoom, _client: MatrixClient, ) { debug!("== on_original_sync_room_member_event =="); - let room_id = room.room_id(); - dbg!(room_id); // let mut store = store_ctx.read().unwrap().to_owned(); // dbg!(store.rooms.keys()); @@ -403,11 +400,11 @@ impl Client { WorkerTask::Init(reply) => { assert!(!self.initialized); self.init(); - reply.send(()); + reply.send(()).await; } WorkerTask::Login(style, reply) => { assert!(self.initialized); - reply.send(self.login_and_sync(style).await); + reply.send(self.login_and_sync(style).await).await; } } } diff --git a/src/matrix_interface/requester.rs b/src/matrix_interface/requester.rs index d1c6953..9174f79 100644 --- a/src/matrix_interface/requester.rs +++ b/src/matrix_interface/requester.rs @@ -1,15 +1,15 @@ -use std::cell::RefCell; use std::sync::Arc; +use dioxus::prelude::*; use matrix_sdk::Client as MatrixClient; use tokio::sync::broadcast::Receiver; use tokio::sync::mpsc::UnboundedSender; use super::client::RoomTopicEvent; -use super::worker_tasks::{oneshot, LoginStyle, WorkerTask}; +use super::worker_tasks::{LoginStyle, WorkerTask}; use crate::base::Room; +use crate::utils::oneshot; -#[derive(Debug)] pub struct Receivers { pub rooms_receiver: RefCell>, pub room_topic_receiver: RefCell>, @@ -24,7 +24,6 @@ impl Clone for Receivers { } } -#[derive(Debug)] pub struct Requester { pub matrix_client: Arc, pub tx: UnboundedSender, @@ -32,15 +31,23 @@ pub struct Requester { } impl Requester { - pub fn init(&self) { - let (reply, response) = oneshot(); + pub async fn init(&self) -> anyhow::Result<()> { + let (reply, mut response) = oneshot(); + // TODO: Handle error case. self.tx.send(WorkerTask::Init(reply)).unwrap(); - response.recv() + match response.recv().await { + Some(result) => Ok(result), + None => Err(anyhow::Error::msg("TBD")), + } } - pub fn login(&self, style: LoginStyle) -> anyhow::Result<()> { - let (reply, response) = oneshot(); + pub async fn login(&self, style: LoginStyle) -> anyhow::Result<()> { + let (reply, mut response) = oneshot(); + // TODO: Handle error case. self.tx.send(WorkerTask::Login(style, reply)).unwrap(); - response.recv() + match response.recv().await { + Some(result) => result, + None => Err(anyhow::Error::msg("TBD")), + } } } diff --git a/src/matrix_interface/worker_tasks.rs b/src/matrix_interface/worker_tasks.rs index 82074f8..e81d4e6 100644 --- a/src/matrix_interface/worker_tasks.rs +++ b/src/matrix_interface/worker_tasks.rs @@ -1,31 +1,6 @@ use std::fmt::{Debug, Formatter}; -use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; - -pub struct ClientResponse(Receiver); -pub struct ClientReply(SyncSender); - -impl ClientResponse { - pub(super) fn recv(self) -> T { - self.0 - .recv() - .expect("failed to receive response from client thread") - } -} - -impl ClientReply { - pub(super) fn send(self, t: T) { - self.0.send(t).unwrap(); - } -} - -pub(super) fn oneshot() -> (ClientReply, ClientResponse) { - let (tx, rx) = sync_channel(1); - let reply = ClientReply(tx); - let response = ClientResponse(rx); - - (reply, response) -} +use crate::utils::Sender; #[derive(Debug)] pub enum LoginStyle { @@ -36,9 +11,9 @@ pub enum LoginStyle { pub enum WorkerTask { // Init(AsyncProgramStore, ClientReply<()>), // Init(ClientReply<()>), - Init(ClientReply<()>), + Init(Sender<()>), //Login(LoginStyle, ClientReply), - Login(LoginStyle, ClientReply>), + Login(LoginStyle, Sender>), } impl Debug for WorkerTask { diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..f55a1d4 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,28 @@ +use tokio::sync::mpsc::{channel, Receiver as _Receiver, Sender as _Sender}; + +pub struct Receiver(_Receiver); + +impl Receiver { + pub(super) async fn recv(&mut self) -> Option { + self.0.recv().await + } +} + +pub struct Sender(_Sender); + +// TODO: Handle error +impl Sender { + pub(super) async fn send(self, t: T) { + // self.0.send(t).unwrap(); + let _ = self.0.send(t).await; + } +} + +// TODO: Rename the name of the following fn +pub fn oneshot() -> (Sender, Receiver) { + let (tx, rx) = channel(32); + let sender = Sender(tx); + let receiver = Receiver(rx); + + (sender, receiver) +}