diff --git a/.gitignore b/.gitignore
index ea8c4bf..a803f50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/target
+/dist
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 821a4d8..ea39ba6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,24 +1,47 @@
#![allow(non_snake_case)]
use std::collections::HashMap;
+use std::future::Future;
use std::str::FromStr;
use std::sync::Arc;
use futures_util::stream::StreamExt;
use dioxus::html::textarea;
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;
+use dioxus_desktop::Config;
use dioxus_elements::{h1, li, ul};
use log::info;
use tokio::select;
use xmpp::{BareJid, ClientBuilder, ClientType, RoomNick};
use xmpp::parsers::message::MessageType;
-fn main() {
+const STYLESHEET: &str = r#"
+ body {
+ margin: 0;
+ padding: 0;
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ color: lightgray;
+ }
+ #main {
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ height: 100%;
+ }
+
+"#;
+
+fn main() {
env_logger::init();
// launch the dioxus app in a webview
- dioxus_desktop::launch(App);
+ dioxus_desktop::launch_cfg(App,
+ Config::default()
+ .with_custom_head(format!(r#""#, STYLESHEET))
+ .with_background_color((32,64,32,255)));
}
#[derive(Props)]
@@ -30,7 +53,6 @@ struct RoomListProps<'a> {
/// A Dioxus component that renders a list of rooms
fn RoomList<'a>(cx: Scope<'a, RoomListProps>) -> Element<'a> {
render! {
- h1 { "Rooms" }
ul {
for room in cx.props.rooms.iter() {
rsx! { li {
@@ -43,48 +65,94 @@ fn RoomList<'a>(cx: Scope<'a, RoomListProps>) -> Element<'a> {
}
}
+#[derive(Debug, Clone)]
+struct Message {
+ sender: String,
+ body: String,
+}
+
#[derive(Props)]
struct RoomViewProps<'a> {
room: BareJid,
- messages: Vec,
+ messages: Vec,
on_message_sent: EventHandler<'a, String>,
}
-// impl PartialEq for RoomViewProps {
-// fn eq(&self, other: &Self) -> bool {
-// self.room == other.room && self.messages == other.messages
-// }
-// }
-
fn RoomView<'a>(cx: Scope<'a, RoomViewProps>) -> Element<'a> {
-
let message = use_state(cx, || "".to_owned());
render! {
- div {
- h1 { "Messages" }
ul {
for message in cx.props.messages.iter() {
rsx! { li {
- "{message}"
+ "{message.sender}: {message.body}"
} }
}
}
- textarea {
- oninput: |evt| {
- message.set(evt.value.clone());
+ div {
+ display: "flex",
+ flex_direction: "row",
+ textarea {
+ flex_grow: 1,
+ oninput: |evt| {
+ message.set(evt.value.clone());
+ }
+ }
+ button {
+ height: "100%",
+ onclick: move |_| {
+ cx.props.on_message_sent.call(message.current().to_string());
+ },
+ "Send"
}
}
- button {
- onclick: move |_| {
- cx.props.on_message_sent.call(message.current().to_string());
- },
- "Send"
- }
+ }
+}
+
+async fn handle_event(event: xmpp::Event,
+ agent: &mut xmpp::Agent,
+ rooms: &mut UseState>,
+ messages: &mut UseState>>) {
+ match event {
+ xmpp::Event::JoinRoom(jid, conference) => {
+ println!("Joining room: {}", &jid);
+ rooms.make_mut().push(jid.clone());
+ agent.join_room(jid, None, None, "en/us", "online").await;
+ },
+ xmpp::Event::ChatMessage(id, sender, body) => {
+ println!("Message from {}: {}", &sender, &body.0);
+ messages.make_mut().entry(sender.clone()).or_insert(vec![]).push(Message {
+ sender: sender.to_string(),
+ body: body.0,
+ });
+ },
+ xmpp::Event::RoomMessage(id, room_jid, sender_nick, body) => {
+ println!("Message in {} from {}: {}", &room_jid, &sender_nick, &body.0);
+ messages.make_mut().entry(room_jid).or_insert(vec![]).push(Message {
+ sender: sender_nick,
+ body: body.0,
+ });
+ }
+ xmpp::Event::RoomPrivateMessage(id, room_jid, sender_nick, body) => {
+ println!("Private message in {} from {}: {}", &room_jid, &sender_nick, &body.0);
+ messages.make_mut().entry(room_jid).or_insert(vec![]).push(Message {
+ sender: sender_nick,
+ body: body.0,
+ });
+ },
+ _ => {
+ log::debug!("Received unsupported event {:?}", event);
}
}
}
+
+#[derive(Debug)]
+struct SendMessage {
+ recipient: BareJid,
+ message: String,
+}
+
// define a component that renders a div with the text "Hello, world!"
fn App(cx: Scope) -> Element {
@@ -92,65 +160,79 @@ fn App(cx: Scope) -> Element {
let messages = use_state(cx, || HashMap::new());
let current_room = use_state(cx, || None::);
- #[derive(Debug)]
- struct SendMessage {
- recipient: BareJid,
- message: String,
+ let cr = use_coroutine(cx,
+ |rx: UnboundedReceiver| run_xmpp(rooms.clone(), messages.clone(), rx)
+ );
+
+ render! {
+ div {
+ margin: 0,
+ padding: 0,
+ display: "flex",
+ width: "100%",
+ height: "100%",
+
+ div {
+ background_color: "#1d852d",
+ RoomList {
+ rooms: rooms.to_vec(),
+ on_room_picked: move |x:BareJid| {
+ println!("Room selected: {:?}", x);
+ current_room.set(Some(x));
+ },
+ }
+ }
+ div {
+ flex_grow: 1,
+ background_color: "#166322",
+ if let Some(room) = current_room.get() {
+ let messages = messages.get().get(&room).expect("Selected non-existant room").to_vec();
+
+ rsx! {
+ RoomView {
+ room: room.clone(),
+ messages: messages,
+ on_message_sent: move |x:String| {
+ println!("Message sent: {:?}", x);
+ cr.send(SendMessage {
+ recipient: room.clone(),
+ message: x,
+ });
+ },
+ }
+ }
+ } else {
+ rsx! {
+ div {
+ "No room selected"
+ }
+ }
+ }
+ }
+ }
}
+}
- let cr = use_coroutine(cx, |mut commands: UnboundedReceiver| {
- let mut rooms = rooms.to_owned();
- let mut messages = messages.to_owned();
- let current_room = current_room.to_owned();
+fn run_xmpp(mut rooms: UseState>,
+ mut messages: UseState>>,
+ mut commands: UnboundedReceiver) -> impl Future