aboutsummaryrefslogblamecommitdiffstats
path: root/src/main.rs
blob: b2308fb7ecd051ec3db2460e86946f3cfc7775f4 (plain) (tree)











































































































































































































                                                                                     
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
    }
}