// Dergchat, a free XMPP client.
// Copyright (C) 2023 Werner Kroneman
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
use crate::types::{DirectMessage, DirectMessageRoom, LoginCredentials, MucMessage, MucRoom, Nickname};
use dioxus::hooks::{UnboundedReceiver, UseRef, UseState};
use futures_util::stream::StreamExt;
use jid::BareJid;
use log::{debug, error, info, warn};
use std::collections::HashMap;
use std::future::Future;
use crate::widgets::login_screen::LoginStatus;
use tokio::select;
use xmpp::parsers::message::MessageType;
use xmpp::{Agent, ClientBuilder, ClientType};
use crate::types::room::{Room, WrongRoomType};
use crate::types::message::MessageInOut;
/// An enum of commands that can be sent to the XMPP interface.
///
/// These very loosely correspond to XMPP stanzas, but are more high level.
#[derive(Debug)]
pub enum NetworkCommand {
/// Start a new login attempt, resulting in either a successful login or an error.
TryLogin { credentials: LoginCredentials },
/// Join a room.
JoinRoom { room: BareJid },
/// Leave a room.
LeaveRoom { room: BareJid },
/// Send a message to a recipient.
SendMessage { recipient: BareJid, message: String },
Logout,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Messages {
pub messages: HashMap,
}
impl Messages {
pub fn new() -> Self {
Self {
messages: HashMap::new(),
}
}
pub fn get(&self, room: &BareJid) -> Option<&Room> {
self.messages.get(room)
}
pub fn get_or_create_mut_direct(&mut self, room: &BareJid) -> Result<&mut DirectMessageRoom, WrongRoomType> {
self.messages.entry(room.clone()).or_insert(Room::Direct(DirectMessageRoom {
messages: vec![],
})).try_into()
}
pub fn get_or_create_mut_muc(&mut self, room: &BareJid) -> Result<&mut MucRoom, WrongRoomType> {
self.messages.entry(room.clone()).or_insert(Room::Muc(MucRoom {
messages: vec![],
})).try_into()
}
}
async fn handle_event(
event: xmpp::Event,
agent: &mut xmpp::Agent,
messages: &mut UseRef,
) {
match event {
xmpp::Event::JoinRoom(jid, _conference) => {
debug!("Will auto-join room: {}", &jid);
agent.join_room(jid, None, None, "en/us", "online").await;
}
xmpp::Event::RoomJoined(room_jid) => {
debug!("Joined room: {}", &room_jid);
messages.with_mut(move |m| {
m.messages.entry(room_jid.clone()).or_insert(Room::Muc(MucRoom {
messages: vec![],
}));
});
}
xmpp::Event::RoomLeft(room_jid) => {
debug!("Left room: {}", &room_jid);
messages.with_mut(move |m| {
m.messages.remove(&room_jid);
});
}
xmpp::Event::ChatMessage(_id, sender, body, timestamp) => {
debug!("Message from {}: {}", &sender, &body.0);
messages.with_mut(move |m| {
let dms : &mut DirectMessageRoom = m.messages.entry(sender.clone()).or_insert(Room::Direct(DirectMessageRoom {
messages: vec![],
})).try_into().expect("Received direct message with a JID from a MUC");
dms.messages.push(DirectMessage {
in_out: MessageInOut::Incoming,
body: body.0,
timestamp,
});
});
}
xmpp::Event::RoomMessage(_id, room_jid, sender_nick, body, timestamp) => {
debug!(
"Message in {} from {}: {}",
&room_jid, &sender_nick, &body.0
);
messages.with_mut(move |m| {
let muc : &mut MucRoom = m.get_or_create_mut_muc(&room_jid)
.expect("Received direct message with a JID from a MUC");
muc.messages.push(MucMessage {
in_out: MessageInOut::Incoming,
sender_nick: Nickname(sender_nick.to_string()),
body: body.0,
timestamp,
is_private: false,
});
});
}
xmpp::Event::RoomPrivateMessage(_id, room_jid, sender_nick, body, timestamp) => {
debug!(
"Private message in {} from {}: {}",
&room_jid, &sender_nick, &body.0
);
messages.with_mut(move |m| {
let muc : &mut MucRoom = m.get_or_create_mut_muc(&room_jid)
.expect("Received direct message with a JID from a MUC");
muc.messages.push(MucMessage {
in_out: MessageInOut::Incoming,
sender_nick: Nickname(sender_nick.to_string()),
body: body.0,
timestamp,
is_private: true,
});
});
}
_ => {
warn!("Received unsupported event {:?}", event);
}
}
}
pub fn xmpp_mainloop<'a>(
agent: &'a mut Agent,
mut room_data: &'a mut UseRef,
commands: &'a mut UnboundedReceiver,
) -> impl Future