diff --git a/src/session/room/event.rs b/src/session/room/event.rs index 01a6cd70..e449b4f2 100644 --- a/src/session/room/event.rs +++ b/src/session/room/event.rs @@ -25,6 +25,7 @@ mod imp { #[derive(Debug, Default)] pub struct Event { pub event: OnceCell>, + pub source: RefCell>, pub relates_to: RefCell>, pub show_header: Cell, pub sender: OnceCell, @@ -55,6 +56,13 @@ mod imp { BoxedAnyRoomEvent::static_type(), glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT, ), + glib::ParamSpec::new_string( + "source", + "Source", + "The source (JSON) of this Event", + None, + glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT | glib::ParamFlags::EXPLICIT_NOTIFY, + ), glib::ParamSpec::new_boolean( "show-header", "Show Header", @@ -101,6 +109,10 @@ mod imp { let event = value.get::().unwrap(); obj.set_matrix_event(event.0); } + "source" => { + let source = value.get().unwrap(); + obj.set_source(source); + } "show-header" => { let show_header = value.get().unwrap(); let _ = obj.set_show_header(show_header); @@ -117,6 +129,7 @@ mod imp { fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { match pspec.name() { + "source" => obj.source().to_value(), "sender" => self.sender.get().to_value(), "show-header" => obj.show_header().to_value(), "can-hide-header" => obj.can_hide_header().to_value(), @@ -136,9 +149,9 @@ glib::wrapper! { /// This is the GObject represatation of a matrix room event impl Event { - pub fn new(event: &AnyRoomEvent, sender: &User) -> Self { + pub fn new(event: &AnyRoomEvent, source: &String, sender: &User) -> Self { let event = BoxedAnyRoomEvent(event.to_owned()); - glib::Object::new(&[("event", &event), ("sender", &sender)]) + glib::Object::new(&[("event", &event), ("source", source), ("sender", sender)]) .expect("Failed to create Event") } @@ -174,6 +187,22 @@ impl Event { fn_event!(event, event_id).clone() } + pub fn source(&self) -> String { + let priv_ = imp::Event::from_instance(&self); + priv_.source.borrow().clone().unwrap_or("".into()) + } + + pub fn set_source(&self, source: Option) { + let priv_ = imp::Event::from_instance(&self); + + if Some(self.source()) == source { + return; + } + + priv_.source.replace(source); + self.notify("source"); + } + pub fn timestamp(&self) -> DateTime { let priv_ = imp::Event::from_instance(&self); let event = &*priv_.event.get().unwrap().borrow(); diff --git a/src/session/room/room.rs b/src/session/room/room.rs index bcd86f31..c62b949e 100644 --- a/src/session/room/room.rs +++ b/src/session/room/room.rs @@ -5,6 +5,7 @@ use matrix_sdk::{ api::r0::sync::sync_events::InvitedRoom, deserialized_responses::{JoinedRoom, LeftRoom}, events::{ + exports::serde::de::DeserializeOwned, room::{ member::{MemberEventContent, MembershipState}, message::{ @@ -18,7 +19,7 @@ use matrix_sdk::{ identifiers::{EventId, RoomId, UserId}, room::Room as MatrixRoom, uuid::Uuid, - MilliSecondsSinceUnixEpoch, RoomMember, + MilliSecondsSinceUnixEpoch, Raw, RoomMember, }; use std::cell::RefCell; @@ -433,13 +434,13 @@ impl Room { } /// Add new events to the timeline - pub fn append_events(&self, batch: Vec) { + pub fn append_events(&self, batch: Vec<(AnyRoomEvent, Raw)>) { let priv_ = imp::Room::from_instance(self); //FIXME: notify only when the count has changed self.notify_notification_count(); - for event in batch.iter() { + for (event, _) in batch.iter() { match event { AnyRoomEvent::State(AnyStateEvent::RoomMember(ref event)) => { self.update_member_for_member_event(event) @@ -649,15 +650,15 @@ impl Room { .timeline .events .into_iter() - .filter_map(|event| { - if let Ok(event) = event.event.deserialize() { - Some(event) + .filter_map(|raw_event| { + if let Ok(event) = raw_event.event.deserialize() { + Some((event, raw_event.event)) } else { - error!("Couldn't deserialize event: {:?}", event); + error!("Couldn't deserialize event: {:?}", raw_event); None } }) - .map(|event| event_from_sync_event!(event, room_id)) + .map(|(event, source)| (event_from_sync_event!(event, room_id), source)) .collect(), ); } @@ -681,15 +682,15 @@ impl Room { .timeline .events .into_iter() - .filter_map(|event| { - if let Ok(event) = event.event.deserialize() { - Some(event) + .filter_map(|raw_event| { + if let Ok(event) = raw_event.event.deserialize() { + Some((event, raw_event.event)) } else { - error!("Couldn't deserialize event: {:?}", event); + error!("Couldn't deserialize event: {:?}", raw_event); None } }) - .map(|event| event_from_sync_event!(event, room_id)) + .map(|(event, source)| (event_from_sync_event!(event, room_id), source)) .collect(), ); } diff --git a/src/session/room/timeline.rs b/src/session/room/timeline.rs index 6a7a9228..3f148122 100644 --- a/src/session/room/timeline.rs +++ b/src/session/room/timeline.rs @@ -1,5 +1,10 @@ use gtk::{gio, glib, prelude::*, subclass::prelude::*}; -use matrix_sdk::{events::AnyRoomEvent, identifiers::EventId}; +use matrix_sdk::{ + events::{exports::serde::de::DeserializeOwned, AnyRoomEvent}, + identifiers::EventId, + Raw, +}; +use serde_json::{to_string_pretty as to_json_string_pretty, to_value as to_json_value}; use crate::fn_event; use crate::session::room::{Event, Item, Room}; @@ -251,7 +256,7 @@ impl Timeline { /// Append the new events // TODO: This should be lazy, for isperation see: https://blogs.gnome.org/ebassi/documentation/lazy-loading/ - pub fn append(&self, batch: Vec) { + pub fn append(&self, batch: Vec<(AnyRoomEvent, Raw)>) { let priv_ = imp::Timeline::from_instance(self); if batch.is_empty() { @@ -269,18 +274,22 @@ impl Timeline { let mut pending_events = priv_.pending_events.borrow_mut(); - for event in batch.into_iter() { + for (event, raw) in batch.into_iter() { let event_id = fn_event!(event, event_id).clone(); let user = self.room().member_by_id(fn_event!(event, sender)); + let source = to_json_value(raw.into_json()) + .and_then(|v| to_json_string_pretty(&v)) + .unwrap(); if let Some(pending_id) = pending_events.remove(&event_id) { if let Some(event_obj) = priv_.event_map.borrow_mut().remove(&pending_id) { event_obj.set_matrix_event(event); + event_obj.set_source(Some(source)); priv_.event_map.borrow_mut().insert(event_id, event_obj); } added -= 1; } else { - let event = Event::new(&event, &user); + let event = Event::new(&event, &source, &user); priv_.event_map.borrow_mut().insert(event_id, event.clone()); if event.is_hidden_event() { @@ -307,7 +316,8 @@ impl Timeline { let index = list.len(); let user = self.room().member_by_id(fn_event!(event, sender)); - let event = Event::new(&event, &user); + let source = to_json_string_pretty(&event).unwrap(); + let event = Event::new(&event, &source, &user); if event.is_hidden_event() { self.add_hidden_event(event); @@ -341,7 +351,7 @@ impl Timeline { /// Prepends a batch of events // TODO: This should be lazy, see: https://blogs.gnome.org/ebassi/documentation/lazy-loading/ - pub fn prepend(&self, batch: Vec) { + pub fn prepend(&self, batch: Vec<(AnyRoomEvent, Raw)>) { let priv_ = imp::Timeline::from_instance(self); let mut added = batch.len(); @@ -349,10 +359,13 @@ impl Timeline { // Extened the size of the list so that rust doesn't need to realocate memory multiple times priv_.list.borrow_mut().reserve(added); - for event in batch { + for (event, raw) in batch { let user = self.room().member_by_id(fn_event!(event, sender)); let event_id = fn_event!(event, event_id).clone(); - let event = Event::new(&event, &user); + let source = to_json_value(raw.into_json()) + .and_then(|v| to_json_string_pretty(&v)) + .unwrap(); + let event = Event::new(&event, &source, &user); priv_.event_map.borrow_mut().insert(event_id, event.clone());