From b81f7f5bb418fb64211e5ec711cbcbecf8a681aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?cel=20=F0=9F=8C=B8?= Date: Thu, 6 Mar 2025 10:43:57 +0000 Subject: [PATCH] feat: order chats by most recent message --- README.md | 6 +++--- TODO.md | 22 +++++++++++----------- jabber/Cargo.toml | 2 +- luz/src/chat.rs | 5 +++++ luz/src/db/mod.rs | 20 ++++++++++++++------ luz/src/lib.rs | 4 ++-- 6 files changed, 36 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 06e98a3..0a7375b 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ## TODO: -- [ ] how to know if stanza has been sent +- [x] how to know if stanza has been sent - [ ] error states for all negotiation parts -- [ ] better errors +- [x] better errors - [x] rename structs - [x] remove commented code - [ ] asynchronous connect (with take_mut?) @@ -16,7 +16,7 @@ ### specs: - [x] rfc 6120: core -- [ ] rfc 6121: im +- [x] rfc 6121: im - [x] rfc 7590: tls - [x] xep-0368: srv records for xmpp over tls - [ ] server side downgrade protection for sasl diff --git a/TODO.md b/TODO.md index 47c86d9..aaa18a9 100644 --- a/TODO.md +++ b/TODO.md @@ -3,18 +3,7 @@ ## next feat(luz): everything in rfc6120 and rfc6121 - feat(luz): handle_online - feat(luz): handle_offline - feat(luz): handle_stanza - feat(luz): database feat(luz): error handling on stream according to rfc6120 - feat(luz): send message - feat(luz): receive message - feat(luz): retreive messages stored in database - feat(luz): get roster (online and offline) - feat(luz): set roster - feat(luz): reconnect supervisorcommand -feat: thiserror everywhere feat(luz): proper stanza ids test: proper tests ci: doc generation @@ -35,3 +24,14 @@ feature: sasl feature: resource binding feature: jabber client connection feature: jid +feat: thiserror everywhere +feat(luz): handle_online +feat(luz): handle_offline +feat(luz): handle_stanza +feat(luz): database +feat(luz): send message +feat(luz): receive message +feat(luz): retreive messages stored in database +feat(luz): get roster (online and offline) +feat(luz): set roster +feat(luz): reconnect supervisorcommand diff --git a/jabber/Cargo.toml b/jabber/Cargo.toml index b6093cf..0a50877 100644 --- a/jabber/Cargo.toml +++ b/jabber/Cargo.toml @@ -11,7 +11,7 @@ async-recursion = "1.0.4" async-trait = "0.1.68" lazy_static = "1.4.0" nanoid = "0.4.0" -# TODO: remove unneeded features +# TODO: remove unneeded features and dependencies rsasl = { version = "2.0.1", default_features = false, features = [ "provider_base64", "plain", diff --git a/luz/src/chat.rs b/luz/src/chat.rs index 97518da..c1194ea 100644 --- a/luz/src/chat.rs +++ b/luz/src/chat.rs @@ -31,10 +31,15 @@ pub struct Body { #[derive(sqlx::FromRow, Debug, Clone)] pub struct Chat { pub correspondent: JID, + // pub unread_messages: i32, + // pub latest_message: Message, + // when a new message is received, the chat should be updated, and the new message should be delivered too. // message history is not stored in chat, retreived separately. // pub message_history: Vec, } +pub enum ChatUpdate {} + impl Chat { pub fn new(correspondent: JID) -> Self { Self { correspondent } diff --git a/luz/src/db/mod.rs b/luz/src/db/mod.rs index 673ba48..d9fb3e3 100644 --- a/luz/src/db/mod.rs +++ b/luz/src/db/mod.rs @@ -129,12 +129,11 @@ impl Db { } pub(crate) async fn read_contact_opt(&self, contact: &JID) -> Result, Error> { - let contact: Option = sqlx::query_as( - "select * from roster full outer join users on jid = user_jid where jid = ?", - ) - .bind(contact) - .fetch_optional(&self.db) - .await?; + let contact: Option = + sqlx::query_as("select * from roster join users on jid = user_jid where jid = ?") + .bind(contact) + .fetch_optional(&self.db) + .await?; if let Some(mut contact) = contact { #[derive(sqlx::FromRow)] struct Row { @@ -325,6 +324,15 @@ impl Db { Ok(chats) } + /// chats ordered by date of last message + // greatest-n-per-group + pub(crate) async fn read_chats_ordered(&self) -> Result, Error> { + let chats = 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?; + Ok(chats) + } + async fn read_chat_id(&self, chat: JID) -> Result { #[derive(sqlx::FromRow)] struct Row { diff --git a/luz/src/lib.rs b/luz/src/lib.rs index c84d5ff..f7d8a23 100644 --- a/luz/src/lib.rs +++ b/luz/src/lib.rs @@ -323,7 +323,7 @@ impl CommandMessage { } } CommandMessage::GetChats(sender) => { - let chats = db.read_chats().await.map_err(|e| e.into()); + let chats = db.read_chats_ordered().await.map_err(|e| e.into()); sender.send(chats); } CommandMessage::GetChat(jid, sender) => { @@ -509,7 +509,7 @@ impl CommandMessage { } } CommandMessage::GetChats(sender) => { - let chats = db.read_chats().await.map_err(|e| e.into()); + let chats = db.read_chats_ordered().await.map_err(|e| e.into()); sender.send(chats); } CommandMessage::GetChat(jid, sender) => {