mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 05:22:14 +03:00
Compare commits
2 Commits
v2.23.0
...
iequidoo/d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d44e521814 | ||
|
|
81c13beba3 |
@@ -3477,28 +3477,6 @@ uint32_t dc_chatlist_get_msg_id (const dc_chatlist_t* chatlist, siz
|
||||
dc_lot_t* dc_chatlist_get_summary (const dc_chatlist_t* chatlist, size_t index, dc_chat_t* chat);
|
||||
|
||||
|
||||
/**
|
||||
* Create a chatlist summary item when the chatlist object is already unref()'d.
|
||||
*
|
||||
* This function is similar to dc_chatlist_get_summary(), however,
|
||||
* it takes the chat ID and the message ID as returned by dc_chatlist_get_chat_id() and dc_chatlist_get_msg_id()
|
||||
* as arguments. The chatlist object itself is not needed directly.
|
||||
*
|
||||
* This maybe useful if you convert the complete object into a different representation
|
||||
* as done e.g. in the node-bindings.
|
||||
* If you have access to the chatlist object in some way, using this function is not recommended,
|
||||
* use dc_chatlist_get_summary() in this case instead.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param chat_id The chat ID to get a summary for.
|
||||
* @param msg_id The message ID to get a summary for.
|
||||
* @return The summary as an dc_lot_t object, see dc_chatlist_get_summary() for details.
|
||||
* Must be freed using dc_lot_unref(). NULL is never returned.
|
||||
*/
|
||||
dc_lot_t* dc_chatlist_get_summary2 (dc_context_t* context, uint32_t chat_id, uint32_t msg_id);
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to get the associated context object.
|
||||
*
|
||||
|
||||
@@ -48,7 +48,6 @@ mod dc_array;
|
||||
mod lot;
|
||||
|
||||
mod string;
|
||||
use deltachat::chatlist::Chatlist;
|
||||
|
||||
use self::string::*;
|
||||
|
||||
@@ -2889,34 +2888,6 @@ pub unsafe extern "C" fn dc_chatlist_get_summary(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chatlist_get_summary2(
|
||||
context: *mut dc_context_t,
|
||||
chat_id: u32,
|
||||
msg_id: u32,
|
||||
) -> *mut dc_lot_t {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_chatlist_get_summary2()");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let ctx = &*context;
|
||||
let msg_id = if msg_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(MsgId::new(msg_id))
|
||||
};
|
||||
let summary = block_on(Chatlist::get_summary2(
|
||||
ctx,
|
||||
ChatId::new(chat_id),
|
||||
msg_id,
|
||||
None,
|
||||
))
|
||||
.context("get_summary2 failed")
|
||||
.log_err(ctx)
|
||||
.unwrap_or_default();
|
||||
Box::into_raw(Box::new(summary.into()))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chatlist_get_context(
|
||||
chatlist: *mut dc_chatlist_t,
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use anyhow::{Context, Result};
|
||||
use deltachat::chat::{get_chat_contacts, ChatVisibility};
|
||||
use deltachat::chat::{Chat, ChatId};
|
||||
use deltachat::chatlist::get_last_message_for_chat;
|
||||
use deltachat::chatlist::Chatlist;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::{Contact, ContactId};
|
||||
use deltachat::{
|
||||
chat::{get_chat_contacts, ChatVisibility},
|
||||
chatlist::Chatlist,
|
||||
};
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use serde::Serialize;
|
||||
use typescript_type_def::TypeDef;
|
||||
@@ -67,10 +64,8 @@ pub(crate) async fn get_chat_list_item_by_id(
|
||||
});
|
||||
}
|
||||
|
||||
let last_msgid = get_last_message_for_chat(ctx, chat_id).await?;
|
||||
|
||||
let chat = Chat::load_from_db(ctx, chat_id).await.context("chat")?;
|
||||
let summary = Chatlist::get_summary2(ctx, chat_id, last_msgid, Some(&chat))
|
||||
let summary = Chatlist::get_summary2(ctx, chat_id, Some(&chat))
|
||||
.await
|
||||
.context("summary")?;
|
||||
|
||||
@@ -86,15 +81,12 @@ pub(crate) async fn get_chat_list_item_by_id(
|
||||
.await?
|
||||
.map(|path| path.to_str().unwrap_or("invalid/path").to_owned());
|
||||
|
||||
let (last_updated, message_type) = match last_msgid {
|
||||
Some(id) => {
|
||||
let last_message = deltachat::message::Message::load_from_db(ctx, id).await?;
|
||||
(
|
||||
Some(last_message.get_timestamp() * 1000),
|
||||
Some(last_message.get_viewtype().into()),
|
||||
)
|
||||
}
|
||||
let (last_updated, message_type) = match summary.id {
|
||||
None => (None, None),
|
||||
Some(_) => (
|
||||
Some(summary.timestamp * 1000),
|
||||
Some(summary.viewtype.into()),
|
||||
),
|
||||
};
|
||||
|
||||
let chat_contacts = get_chat_contacts(ctx, chat_id).await?;
|
||||
@@ -145,6 +137,6 @@ pub(crate) async fn get_chat_list_item_by_id(
|
||||
dm_chat_contact,
|
||||
was_seen_recently,
|
||||
last_message_type: message_type,
|
||||
last_message_id: last_msgid.map(|id| id.to_u32()),
|
||||
last_message_id: summary.id.map(|id| id.to_u32()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -345,13 +345,6 @@ export class Context extends EventEmitter {
|
||||
return binding.dcn_get_mime_headers(this.dcn_context, Number(messageId))
|
||||
}
|
||||
|
||||
getChatlistItemSummary(chatId: number, messageId: number) {
|
||||
debug(`getChatlistItemSummary ${chatId} ${messageId}`)
|
||||
return new Lot(
|
||||
binding.dcn_chatlist_get_summary2(this.dcn_context, chatId, messageId)
|
||||
)
|
||||
}
|
||||
|
||||
getChatMessages(chatId: number, flags: number, marker1before: number) {
|
||||
debug(`getChatMessages ${chatId} ${flags} ${marker1before}`)
|
||||
return binding.dcn_get_chat_msgs(
|
||||
|
||||
@@ -1792,28 +1792,6 @@ NAPI_METHOD(dcn_chatlist_get_summary) {
|
||||
return result;
|
||||
}
|
||||
|
||||
NAPI_METHOD(dcn_chatlist_get_summary2) {
|
||||
NAPI_ARGV(3);
|
||||
NAPI_DCN_CONTEXT();
|
||||
NAPI_ARGV_INT32(chat_id, 1);
|
||||
NAPI_ARGV_INT32(message_id, 2);
|
||||
|
||||
//TRACE("calling..");
|
||||
dc_lot_t* summary = dc_chatlist_get_summary2(dcn_context->dc_context, chat_id, message_id);
|
||||
|
||||
napi_value result;
|
||||
if (summary == NULL) {
|
||||
NAPI_STATUS_THROWS(napi_get_null(env, &result));
|
||||
} else {
|
||||
NAPI_STATUS_THROWS(napi_create_external(env, summary,
|
||||
finalize_lot,
|
||||
NULL, &result));
|
||||
}
|
||||
//TRACE("done");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* dc_contact_t
|
||||
*/
|
||||
@@ -3511,7 +3489,6 @@ NAPI_INIT() {
|
||||
NAPI_EXPORT_FUNCTION(dcn_chatlist_get_cnt);
|
||||
NAPI_EXPORT_FUNCTION(dcn_chatlist_get_msg_id);
|
||||
NAPI_EXPORT_FUNCTION(dcn_chatlist_get_summary);
|
||||
NAPI_EXPORT_FUNCTION(dcn_chatlist_get_summary2);
|
||||
|
||||
/**
|
||||
* dc_contact_t
|
||||
|
||||
47
src/chat.rs
47
src/chat.rs
@@ -39,6 +39,7 @@ use crate::receive_imf::ReceivedMsg;
|
||||
use crate::smtp::send_msg_to_smtp;
|
||||
use crate::sql;
|
||||
use crate::stock_str;
|
||||
use crate::summary::Summary;
|
||||
use crate::sync::{self, Sync::*, SyncData};
|
||||
use crate::tools::{
|
||||
buf_compress, create_id, create_outgoing_rfc724_mid, create_smeared_timestamp,
|
||||
@@ -1329,6 +1330,52 @@ impl ChatId {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a summary for a given chat.
|
||||
pub async fn get_summary(
|
||||
self,
|
||||
context: &Context,
|
||||
chat: Option<&Chat>,
|
||||
msg: Option<&Message>,
|
||||
) -> Result<Summary> {
|
||||
let chat_loaded: Chat;
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
let chat = Chat::load_from_db(context, self).await?;
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
};
|
||||
|
||||
let lastcontact = if let Some(msg) = msg {
|
||||
if msg.from_id == ContactId::SELF {
|
||||
None
|
||||
} else {
|
||||
match chat.typ {
|
||||
Chattype::Group | Chattype::Broadcast | Chattype::Mailinglist => {
|
||||
let lastcontact = Contact::get_by_id(context, msg.from_id)
|
||||
.await
|
||||
.context("Loading contact failed")?;
|
||||
Some(lastcontact)
|
||||
}
|
||||
Chattype::Single => None,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if chat.id.is_archived_link() {
|
||||
Ok(Default::default())
|
||||
} else if let Some(msg) = msg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
|
||||
Summary::new(context, msg, chat, lastcontact.as_ref()).await
|
||||
} else {
|
||||
Ok(Summary {
|
||||
text: stock_str::no_messages(context).await,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the chat is protected.
|
||||
pub async fn is_protected(self, context: &Context) -> Result<ProtectionStatus> {
|
||||
let protection_status = context
|
||||
|
||||
@@ -8,11 +8,10 @@ use crate::constants::{
|
||||
Blocked, Chattype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK, DC_GCL_ADD_ALLDONE_HINT,
|
||||
DC_GCL_ARCHIVED_ONLY, DC_GCL_FOR_FORWARDING, DC_GCL_NO_SPECIALS,
|
||||
};
|
||||
use crate::contact::{Contact, ContactId};
|
||||
use crate::contact::ContactId;
|
||||
use crate::context::Context;
|
||||
use crate::message::{Message, MessageState, MsgId};
|
||||
use crate::param::{Param, Params};
|
||||
use crate::stock_str;
|
||||
use crate::summary::Summary;
|
||||
use crate::tools::IsNoneOrEmpty;
|
||||
|
||||
@@ -22,7 +21,8 @@ pub static IS_UNREAD_FILTER: Lazy<regex::Regex> =
|
||||
|
||||
/// An object representing a single chatlist in memory.
|
||||
///
|
||||
/// Chatlist objects contain chat IDs and, if possible, message IDs belonging to them.
|
||||
/// Chatlist objects contain IDs of chats and, optionally, their last messages, recently updated
|
||||
/// come first.
|
||||
/// The chatlist object is not updated; if you want an update, you have to recreate the object.
|
||||
///
|
||||
/// For a **typical chat overview**, the idea is to get the list of all chats via dc_get_chatlist()
|
||||
@@ -347,9 +347,11 @@ impl Chatlist {
|
||||
Ok(*chat_id)
|
||||
}
|
||||
|
||||
/// Get a single message ID of a chatlist.
|
||||
/// Get the last message ID of a chat with index `index`.
|
||||
///
|
||||
/// To get the message object from the message ID, use dc_get_msg().
|
||||
/// If you want to obtain the [Message] object further, it's not recommended to use as the
|
||||
/// message may expire (if it's ephemeral f.e.). Better use [`Self::get_chat_id()`] +
|
||||
/// [`Message::load_from_db_last_for_chat()`].
|
||||
pub fn get_msg_id(&self, index: usize) -> Result<Option<MsgId>> {
|
||||
let (_chat_id, msg_id) = self
|
||||
.ids
|
||||
@@ -373,56 +375,28 @@ impl Chatlist {
|
||||
.ids
|
||||
.get(index)
|
||||
.context("chatlist index is out of range")?;
|
||||
Chatlist::get_summary2(context, *chat_id, *lastmsg_id, chat).await
|
||||
let lastmsg = if let Some(lastmsg_id) = lastmsg_id {
|
||||
Some(
|
||||
Message::load_from_db(context, *lastmsg_id)
|
||||
.await
|
||||
.with_context(|| format!("Loading message {lastmsg_id} failed"))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
chat_id.get_summary(context, chat, lastmsg.as_ref()).await
|
||||
}
|
||||
|
||||
/// Returns a summary for a given chatlist item.
|
||||
/// Returns a summary for a given chat.
|
||||
pub async fn get_summary2(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
lastmsg_id: Option<MsgId>,
|
||||
chat: Option<&Chat>,
|
||||
) -> Result<Summary> {
|
||||
let chat_loaded: Chat;
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
let chat = Chat::load_from_db(context, chat_id).await?;
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
};
|
||||
|
||||
let (lastmsg, lastcontact) = if let Some(lastmsg_id) = lastmsg_id {
|
||||
let lastmsg = Message::load_from_db(context, lastmsg_id)
|
||||
.await
|
||||
.context("loading message failed")?;
|
||||
if lastmsg.from_id == ContactId::SELF {
|
||||
(Some(lastmsg), None)
|
||||
} else {
|
||||
match chat.typ {
|
||||
Chattype::Group | Chattype::Broadcast | Chattype::Mailinglist => {
|
||||
let lastcontact = Contact::get_by_id(context, lastmsg.from_id)
|
||||
.await
|
||||
.context("loading contact failed")?;
|
||||
(Some(lastmsg), Some(lastcontact))
|
||||
}
|
||||
Chattype::Single => (Some(lastmsg), None),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
if chat.id.is_archived_link() {
|
||||
Ok(Default::default())
|
||||
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
|
||||
Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await
|
||||
} else {
|
||||
Ok(Summary {
|
||||
text: stock_str::no_messages(context).await,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
let lastmsg = Message::load_from_db_last_for_chat(context, chat_id)
|
||||
.await
|
||||
.context("Loading message failed")?;
|
||||
chat_id.get_summary(context, chat, lastmsg.as_ref()).await
|
||||
}
|
||||
|
||||
/// Returns chatlist item position for the given chat ID.
|
||||
@@ -431,6 +405,9 @@ impl Chatlist {
|
||||
}
|
||||
|
||||
/// An iterator visiting all chatlist items.
|
||||
///
|
||||
/// See [`Self::get_msg_id()`] why it's not recommended to use returned [MsgId]-s to obtain
|
||||
/// [Message] objects and what to use instead.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &(ChatId, Option<MsgId>)> {
|
||||
self.ids.iter()
|
||||
}
|
||||
@@ -448,8 +425,10 @@ pub async fn get_archived_cnt(context: &Context) -> Result<usize> {
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Gets the last message of a chat, the message that would also be displayed in the ChatList
|
||||
/// Used for passing to `deltachat::chatlist::Chatlist::get_summary2`
|
||||
/// Gets the id of the last user-visible message of a chat.
|
||||
///
|
||||
/// See [`Chatlist::get_msg_id()`] why it's not recommended to use returned [MsgId] to obtain the
|
||||
/// [Message] object and what to use instead.
|
||||
pub async fn get_last_message_for_chat(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
@@ -474,7 +453,8 @@ mod tests {
|
||||
add_contact_to_chat, create_group_chat, get_chat_contacts, remove_contact_from_chat,
|
||||
send_text_msg, ProtectionStatus,
|
||||
};
|
||||
use crate::message::Viewtype;
|
||||
use crate::contact::Contact;
|
||||
use crate::message::{Message, Viewtype};
|
||||
use crate::receive_imf::receive_imf;
|
||||
use crate::stock_str::StockMessage;
|
||||
use crate::test_utils::TestContext;
|
||||
|
||||
183
src/message.rs
183
src/message.rs
@@ -480,87 +480,9 @@ impl Message {
|
||||
let msg = context
|
||||
.sql
|
||||
.query_row_optional(
|
||||
concat!(
|
||||
"SELECT",
|
||||
" m.id AS id,",
|
||||
" rfc724_mid AS rfc724mid,",
|
||||
" m.mime_in_reply_to AS mime_in_reply_to,",
|
||||
" m.chat_id AS chat_id,",
|
||||
" m.from_id AS from_id,",
|
||||
" m.to_id AS to_id,",
|
||||
" m.timestamp AS timestamp,",
|
||||
" m.timestamp_sent AS timestamp_sent,",
|
||||
" m.timestamp_rcvd AS timestamp_rcvd,",
|
||||
" m.ephemeral_timer AS ephemeral_timer,",
|
||||
" m.ephemeral_timestamp AS ephemeral_timestamp,",
|
||||
" m.type AS type,",
|
||||
" m.state AS state,",
|
||||
" m.download_state AS download_state,",
|
||||
" m.error AS error,",
|
||||
" m.msgrmsg AS msgrmsg,",
|
||||
" m.mime_modified AS mime_modified,",
|
||||
" m.txt AS txt,",
|
||||
" m.subject AS subject,",
|
||||
" m.param AS param,",
|
||||
" m.hidden AS hidden,",
|
||||
" m.location_id AS location,",
|
||||
" c.blocked AS blocked",
|
||||
" FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id",
|
||||
" WHERE m.id=?;"
|
||||
),
|
||||
&Self::select_query_with_filter("WHERE m.id=?"),
|
||||
(id,),
|
||||
|row| {
|
||||
let text = match row.get_ref("txt")? {
|
||||
rusqlite::types::ValueRef::Text(buf) => {
|
||||
match String::from_utf8(buf.to_vec()) {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
warn!(
|
||||
context,
|
||||
concat!(
|
||||
"dc_msg_load_from_db: could not get ",
|
||||
"text column as non-lossy utf8 id {}"
|
||||
),
|
||||
id
|
||||
);
|
||||
String::from_utf8_lossy(buf).into_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
let msg = Message {
|
||||
id: row.get("id")?,
|
||||
rfc724_mid: row.get::<_, String>("rfc724mid")?,
|
||||
in_reply_to: row
|
||||
.get::<_, Option<String>>("mime_in_reply_to")?
|
||||
.and_then(|in_reply_to| parse_message_id(&in_reply_to).ok()),
|
||||
chat_id: row.get("chat_id")?,
|
||||
from_id: row.get("from_id")?,
|
||||
to_id: row.get("to_id")?,
|
||||
timestamp_sort: row.get("timestamp")?,
|
||||
timestamp_sent: row.get("timestamp_sent")?,
|
||||
timestamp_rcvd: row.get("timestamp_rcvd")?,
|
||||
ephemeral_timer: row.get("ephemeral_timer")?,
|
||||
ephemeral_timestamp: row.get("ephemeral_timestamp")?,
|
||||
viewtype: row.get("type")?,
|
||||
state: row.get("state")?,
|
||||
download_state: row.get("download_state")?,
|
||||
error: Some(row.get::<_, String>("error")?)
|
||||
.filter(|error| !error.is_empty()),
|
||||
is_dc_message: row.get("msgrmsg")?,
|
||||
mime_modified: row.get("mime_modified")?,
|
||||
text,
|
||||
subject: row.get("subject")?,
|
||||
param: row.get::<_, String>("param")?.parse().unwrap_or_default(),
|
||||
hidden: row.get("hidden")?,
|
||||
location_id: row.get("location")?,
|
||||
chat_blocked: row
|
||||
.get::<_, Option<Blocked>>("blocked")?
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
Ok(msg)
|
||||
},
|
||||
|row| Self::from_row(context, row),
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("failed to load message {id} from the database"))?;
|
||||
@@ -568,6 +490,107 @@ impl Message {
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Loads the last user-visible message of the chat with given `id` from the database.
|
||||
pub async fn load_from_db_last_for_chat(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
) -> Result<Option<Message>> {
|
||||
let msg = context
|
||||
.sql
|
||||
.query_row_optional(
|
||||
&Self::select_query_with_filter(concat!(
|
||||
"WHERE m.chat_id=? ",
|
||||
"AND (m.hidden=0 OR m.state=?) ",
|
||||
"ORDER BY m.timestamp DESC, m.id DESC LIMIT 1",
|
||||
)),
|
||||
(chat_id, MessageState::OutDraft),
|
||||
|row| Self::from_row(context, row),
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Failed to load last message for chat {chat_id} from the database")
|
||||
})?;
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
fn select_query_with_filter(filter: &str) -> String {
|
||||
"\
|
||||
SELECT \
|
||||
m.id AS id, \
|
||||
rfc724_mid AS rfc724mid, \
|
||||
m.mime_in_reply_to AS mime_in_reply_to, \
|
||||
m.chat_id AS chat_id, \
|
||||
m.from_id AS from_id, \
|
||||
m.to_id AS to_id, \
|
||||
m.timestamp AS timestamp, \
|
||||
m.timestamp_sent AS timestamp_sent, \
|
||||
m.timestamp_rcvd AS timestamp_rcvd, \
|
||||
m.ephemeral_timer AS ephemeral_timer, \
|
||||
m.ephemeral_timestamp AS ephemeral_timestamp, \
|
||||
m.type AS type, \
|
||||
m.state AS state, \
|
||||
m.download_state AS download_state, \
|
||||
m.error AS error, \
|
||||
m.msgrmsg AS msgrmsg, \
|
||||
m.mime_modified AS mime_modified, \
|
||||
m.txt AS txt, \
|
||||
m.subject AS subject, \
|
||||
m.param AS param, \
|
||||
m.hidden AS hidden, \
|
||||
m.location_id AS location, \
|
||||
c.blocked AS blocked \
|
||||
FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id "
|
||||
.to_string()
|
||||
+ filter
|
||||
}
|
||||
|
||||
fn from_row(context: &Context, row: &rusqlite::Row) -> rusqlite::Result<Message> {
|
||||
let text = match row.get_ref("txt")? {
|
||||
rusqlite::types::ValueRef::Text(buf) => match String::from_utf8(buf.to_vec()) {
|
||||
Ok(t) => t,
|
||||
Err(_) => {
|
||||
warn!(
|
||||
context,
|
||||
"Message::from_row: could not get text column as a non-lossy UTF-8",
|
||||
);
|
||||
String::from_utf8_lossy(buf).into_owned()
|
||||
}
|
||||
},
|
||||
_ => String::new(),
|
||||
};
|
||||
let msg = Message {
|
||||
id: row.get("id")?,
|
||||
rfc724_mid: row.get::<_, String>("rfc724mid")?,
|
||||
in_reply_to: row
|
||||
.get::<_, Option<String>>("mime_in_reply_to")?
|
||||
.and_then(|in_reply_to| parse_message_id(&in_reply_to).ok()),
|
||||
chat_id: row.get("chat_id")?,
|
||||
from_id: row.get("from_id")?,
|
||||
to_id: row.get("to_id")?,
|
||||
timestamp_sort: row.get("timestamp")?,
|
||||
timestamp_sent: row.get("timestamp_sent")?,
|
||||
timestamp_rcvd: row.get("timestamp_rcvd")?,
|
||||
ephemeral_timer: row.get("ephemeral_timer")?,
|
||||
ephemeral_timestamp: row.get("ephemeral_timestamp")?,
|
||||
viewtype: row.get("type")?,
|
||||
state: row.get("state")?,
|
||||
download_state: row.get("download_state")?,
|
||||
error: Some(row.get::<_, String>("error")?).filter(|error| !error.is_empty()),
|
||||
is_dc_message: row.get("msgrmsg")?,
|
||||
mime_modified: row.get("mime_modified")?,
|
||||
text,
|
||||
subject: row.get("subject")?,
|
||||
param: row.get::<_, String>("param")?.parse().unwrap_or_default(),
|
||||
hidden: row.get("hidden")?,
|
||||
location_id: row.get("location")?,
|
||||
chat_blocked: row
|
||||
.get::<_, Option<Blocked>>("blocked")?
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Returns the MIME type of an attached file if it exists.
|
||||
///
|
||||
/// If the MIME type is not known, the function guesses the MIME type
|
||||
|
||||
@@ -3447,8 +3447,11 @@ On 2020-10-25, Bob wrote:
|
||||
.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).await.unwrap();
|
||||
let msg_id = chats.get_msg_id(0).unwrap().unwrap();
|
||||
let msg = Message::load_from_db(&t.ctx, msg_id).await.unwrap();
|
||||
let chat_id = chats.get_chat_id(0).unwrap();
|
||||
let msg = Message::load_from_db_last_for_chat(&t.ctx, chat_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(msg.text, "subj with important info – body text");
|
||||
assert_eq!(msg.viewtype, Viewtype::Image);
|
||||
|
||||
@@ -650,9 +650,9 @@ impl Peerstate {
|
||||
.await
|
||||
}
|
||||
};
|
||||
for (chat_id, msg_id) in chats.iter() {
|
||||
let timestamp_sort = if let Some(msg_id) = msg_id {
|
||||
let lastmsg = Message::load_from_db(context, *msg_id).await?;
|
||||
for (chat_id, _) in chats.iter() {
|
||||
let lastmsg = Message::load_from_db_last_for_chat(context, *chat_id).await?;
|
||||
let timestamp_sort = if let Some(lastmsg) = lastmsg {
|
||||
lastmsg.timestamp_sort
|
||||
} else {
|
||||
context
|
||||
|
||||
@@ -413,6 +413,7 @@ async fn test_no_from() {
|
||||
let context = &t;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert!(chats.get_chat_id(0).is_err());
|
||||
assert!(chats.get_msg_id(0).is_err());
|
||||
|
||||
let received = receive_imf(
|
||||
@@ -455,6 +456,7 @@ async fn test_no_message_id_header() {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
assert!(chats.get_chat_id(0).is_err());
|
||||
assert!(chats.get_msg_id(0).is_err());
|
||||
|
||||
let received = receive_imf(
|
||||
@@ -557,8 +559,9 @@ async fn test_escaped_recipients() {
|
||||
assert_eq!(contact.get_display_name(), "h2");
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
let msg = Message::load_from_db(&t, chats.get_msg_id(0).unwrap().unwrap())
|
||||
let msg = Message::load_from_db_last_for_chat(&t, chats.get_chat_id(0).unwrap())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(msg.is_dc_message, MessengerMessage::Yes);
|
||||
assert_eq!(msg.text, "hello");
|
||||
@@ -745,7 +748,7 @@ async fn test_parse_ndn(
|
||||
.unwrap();
|
||||
|
||||
let chats = Chatlist::try_load(&t, 0, None, None).await.unwrap();
|
||||
let msg_id = chats.get_msg_id(0).unwrap().unwrap();
|
||||
let chat_id = chats.get_chat_id(0).unwrap();
|
||||
|
||||
// Check that the ndn would be downloaded:
|
||||
let headers = mailparse::parse_mail(raw_ndn).unwrap().headers;
|
||||
@@ -756,7 +759,10 @@ async fn test_parse_ndn(
|
||||
);
|
||||
|
||||
receive_imf(&t, raw_ndn, false).await.unwrap();
|
||||
let msg = Message::load_from_db(&t, msg_id).await.unwrap();
|
||||
let msg = Message::load_from_db_last_for_chat(&t, chat_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
msg.state,
|
||||
@@ -768,7 +774,7 @@ async fn test_parse_ndn(
|
||||
);
|
||||
|
||||
assert_eq!(msg.error(), error_msg.map(|error| error.to_string()));
|
||||
(t, msg_id)
|
||||
(t, msg.id)
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
@@ -860,8 +866,11 @@ async fn load_imf_email(context: &Context, imf_raw: &[u8]) -> Message {
|
||||
.unwrap();
|
||||
receive_imf(context, imf_raw, false).await.unwrap();
|
||||
let chats = Chatlist::try_load(context, 0, None, None).await.unwrap();
|
||||
let msg_id = chats.get_msg_id(0).unwrap().unwrap();
|
||||
Message::load_from_db(context, msg_id).await.unwrap()
|
||||
let chat_id = chats.get_chat_id(0).unwrap();
|
||||
Message::load_from_db_last_for_chat(context, chat_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::chat::Chat;
|
||||
use crate::constants::Chattype;
|
||||
use crate::contact::{Contact, ContactId};
|
||||
use crate::context::Context;
|
||||
use crate::message::{Message, MessageState, Viewtype};
|
||||
use crate::message::{Message, MessageState, MsgId, Viewtype};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::stock_str;
|
||||
use crate::stock_str::msg_reacted;
|
||||
@@ -54,6 +54,12 @@ pub struct Summary {
|
||||
|
||||
/// Message preview image path
|
||||
pub thumbnail_path: Option<String>,
|
||||
|
||||
/// Message viewtype.
|
||||
pub viewtype: Viewtype,
|
||||
|
||||
/// Message ID.
|
||||
pub id: Option<MsgId>,
|
||||
}
|
||||
|
||||
impl Summary {
|
||||
@@ -79,6 +85,8 @@ impl Summary {
|
||||
timestamp: msg.get_timestamp(), // message timestamp (not reaction) to make timestamps more consistent with chats ordering
|
||||
state: msg.state, // message state (not reaction) - indicating if it was me sending the last message
|
||||
thumbnail_path: None,
|
||||
viewtype: msg.viewtype,
|
||||
id: Some(msg.id),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,6 +135,8 @@ impl Summary {
|
||||
timestamp: msg.get_timestamp(),
|
||||
state: msg.state,
|
||||
thumbnail_path,
|
||||
viewtype: msg.viewtype,
|
||||
id: Some(msg.id),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -561,10 +561,11 @@ impl TestContext {
|
||||
// The chatlist describes what you see when you open DC, a list of chats and in each of them
|
||||
// the first words of the last message. To get the last message overall, we look at the chat at the top of the
|
||||
// list, which has the index 0.
|
||||
let msg_id = chats.get_msg_id(0).unwrap().unwrap();
|
||||
Message::load_from_db(&self.ctx, msg_id)
|
||||
let chat_id = chats.get_chat_id(0).unwrap();
|
||||
Message::load_from_db_last_for_chat(&self.ctx, chat_id)
|
||||
.await
|
||||
.expect("failed to load msg")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Returns the [`Contact`] for the other [`TestContext`], creating it if necessary.
|
||||
|
||||
Reference in New Issue
Block a user