diff --git a/client/src/client_connection.rs b/client/src/client_connection.rs index 648038d..f779842 100644 --- a/client/src/client_connection.rs +++ b/client/src/client_connection.rs @@ -622,6 +622,7 @@ impl ClientConnection { .to_string(); let height = cv.get_data(DataType::Height).as_number().unwrap_or(0) as i64; + let reply_to = cv.get_data(DataType::ReplyId).as_number().map(|n| n as i64); chat_files::add_message( timestamp as u128, @@ -630,6 +631,7 @@ impl ClientConnection { *sender_id as i64, &content, height, + reply_to, ); // Build user_forward using the parsed numeric timestamp and safe content string @@ -642,14 +644,23 @@ impl ClientConnection { ) .add_typed_default( DataType::Message, - typed_container(vec![ - (DataType::Content, DataValue::Str(content.clone())), - ( - DataType::SendTime, - DataValue::SignedNumber(timestamp as i128), - ), - (DataType::Height, DataValue::SignedNumber(height as i128)), - ]), + { + let mut msg_fields = vec![ + (DataType::Content, DataValue::Str(content.clone())), + ( + DataType::SendTime, + DataValue::SignedNumber(timestamp as i128), + ), + (DataType::Height, DataValue::SignedNumber(height as i128)), + ]; + if let Some(rt) = reply_to { + msg_fields.push(( + DataType::ReplyId, + DataValue::UnsignedNumber(rt as u64 as u128), + )); + } + typed_container(msg_fields) + }, ); let user_resp = self diff --git a/iota-storage/src/util/chat_files.rs b/iota-storage/src/util/chat_files.rs index 143aa14..a5b50fe 100644 --- a/iota-storage/src/util/chat_files.rs +++ b/iota-storage/src/util/chat_files.rs @@ -58,6 +58,7 @@ pub fn add_message( external_user: i64, message: &str, height: i64, + reply_to: Option, ) { let message_time = match i64::try_from(send_time) { Ok(v) => v, @@ -78,8 +79,9 @@ pub fn add_message( content, sent_by_self, message_state, - height - ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7) + height, + reply_to + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) "#, params![ storage_owner, @@ -93,6 +95,7 @@ pub fn add_message( }, MessageState::Sending.as_str(), height, + reply_to, ], )?; Ok(()) @@ -191,7 +194,8 @@ pub fn get_messages( content, sent_by_self, message_state, - height + height, + reply_to FROM messages WHERE storage_owner = ?1 AND external_user = ?2 @@ -208,21 +212,25 @@ pub fn get_messages( let sent_by_self: i64 = row.get(2)?; let message_state: String = row.get(3)?; let height: i64 = row.get(4).unwrap_or(0); - Ok((message_time, content, sent_by_self, message_state, height)) + let reply_to: Option = row.get(5).ok().flatten(); + Ok((message_time, content, sent_by_self, message_state, height, reply_to)) }, )?; let mut out = array![]; for row in rows { match row { - Ok((message_time, content, sent_by_self, message_state, height)) => { - let msg = object! { + Ok((message_time, content, sent_by_self, message_state, height, reply_to)) => { + let mut msg = object! { "message_time" => message_time, "content" => content, "sent_by_self" => (sent_by_self != 0), "message_state" => message_state, "height" => height }; + if let Some(rt) = reply_to { + let _ = msg.insert("reply_to", rt); + } if let Err(e) = out.push(msg) { // out.push returns a JsonError; log it instead of using `?` to avoid // incompatible error conversions inside the DB closure. diff --git a/iota-storage/src/util/db.rs b/iota-storage/src/util/db.rs index eb48400..c39a4bf 100644 --- a/iota-storage/src/util/db.rs +++ b/iota-storage/src/util/db.rs @@ -162,6 +162,15 @@ pub fn create_general_messages_db() -> Result>, String> { ); Ok(()) }); + // Attempt to add the reply_to column for backwards compatibility. + // This will fail if the column already exists, which is expected. + let _ = with_conn(&shared_conn, |conn| { + let _ = conn.execute( + "ALTER TABLE messages ADD COLUMN reply_to INTEGER", + [], + ); + Ok(()) + }); Ok(shared_conn) } Err(e) => Err(e), diff --git a/omikron-connector/src/omikron_connection.rs b/omikron-connector/src/omikron_connection.rs index 2c56ee5..8d1a002 100755 --- a/omikron-connector/src/omikron_connection.rs +++ b/omikron-connector/src/omikron_connection.rs @@ -1262,6 +1262,7 @@ impl OmikronConnection { .to_string(); let height = cv.get_data(DataType::Height).as_number().unwrap_or(0) as i64; + let reply_to = cv.get_data(DataType::ReplyId).as_number().map(|n| n as i64); let is_local = iota_storage::users::user_manager::get_user(receiver_id).is_some(); @@ -1274,6 +1275,7 @@ impl OmikronConnection { sender_id as i64, &content, height, + reply_to, ); } @@ -1285,6 +1287,7 @@ impl OmikronConnection { receiver_id as i64, &content, height, + reply_to, ); // send confirmation back to sender @@ -1294,7 +1297,7 @@ impl OmikronConnection { self.send_message(&conf_msg).await; if !is_local { - let fw_msg = CommunicationValue::new(CommunicationType::MessageOtherIota) + let mut fw_msg = CommunicationValue::new(CommunicationType::MessageOtherIota) .with_id(cv.get_id()) .with_receiver(receiver_id as u64) .with_sender(sender_id as u64) @@ -1304,6 +1307,12 @@ impl OmikronConnection { DataType::SendTime, DataValue::SignedNumber(timestamp_i64 as i128), ); + if let Some(rt) = reply_to { + fw_msg = fw_msg.add_typed_default( + DataType::ReplyId, + DataValue::UnsignedNumber(rt as u64 as u128), + ); + } let other_iota_resp = self .clone() @@ -1383,14 +1392,23 @@ impl OmikronConnection { ) .add_typed_default( DataType::Message, - typed_container(vec![ - (DataType::Content, DataValue::Str(content.clone())), - ( - DataType::SendTime, - DataValue::SignedNumber(timestamp_i64 as i128), - ), - (DataType::Height, DataValue::SignedNumber(height as i128)), - ]), + { + let mut msg_fields = vec![ + (DataType::Content, DataValue::Str(content.clone())), + ( + DataType::SendTime, + DataValue::SignedNumber(timestamp_i64 as i128), + ), + (DataType::Height, DataValue::SignedNumber(height as i128)), + ]; + if let Some(rt) = reply_to { + msg_fields.push(( + DataType::ReplyId, + DataValue::UnsignedNumber(rt as u64 as u128), + )); + } + typed_container(msg_fields) + }, ); // Attempt delivery and await a response from the local client @@ -1520,6 +1538,7 @@ impl OmikronConnection { .to_string(); let height = cv.get_data(DataType::Height).as_number().unwrap_or(0) as i64; + let reply_to = cv.get_data(DataType::ReplyId).as_number().map(|n| n as i64); chat_files::add_message( timestamp as u128, @@ -1528,6 +1547,7 @@ impl OmikronConnection { *sender_id as i64, &content, height, + reply_to, ); // Build user_forward using the parsed numeric timestamp and safe content string @@ -1540,14 +1560,23 @@ impl OmikronConnection { ) .add_typed_default( DataType::Message, - typed_container(vec![ - (DataType::Content, DataValue::Str(content.clone())), - ( - DataType::SendTime, - DataValue::SignedNumber(timestamp as i128), - ), - (DataType::Height, DataValue::SignedNumber(height as i128)), - ]), + { + let mut msg_fields = vec![ + (DataType::Content, DataValue::Str(content.clone())), + ( + DataType::SendTime, + DataValue::SignedNumber(timestamp as i128), + ), + (DataType::Height, DataValue::SignedNumber(height as i128)), + ]; + if let Some(rt) = reply_to { + msg_fields.push(( + DataType::ReplyId, + DataValue::UnsignedNumber(rt as u64 as u128), + )); + } + typed_container(msg_fields) + }, ); let user_resp = self @@ -1675,6 +1704,12 @@ impl OmikronConnection { container.push((DataType::MessageState, DataValue::Str(message_state))); container.push((DataType::Height, DataValue::SignedNumber(height as i128))); container.push((DataType::SentBySelf, DataValue::Bool(sent_by_self))); + if let Some(rt) = m["reply_to"].as_i64() { + container.push(( + DataType::ReplyId, + DataValue::UnsignedNumber(rt as u64 as u128), + )); + } msg_array.push(typed_container(container)); } diff --git a/type-maps.yaml b/type-maps.yaml index db7fc47..0eebb7f 100644 --- a/type-maps.yaml +++ b/type-maps.yaml @@ -218,3 +218,4 @@ type_maps: SenderUserId: 152 RecipientUserId: 153 Recipients: 154 + ReplyId: 155