mirror of
https://github.com/chatmail/core.git
synced 2026-04-01 21:12:13 +03:00
feat: mark messages as "fresh"
this adds an api to make the newest incoming message of a chat as "fresh", so that UI can offer a "mark chat unread" option as usual for messengers (eg. swipe right on iOS toggels between "read" and "unread"). "mark unread" is one of the most requested missing features, used by many ppl to organize their every day messenger usage - tho "pinning" and "saved messages" are similar, it seems to be missed often. we follow a very simple approach here and just reset the state to `MessageState::InFresh`. this does not introduce new states or flows. therefore, chats without any incoming message cannot be marked as fresh. in practise, this is probably not really an issue, as the "mark fresh" is usually used to undo a "mark noticed" operation - and then you have incoming message. also, most status messages as "all messages are e2ee" count as incoming. to avoid double sending of MDN, we remove `Param::WantsMdn` once the MDN is scheduled. in case MDN are used for syncing, MDN is still sent as before. many other messenger show a "badge without number", if we want that as well, we can always track the "manually set as fresh" state in a parameter. but for now, it is fine without and showing a "1", which alsso makes sense as badges may be summed up. there is an iOS pr that uses this new feature, jsonrpc is left out until api is settled. also out of scope is synchronisation - main reason is that "mark noticed" is not synced as well, so we avoid an imbalance here. both, "mark noticed" as well as "mark fresh" should be synced however, as soon as this feature is merged.
This commit is contained in:
@@ -1569,7 +1569,7 @@ dc_array_t* dc_wait_next_msgs (dc_context_t* context);
|
||||
* (read receipts aren't sent for noticed messages).
|
||||
*
|
||||
* Calling this function usually results in the event #DC_EVENT_MSGS_NOTICED.
|
||||
* See also dc_markseen_msgs().
|
||||
* See also dc_markseen_msgs() and dc_markfresh_chat().
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
@@ -1578,6 +1578,29 @@ dc_array_t* dc_wait_next_msgs (dc_context_t* context);
|
||||
void dc_marknoticed_chat (dc_context_t* context, uint32_t chat_id);
|
||||
|
||||
|
||||
/**
|
||||
* Mark the last incoming message in chat as _fresh_.
|
||||
*
|
||||
* UI can use this to offer a "mark unread" option,
|
||||
* so that already noticed chats (see dc_marknoticed_chat()) get a badge counter again.
|
||||
*
|
||||
* dc_get_fresh_msg_cnt() and dc_get_fresh_msgs() usually is increased by one afterwards.
|
||||
*
|
||||
* #DC_EVENT_MSGS_CHANGED is fired as usual,
|
||||
* however, #DC_EVENT_INCOMING_MSG is _not_ fired again.
|
||||
* This is to not add complexity to incoming messages code,
|
||||
* e.g. UI usually does not add notifications for manually unread chats.
|
||||
* If the UI wants to update system badge counters,
|
||||
* they should do so directly after calling dc_markfresh_chat().
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object as returned from dc_context_new().
|
||||
* @param chat_id The chat ID of which messages should be marked as fresh.
|
||||
* If the chat does not have incoming messages, nothing happens.
|
||||
*/
|
||||
void dc_markfresh_chat (dc_context_t* context, uint32_t chat_id);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all message IDs of the given types in a given chat or any chat.
|
||||
* Typically used to show a gallery.
|
||||
|
||||
@@ -1523,6 +1523,23 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_markfresh_chat(context: *mut dc_context_t, chat_id: u32) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_markfresh_chat()");
|
||||
return;
|
||||
}
|
||||
let ctx = &*context;
|
||||
|
||||
block_on(async move {
|
||||
chat::markfresh_chat(ctx, ChatId::new(chat_id))
|
||||
.await
|
||||
.context("Failed markfresh chat")
|
||||
.log_err(ctx)
|
||||
.unwrap_or(())
|
||||
})
|
||||
}
|
||||
|
||||
fn from_prim<S, T>(s: S) -> Option<T>
|
||||
where
|
||||
T: FromPrimitive,
|
||||
|
||||
32
src/chat.rs
32
src/chat.rs
@@ -3406,6 +3406,38 @@ pub(crate) async fn mark_old_messages_as_noticed(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Marks last incoming message in a chat as fresh.
|
||||
pub async fn markfresh_chat(context: &Context, chat_id: ChatId) -> Result<()> {
|
||||
let affected_rows = context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs
|
||||
SET state=?1
|
||||
WHERE id=(SELECT id
|
||||
FROM msgs
|
||||
WHERE state IN (?1, ?2, ?3) AND hidden=0 AND chat_id=?4
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
LIMIT 1)
|
||||
AND state!=?1",
|
||||
(
|
||||
MessageState::InFresh,
|
||||
MessageState::InNoticed,
|
||||
MessageState::InSeen,
|
||||
chat_id,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if affected_rows == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
context.emit_msgs_changed_without_msg_id(chat_id);
|
||||
chatlist_events::emit_chatlist_item_changed(context, chat_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns all database message IDs of the given types.
|
||||
///
|
||||
/// If `chat_id` is None, return messages from any chat.
|
||||
|
||||
@@ -1939,6 +1939,17 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
&& curr_param.get_cmd() == SystemMessage::Unknown
|
||||
&& context.should_send_mdns().await?
|
||||
{
|
||||
// Clear WantsMdn to not handle a MDN twice
|
||||
// if the state later is InFresh again as markfresh_chat() was called.
|
||||
// BccSelf MDN messages in the next branch may be sent twice for syncing.
|
||||
context
|
||||
.sql
|
||||
.execute(
|
||||
"UPDATE msgs SET param=? WHERE id=?",
|
||||
(curr_param.clone().remove(Param::WantsMdn).to_string(), id),
|
||||
)
|
||||
.await
|
||||
.context("failed to clear WantsMdn")?;
|
||||
Some(curr_from_id)
|
||||
} else if context.get_config_bool(Config::BccSelf).await? {
|
||||
Some(ContactId::SELF)
|
||||
@@ -1956,6 +1967,7 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()>
|
||||
.context("failed to insert into smtp_mdns")?;
|
||||
context.scheduler.interrupt_smtp().await;
|
||||
}
|
||||
|
||||
if !curr_hidden {
|
||||
updated_chat_ids.insert(curr_chat_id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user