summaryrefslogblamecommitdiffstats
path: root/src/views/macaw.rs
blob: 0ef8255febd5c8a711aa1a0cc16c60c84fbbe755 (plain) (tree)
1
2
3
4
5
6
7
                                         
 




                                              








                                            











                                                  


                    
                     
                 








                                                              
                                                       












                                                                                
                                                                                                    



                                                           
                                                            




                                                          



                                                                                       



                                                              
                                                                                               




                                                                             

                                                                                   


                                                         

                                                      





                                                                                
                     






                                                                                                               


                                                                                                  




                                                                                        
                                                            









                                                               




                                                           
                                                                     


                                                                             




                                                                                  



                                                                               




                                                                 
                                                                                                 










                                                                          

                                                                                      



                                                            


                                                            

                                                             
                                                                



                                                                          




                                                                       








                              





                                                    


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