use std::collections::{HashMap, HashSet};
use filamento::{
UpdateMessage,
chat::{Chat, Message, MessageStoreFields},
user::User,
};
use jid::BareJID;
use leptos::{prelude::*, task::spawn_local};
use open_chats_panel::OpenChatsPanelView;
use reactive_stores::{ArcStore, Store};
use settings::{Settings, SettingsPage};
use tokio::sync::mpsc::Receiver;
use tracing::debug;
use uuid::Uuid;
use crate::{
client::Client,
components::sidebar::Sidebar,
contact::{ArcMacawContact, MacawContact},
message::{ArcMacawMessage, MacawMessage},
message_subscriptions::MessageSubscriptions,
open_chats::OpenChatsPanel,
roster::{Roster, RosterStoreFields},
state_store::StateStore,
user::{ArcMacawUser, MacawUser, fetch_avatar},
user_presences::{Presences, UserPresences},
};
use super::AppState;
mod open_chats_panel;
pub mod settings;
#[component]
pub fn Macaw(
// TODO: logout
// app_state: WriteSignal<Option<essage>)>, LocalStorage>,
client: Client,
mut updates: Receiver<UpdateMessage>,
set_app: WriteSignal<AppState>,
) -> impl IntoView {
let (updates, set_updates) = signal(Some(updates));
provide_context(set_app);
provide_context(client);
let roster = Store::new(Roster::new());
provide_context(roster);
let message_subscriptions = RwSignal::new(MessageSubscriptions::new());
provide_context(message_subscriptions);
let messages_store: StateStore<Uuid, ArcStore<Message>> = StateStore::new();
provide_context(messages_store);
let chats_store: StateStore<BareJID, ArcStore<Chat>> = StateStore::new();
provide_context(chats_store);
let users_store: StateStore<BareJID, (ArcStore<User>, ArcRwSignal<String>)> = StateStore::new();
provide_context(users_store);
let open_chats = Store::new(OpenChatsPanel::default());
provide_context(open_chats);
let show_settings = RwSignal::new(None::<SettingsPage>);
provide_context(show_settings);
let user_presences = Store::new(UserPresences::new());
provide_context(user_presences);
let client_user: LocalResource<MacawUser> = LocalResource::new(move || async move {
let client = use_context::<Client>().expect("client not in context");
let user = client.get_user((*client.jid).clone()).await.unwrap();
ArcMacawUser::got_user(user).await.into()
});
provide_context(client_user);
// TODO: timestamp incoming/outgoing subscription requests
let (subscription_requests, set_subscription_requests) = signal(HashSet::<BareJID>::new());
provide_context(subscription_requests);
provide_context(set_subscription_requests);
// TODO: get cached contacts on login before getting the updated contacts
LocalResource::new(move || async move {
let mut updates = set_updates.write().take().expect("main loop ran twice");
while let Some(update) = updates.recv().await {
match update {
UpdateMessage::Online(online, items) => {
let mut contacts = HashMap::new();
for (contact, user) in items {
contacts.insert(
contact.user_jid.clone(),
ArcMacawContact::got_contact_and_user(contact, user)
.await
.into(),
);
}
roster.contacts().set(contacts);
}
UpdateMessage::Offline(offline) => {
// when offline, will no longer receive updated user presences, consider everybody offline.
user_presences.write().clear();
}
UpdateMessage::RosterUpdate(contact, user) => {
let new_contact = ArcMacawContact::got_contact_and_user(contact.clone(), user)
.await
.into();
roster.contacts().update(|roster| {
if let Some(macaw_contact) = roster.get_mut(&contact.user_jid) {
macaw_contact.set(contact);
} else {
let jid = contact.user_jid.clone();
roster.insert(jid, new_contact);
}
});
}
UpdateMessage::RosterDelete(jid) => {
roster.contacts().update(|roster| {
roster.remove(&jid);
});
}
UpdateMessage::Presence { from, presence } => {
let bare_jid = from.to_bare();
if let Some(presences) = user_presences
.read_untracked()
.user_presences
.get(&bare_jid)
{
if let Some(resource) = from.resourcepart() {
presences
.write()
.update_presence(resource.clone(), presence);
}
} else {
if let Some(resource) = from.resourcepart() {
let mut presences = Presences::new();
presences.update_presence(resource.clone(), presence);
user_presences
.write()
.user_presences
.insert(bare_jid, ArcRwSignal::new(presences));
}
}
}
UpdateMessage::Message { to, from, message } => {
debug!("before got message");
let new_message = ArcMacawMessage::got_message_and_user(message, from).await;
debug!("after got message");
spawn_local(async move {
message_subscriptions
.write()
.broadcast(to, new_message)
.await
});
debug!("after set message");
}
UpdateMessage::MessageDelivery { id, chat, delivery } => {
messages_store.modify(&id, |message| {
<ArcStore<filamento::chat::Message> as Clone>::clone(&message)
.delivery()
.set(Some(delivery))
});
}
UpdateMessage::SubscriptionRequest(jid) => {
set_subscription_requests.update(|req| {
req.insert(jid);
});
}
UpdateMessage::NickChanged { jid, nick } => {
users_store.modify(&jid, |(user, _avatar)| {
user.update(|user| *&mut user.nick = nick.clone())
});
}
UpdateMessage::AvatarChanged { jid, id } => {
let new_avatar = fetch_avatar(id.as_deref()).await;
users_store.modify(&jid, |(user, avatar)| {
*&mut user.write().avatar = id.clone();
*&mut avatar.set(new_avatar.clone())
});
}
}
}
});
view! {
<Sidebar />
// <ChatsList />
<OpenChatsPanelView />
{move || {
if let Some(_) = *show_settings.read() {
view! { <Settings /> }.into_any()
} else {
view! {}.into_any()
}
}}
}
}