diff options
author | 2025-04-11 07:52:11 +0100 | |
---|---|---|
committer | 2025-04-11 08:21:34 +0100 | |
commit | 15faa6b95ffb5c64a56e212cf386c29fef56efc8 (patch) | |
tree | b1eb9895f1452a1d1ff68ced65458c35c037a4c2 /filamento/src | |
parent | 1e5d4b504901bbd8fbf884e0f006d5717e1bc88c (diff) | |
download | luz-15faa6b95ffb5c64a56e212cf386c29fef56efc8.tar.gz luz-15faa6b95ffb5c64a56e212cf386c29fef56efc8.tar.bz2 luz-15faa6b95ffb5c64a56e212cf386c29fef56efc8.zip |
feat(filamento): `get_chats_ordered_with_latest_messages_and_users()`
Diffstat (limited to '')
-rw-r--r-- | filamento/src/db.rs | 198 | ||||
-rw-r--r-- | filamento/src/lib.rs | 20 | ||||
-rw-r--r-- | filamento/src/logic/local_only.rs | 9 | ||||
-rw-r--r-- | filamento/src/logic/offline.rs | 8 | ||||
-rw-r--r-- | filamento/src/logic/online.rs | 7 | ||||
-rw-r--r-- | filamento/src/logic/process_stanza.rs | 1 | ||||
-rw-r--r-- | filamento/src/user.rs | 2 |
7 files changed, 226 insertions, 19 deletions
diff --git a/filamento/src/db.rs b/filamento/src/db.rs index cf21325..d9206cc 100644 --- a/filamento/src/db.rs +++ b/filamento/src/db.rs @@ -1,12 +1,12 @@ use std::{collections::HashSet, path::Path}; -use chrono::Utc; +use chrono::{DateTime, Utc}; use jid::JID; use sqlx::{SqlitePool, migrate}; use uuid::Uuid; use crate::{ - chat::{Chat, Message}, + chat::{Body, Chat, Delivery, Message}, error::{DatabaseError as Error, DatabaseOpenError}, presence::Online, roster::Contact, @@ -51,10 +51,9 @@ impl Db { pub(crate) async fn create_user(&self, user: User) -> Result<(), Error> { sqlx::query!( - "insert into users ( jid, nick, cached_status_message ) values ( ?, ?, ? )", + "insert into users ( jid, nick ) values ( ?, ? )", user.jid, user.nick, - user.cached_status_message ) .execute(&self.db) .await?; @@ -184,8 +183,7 @@ impl Db { pub(crate) async fn update_user(&self, user: User) -> Result<(), Error> { sqlx::query!( - "update users set cached_status_message = ?, nick = ? where jid = ?", - user.cached_status_message, + "update users set nick = ? where jid = ?", user.nick, user.jid ) @@ -499,23 +497,197 @@ impl Db { &self, ) -> Result<Vec<(Chat, Message)>, Error> { #[derive(sqlx::FromRow)] - pub struct ChatWithMessage { + pub struct RowChat { + chat_correspondent: JID, + chat_have_chatted: bool, + } + impl From<RowChat> for Chat { + fn from(value: RowChat) -> Self { + Self { + correspondent: value.chat_correspondent, + have_chatted: value.chat_have_chatted, + } + } + } + #[derive(sqlx::FromRow)] + pub struct RowMessage { + message_id: Uuid, + message_body: String, + message_delivery: Option<Delivery>, + message_timestamp: DateTime<Utc>, + message_from_jid: JID, + } + impl From<RowMessage> for Message { + fn from(value: RowMessage) -> Self { + Self { + id: value.message_id, + from: value.message_from_jid, + delivery: value.message_delivery, + timestamp: value.message_timestamp, + body: Body { + body: value.message_body, + }, + } + } + } + + #[derive(sqlx::FromRow)] + pub struct ChatWithMessageRow { #[sqlx(flatten)] - pub chat: Chat, + pub chat: RowChat, #[sqlx(flatten)] - pub message: Message, + pub message: RowMessage, + } + + pub struct ChatWithMessage { + chat: Chat, + message: Message, + } + + impl From<ChatWithMessageRow> for ChatWithMessage { + fn from(value: ChatWithMessageRow) -> Self { + Self { + chat: value.chat.into(), + message: value.message.into(), + } + } } // TODO: sometimes chats have no messages. - // TODO: i don't know if this will assign the right uuid to the latest message or the chat's id. should probably check but i don't think it matters as nothing ever gets called with the id of the latest message in the chats list - // TODO: it does matter in fact, as message updates and delivery receipts need to go to the right latest_message - let chats: Vec<ChatWithMessage> = sqlx::query_as("select c.*, m.* from chats c join (select chat_id, max(timestamp) max_timestamp from messages group by chat_id) max_timestamps on c.id = max_timestamps.chat_id join messages m on max_timestamps.chat_id = m.chat_id and max_timestamps.max_timestamp = m.timestamp order by m.timestamp desc") + let chats: Vec<ChatWithMessageRow> = sqlx::query_as("select c.*, m.* from chats c join (select chat_id, max(timestamp) max_timestamp from messages group by chat_id) max_timestamps on c.id = max_timestamps.chat_id join messages m on max_timestamps.chat_id = m.chat_id and max_timestamps.max_timestamp = m.timestamp order by m.timestamp desc") + .fetch_all(&self.db) + .await?; + + let chats = chats + .into_iter() + .map(|chat_with_message_row| { + let chat_with_message: ChatWithMessage = chat_with_message_row.into(); + (chat_with_message.chat, chat_with_message.message) + }) + .collect(); + + Ok(chats) + } + + /// chats ordered by date of last message + // greatest-n-per-group + pub(crate) async fn read_chats_ordered_with_latest_messages_and_users( + &self, + ) -> Result<Vec<((Chat, User), (Message, User))>, Error> { + #[derive(sqlx::FromRow)] + pub struct RowChat { + chat_correspondent: JID, + chat_have_chatted: bool, + } + impl From<RowChat> for Chat { + fn from(value: RowChat) -> Self { + Self { + correspondent: value.chat_correspondent, + have_chatted: value.chat_have_chatted, + } + } + } + #[derive(sqlx::FromRow)] + pub struct RowMessage { + message_id: Uuid, + message_body: String, + message_delivery: Option<Delivery>, + message_timestamp: DateTime<Utc>, + message_from_jid: JID, + } + impl From<RowMessage> for Message { + fn from(value: RowMessage) -> Self { + Self { + id: value.message_id, + from: value.message_from_jid, + delivery: value.message_delivery, + timestamp: value.message_timestamp, + body: Body { + body: value.message_body, + }, + } + } + } + #[derive(sqlx::FromRow)] + pub struct RowChatUser { + chat_user_jid: JID, + chat_user_nick: Option<String>, + chat_user_avatar: Option<String>, + } + impl From<RowChatUser> for User { + fn from(value: RowChatUser) -> Self { + Self { + jid: value.chat_user_jid, + nick: value.chat_user_nick, + avatar: value.chat_user_avatar, + } + } + } + #[derive(sqlx::FromRow)] + pub struct RowMessageUser { + message_user_jid: JID, + message_user_nick: Option<String>, + message_user_avatar: Option<String>, + } + impl From<RowMessageUser> for User { + fn from(value: RowMessageUser) -> Self { + Self { + jid: value.message_user_jid, + nick: value.message_user_nick, + avatar: value.message_user_avatar, + } + } + } + #[derive(sqlx::FromRow)] + pub struct ChatWithMessageAndUsersRow { + #[sqlx(flatten)] + pub chat: RowChat, + #[sqlx(flatten)] + pub chat_user: RowChatUser, + #[sqlx(flatten)] + pub message: RowMessage, + #[sqlx(flatten)] + pub message_user: RowMessageUser, + } + + impl From<ChatWithMessageAndUsersRow> for ChatWithMessageAndUsers { + fn from(value: ChatWithMessageAndUsersRow) -> Self { + Self { + chat: value.chat.into(), + chat_user: value.chat_user.into(), + message: value.message.into(), + message_user: value.message_user.into(), + } + } + } + + pub struct ChatWithMessageAndUsers { + chat: Chat, + chat_user: User, + message: Message, + message_user: User, + } + + let chats: Vec<ChatWithMessageAndUsersRow> = sqlx::query_as("select c.id as chat_id, c.correspondent as chat_correspondent, c.have_chatted as chat_have_chatted, m.id as message_id, m.body as message_body, m.delivery as message_delivery, m.timestamp as message_timestamp, m.from_jid as message_from_jid, cu.jid as chat_user_jid, cu.nick as chat_user_nick, cu.avatar as chat_user_avatar, mu.jid as message_user_jid, mu.nick as message_user_nick, mu.avatar as message_user_avatar from chats c join (select chat_id, max(timestamp) max_timestamp from messages group by chat_id) max_timestamps on c.id = max_timestamps.chat_id join messages m on max_timestamps.chat_id = m.chat_id and max_timestamps.max_timestamp = m.timestamp join users as cu on cu.jid = c.correspondent join users as mu on mu.jid = m.from_jid order by m.timestamp desc") .fetch_all(&self.db) .await?; let chats = chats .into_iter() - .map(|chat_with_message| (chat_with_message.chat, chat_with_message.message)) + .map(|chat_with_message_and_users_row| { + let chat_with_message_and_users: ChatWithMessageAndUsers = + chat_with_message_and_users_row.into(); + ( + ( + chat_with_message_and_users.chat, + chat_with_message_and_users.chat_user, + ), + ( + chat_with_message_and_users.message, + chat_with_message_and_users.message_user, + ), + ) + }) .collect(); Ok(chats) diff --git a/filamento/src/lib.rs b/filamento/src/lib.rs index 97cc9cd..14b0cae 100644 --- a/filamento/src/lib.rs +++ b/filamento/src/lib.rs @@ -61,6 +61,10 @@ pub enum Command<Fs: FileStore> { GetChatsOrdered(oneshot::Sender<Result<Vec<Chat>, DatabaseError>>), // TODO: paging and filtering GetChatsOrderedWithLatestMessages(oneshot::Sender<Result<Vec<(Chat, Message)>, DatabaseError>>), + // TODO: paging and filtering, nullabillity for latest message + GetChatsOrderedWithLatestMessagesAndUsers( + oneshot::Sender<Result<Vec<((Chat, User), (Message, User))>, DatabaseError>>, + ), /// get a specific chat by jid GetChat(JID, oneshot::Sender<Result<Chat, DatabaseError>>), /// get message history for chat (does appropriate mam things) @@ -339,6 +343,22 @@ impl<Fs: FileStore> Client<Fs> { Ok(chats) } + pub async fn get_chats_ordered_with_latest_messages_and_users( + &self, + ) -> Result<Vec<((Chat, User), (Message, User))>, CommandError<DatabaseError>> { + let (send, recv) = oneshot::channel(); + self.send(CoreClientCommand::Command( + Command::GetChatsOrderedWithLatestMessagesAndUsers(send), + )) + .await + .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))?; + let chats = timeout(self.timeout, recv) + .await + .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))? + .map_err(|e| CommandError::Actor(Into::<ActorError>::into(e)))??; + Ok(chats) + } + pub async fn get_chat(&self, jid: JID) -> Result<Chat, CommandError<DatabaseError>> { let (send, recv) = oneshot::channel(); self.send(CoreClientCommand::Command(Command::GetChat(jid, send))) diff --git a/filamento/src/logic/local_only.rs b/filamento/src/logic/local_only.rs index 9db9d4d..dc94d2c 100644 --- a/filamento/src/logic/local_only.rs +++ b/filamento/src/logic/local_only.rs @@ -28,6 +28,15 @@ pub async fn handle_get_chats_ordered_with_latest_messages<Fs: FileStore + Clone Ok(logic.db().read_chats_ordered_with_latest_messages().await?) } +pub async fn handle_get_chats_ordered_with_latest_messages_and_users<Fs: FileStore + Clone>( + logic: &ClientLogic<Fs>, +) -> Result<Vec<((Chat, User), (Message, User))>, DatabaseError> { + Ok(logic + .db() + .read_chats_ordered_with_latest_messages_and_users() + .await?) +} + pub async fn handle_get_chat<Fs: FileStore + Clone>( logic: &ClientLogic<Fs>, jid: JID, diff --git a/filamento/src/logic/offline.rs b/filamento/src/logic/offline.rs index 9f1a3bc..b87484c 100644 --- a/filamento/src/logic/offline.rs +++ b/filamento/src/logic/offline.rs @@ -23,7 +23,8 @@ use super::{ local_only::{ handle_delete_chat, handle_delete_messaage, handle_get_chat, handle_get_chats, handle_get_chats_ordered, handle_get_chats_ordered_with_latest_messages, - handle_get_messages, handle_get_messages_with_users, handle_get_user, + handle_get_chats_ordered_with_latest_messages_and_users, handle_get_messages, + handle_get_messages_with_users, handle_get_user, }, }; @@ -80,6 +81,10 @@ pub async fn handle_offline_result<Fs: FileStore + Clone>( let chats = handle_get_chats_ordered_with_latest_messages(logic).await; sender.send(chats); } + Command::GetChatsOrderedWithLatestMessagesAndUsers(sender) => { + let chats = handle_get_chats_ordered_with_latest_messages_and_users(logic).await; + sender.send(chats); + } Command::GetChat(jid, sender) => { let chats = handle_get_chat(logic, jid).await; sender.send(chats); @@ -176,7 +181,6 @@ pub async fn handle_offline_result<Fs: FileStore + Clone>( jid: logic.bare_jid.clone(), nick: None, avatar: None, - cached_status_message: None, } } }; diff --git a/filamento/src/logic/online.rs b/filamento/src/logic/online.rs index 2cf2d5c..c560b9b 100644 --- a/filamento/src/logic/online.rs +++ b/filamento/src/logic/online.rs @@ -23,7 +23,7 @@ use crate::{ use super::{ local_only::{ - handle_delete_chat, handle_delete_messaage, handle_get_chat, handle_get_chats, handle_get_chats_ordered, handle_get_chats_ordered_with_latest_messages, handle_get_messages, handle_get_messages_with_users, handle_get_user + handle_delete_chat, handle_delete_messaage, handle_get_chat, handle_get_chats, handle_get_chats_ordered, handle_get_chats_ordered_with_latest_messages, handle_get_chats_ordered_with_latest_messages_and_users, handle_get_messages, handle_get_messages_with_users, handle_get_user }, ClientLogic }; @@ -487,7 +487,6 @@ pub async fn handle_send_message<Fs: FileStore + Clone>(logic: &ClientLogic<Fs>, jid: logic.bare_jid.clone(), nick: None, avatar: None, - cached_status_message: None, } }, }; @@ -1047,6 +1046,10 @@ pub async fn handle_online_result<Fs: FileStore + Clone>( let chats = handle_get_chats_ordered_with_latest_messages(logic).await; let _ = sender.send(chats); } + Command::GetChatsOrderedWithLatestMessagesAndUsers(sender) => { + let chats = handle_get_chats_ordered_with_latest_messages_and_users(logic).await; + sender.send(chats); + } Command::GetChat(jid, sender) => { let chat = handle_get_chat(logic, jid).await; let _ = sender.send(chat); diff --git a/filamento/src/logic/process_stanza.rs b/filamento/src/logic/process_stanza.rs index 1a29f70..cdaff97 100644 --- a/filamento/src/logic/process_stanza.rs +++ b/filamento/src/logic/process_stanza.rs @@ -112,7 +112,6 @@ pub async fn recv_message<Fs: FileStore + Clone>( jid: from.as_bare(), nick: None, avatar: None, - cached_status_message: None, } } }; diff --git a/filamento/src/user.rs b/filamento/src/user.rs index 3d5dcb4..8669fc3 100644 --- a/filamento/src/user.rs +++ b/filamento/src/user.rs @@ -5,5 +5,5 @@ pub struct User { pub jid: JID, pub nick: Option<String>, pub avatar: Option<String>, - pub cached_status_message: Option<String>, + // pub cached_status_message: Option<String>, } |