surface message edit history (no edit_message tool - shard commits to its words)
This commit is contained in:
parent
9d490f5ca8
commit
bc3a4782cc
4 changed files with 75 additions and 5 deletions
|
|
@ -116,6 +116,7 @@ pub fn wire_event_from(
|
|||
is_self,
|
||||
ts,
|
||||
in_reply_to,
|
||||
edit_history,
|
||||
} => {
|
||||
let read_by: Vec<String> = read_markers
|
||||
.get(event_id)
|
||||
|
|
@ -135,6 +136,7 @@ pub fn wire_event_from(
|
|||
body: body.clone(),
|
||||
in_reply_to: in_reply_to.as_ref().map(|e| e.as_str().to_owned()),
|
||||
read_by,
|
||||
edit_history: edit_history.clone(),
|
||||
}
|
||||
}
|
||||
TimelineItem::Reaction {
|
||||
|
|
|
|||
|
|
@ -379,6 +379,7 @@ async fn fetch_event(
|
|||
body: text.body.clone(),
|
||||
in_reply_to,
|
||||
read_by: Vec::new(),
|
||||
edit_history: Vec::new(),
|
||||
})
|
||||
}
|
||||
matrix_sdk::ruma::events::AnySyncMessageLikeEvent::Reaction(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use matrix_sdk::{
|
|||
ruma::{OwnedEventId, OwnedUserId, events::room::message::MessageType},
|
||||
};
|
||||
|
||||
use crate::types::TimelineItem;
|
||||
use crate::types::{EditRecord, TimelineItem};
|
||||
|
||||
/// Format a unix-seconds timestamp as `YYYY-MM-DD HH:MM` UTC. Returns "?" for 0.
|
||||
pub fn format_ts(secs: i64) -> String {
|
||||
|
|
@ -83,8 +83,13 @@ pub async fn load_timeline(
|
|||
let (cache, _handles) = room.event_cache().await?;
|
||||
let events = cache.events().await;
|
||||
|
||||
use matrix_sdk::ruma::events::room::message::Relation;
|
||||
|
||||
let mut messages: Vec<TimelineItem> = Vec::new();
|
||||
let mut reactions: Vec<TimelineItem> = Vec::new();
|
||||
// Edits stashed by target event_id. Walk is newest-first, so edits may
|
||||
// arrive before their target original. (body, ts) pairs.
|
||||
let mut pending_edits: HashMap<OwnedEventId, Vec<(String, i64)>> = HashMap::new();
|
||||
let mut earliest_message_ts: Option<i64> = None;
|
||||
|
||||
for ev in events.iter().rev() {
|
||||
|
|
@ -100,17 +105,28 @@ pub async fn load_timeline(
|
|||
matrix_sdk::ruma::events::AnySyncMessageLikeEvent::RoomMessage(
|
||||
matrix_sdk::ruma::events::SyncMessageLikeEvent::Original(orig),
|
||||
) => {
|
||||
let ts = ts_secs_from(orig.origin_server_ts.0);
|
||||
|
||||
// Edit event? Stash the new body for the target original; do
|
||||
// NOT count toward the message limit.
|
||||
if let Some(Relation::Replacement(replacement)) = &orig.content.relates_to {
|
||||
if let MessageType::Text(text) = &replacement.new_content.msgtype {
|
||||
pending_edits
|
||||
.entry(replacement.event_id.clone())
|
||||
.or_default()
|
||||
.push((text.body.clone(), ts));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if messages.len() >= limit {
|
||||
continue;
|
||||
}
|
||||
let MessageType::Text(text) = &orig.content.msgtype else {
|
||||
continue;
|
||||
};
|
||||
let ts = ts_secs_from(orig.origin_server_ts.0);
|
||||
let in_reply_to = match &orig.content.relates_to {
|
||||
Some(matrix_sdk::ruma::events::room::message::Relation::Reply {
|
||||
in_reply_to,
|
||||
}) => Some(in_reply_to.event_id.clone()),
|
||||
Some(Relation::Reply { in_reply_to }) => Some(in_reply_to.event_id.clone()),
|
||||
_ => None,
|
||||
};
|
||||
if earliest_message_ts.is_none_or(|e| ts < e) {
|
||||
|
|
@ -123,6 +139,7 @@ pub async fn load_timeline(
|
|||
is_self: &orig.sender == own_user,
|
||||
ts,
|
||||
in_reply_to,
|
||||
edit_history: Vec::new(),
|
||||
});
|
||||
}
|
||||
matrix_sdk::ruma::events::AnySyncMessageLikeEvent::Reaction(
|
||||
|
|
@ -141,6 +158,39 @@ pub async fn load_timeline(
|
|||
}
|
||||
}
|
||||
|
||||
// Apply pending edits to their target messages. Chain = [original body,
|
||||
// edits sorted oldest-first]; current `body` becomes the latest, and
|
||||
// everything before it ends up in `edit_history`.
|
||||
for item in &mut messages {
|
||||
if let TimelineItem::Message {
|
||||
event_id,
|
||||
body,
|
||||
ts,
|
||||
edit_history,
|
||||
..
|
||||
} = item
|
||||
{
|
||||
let edits = pending_edits.remove(event_id).unwrap_or_default();
|
||||
if edits.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut chain: Vec<(String, i64)> = vec![(body.clone(), *ts)];
|
||||
chain.extend(edits);
|
||||
chain.sort_by_key(|(_, t)| *t);
|
||||
// pop the most recent - that's the visible body
|
||||
let (latest, _) = chain.pop().expect("chain has at least the original");
|
||||
*body = latest;
|
||||
*edit_history = chain
|
||||
.into_iter()
|
||||
.map(|(b, t)| EditRecord {
|
||||
body: b,
|
||||
ts: t,
|
||||
ts_human: format!("{} UTC", format_ts(t)),
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(min_ts) = earliest_message_ts {
|
||||
reactions.retain(|r| r.ts() >= min_ts);
|
||||
}
|
||||
|
|
@ -182,6 +232,7 @@ pub async fn fetch_message(
|
|||
is_self: &orig.sender == own_user,
|
||||
ts,
|
||||
in_reply_to: None,
|
||||
edit_history: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
16
src/types.rs
16
src/types.rs
|
|
@ -6,6 +6,14 @@ use matrix_sdk::{
|
|||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// One prior version of an edited message.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct EditRecord {
|
||||
pub body: String,
|
||||
pub ts: i64,
|
||||
pub ts_human: String,
|
||||
}
|
||||
|
||||
/// Serializable shape for one timeline event, used both in matrix_turn JSON
|
||||
/// (input to the shard) and tool response JSON (get_room_history,
|
||||
/// fetch_event).
|
||||
|
|
@ -22,6 +30,9 @@ pub enum WireEvent {
|
|||
body: String,
|
||||
in_reply_to: Option<String>,
|
||||
read_by: Vec<String>,
|
||||
/// Prior versions of this message, oldest first. Empty when the
|
||||
/// message has never been edited.
|
||||
edit_history: Vec<EditRecord>,
|
||||
},
|
||||
Reaction {
|
||||
sender: String,
|
||||
|
|
@ -94,11 +105,16 @@ pub enum TimelineItem {
|
|||
Message {
|
||||
event_id: OwnedEventId,
|
||||
sender: OwnedUserId,
|
||||
/// Latest version of the message body. If the user edited this message,
|
||||
/// this is the most recent edit's content.
|
||||
body: String,
|
||||
is_self: bool,
|
||||
/// Unix seconds. 0 if unknown.
|
||||
ts: i64,
|
||||
in_reply_to: Option<OwnedEventId>,
|
||||
/// Prior versions of this message (oldest first), excluding the
|
||||
/// current `body`. Empty when the message has never been edited.
|
||||
edit_history: Vec<EditRecord>,
|
||||
},
|
||||
Reaction {
|
||||
sender: OwnedUserId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue