[Add] Mesage util

This commit is contained in:
Alex Emmet 2026-09-20 21:11:57 +02:00
commit 2b578693aa
No known key found for this signature in database
5 changed files with 671 additions and 293 deletions

View file

@ -14,6 +14,12 @@ pub enum MessageState {
Sending,
}
#[derive(Clone, Debug)]
pub struct MessageStateUpdate {
pub send_time: i64,
pub state: MessageState,
}
impl MessageState {
pub fn as_str(&self) -> &'static str {
match self {
@ -979,23 +985,29 @@ pub fn change_message_state_by_relay_id(
relay_signer_principal: iota_identity::PrincipalHandle,
relay_message_id: &str,
new_state: MessageState,
) -> Result<(), StorageError> {
) -> Result<Option<MessageStateUpdate>, StorageError> {
db::with_db(|conn| {
let tx = conn.unchecked_transaction()?;
let Some((msg_id, current)) = tx
let Some((msg_id, current, message_time)) = tx
.query_row(
"SELECT id, message_state FROM messages WHERE storage_owner = ?1 AND relay_signer_principal = ?2 AND relay_message_id = ?3",
"SELECT id, message_state, message_time FROM messages WHERE storage_owner = ?1 AND relay_signer_principal = ?2 AND relay_message_id = ?3",
params![storage_owner, relay_signer_principal.0, relay_message_id],
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, String>(1)?)),
|row| {
Ok((
row.get::<_, i64>(0)?,
row.get::<_, String>(1)?,
row.get::<_, i64>(2)?,
))
},
)
.optional()?
else {
return Ok(());
return Ok(None);
};
let state = MessageState::from_str(&current).upgrade(new_state).as_str();
let final_state = MessageState::from_str(&current).upgrade(new_state);
tx.execute(
"UPDATE messages SET message_state = ?1 WHERE id = ?2",
params![state, msg_id],
params![final_state.as_str(), msg_id],
)?;
sync::record_event(
&tx,
@ -1005,7 +1017,10 @@ pub fn change_message_state_by_relay_id(
Operation::Upsert,
)?;
tx.commit()?;
Ok(())
Ok(Some(MessageStateUpdate {
send_time: message_time,
state: final_state,
}))
})
}
@ -1020,7 +1035,7 @@ pub fn record_message_receipt(
receipt_type: MessageState,
event_at: i64,
recorded_at: i64,
) -> Result<(), StorageError> {
) -> Result<Option<MessageStateUpdate>, StorageError> {
let receipt_type = match receipt_type {
MessageState::Received => "received",
MessageState::Read => "read",
@ -1028,11 +1043,19 @@ pub fn record_message_receipt(
};
db::with_db(|conn| {
let tx = conn.unchecked_transaction()?;
let Some((message_id, external_principal, authored_at, history_deleted)) = tx
let Some((message_id, message_time, external_principal, authored_at, history_deleted)) = tx
.query_row(
"SELECT id, external_principal, authored_at, history_deleted FROM messages WHERE storage_owner = ?1 AND relay_signer_principal = ?2 AND relay_message_id = ?3",
"SELECT id, message_time, external_principal, authored_at, history_deleted FROM messages WHERE storage_owner = ?1 AND relay_signer_principal = ?2 AND relay_message_id = ?3",
params![storage_owner, target_signer_principal.0, target_message_id],
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?, row.get::<_, Option<i64>>(2)?, row.get::<_, i64>(3)?)),
|row| {
Ok((
row.get::<_, i64>(0)?,
row.get::<_, i64>(1)?,
row.get::<_, i64>(2)?,
row.get::<_, Option<i64>>(3)?,
row.get::<_, i64>(4)?,
))
},
)
.optional()?
else {
@ -1051,7 +1074,7 @@ pub fn record_message_receipt(
));
}
if history_deleted != 0 {
return Ok(());
return Ok(None);
}
tx.execute(
"INSERT OR IGNORE INTO message_receipts (storage_owner, target_signer_id, target_signer_principal, target_message_id, receipt_signer_id, receipt_signer_principal, receipt_message_id, receipt_type, event_at, recorded_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
@ -1062,17 +1085,15 @@ pub fn record_message_receipt(
} else {
("client_received_at", "client_received_recorded_at")
};
let state = MessageState::from_str(&tx.query_row(
let final_state = MessageState::from_str(&tx.query_row(
"SELECT message_state FROM messages WHERE id = ?1",
[message_id],
|row| row.get::<_, String>(0),
)?)
.upgrade(MessageState::from_str(receipt_type))
.as_str()
.to_string();
.upgrade(MessageState::from_str(receipt_type));
tx.execute(
&format!("UPDATE messages SET {state_column} = COALESCE({state_column}, ?1), {recorded_column} = COALESCE({recorded_column}, ?2), message_state = ?3 WHERE id = ?4"),
params![event_at, recorded_at, state, message_id],
params![event_at, recorded_at, final_state.as_str(), message_id],
)?;
sync::record_event(
&tx,
@ -1082,7 +1103,10 @@ pub fn record_message_receipt(
Operation::Upsert,
)?;
tx.commit()?;
Ok(())
Ok(Some(MessageStateUpdate {
send_time: message_time,
state: final_state,
}))
})
}
@ -1579,6 +1603,10 @@ mod tests {
MessageState::Received.upgrade(MessageState::Read),
MessageState::Read
);
assert_eq!(
MessageState::Read.upgrade(MessageState::Received),
MessageState::Read
);
}
#[test]

View file

@ -0,0 +1,84 @@
use iota_identity::PrincipalHandle;
use iota_storage::util::chat_files::{self, MessageState, NewMessage};
#[test]
fn message_offset_counts_visible_newer_messages_in_one_chat() {
let storage = tempfile::tempdir().unwrap();
iota_util::file_util::configure_storage_directory(storage.path().to_owned());
iota_storage::util::db::initialize_database().unwrap();
iota_storage::util::db::with_db(|connection| {
connection.execute(
"INSERT INTO principals (authority_kind, authority_id, remote_user_id, descriptor_revision, last_resolved_at) VALUES ('iota', 'iota:peer', 2, 1, 1)",
[],
)?;
let principal = connection.last_insert_rowid();
connection.execute(
"INSERT INTO contacts (storage_owner, user_id, principal_handle, created_at) VALUES (1, 2, ?1, 1)",
[principal],
)?;
connection.execute(
"INSERT INTO principals (authority_kind, authority_id, remote_user_id, descriptor_revision, last_resolved_at) VALUES ('iota', 'iota:other', 3, 1, 1)",
[],
)?;
let other_principal = connection.last_insert_rowid();
connection.execute(
"INSERT INTO contacts (storage_owner, user_id, principal_handle, created_at) VALUES (1, 3, ?1, 1)",
[other_principal],
)?;
Ok(())
})
.unwrap();
for (send_time, relay_message_id, external_user, external_principal) in [
(10, "first", 2, 1),
(20, "second", 2, 1),
(30, "hidden", 2, 1),
(40, "other-chat", 3, 2),
] {
chat_files::add_message(NewMessage {
relay_signer_id: external_user,
relay_signer_principal: PrincipalHandle(external_principal),
relay_message_id,
authored_at: send_time,
send_time,
storage_owner: 1,
external_user,
external_principal: PrincipalHandle(external_principal),
sent_by_self: false,
content: relay_message_id,
height: 0,
key_version: 1,
reply_to: None,
origin_iota_received_at: None,
destination_iota_received_at: Some(send_time),
initial_state: MessageState::Sent,
})
.unwrap();
}
iota_storage::util::db::with_db(|connection| {
connection.execute(
"UPDATE messages SET history_deleted = 1 WHERE relay_message_id = 'hidden'",
[],
)?;
Ok(())
})
.unwrap();
let newest = chat_files::get_message_with_offset(1, 2, 20)
.unwrap()
.unwrap();
assert_eq!(newest.1, 0);
let first = chat_files::get_message_with_offset(1, 2, 10)
.unwrap()
.unwrap();
assert_eq!(first.1, 1);
assert!(
chat_files::get_message_with_offset(1, 3, 10)
.unwrap()
.is_none()
);
}