feat: Group and broadcast channel descriptions (#7829)

fix https://github.com/chatmail/core/issues/7766

Implementation notes:

- Descriptions are only sent with member additions, when the description
is changed, and when promoting a previously-unpromoted group, in order
not to waste bandwith.
- Descriptions are not loaded everytime a chat object is loaded, because
they are only needed for the profile. Instead, they are in their own
table, and can be loaded with their own JsonRPC call.

---------

Co-authored-by: iequidoo <117991069+iequidoo@users.noreply.github.com>
This commit is contained in:
Hocuri
2026-02-10 22:28:12 +01:00
committed by GitHub
parent c475882727
commit 3fdda6f3b8
15 changed files with 411 additions and 24 deletions

View File

@@ -8,7 +8,8 @@ use std::sync::LazyLock;
use anyhow::{Context as _, Result, ensure};
use data_encoding::BASE32_NOPAD;
use deltachat_contact_tools::{
ContactAddress, addr_cmp, addr_normalize, may_be_valid_addr, sanitize_single_line,
ContactAddress, addr_cmp, addr_normalize, may_be_valid_addr, sanitize_bidi_characters,
sanitize_single_line,
};
use iroh_gossip::proto::TopicId;
use mailparse::SingleInfo;
@@ -3101,7 +3102,7 @@ async fn apply_group_changes(
}
if is_from_in_chat {
apply_chat_name_and_avatar_changes(
apply_chat_name_avatar_and_description_changes(
context,
mime_parser,
from_id,
@@ -3280,7 +3281,7 @@ async fn apply_group_changes(
///
/// - `send_event_chat_modified` is set to `true` if ChatModified event should be sent
/// - `better_msg` is filled with an info message about name change, if necessary
async fn apply_chat_name_and_avatar_changes(
async fn apply_chat_name_avatar_and_description_changes(
context: &Context,
mime_parser: &MimeMessage,
from_id: ContactId,
@@ -3339,6 +3340,51 @@ async fn apply_chat_name_and_avatar_changes(
}
}
// ========== Apply chat description changes ==========
if let Some(new_description) = mime_parser
.get_header(HeaderDef::ChatGroupDescription)
.map(|d| d.trim())
{
let new_description = sanitize_bidi_characters(new_description.trim());
let old_description = chat::get_chat_description(context, chat.id).await?;
let old_timestamp = chat
.param
.get_i64(Param::GroupDescriptionTimestamp)
.unwrap_or(0);
let timestamp_in_header = mime_parser
.get_header(HeaderDef::ChatGroupDescriptionTimestamp)
.and_then(|s| s.parse::<i64>().ok());
let new_timestamp = timestamp_in_header.unwrap_or(mime_parser.timestamp_sent);
// To provide consistency, compare descriptions if timestamps are equal.
if (old_timestamp, &old_description) < (new_timestamp, &new_description)
&& chat
.id
.update_timestamp(context, Param::GroupDescriptionTimestamp, new_timestamp)
.await?
&& new_description != old_description
{
info!(context, "Updating description for chat {}.", chat.id);
context
.sql
.execute(
"INSERT OR REPLACE INTO chats_descriptions(chat_id, description) VALUES(?, ?)",
(chat.id, &new_description),
)
.await?;
*send_event_chat_modified = true;
}
if mime_parser
.get_header(HeaderDef::ChatGroupDescriptionChanged)
.is_some()
{
better_msg
.get_or_insert(stock_str::msg_chat_description_changed(context, from_id).await);
}
}
// ========== Apply chat avatar changes ==========
if let (Some(value), None) = (mime_parser.get_header(HeaderDef::ChatContent), &better_msg)
@@ -3667,7 +3713,7 @@ async fn apply_out_broadcast_changes(
let mut better_msg = None;
if from_id == ContactId::SELF {
apply_chat_name_and_avatar_changes(
apply_chat_name_avatar_and_description_changes(
context,
mime_parser,
from_id,
@@ -3757,7 +3803,7 @@ async fn apply_in_broadcast_changes(
let mut send_event_chat_modified = false;
let mut better_msg = None;
apply_chat_name_and_avatar_changes(
apply_chat_name_avatar_and_description_changes(
context,
mime_parser,
from_id,