diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 204 |
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 + } +} |