diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index ef64ed58d..3c24a3770 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -2439,6 +2439,32 @@ dc_lot_t* dc_chatlist_get_summary (const dc_chatlist_t* chatlist, siz dc_context_t* dc_chatlist_get_context (dc_chatlist_t* chatlist); +/** + * Get a summary for a chat (same as dc_chatlist_get_summary only without the chatlist). + * + * The summary is returned by a dc_lot_t object with the following fields: + * + * - dc_lot_t::text1: contains the username or the strings "Me", "Draft" and so on. + * The string may be colored by having a look at text1_meaning. + * If there is no such name or it should not be displayed, the element is NULL. + * + * - dc_lot_t::text1_meaning: one of DC_TEXT1_USERNAME, DC_TEXT1_SELF or DC_TEXT1_DRAFT. + * Typically used to show dc_lot_t::text1 with different colors. 0 if not applicable. + * + * - dc_lot_t::text2: contains an excerpt of the message text or strings as + * "No messages". May be NULL of there is no such text (eg. for the archive link) + * + * - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable. + * + * - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()). 0 if not applicable. + * + * @memberof dc_chat_t + * @param context + * @param chat The chat object. + * @return The summary as an dc_lot_t object. Must be freed using dc_lot_unref(). NULL is never returned. + */ +dc_lot_t* dc_chat_get_summary (dc_context_t* context, dc_chat_t* chat); + /** * Get info summary for a chat, in json format. * diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index b36b78976..76084d541 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -2319,6 +2319,23 @@ pub unsafe extern "C" fn dc_chat_get_visibility(chat: *mut dc_chat_t) -> libc::c } } +#[no_mangle] +pub unsafe extern "C" fn dc_chat_get_summary( + context: *mut dc_context_t, + chat: *mut dc_chat_t, +) -> *mut dc_lot_t { + if chat.is_null() { + eprintln!("ignoring careless call to dc_chat_get_summary()"); + return ptr::null_mut(); + } + let ffi_chat = &*chat; + let ctx = &*context; + block_on(async move { + let lot = ffi_chat.chat.get_summary(&ctx).await; + Box::into_raw(Box::new(lot)) + }) +} + #[no_mangle] pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int { if chat.is_null() { diff --git a/python/src/deltachat/chat.py b/python/src/deltachat/chat.py index 54a7ede0b..3fc3e94cc 100644 --- a/python/src/deltachat/chat.py +++ b/python/src/deltachat/chat.py @@ -5,7 +5,7 @@ import calendar import json from datetime import datetime import os -from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array +from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot from .capi import lib, ffi from . import const from .message import Message @@ -344,6 +344,11 @@ class Chat(object): s = from_dc_charpointer(dc_res) return json.loads(s) + def get_summary_lot(self): + """ return summary lot of chat (used for displaying last message in a chatlist)""" + lot = lib.dc_chat_get_summary(self.account._dc_context, self._dc_chat) + return DCLot(lot) + # ------ group management API ------------------------------ def add_contact(self, obj): diff --git a/src/chat.rs b/src/chat.rs index f21ce144a..11832456c 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -23,7 +23,7 @@ use crate::message::{self, InvalidMsgId, Message, MessageState, MsgId}; use crate::mimeparser::SystemMessage; use crate::param::*; use crate::sql; -use crate::stock::StockMessage; +use crate::{lot::Lot, stock::StockMessage}; /// An chat item, such as a message or a marker. #[derive(Debug, Copy, Clone)] @@ -734,6 +734,65 @@ impl Chat { }) } + /// Version of get_summary that just needs the chat no chatlist required + pub async fn get_summary(&self, context: &Context) -> Lot { + // The summary is created by the chat, not by the last message. + // This is because we may want to display drafts here or stuff as + // "is typing". + // Also, sth. as "No messages" would not work if the summary comes from a message. + let mut ret = Lot::new(); + + let lastmsg_id = context + .sql + .query_row( + concat!( + "SELECT id, chat_id", + " FROM msgs", + " WHERE chat_id = ?", + " ORDER BY id DESC", + " LIMIT 1" + ), + paramsv![self.id], + |row| { + let msg_id: MsgId = row.get("id")?; + Ok(msg_id) + }, + ) + .await + .unwrap_or_default(); + + let mut lastcontact = None; + + let lastmsg = if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id).await { + if lastmsg.from_id != DC_CONTACT_ID_SELF + && (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup) + { + lastcontact = Contact::load_from_db(context, lastmsg.from_id).await.ok(); + } + + Some(lastmsg) + } else { + None + }; + + if self.id.is_archived_link() { + ret.text2 = None; + } else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED + { + ret.text2 = Some( + context + .stock_str(StockMessage::NoMessages) + .await + .to_string(), + ); + } else { + ret.fill(&mut lastmsg.unwrap(), self, lastcontact.as_ref(), context) + .await; + } + + ret + } + pub fn get_visibility(&self) -> ChatVisibility { self.visibility } @@ -3497,4 +3556,20 @@ mod tests { chat_id.set_draft(&t.ctx, Some(&mut msg)).await; assert!(!chat_id.parent_is_encrypted(&t.ctx).await.unwrap()); } + + #[async_std::test] + async fn test_get_summary_unwrap() { + let t = TestContext::new().await; + let chat_id1 = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "a chat") + .await + .unwrap(); + + let mut msg = Message::new(Viewtype::Text); + msg.set_text(Some("foo:\nbar \r\n test".to_string())); + chat_id1.set_draft(&t.ctx, Some(&mut msg)).await; + + let chat = Chat::load_from_db(&t.ctx, chat_id1).await.unwrap(); + let summary = chat.get_summary(&t.ctx).await; + assert_eq!(summary.get_text2().unwrap(), "foo: bar test"); // the linebreak should be removed from summary + } }