aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..b2308fb
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,204 @@
+use std::collections::{HashMap, HashSet};
+
+use iced::futures::{SinkExt, Stream, StreamExt};
+use iced::widget::{button, column, row, text, text_input};
+use iced::{stream, Element, Subscription, Task, Theme};
+use jid::JID;
+use luz::chat::{Chat, Message as ChatMessage};
+use luz::presence::{Offline, Presence};
+use luz::CommandMessage;
+use luz::{roster::Contact, user::User, LuzHandle, UpdateMessage};
+use tokio::sync::oneshot;
+use tokio_stream::wrappers::ReceiverStream;
+
+#[derive(Default)]
+pub struct Macaw {
+ client: Option<LuzHandle>,
+ roster: HashMap<JID, Contact>,
+ users: HashMap<JID, User>,
+ presences: HashMap<JID, Presence>,
+ chats: HashMap<JID, (Chat, Vec<ChatMessage>)>,
+ subscription_requests: HashSet<JID>,
+ connection_status: Option<Presence>,
+}
+
+fn main() -> iced::Result {
+ tracing_subscriber::fmt::init();
+
+ iced::application("Macaw", Macaw::update, Macaw::view)
+ .subscription(Macaw::subscription)
+ .run()
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ ClientCreated(LuzHandle),
+ Luz(UpdateMessage),
+ Roster(HashMap<JID, Contact>),
+ Connect,
+ Disconnect,
+ OpenChat(JID),
+}
+
+impl Macaw {
+ fn stream() -> impl Stream<Item = Message> {
+ stream::channel(100, |mut output| async {
+ let (luz, recv) = LuzHandle::new(
+ "test@blos.sm".try_into().unwrap(),
+ "slayed".to_string(),
+ "./macaw.db",
+ )
+ .await
+ .unwrap();
+ output.send(Message::ClientCreated(luz)).await;
+ let stream = ReceiverStream::new(recv);
+ let stream = stream.map(|message| Message::Luz(message)).map(Ok);
+ stream.forward(output).await;
+ })
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ Subscription::run(Macaw::stream)
+ }
+
+ fn update(&mut self, message: Message) -> Task<Message> {
+ match message {
+ Message::Luz(update_message) => match update_message {
+ UpdateMessage::Error(error) => {
+ tracing::error!("Luz error: {:?}", error);
+ Task::none()
+ }
+ UpdateMessage::Online(online, vec) => {
+ self.connection_status = Some(Presence::Online(online));
+ let mut roster = HashMap::new();
+ for contact in vec {
+ roster.insert(contact.user_jid.clone(), contact);
+ }
+ self.roster = roster;
+ Task::none()
+ }
+ UpdateMessage::Offline(offline) => {
+ self.connection_status = Some(Presence::Offline(offline));
+ Task::none()
+ }
+ UpdateMessage::FullRoster(vec) => {
+ let mut macaw_roster = HashMap::new();
+ for contact in vec {
+ macaw_roster.insert(contact.user_jid.clone(), contact);
+ }
+ self.roster = macaw_roster;
+ Task::none()
+ }
+ UpdateMessage::RosterUpdate(contact) => {
+ self.roster.insert(contact.user_jid.clone(), contact);
+ Task::none()
+ }
+ UpdateMessage::RosterDelete(jid) => {
+ self.roster.remove(&jid);
+ Task::none()
+ }
+ UpdateMessage::Presence { from, presence } => {
+ self.presences.insert(from, presence);
+ Task::none()
+ }
+ UpdateMessage::Message { to, message } => {
+ if let Some((_chat, message_history)) = self.chats.get_mut(&to) {
+ message_history.push(message);
+ } else {
+ let chat = Chat {
+ correspondent: to.clone(),
+ };
+ let message_history = vec![message];
+ self.chats.insert(to, (chat, message_history));
+ }
+ Task::none()
+ }
+ UpdateMessage::SubscriptionRequest(jid) => {
+ // TODO: subscription requests
+ Task::none()
+ }
+ },
+ Message::ClientCreated(luz_handle) => {
+ let cloned: LuzHandle = luz_handle.clone();
+ self.client = Some(cloned);
+ let (send, recv) = oneshot::channel();
+ Task::perform(
+ async move {
+ luz_handle.send(CommandMessage::GetRoster(send)).await;
+ recv.await
+ },
+ |result| {
+ let roster = result.unwrap().unwrap();
+ let mut macaw_roster = HashMap::new();
+ for contact in roster {
+ macaw_roster.insert(contact.user_jid.clone(), contact);
+ }
+ Message::Roster(macaw_roster)
+ },
+ )
+ }
+ Message::Roster(hash_map) => {
+ self.roster = hash_map;
+ Task::none()
+ }
+ Message::Connect => {
+ let client = self.client.clone();
+ Task::future(async move {
+ client.clone().unwrap().send(CommandMessage::Connect).await;
+ })
+ .discard()
+ }
+ Message::Disconnect => {
+ let client = self.client.clone();
+ Task::future(async move {
+ client
+ .clone()
+ .unwrap()
+ .send(CommandMessage::Disconnect(Offline::default()))
+ .await;
+ })
+ .discard()
+ }
+ Message::OpenChat(jid) => todo!(),
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let mut contacts: Vec<Element<Message>> = Vec::new();
+ for (_, contact) in &self.roster {
+ contacts.push(
+ button(match &contact.user_jid.localpart {
+ Some(u) => u,
+ None => "no username",
+ })
+ .on_press(Message::OpenChat(contact.user_jid.clone()))
+ .into(),
+ );
+ }
+ let column = column(contacts);
+ let connection_status = match &self.connection_status {
+ Some(s) => match s {
+ Presence::Online(online) => "connected",
+ Presence::Offline(offline) => "disconnected",
+ },
+ None => "no account",
+ };
+ column![
+ row![
+ text("test@blos.sm"),
+ text(connection_status),
+ button("connect").on_press(Message::Connect),
+ button("disconnect").on_press(Message::Disconnect)
+ ],
+ text("Buddy List:"),
+ //
+ //
+ column,
+ ]
+ .into()
+ }
+
+ fn theme(&self) -> Theme {
+ Theme::Dark
+ }
+}