mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 04:46:29 +03:00
feat: show reactions in summaries (#5387)
shows the last reaction in chatlist's summaries if there is no newer message. the reason to show reactions in the summary, is to make them a _little_ more visible when one is not in the chat. esp. in not-so-chatty or in one-to-ones chats this becomes handy: imaging a question and someone "answers" with "thumbs up" ... otoh, reactions are still tuned down on purpose: no notifications, chats are opend as usual, the chatlist is not sorted by reactions and also the date in the summary refer to the last message - i thought quite a bit about that, this seems to be good compromise and will raise the fewest questions. it is somehow clear to the users that reactions are not the same as a real message. also, it is comparable easy to implement - no UI changes required :) all that is very close to what whatsapp is doing (figured that out by quite some testing ... to cite @adbenitez: if in doubt, we can blame whatsapp :) technically, i first wanted to go for the "big solution" and add two more columns, chat_id and timestamp, however, it seemed a bit bloated if we really only need the last one. therefore, i just added the last reaction information to the chat's param, which seems more performant but also easier to code :)
This commit is contained in:
@@ -7296,6 +7296,22 @@ void dc_event_unref(dc_event_t* event);
|
|||||||
/// `%1$s` will be replaced by the provider's domain.
|
/// `%1$s` will be replaced by the provider's domain.
|
||||||
#define DC_STR_INVALID_UNENCRYPTED_MAIL 174
|
#define DC_STR_INVALID_UNENCRYPTED_MAIL 174
|
||||||
|
|
||||||
|
/// "You reacted %1$s to '%2$s'"
|
||||||
|
///
|
||||||
|
/// `%1$s` will be replaced by the reaction, usually an emoji
|
||||||
|
/// `%2$s` will be replaced by the summary of the message the reaction refers to
|
||||||
|
///
|
||||||
|
/// Used in summaries.
|
||||||
|
#define DC_STR_YOU_REACTED 176
|
||||||
|
|
||||||
|
/// "%1$s reacted %2$s to '%3$s'"
|
||||||
|
///
|
||||||
|
/// `%1$s` will be replaced by the name the contact who reacted
|
||||||
|
/// `%2$s` will be replaced by the reaction, usually an emoji
|
||||||
|
/// `%3$s` will be replaced by the summary of the message the reaction refers to
|
||||||
|
///
|
||||||
|
/// Used in summaries.
|
||||||
|
#define DC_STR_REACTED_BY 177
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ impl Chatlist {
|
|||||||
if chat.id.is_archived_link() {
|
if chat.id.is_archived_link() {
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
|
} else if let Some(lastmsg) = lastmsg.filter(|msg| msg.from_id != ContactId::UNDEFINED) {
|
||||||
Ok(Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await)
|
Summary::new(context, &lastmsg, chat, lastcontact.as_ref()).await
|
||||||
} else {
|
} else {
|
||||||
Ok(Summary {
|
Ok(Summary {
|
||||||
text: stock_str::no_messages(context).await,
|
text: stock_str::no_messages(context).await,
|
||||||
|
|||||||
@@ -796,7 +796,7 @@ impl Message {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Summary::new(context, self, chat, contact.as_ref()).await)
|
Summary::new(context, self, chat, contact.as_ref()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's a little unfortunate that the UI has to first call `dc_msg_get_override_sender_name` and then if it was `NULL`, call
|
// It's a little unfortunate that the UI has to first call `dc_msg_get_override_sender_name` and then if it was `NULL`, call
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ pub enum Param {
|
|||||||
/// For Messages: the message is a reaction.
|
/// For Messages: the message is a reaction.
|
||||||
Reaction = b'x',
|
Reaction = b'x',
|
||||||
|
|
||||||
|
/// For Chats: the timestamp of the last reaction.
|
||||||
|
LastReactionTimestamp = b'y',
|
||||||
|
|
||||||
|
/// For Chats: Message ID of the last reaction.
|
||||||
|
LastReactionMsgId = b'Y',
|
||||||
|
|
||||||
|
/// For Chats: Contact ID of the last reaction.
|
||||||
|
LastReactionContactId = b'1',
|
||||||
|
|
||||||
/// For Messages: a message with "Auto-Submitted: auto-generated" header ("bot").
|
/// For Messages: a message with "Auto-Submitted: auto-generated" header ("bot").
|
||||||
Bot = b'b',
|
Bot = b'b',
|
||||||
|
|
||||||
|
|||||||
181
src/reaction.rs
181
src/reaction.rs
@@ -20,11 +20,12 @@ use std::fmt;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::chat::{send_msg, ChatId};
|
use crate::chat::{send_msg, Chat, ChatId};
|
||||||
use crate::contact::ContactId;
|
use crate::contact::ContactId;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::message::{rfc724_mid_exists, Message, MsgId, Viewtype};
|
use crate::message::{rfc724_mid_exists, Message, MsgId, Viewtype};
|
||||||
|
use crate::param::Param;
|
||||||
|
|
||||||
/// A single reaction consisting of multiple emoji sequences.
|
/// A single reaction consisting of multiple emoji sequences.
|
||||||
///
|
///
|
||||||
@@ -170,6 +171,7 @@ async fn set_msg_id_reaction(
|
|||||||
msg_id: MsgId,
|
msg_id: MsgId,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
contact_id: ContactId,
|
contact_id: ContactId,
|
||||||
|
timestamp: i64,
|
||||||
reaction: Reaction,
|
reaction: Reaction,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if reaction.is_empty() {
|
if reaction.is_empty() {
|
||||||
@@ -194,6 +196,17 @@ async fn set_msg_id_reaction(
|
|||||||
(msg_id, contact_id, reaction.as_str()),
|
(msg_id, contact_id, reaction.as_str()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
let mut chat = Chat::load_from_db(context, chat_id).await?;
|
||||||
|
if chat
|
||||||
|
.param
|
||||||
|
.update_timestamp(Param::LastReactionTimestamp, timestamp)?
|
||||||
|
{
|
||||||
|
chat.param
|
||||||
|
.set_i64(Param::LastReactionMsgId, i64::from(msg_id.to_u32()));
|
||||||
|
chat.param
|
||||||
|
.set_i64(Param::LastReactionContactId, i64::from(contact_id.to_u32()));
|
||||||
|
chat.update_param(context).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.emit_event(EventType::ReactionsChanged {
|
context.emit_event(EventType::ReactionsChanged {
|
||||||
@@ -223,7 +236,15 @@ pub async fn send_reaction(context: &Context, msg_id: MsgId, reaction: &str) ->
|
|||||||
let reaction_msg_id = send_msg(context, chat_id, &mut reaction_msg).await?;
|
let reaction_msg_id = send_msg(context, chat_id, &mut reaction_msg).await?;
|
||||||
|
|
||||||
// Only set reaction if we successfully sent the message.
|
// Only set reaction if we successfully sent the message.
|
||||||
set_msg_id_reaction(context, msg_id, msg.chat_id, ContactId::SELF, reaction).await?;
|
set_msg_id_reaction(
|
||||||
|
context,
|
||||||
|
msg_id,
|
||||||
|
msg.chat_id,
|
||||||
|
ContactId::SELF,
|
||||||
|
reaction_msg.timestamp_sort,
|
||||||
|
reaction,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(reaction_msg_id)
|
Ok(reaction_msg_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,10 +271,11 @@ pub(crate) async fn set_msg_reaction(
|
|||||||
in_reply_to: &str,
|
in_reply_to: &str,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
contact_id: ContactId,
|
contact_id: ContactId,
|
||||||
|
timestamp: i64,
|
||||||
reaction: Reaction,
|
reaction: Reaction,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some((msg_id, _)) = rfc724_mid_exists(context, in_reply_to).await? {
|
if let Some((msg_id, _)) = rfc724_mid_exists(context, in_reply_to).await? {
|
||||||
set_msg_id_reaction(context, msg_id, chat_id, contact_id, reaction).await
|
set_msg_id_reaction(context, msg_id, chat_id, contact_id, timestamp, reaction).await
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
context,
|
context,
|
||||||
@@ -307,18 +329,68 @@ pub async fn get_msg_reactions(context: &Context, msg_id: MsgId) -> Result<React
|
|||||||
Ok(Reactions { reactions })
|
Ok(Reactions { reactions })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Chat {
|
||||||
|
/// Check if there is a reaction newer than the given timestamp.
|
||||||
|
///
|
||||||
|
/// If so, reaction details are returned and can be used to create a summary string.
|
||||||
|
pub async fn get_last_reaction_if_newer_than(
|
||||||
|
&self,
|
||||||
|
context: &Context,
|
||||||
|
timestamp: i64,
|
||||||
|
) -> Result<Option<(Message, ContactId, String)>> {
|
||||||
|
if let Some(reaction_timestamp) = self.param.get_i64(Param::LastReactionTimestamp) {
|
||||||
|
if reaction_timestamp > timestamp {
|
||||||
|
let reaction_msg = Message::load_from_db(
|
||||||
|
context,
|
||||||
|
MsgId::new(
|
||||||
|
self.param
|
||||||
|
.get_int(Param::LastReactionMsgId)
|
||||||
|
.unwrap_or_default() as u32,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
if !reaction_msg.chat_id.is_trash() {
|
||||||
|
let reaction_contact_id = ContactId::new(
|
||||||
|
self.param
|
||||||
|
.get_int(Param::LastReactionContactId)
|
||||||
|
.unwrap_or_default() as u32,
|
||||||
|
);
|
||||||
|
if let Some(reaction) = context
|
||||||
|
.sql
|
||||||
|
.query_row_optional(
|
||||||
|
r#"SELECT reaction FROM reactions WHERE msg_id=? AND contact_id=?"#,
|
||||||
|
(reaction_msg.id, reaction_contact_id),
|
||||||
|
|row| {
|
||||||
|
let reaction: String = row.get(0)?;
|
||||||
|
Ok(reaction)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
return Ok(Some((reaction_msg, reaction_contact_id, reaction)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::chat::{get_chat_msgs, send_text_msg};
|
use crate::chat::{get_chat_msgs, send_text_msg};
|
||||||
|
use crate::chatlist::Chatlist;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::DC_CHAT_ID_TRASH;
|
use crate::constants::DC_CHAT_ID_TRASH;
|
||||||
use crate::contact::{Contact, ContactAddress, Origin};
|
use crate::contact::{Contact, ContactAddress, Origin};
|
||||||
use crate::download::DownloadState;
|
use crate::download::DownloadState;
|
||||||
use crate::message::MessageState;
|
use crate::message::{delete_msgs, MessageState};
|
||||||
use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
|
use crate::receive_imf::{receive_imf, receive_imf_from_inbox};
|
||||||
use crate::test_utils::TestContext;
|
use crate::test_utils::TestContext;
|
||||||
use crate::test_utils::TestContextManager;
|
use crate::test_utils::TestContextManager;
|
||||||
|
use crate::tools::SystemTime;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_reaction() {
|
fn test_parse_reaction() {
|
||||||
@@ -549,6 +621,107 @@ Here's my footer -- bob@example.net"
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn assert_summary(t: &TestContext, expected: &str) {
|
||||||
|
let chatlist = Chatlist::try_load(t, 0, None, None).await.unwrap();
|
||||||
|
let summary = chatlist.get_summary(t, 0, None).await.unwrap();
|
||||||
|
assert_eq!(summary.text, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_reaction_summary() -> Result<()> {
|
||||||
|
let alice = TestContext::new_alice().await;
|
||||||
|
let bob = TestContext::new_bob().await;
|
||||||
|
alice.set_config(Config::Displayname, Some("ALICE")).await?;
|
||||||
|
bob.set_config(Config::Displayname, Some("BOB")).await?;
|
||||||
|
|
||||||
|
// Alice sends message to Bob
|
||||||
|
let alice_chat = alice.create_chat(&bob).await;
|
||||||
|
let alice_msg1 = alice.send_text(alice_chat.id, "Party?").await;
|
||||||
|
let bob_msg1 = bob.recv_msg(&alice_msg1).await;
|
||||||
|
|
||||||
|
// Bob reacts to Alice's message, this is shown in the summaries
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
bob_msg1.chat_id.accept(&bob).await?;
|
||||||
|
send_reaction(&bob, bob_msg1.id, "👍").await?;
|
||||||
|
let bob_send_reaction = bob.pop_sent_msg().await;
|
||||||
|
let alice_rcvd_reaction = alice.recv_msg(&bob_send_reaction).await;
|
||||||
|
assert!(alice_rcvd_reaction.get_timestamp() > bob_msg1.get_timestamp());
|
||||||
|
|
||||||
|
let chatlist = Chatlist::try_load(&bob, 0, None, None).await?;
|
||||||
|
let summary = chatlist.get_summary(&bob, 0, None).await?;
|
||||||
|
assert_eq!(summary.text, "You reacted 👍 to \"Party?\"");
|
||||||
|
assert_eq!(summary.timestamp, bob_msg1.get_timestamp()); // time refers to message, not to reaction
|
||||||
|
assert_eq!(summary.state, MessageState::InFresh); // state refers to message, not to reaction
|
||||||
|
assert!(summary.prefix.is_none());
|
||||||
|
assert!(summary.thumbnail_path.is_none());
|
||||||
|
assert_summary(&alice, "BOB reacted 👍 to \"Party?\"").await;
|
||||||
|
|
||||||
|
// Alice reacts to own message as well
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
send_reaction(&alice, alice_msg1.sender_msg_id, "🍿").await?;
|
||||||
|
let alice_send_reaction = alice.pop_sent_msg().await;
|
||||||
|
bob.recv_msg(&alice_send_reaction).await;
|
||||||
|
|
||||||
|
assert_summary(&alice, "You reacted 🍿 to \"Party?\"").await;
|
||||||
|
assert_summary(&bob, "ALICE reacted 🍿 to \"Party?\"").await;
|
||||||
|
|
||||||
|
// Alice sends a newer message, this overwrites reaction summaries
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
let alice_msg2 = alice.send_text(alice_chat.id, "kewl").await;
|
||||||
|
bob.recv_msg(&alice_msg2).await;
|
||||||
|
|
||||||
|
assert_summary(&alice, "kewl").await;
|
||||||
|
assert_summary(&bob, "kewl").await;
|
||||||
|
|
||||||
|
// Reactions to older messages still overwrite newer messages
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
send_reaction(&alice, alice_msg1.sender_msg_id, "🤘").await?;
|
||||||
|
let alice_send_reaction = alice.pop_sent_msg().await;
|
||||||
|
bob.recv_msg(&alice_send_reaction).await;
|
||||||
|
|
||||||
|
assert_summary(&alice, "You reacted 🤘 to \"Party?\"").await;
|
||||||
|
assert_summary(&bob, "ALICE reacted 🤘 to \"Party?\"").await;
|
||||||
|
|
||||||
|
// Retracted reactions remove all summary reactions
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
send_reaction(&alice, alice_msg1.sender_msg_id, "").await?;
|
||||||
|
let alice_remove_reaction = alice.pop_sent_msg().await;
|
||||||
|
bob.recv_msg(&alice_remove_reaction).await;
|
||||||
|
|
||||||
|
assert_summary(&alice, "kewl").await;
|
||||||
|
assert_summary(&bob, "kewl").await;
|
||||||
|
|
||||||
|
// Alice adds another reaction and then deletes the message reacted to; this will also delete reaction summary
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
send_reaction(&alice, alice_msg1.sender_msg_id, "🧹").await?;
|
||||||
|
assert_summary(&alice, "You reacted 🧹 to \"Party?\"").await;
|
||||||
|
|
||||||
|
delete_msgs(&alice, &[alice_msg1.sender_msg_id]).await?;
|
||||||
|
assert_summary(&alice, "kewl").await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_reaction_self_chat_multidevice_summary() -> Result<()> {
|
||||||
|
let alice0 = TestContext::new_alice().await;
|
||||||
|
let alice1 = TestContext::new_alice().await;
|
||||||
|
let chat = alice0.get_self_chat().await;
|
||||||
|
|
||||||
|
let msg_id = send_text_msg(&alice0, chat.id, "mom's birthday!".to_string()).await?;
|
||||||
|
alice1.recv_msg(&alice0.pop_sent_msg().await).await;
|
||||||
|
|
||||||
|
SystemTime::shift(Duration::from_secs(10));
|
||||||
|
send_reaction(&alice0, msg_id, "👆").await?;
|
||||||
|
let sync = alice0.pop_sent_msg().await;
|
||||||
|
receive_imf(&alice1, sync.payload().as_bytes(), false).await?;
|
||||||
|
|
||||||
|
assert_summary(&alice0, "You reacted 👆 to \"mom's birthday!\"").await;
|
||||||
|
assert_summary(&alice1, "You reacted 👆 to \"mom's birthday!\"").await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_partial_download_and_reaction() -> Result<()> {
|
async fn test_partial_download_and_reaction() -> Result<()> {
|
||||||
let alice = TestContext::new_alice().await;
|
let alice = TestContext::new_alice().await;
|
||||||
|
|||||||
@@ -1374,6 +1374,7 @@ async fn add_parts(
|
|||||||
&mime_in_reply_to,
|
&mime_in_reply_to,
|
||||||
orig_chat_id.unwrap_or_default(),
|
orig_chat_id.unwrap_or_default(),
|
||||||
from_id,
|
from_id,
|
||||||
|
sort_timestamp,
|
||||||
Reaction::from(reaction_str.as_str()),
|
Reaction::from(reaction_str.as_str()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -429,6 +429,12 @@ pub enum StockMessage {
|
|||||||
fallback = "⚠️ It seems you are using Delta Chat on multiple devices that cannot decrypt each other's outgoing messages. To fix this, on the older device use \"Settings / Add Second Device\" and follow the instructions."
|
fallback = "⚠️ It seems you are using Delta Chat on multiple devices that cannot decrypt each other's outgoing messages. To fix this, on the older device use \"Settings / Add Second Device\" and follow the instructions."
|
||||||
))]
|
))]
|
||||||
CantDecryptOutgoingMsgs = 175,
|
CantDecryptOutgoingMsgs = 175,
|
||||||
|
|
||||||
|
#[strum(props(fallback = "You reacted %1$s to \"%2$s\""))]
|
||||||
|
MsgYouReacted = 176,
|
||||||
|
|
||||||
|
#[strum(props(fallback = "%1$s reacted %2$s to \"%3$s\""))]
|
||||||
|
MsgReactedBy = 177,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StockMessage {
|
impl StockMessage {
|
||||||
@@ -730,6 +736,27 @@ pub(crate) async fn msg_group_left_local(context: &Context, by_contact: ContactI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stock string: `You reacted %1$s to "%2$s"` or `%1$s reacted %2$s to "%3$s"`.
|
||||||
|
pub(crate) async fn msg_reacted(
|
||||||
|
context: &Context,
|
||||||
|
by_contact: ContactId,
|
||||||
|
reaction: &str,
|
||||||
|
summary: &str,
|
||||||
|
) -> String {
|
||||||
|
if by_contact == ContactId::SELF {
|
||||||
|
translated(context, StockMessage::MsgYouReacted)
|
||||||
|
.await
|
||||||
|
.replace1(reaction)
|
||||||
|
.replace2(summary)
|
||||||
|
} else {
|
||||||
|
translated(context, StockMessage::MsgReactedBy)
|
||||||
|
.await
|
||||||
|
.replace1(&by_contact.get_stock_name(context).await)
|
||||||
|
.replace2(reaction)
|
||||||
|
.replace3(summary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Stock string: `GIF`.
|
/// Stock string: `GIF`.
|
||||||
pub(crate) async fn gif(context: &Context) -> String {
|
pub(crate) async fn gif(context: &Context) -> String {
|
||||||
translated(context, StockMessage::Gif).await
|
translated(context, StockMessage::Gif).await
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use crate::context::Context;
|
|||||||
use crate::message::{Message, MessageState, Viewtype};
|
use crate::message::{Message, MessageState, Viewtype};
|
||||||
use crate::mimeparser::SystemMessage;
|
use crate::mimeparser::SystemMessage;
|
||||||
use crate::stock_str;
|
use crate::stock_str;
|
||||||
|
use crate::stock_str::msg_reacted;
|
||||||
use crate::tools::truncate;
|
use crate::tools::truncate;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
/// Prefix displayed before message and separated by ":" in the chatlist.
|
/// Prefix displayed before message and separated by ":" in the chatlist.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -62,7 +64,24 @@ impl Summary {
|
|||||||
msg: &Message,
|
msg: &Message,
|
||||||
chat: &Chat,
|
chat: &Chat,
|
||||||
contact: Option<&Contact>,
|
contact: Option<&Contact>,
|
||||||
) -> Self {
|
) -> Result<Summary> {
|
||||||
|
if let Some((reaction_msg, reaction_contact_id, reaction)) = chat
|
||||||
|
.get_last_reaction_if_newer_than(context, msg.timestamp_sort)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
// there is a reaction newer than the latest message, show that.
|
||||||
|
// sorting and therefore date is still the one of the last message,
|
||||||
|
// the reaction is is more sth. that overlays temporarily.
|
||||||
|
let summary = reaction_msg.get_summary_text(context).await;
|
||||||
|
return Ok(Summary {
|
||||||
|
prefix: None,
|
||||||
|
text: msg_reacted(context, reaction_contact_id, &reaction, &summary).await,
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let prefix = if msg.state == MessageState::OutDraft {
|
let prefix = if msg.state == MessageState::OutDraft {
|
||||||
Some(SummaryPrefix::Draft(stock_str::draft(context).await))
|
Some(SummaryPrefix::Draft(stock_str::draft(context).await))
|
||||||
} else if msg.from_id == ContactId::SELF {
|
} else if msg.from_id == ContactId::SELF {
|
||||||
@@ -102,13 +121,13 @@ impl Summary {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Ok(Summary {
|
||||||
prefix,
|
prefix,
|
||||||
text,
|
text,
|
||||||
timestamp: msg.get_timestamp(),
|
timestamp: msg.get_timestamp(),
|
||||||
state: msg.state,
|
state: msg.state,
|
||||||
thumbnail_path,
|
thumbnail_path,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`Summary::text`] attribute truncated to an approximate length.
|
/// Returns the [`Summary::text`] attribute truncated to an approximate length.
|
||||||
|
|||||||
Reference in New Issue
Block a user