summaryrefslogtreecommitdiffstats
path: root/src/components/message_history_buffer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/message_history_buffer.rs')
-rw-r--r--src/components/message_history_buffer.rs176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/components/message_history_buffer.rs b/src/components/message_history_buffer.rs
new file mode 100644
index 0000000..36439a8
--- /dev/null
+++ b/src/components/message_history_buffer.rs
@@ -0,0 +1,176 @@
+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::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.chat).into();
+ let chat_user: Store<User> =
+ <ArcStore<filamento::user::User> as Clone>::clone(&chat.user).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,
+ MacawMessage::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)
+ .timestamp()
+ .read()
+ < *<ArcStore<filamento::chat::Message> as Clone>::clone(&new_message.message)
+ .timestamp()
+ .read()
+ {
+ messages.insert(
+ <ArcStore<filamento::chat::Message> as Clone>::clone(
+ &new_message.message,
+ )
+ .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)
+ .timestamp()
+ .read()
+ .cmp(
+ &<ArcStore<filamento::chat::Message> as Clone>::clone(
+ &new_message.message,
+ )
+ .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,
+ )
+ .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)
+ .id()
+ .get(),
+ new_message,
+ );
+ debug!("set the new message in message buffer");
+ }
+ }
+ }
+ });
+ on_cleanup(move || {
+ if let Some(sub_id) = sub_id.get() {
+ new_messages_signal
+ .write()
+ .unsubscribe_chat(sub_id, chat_chat.correspondent().get());
+ }
+ });
+
+ 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)
+ .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.read().from)
+ || message_timestamp - last_timestamp > TimeDelta::minutes(3)
+ {
+ true
+ } else {
+ false
+ };
+ last_user = Some(
+ <ArcStore<filamento::chat::Message> as Clone>::clone(&message.message)
+ .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 major=message.1.1 r#final=message.1.2 />
+ </For>
+ </div>
+ }
+}
+