summaryrefslogblamecommitdiffstats
path: root/src/components/message_history_buffer.rs
blob: ecdba5b5409cffa208f0870b701cbe4480292ca9 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                               
                                                                                                                                                                  




                                                               
                                                                              
                                
                                                                                   
















                                                                                 
                                                                                             




























                                                                                     
                                                                                                 

                                    
                                                                                                           




                                                                                 
                                                           







                                                                                
                                                                                                      



                                                                                          
                                                                   











                                                                                 
                                                           








                                                                        
                                                                                                        









                                                                    
                                                      

                               
                                                                                     










                                                    
                                                                                                






                                                                                          
                                                                                             






                                                                                 
                                                                                                















                                                                                            
                                                                                            




                  
use chrono::{NaiveDateTime, TimeDelta};
use filamento::{chat::{Chat, ChatStoreFields, MessageStoreFields}, user::User};
use indexmap::IndexMap;
use jid::BareJID;
use leptos::prelude::*;
use reactive_stores::{ArcStore, Store};
use tracing::{debug, error};
use uuid::Uuid;

use crate::{chat::MacawChat, client::Client, components::message::Message, message::{ArcMacawMessage, MacawMessage}, message_subscriptions::MessageSubscriptions};

#[component]
pub fn MessageHistoryBuffer(chat: MacawChat) -> impl IntoView {
    let (messages, set_messages) = arc_signal(IndexMap::new());
    let chat_chat: Store<Chat> =
        <ArcStore<filamento::chat::Chat> as Clone>::clone(&chat.get()).into();
    let chat_user: Store<User> =
        <ArcStore<filamento::user::User> as Clone>::clone(&chat.user.get()).into();

    let load_set_messages = set_messages.clone();
    let load_messages = LocalResource::new(move || {
        let load_set_messages = load_set_messages.clone();
        async move {
            let client = use_context::<Client>().expect("client not in context");
            let messages = client
                .get_messages_with_users(chat_chat.correspondent().get())
                .await
                .map_err(|e| e.to_string());
            match messages {
                Ok(m) => {
                    let messages = m
                        .into_iter()
                        .map(|(message, message_user)| {
                            (
                                message.id,
                                ArcMacawMessage::got_message_and_user(message, message_user),
                            )
                        })
                        .collect::<IndexMap<Uuid, _>>();
                    load_set_messages.set(messages);
                }
                Err(err) => {
                    error!("{err}")
                    // TODO: show error message at top of chats list
                }
            }
        }
    });

    // TODO: filter new messages signal
    let new_messages_signal: RwSignal<MessageSubscriptions> = use_context().unwrap();
    let (sub_id, set_sub_id) = signal(None);
    let load_new_messages_set = set_messages.clone();
    let _load_new_messages = LocalResource::new(move || {
        let load_new_messages_set = load_new_messages_set.clone();
        async move {
            load_messages.await;
            let (sub_id, mut new_messages) = new_messages_signal
                .write()
                .subscribe_chat(chat_chat.correspondent().get());
            set_sub_id.set(Some(sub_id));
            while let Some(new_message) = new_messages.recv().await {
                debug!("got new message in let message buffer");
                let mut messages = load_new_messages_set.write();
                if let Some((_, last)) = messages.last() {
                    if *<ArcStore<filamento::chat::Message> as Clone>::clone(&last.message.get())
                        .timestamp()
                        .read()
                        < *<ArcStore<filamento::chat::Message> as Clone>::clone(&new_message.message.get())
                            .timestamp()
                            .read()
                    {
                        messages.insert(
                            <ArcStore<filamento::chat::Message> as Clone>::clone(
                                &new_message.message.get(),
                            )
                            .id()
                            .get(),
                            new_message,
                        );
                        debug!("set the new message in message buffer");
                    } else {
                        let index = match messages.binary_search_by(|_, value| {
                            <ArcStore<filamento::chat::Message> as Clone>::clone(&value.message.get())
                                .timestamp()
                                .read()
                                .cmp(
                                    &<ArcStore<filamento::chat::Message> as Clone>::clone(
                                        &new_message.message.get(),
                                    )
                                    .timestamp()
                                    .read(),
                                )
                        }) {
                            Ok(i) => i,
                            Err(i) => i,
                        };
                        messages.insert_before(
                            // TODO: check if this logic is correct
                            index,
                            <ArcStore<filamento::chat::Message> as Clone>::clone(
                                &new_message.message.get(),
                            )
                            .id()
                            .get(),
                            new_message,
                        );
                        debug!("set the new message in message buffer");
                    }
                } else {
                    messages.insert(
                        <ArcStore<filamento::chat::Message> as Clone>::clone(&new_message.message.get())
                            .id()
                            .get(),
                        new_message,
                    );
                    debug!("set the new message in message buffer");
                }
            }
        }
    });
    on_cleanup(move || {
        if let Some(sub_id) = sub_id.get_untracked() {
            new_messages_signal
                .write()
                .unsubscribe_chat(sub_id, chat_chat.correspondent().get_untracked());
        }
    });

    let each = move || {
        let mut last_timestamp = NaiveDateTime::MIN;
        let mut last_user: Option<BareJID> = None;
        let mut messages = messages
            .get()
            .into_iter()
            .map(|(id, message)| {
                let message_timestamp =
                    <ArcStore<filamento::chat::Message> as Clone>::clone(&message.message.get())
                        .timestamp()
                        .read()
                        .naive_local();
                // TODO: mark new day
                // if message_timestamp.date() > last_timestamp.date() {
                //     messages_view = messages_view.push(date(message_timestamp.date()));
                // }
                let major = if last_user.as_ref() != Some(&message.message.get().read().from)
                    || message_timestamp - last_timestamp > TimeDelta::minutes(3)
                {
                    true
                } else {
                    false
                };
                last_user = Some(
                    <ArcStore<filamento::chat::Message> as Clone>::clone(&message.message.get())
                        .from()
                        .get(),
                );
                last_timestamp = message_timestamp;
                (id, (message, major, false))
            })
            .collect::<Vec<_>>();
        if let Some((_id, (_, _, last))) = messages.last_mut() {
            *last = true
        }
        messages.into_iter().rev()
    };

    view! {
        <div class="messages-buffer">
            <For each=each key=|message| (message.0, message.1.1, message.1.2) let(message)>
                <Message message=message.1.0.into() major=message.1.1 r#final=message.1.2 />
            </For>
        </div>
    }
}