diff --git a/src/session/content/item_row.rs b/src/session/content/item_row.rs index 4ae02192..1c70d0c0 100644 --- a/src/session/content/item_row.rs +++ b/src/session/content/item_row.rs @@ -1,21 +1,23 @@ use adw::{prelude::*, subclass::prelude::*}; use gettextrs::gettext; -use gtk::{gio, glib, prelude::*, subclass::prelude::*}; +use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*}; use crate::components::{ContextMenuBin, ContextMenuBinExt, ContextMenuBinImpl}; use crate::session::content::{DividerRow, MessageRow, StateRow}; use crate::session::event_source_dialog::EventSourceDialog; -use crate::session::room::{Item, ItemType}; -use matrix_sdk::ruma::events::AnyRoomEvent; +use crate::session::room::{Event, Item, ItemType}; +use matrix_sdk::ruma::events::AnySyncRoomEvent; mod imp { use super::*; + use glib::signal::SignalHandlerId; use std::cell::RefCell; #[derive(Debug, Default)] pub struct ItemRow { pub item: RefCell>, pub menu_model: RefCell>, + pub event_notify_handler: RefCell>, } #[glib::object_subclass] @@ -73,6 +75,16 @@ mod imp { _ => unimplemented!(), } } + + fn dispose(&self, _obj: &Self::Type) { + if let Some(ItemType::Event(event)) = + self.item.borrow().as_ref().map(|item| item.type_()) + { + if let Some(handler) = self.event_notify_handler.borrow_mut().take() { + event.disconnect(handler); + } + } + } } impl WidgetImpl for ItemRow {} @@ -113,6 +125,13 @@ impl ItemRow { fn set_item(&self, item: Option) { let priv_ = imp::ItemRow::from_instance(&self); + if let Some(ItemType::Event(event)) = priv_.item.borrow().as_ref().map(|item| item.type_()) + { + if let Some(handler) = priv_.event_notify_handler.borrow_mut().take() { + event.disconnect(handler); + } + } + if let Some(ref item) = item { match item.type_() { ItemType::Event(event) => { @@ -126,57 +145,19 @@ impl ItemRow { self.enable_gactions(); } - match event.matrix_event() { - AnyRoomEvent::Message(_message) => { - let child = if let Some(Ok(child)) = - self.child().map(|w| w.downcast::()) - { - child - } else { - let child = MessageRow::new(); - self.set_child(Some(&child)); - child - }; - child.set_event(event.clone()); - } - AnyRoomEvent::State(state) => { - let child = if let Some(Ok(child)) = - self.child().map(|w| w.downcast::()) - { - child - } else { - let child = StateRow::new(); - self.set_child(Some(&child)); - child - }; - - child.update(&state); - } - AnyRoomEvent::RedactedMessage(_) => { - let child = if let Some(Ok(child)) = - self.child().map(|w| w.downcast::()) - { - child - } else { - let child = MessageRow::new(); - self.set_child(Some(&child)); - child - }; - child.set_event(event.clone()); - } - AnyRoomEvent::RedactedState(_) => { - let child = if let Some(Ok(child)) = - self.child().map(|w| w.downcast::()) - { - child - } else { - let child = MessageRow::new(); - self.set_child(Some(&child)); - child - }; - child.set_event(event.clone()); - } - } + let event_notify_handler = event.connect_notify_local( + Some("event"), + clone!(@weak self as obj => move |event, _| { + obj.set_event_widget(event); + }), + ); + + priv_ + .event_notify_handler + .borrow_mut() + .replace(event_notify_handler); + + self.set_event_widget(event); } ItemType::DayDivider(date) => { if self.context_menu().is_some() { @@ -219,4 +200,31 @@ impl ItemRow { } priv_.item.replace(item); } + + fn set_event_widget(&self, event: &Event) { + match event.matrix_event() { + Some(AnySyncRoomEvent::State(state)) => { + let child = if let Some(Ok(child)) = self.child().map(|w| w.downcast::()) + { + child + } else { + let child = StateRow::new(); + self.set_child(Some(&child)); + child + }; + child.update(&state); + } + _ => { + let child = + if let Some(Ok(child)) = self.child().map(|w| w.downcast::()) { + child + } else { + let child = MessageRow::new(); + self.set_child(Some(&child)); + child + }; + child.set_event(event.clone()); + } + } + } } diff --git a/src/session/content/message_row.rs b/src/session/content/message_row.rs index c1fc0d60..996c3b17 100644 --- a/src/session/content/message_row.rs +++ b/src/session/content/message_row.rs @@ -1,5 +1,6 @@ use crate::components::Avatar; use adw::{prelude::*, subclass::prelude::*}; +use gettextrs::gettext; use gtk::{ gio, glib, glib::clone, glib::signal::SignalHandlerId, prelude::*, subclass::prelude::*, CompositeTemplate, @@ -12,7 +13,7 @@ use log::warn; use matrix_sdk::ruma::events::{ room::message::{FormattedBody, MessageFormat, MessageType, Relation}, room::redaction::RedactionEventContent, - AnyMessageEvent, AnyMessageEventContent, AnyRoomEvent, + AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, }; use sourceview::prelude::*; @@ -190,10 +191,10 @@ impl MessageRow { if let Some(replacement_event) = event.relates_to().iter().rev().find(|event| { let matrix_event = event.matrix_event(); match matrix_event { - AnyRoomEvent::Message(AnyMessageEvent::RoomMessage(message)) => { + Some(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(message))) => { message.content.relates_to.is_some() } - AnyRoomEvent::Message(AnyMessageEvent::RoomRedaction(_)) => true, + Some(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomRedaction(_))) => true, _ => false, } }) { @@ -207,24 +208,32 @@ impl MessageRow { } } /// Find the content we need to display - fn find_content(&self, event: &Event) -> AnyMessageEventContent { + fn find_content(&self, event: &Event) -> Option { match self.find_last_event(event).matrix_event() { - AnyRoomEvent::Message(message) => message.content(), - AnyRoomEvent::RedactedMessage(message) => { + Some(AnySyncRoomEvent::Message(message)) => Some(message.content()), + Some(AnySyncRoomEvent::RedactedMessage(message)) => { if let Some(ref redaction_event) = message.unsigned().redacted_because { - AnyMessageEventContent::RoomRedaction(redaction_event.content.clone()) + Some(AnyMessageEventContent::RoomRedaction( + redaction_event.content.clone(), + )) } else { - AnyMessageEventContent::RoomRedaction(RedactionEventContent::new()) + Some(AnyMessageEventContent::RoomRedaction( + RedactionEventContent::new(), + )) } } - AnyRoomEvent::RedactedState(state) => { + Some(AnySyncRoomEvent::RedactedState(state)) => { if let Some(ref redaction_event) = state.unsigned().redacted_because { - AnyMessageEventContent::RoomRedaction(redaction_event.content.clone()) + Some(AnyMessageEventContent::RoomRedaction( + redaction_event.content.clone(), + )) } else { - AnyMessageEventContent::RoomRedaction(RedactionEventContent::new()) + Some(AnyMessageEventContent::RoomRedaction( + RedactionEventContent::new(), + )) } } - _ => panic!("This event isn’t a room message event or redacted event"), + _ => None, } } @@ -234,8 +243,9 @@ impl MessageRow { // TODO: create widgets for all event types // TODO: display reaction events from event.relates_to() + match content { - AnyMessageEventContent::RoomMessage(message) => { + Some(AnyMessageEventContent::RoomMessage(message)) => { let msgtype = if let Some(Relation::Replacement(replacement)) = message.relates_to { replacement.new_content.msgtype } else { @@ -300,10 +310,10 @@ impl MessageRow { } } } - AnyMessageEventContent::RoomRedaction(_) => { - self.show_label_with_text("This message was removed."); + Some(AnyMessageEventContent::RoomRedaction(_)) => { + self.show_label_with_text(&gettext("This message was removed.")) } - _ => warn!("Event not supported: {:?}", content), + _ => self.show_label_with_text(&gettext("Unsupported event")), } } diff --git a/src/session/content/state_row.rs b/src/session/content/state_row.rs index 4dd0f6c9..7f4b4aff 100644 --- a/src/session/content/state_row.rs +++ b/src/session/content/state_row.rs @@ -3,7 +3,7 @@ use gettextrs::gettext; use gtk::{glib, prelude::*, subclass::prelude::*, CompositeTemplate}; use log::warn; use matrix_sdk::ruma::events::{ - room::member::MembershipState, AnyStateEvent, AnyStateEventContent, + room::member::MembershipState, AnyStateEventContent, AnySyncStateEvent, }; mod imp { @@ -51,7 +51,7 @@ impl StateRow { glib::Object::new(&[]).expect("Failed to create StateRow") } - pub fn update(&self, state: &AnyStateEvent) { + pub fn update(&self, state: &AnySyncStateEvent) { let _priv_ = imp::StateRow::from_instance(self); // We may want to show more state events in the future // For a full list of state events see: diff --git a/src/session/room/event.rs b/src/session/room/event.rs index 2ab260ef..4dcfb7a0 100644 --- a/src/session/room/event.rs +++ b/src/session/room/event.rs @@ -1,20 +1,23 @@ use gtk::{glib, glib::DateTime, prelude::*, subclass::prelude::*}; -use matrix_sdk::ruma::{ - events::{ - room::message::MessageType, room::message::Relation, AnyMessageEvent, - AnyMessageEventContent, AnyRedactedMessageEvent, AnyRedactedStateEvent, AnyRoomEvent, - AnyStateEvent, +use matrix_sdk::{ + deserialized_responses::SyncRoomEvent, + ruma::{ + events::{ + room::message::MessageType, room::message::Relation, AnyMessageEventContent, + AnyRedactedSyncMessageEvent, AnyRedactedSyncStateEvent, AnySyncMessageEvent, + AnySyncRoomEvent, AnySyncStateEvent, + }, + identifiers::{EventId, UserId}, + MilliSecondsSinceUnixEpoch, }, - identifiers::{EventId, UserId}, }; -use crate::fn_event; -use crate::session::User; -use std::cell::RefCell; +use crate::session::{Room, User}; +use log::warn; #[derive(Clone, Debug, glib::GBoxed)] -#[gboxed(type_name = "BoxedAnyRoomEvent")] -pub struct BoxedAnyRoomEvent(AnyRoomEvent); +#[gboxed(type_name = "BoxedSyncRoomEvent")] +pub struct BoxedSyncRoomEvent(SyncRoomEvent); mod imp { use super::*; @@ -24,11 +27,13 @@ mod imp { #[derive(Debug, Default)] pub struct Event { - pub event: OnceCell>, - pub source: RefCell>, + /// The deserialized matrix event + pub event: RefCell>, + /// The SDK event containing encryption information and the serialized event as `Raw` + pub pure_event: RefCell>, pub relates_to: RefCell>, pub show_header: Cell, - pub sender: OnceCell, + pub room: OnceCell, } #[glib::object_subclass] @@ -53,15 +58,15 @@ mod imp { "event", "event", "The matrix event of this Event", - BoxedAnyRoomEvent::static_type(), - glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT, + BoxedSyncRoomEvent::static_type(), + glib::ParamFlags::WRITABLE, ), glib::ParamSpec::new_string( "source", "Source", "The source (JSON) of this Event", None, - glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT | glib::ParamFlags::EXPLICIT_NOTIFY, + glib::ParamFlags::READABLE | glib::ParamFlags::EXPLICIT_NOTIFY, ), glib::ParamSpec::new_boolean( "show-header", @@ -82,6 +87,13 @@ mod imp { "Sender", "The sender of this matrix event", User::static_type(), + glib::ParamFlags::READABLE, + ), + glib::ParamSpec::new_object( + "room", + "Room", + "The room containing this event", + Room::static_type(), glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY, ), glib::ParamSpec::new_string( @@ -106,22 +118,15 @@ mod imp { ) { match pspec.name() { "event" => { - let event = value.get::().unwrap(); - obj.set_matrix_event(event.0); - } - "source" => { - let source = value.get().unwrap(); - obj.set_source(source); + let event = value.get::().unwrap(); + obj.set_matrix_pure_event(event.0); } "show-header" => { let show_header = value.get().unwrap(); let _ = obj.set_show_header(show_header); } - "sender" => { - let sender = value.get().unwrap(); - if let Some(sender) = sender { - let _ = self.sender.set(sender).unwrap(); - } + "room" => { + let _ = self.room.set(value.get().unwrap()); } _ => unimplemented!(), } @@ -130,7 +135,8 @@ 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(), + "sender" => obj.sender().to_value(), + "room" => self.room.get().unwrap().to_value(), "show-header" => obj.show_header().to_value(), "can-hide-header" => obj.can_hide_header().to_value(), "time" => obj.time().to_value(), @@ -149,68 +155,108 @@ glib::wrapper! { /// This is the GObject representation of a matrix room event impl Event { - pub fn new(event: &AnyRoomEvent, source: &String, sender: &User) -> Self { - let event = BoxedAnyRoomEvent(event.to_owned()); - glib::Object::new(&[("event", &event), ("source", source), ("sender", sender)]) - .expect("Failed to create Event") + pub fn new(event: SyncRoomEvent, room: &Room) -> Self { + let event = BoxedSyncRoomEvent(event); + glib::Object::new(&[("event", &event), ("room", room)]).expect("Failed to create Event") } - pub fn sender(&self) -> &User { + pub fn sender(&self) -> User { let priv_ = imp::Event::from_instance(&self); - priv_.sender.get().unwrap() + priv_ + .room + .get() + .unwrap() + .member_by_id(&self.matrix_sender()) } - pub fn matrix_event(&self) -> AnyRoomEvent { + /// Get the matrix event + /// + /// If the `SyncRoomEvent` couldn't be deserialized this is `None` + pub fn matrix_event(&self) -> Option { let priv_ = imp::Event::from_instance(&self); - priv_.event.get().unwrap().borrow().clone() + priv_.event.borrow().clone() } - pub fn set_matrix_event(&self, event: AnyRoomEvent) { + pub fn matrix_pure_event(&self) -> SyncRoomEvent { let priv_ = imp::Event::from_instance(&self); - if let Some(value) = priv_.event.get() { - value.replace(event); + priv_.pure_event.borrow().clone().unwrap() + } + + pub fn set_matrix_pure_event(&self, event: SyncRoomEvent) { + let priv_ = imp::Event::from_instance(&self); + + if let Ok(deserialized) = event.event.deserialize() { + priv_.event.replace(Some(deserialized)); } else { - priv_.event.set(RefCell::new(event)).unwrap(); + warn!("Failed to deserialize event: {:?}", event); } + + priv_.pure_event.replace(Some(event)); + self.notify("event"); } pub fn matrix_sender(&self) -> UserId { let priv_ = imp::Event::from_instance(&self); - let event = &*priv_.event.get().unwrap().borrow(); - fn_event!(event, sender).clone() + + if let Some(event) = priv_.event.borrow().as_ref() { + event.sender().to_owned() + } else { + priv_ + .pure_event + .borrow() + .as_ref() + .unwrap() + .event + .get_field::("sender") + .unwrap() + .unwrap() + } } pub fn matrix_event_id(&self) -> EventId { let priv_ = imp::Event::from_instance(&self); - let event = &*priv_.event.get().unwrap().borrow(); - 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()) + if let Some(event) = priv_.event.borrow().as_ref() { + event.event_id().to_owned() + } else { + priv_ + .pure_event + .borrow() + .as_ref() + .unwrap() + .event + .get_field::("event_id") + .unwrap() + .unwrap() + } } - pub fn set_source(&self, source: Option) { + pub fn source(&self) -> String { let priv_ = imp::Event::from_instance(&self); - - if Some(self.source()) == source { - return; - } - - priv_.source.replace(source); - self.notify("source"); + serde_json::to_string_pretty(priv_.pure_event.borrow().as_ref().unwrap().event.json()) + .unwrap() } pub fn timestamp(&self) -> DateTime { let priv_ = imp::Event::from_instance(&self); - let event = &*priv_.event.get().unwrap().borrow(); - - let ts = fn_event!(event, origin_server_ts).clone(); - // FIXME: we need to add `as_secs()` to `MilliSecondsSinceUnixEpoch` - DateTime::from_unix_utc(i64::from(ts.0) / 1000) + let ts = if let Some(event) = priv_.event.borrow().as_ref() { + event.origin_server_ts().as_secs() + } else { + priv_ + .pure_event + .borrow() + .as_ref() + .unwrap() + .event + .get_field::("origin_server_ts") + .unwrap() + .unwrap() + .as_secs() + }; + + DateTime::from_unix_utc(ts.into()) .and_then(|t| t.to_local()) .unwrap() } @@ -234,9 +280,9 @@ impl Event { pub fn related_matrix_event(&self) -> Option { let priv_ = imp::Event::from_instance(&self); - match *priv_.event.get().unwrap().borrow() { - AnyRoomEvent::Message(ref message) => match message { - AnyMessageEvent::RoomRedaction(event) => Some(event.redacts.clone()), + match priv_.event.borrow().as_ref()? { + AnySyncRoomEvent::Message(ref message) => match message { + AnySyncMessageEvent::RoomRedaction(event) => Some(event.redacts.clone()), _ => match message.content() { AnyMessageEventContent::Reaction(event) => Some(event.relates_to.event_id), AnyMessageEventContent::RoomMessage(event) => match event.relates_to { @@ -265,75 +311,79 @@ impl Event { return true; } - match &*priv_.event.get().unwrap().borrow() { - AnyRoomEvent::Message(message) => match message { - AnyMessageEvent::CallAnswer(_) => true, - AnyMessageEvent::CallInvite(_) => true, - AnyMessageEvent::CallHangup(_) => true, - AnyMessageEvent::CallCandidates(_) => true, - AnyMessageEvent::KeyVerificationReady(_) => true, - AnyMessageEvent::KeyVerificationStart(_) => true, - AnyMessageEvent::KeyVerificationCancel(_) => true, - AnyMessageEvent::KeyVerificationAccept(_) => true, - AnyMessageEvent::KeyVerificationKey(_) => true, - AnyMessageEvent::KeyVerificationMac(_) => true, - AnyMessageEvent::KeyVerificationDone(_) => true, - AnyMessageEvent::RoomEncrypted(_) => true, - AnyMessageEvent::RoomMessageFeedback(_) => true, - AnyMessageEvent::RoomRedaction(_) => true, - AnyMessageEvent::Sticker(_) => true, - _ => false, - }, - AnyRoomEvent::State(state) => match state { - AnyStateEvent::PolicyRuleRoom(_) => true, - AnyStateEvent::PolicyRuleServer(_) => true, - AnyStateEvent::PolicyRuleUser(_) => true, - AnyStateEvent::RoomAliases(_) => true, - AnyStateEvent::RoomAvatar(_) => true, - AnyStateEvent::RoomCanonicalAlias(_) => true, - AnyStateEvent::RoomEncryption(_) => true, - AnyStateEvent::RoomJoinRules(_) => true, - AnyStateEvent::RoomName(_) => true, - AnyStateEvent::RoomPinnedEvents(_) => true, - AnyStateEvent::RoomPowerLevels(_) => true, - AnyStateEvent::RoomServerAcl(_) => true, - AnyStateEvent::RoomTopic(_) => true, - _ => false, - }, - AnyRoomEvent::RedactedMessage(message) => match message { - AnyRedactedMessageEvent::CallAnswer(_) => true, - AnyRedactedMessageEvent::CallInvite(_) => true, - AnyRedactedMessageEvent::CallHangup(_) => true, - AnyRedactedMessageEvent::CallCandidates(_) => true, - AnyRedactedMessageEvent::KeyVerificationReady(_) => true, - AnyRedactedMessageEvent::KeyVerificationStart(_) => true, - AnyRedactedMessageEvent::KeyVerificationCancel(_) => true, - AnyRedactedMessageEvent::KeyVerificationAccept(_) => true, - AnyRedactedMessageEvent::KeyVerificationKey(_) => true, - AnyRedactedMessageEvent::KeyVerificationMac(_) => true, - AnyRedactedMessageEvent::KeyVerificationDone(_) => true, - AnyRedactedMessageEvent::RoomEncrypted(_) => true, - AnyRedactedMessageEvent::RoomMessageFeedback(_) => true, - AnyRedactedMessageEvent::RoomRedaction(_) => true, - AnyRedactedMessageEvent::Sticker(_) => true, - _ => false, - }, - AnyRoomEvent::RedactedState(state) => match state { - AnyRedactedStateEvent::PolicyRuleRoom(_) => true, - AnyRedactedStateEvent::PolicyRuleServer(_) => true, - AnyRedactedStateEvent::PolicyRuleUser(_) => true, - AnyRedactedStateEvent::RoomAliases(_) => true, - AnyRedactedStateEvent::RoomAvatar(_) => true, - AnyRedactedStateEvent::RoomCanonicalAlias(_) => true, - AnyRedactedStateEvent::RoomEncryption(_) => true, - AnyRedactedStateEvent::RoomJoinRules(_) => true, - AnyRedactedStateEvent::RoomName(_) => true, - AnyRedactedStateEvent::RoomPinnedEvents(_) => true, - AnyRedactedStateEvent::RoomPowerLevels(_) => true, - AnyRedactedStateEvent::RoomServerAcl(_) => true, - AnyRedactedStateEvent::RoomTopic(_) => true, - _ => false, - }, + if let Some(event) = priv_.event.borrow().as_ref() { + match event { + AnySyncRoomEvent::Message(message) => match message { + AnySyncMessageEvent::CallAnswer(_) => true, + AnySyncMessageEvent::CallInvite(_) => true, + AnySyncMessageEvent::CallHangup(_) => true, + AnySyncMessageEvent::CallCandidates(_) => true, + AnySyncMessageEvent::KeyVerificationReady(_) => true, + AnySyncMessageEvent::KeyVerificationStart(_) => true, + AnySyncMessageEvent::KeyVerificationCancel(_) => true, + AnySyncMessageEvent::KeyVerificationAccept(_) => true, + AnySyncMessageEvent::KeyVerificationKey(_) => true, + AnySyncMessageEvent::KeyVerificationMac(_) => true, + AnySyncMessageEvent::KeyVerificationDone(_) => true, + AnySyncMessageEvent::RoomEncrypted(_) => true, + AnySyncMessageEvent::RoomMessageFeedback(_) => true, + AnySyncMessageEvent::RoomRedaction(_) => true, + AnySyncMessageEvent::Sticker(_) => true, + _ => false, + }, + AnySyncRoomEvent::State(state) => match state { + AnySyncStateEvent::PolicyRuleRoom(_) => true, + AnySyncStateEvent::PolicyRuleServer(_) => true, + AnySyncStateEvent::PolicyRuleUser(_) => true, + AnySyncStateEvent::RoomAliases(_) => true, + AnySyncStateEvent::RoomAvatar(_) => true, + AnySyncStateEvent::RoomCanonicalAlias(_) => true, + AnySyncStateEvent::RoomEncryption(_) => true, + AnySyncStateEvent::RoomJoinRules(_) => true, + AnySyncStateEvent::RoomName(_) => true, + AnySyncStateEvent::RoomPinnedEvents(_) => true, + AnySyncStateEvent::RoomPowerLevels(_) => true, + AnySyncStateEvent::RoomServerAcl(_) => true, + AnySyncStateEvent::RoomTopic(_) => true, + _ => false, + }, + AnySyncRoomEvent::RedactedMessage(message) => match message { + AnyRedactedSyncMessageEvent::CallAnswer(_) => true, + AnyRedactedSyncMessageEvent::CallInvite(_) => true, + AnyRedactedSyncMessageEvent::CallHangup(_) => true, + AnyRedactedSyncMessageEvent::CallCandidates(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationReady(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationStart(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationCancel(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationAccept(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationKey(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationMac(_) => true, + AnyRedactedSyncMessageEvent::KeyVerificationDone(_) => true, + AnyRedactedSyncMessageEvent::RoomEncrypted(_) => true, + AnyRedactedSyncMessageEvent::RoomMessageFeedback(_) => true, + AnyRedactedSyncMessageEvent::RoomRedaction(_) => true, + AnyRedactedSyncMessageEvent::Sticker(_) => true, + _ => false, + }, + AnySyncRoomEvent::RedactedState(state) => match state { + AnyRedactedSyncStateEvent::PolicyRuleRoom(_) => true, + AnyRedactedSyncStateEvent::PolicyRuleServer(_) => true, + AnyRedactedSyncStateEvent::PolicyRuleUser(_) => true, + AnyRedactedSyncStateEvent::RoomAliases(_) => true, + AnyRedactedSyncStateEvent::RoomAvatar(_) => true, + AnyRedactedSyncStateEvent::RoomCanonicalAlias(_) => true, + AnyRedactedSyncStateEvent::RoomEncryption(_) => true, + AnyRedactedSyncStateEvent::RoomJoinRules(_) => true, + AnyRedactedSyncStateEvent::RoomName(_) => true, + AnyRedactedSyncStateEvent::RoomPinnedEvents(_) => true, + AnyRedactedSyncStateEvent::RoomPowerLevels(_) => true, + AnyRedactedSyncStateEvent::RoomServerAcl(_) => true, + AnyRedactedSyncStateEvent::RoomTopic(_) => true, + _ => false, + }, + } + } else { + false } } @@ -353,23 +403,25 @@ impl Event { } pub fn can_hide_header(&self) -> bool { - let priv_ = imp::Event::from_instance(&self); - - match &*priv_.event.get().unwrap().borrow() { - AnyRoomEvent::Message(ref message) => match message.content() { - AnyMessageEventContent::RoomMessage(message) => match message.msgtype { - MessageType::Audio(_) => true, - MessageType::File(_) => true, - MessageType::Image(_) => true, - MessageType::Location(_) => true, - MessageType::Notice(_) => true, - MessageType::Text(_) => true, - MessageType::Video(_) => true, + if let Some(event) = self.matrix_event() { + match event { + AnySyncRoomEvent::Message(ref message) => match message.content() { + AnyMessageEventContent::RoomMessage(message) => match message.msgtype { + MessageType::Audio(_) => true, + MessageType::File(_) => true, + MessageType::Image(_) => true, + MessageType::Location(_) => true, + MessageType::Notice(_) => true, + MessageType::Text(_) => true, + MessageType::Video(_) => true, + _ => false, + }, _ => false, }, _ => false, - }, - _ => false, + } + } else { + false } } diff --git a/src/session/room/item.rs b/src/session/room/item.rs index 08b4d69b..dd695908 100644 --- a/src/session/room/item.rs +++ b/src/session/room/item.rs @@ -1,6 +1,6 @@ use gtk::{glib, glib::DateTime, prelude::*, subclass::prelude::*}; use matrix_sdk::ruma::{ - events::AnyRoomEvent, + events::AnySyncRoomEvent, identifiers::{EventId, UserId}, }; @@ -115,7 +115,7 @@ glib::wrapper! { } /// This represents any row inside the room history. -/// This can be AnyRoomEvent, a day divider or new message divider. +/// This can be AnySyncRoomEvent, a day divider or new message divider. impl Item { pub fn for_event(event: Event) -> Self { let type_ = BoxedItemType(ItemType::Event(event)); @@ -141,10 +141,10 @@ impl Item { } } - pub fn matrix_event(&self) -> Option { + pub fn matrix_event(&self) -> Option { let priv_ = imp::Item::from_instance(&self); if let ItemType::Event(event) = priv_.type_.get().unwrap() { - Some(event.matrix_event()) + event.matrix_event() } else { None } diff --git a/src/session/room/room.rs b/src/session/room/room.rs index 78e91b7c..638e14f9 100644 --- a/src/session/room/room.rs +++ b/src/session/room/room.rs @@ -7,7 +7,6 @@ use matrix_sdk::{ ruma::{ api::client::r0::sync::sync_events::InvitedRoom, events::{ - exports::serde::de::DeserializeOwned, room::{ member::{MemberEventContent, MembershipState}, message::{ @@ -16,8 +15,8 @@ use matrix_sdk::{ }, }, tag::TagName, - AnyMessageEvent, AnyRoomAccountDataEvent, AnyRoomEvent, AnyStateEvent, - AnyStrippedStateEvent, AnySyncRoomEvent, MessageEvent, StateEvent, Unsigned, + AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncMessageEvent, AnySyncRoomEvent, + AnySyncStateEvent, SyncMessageEvent, SyncStateEvent, Unsigned, }, identifiers::{EventId, RoomId, UserId}, serde::Raw, @@ -26,13 +25,13 @@ use matrix_sdk::{ uuid::Uuid, RoomMember, }; +use serde_json::value::RawValue; use std::cell::RefCell; use std::convert::TryFrom; use crate::components::{LabelWithWidgets, Pill}; -use crate::event_from_sync_event; use crate::session::{ - room::{HighlightFlags, RoomType, Timeline}, + room::{Event, HighlightFlags, RoomType, Timeline}, Avatar, Session, User, }; use crate::utils::do_async; @@ -539,28 +538,30 @@ impl Room { } /// Add new events to the timeline - pub fn append_events(&self, batch: Vec<(AnyRoomEvent, Raw)>) { + pub fn append_events(&self, batch: Vec) { let priv_ = imp::Room::from_instance(self); //FIXME: notify only when the count has changed self.notify_notification_count(); - for (event, _) in batch.iter() { - match event { - AnyRoomEvent::State(AnyStateEvent::RoomMember(ref event)) => { - self.update_member_for_member_event(event) - } - AnyRoomEvent::State(AnyStateEvent::RoomAvatar(event)) => { - self.avatar().set_url(event.content.url.to_owned()); - } - AnyRoomEvent::State(AnyStateEvent::RoomName(_)) => { - // FIXME: this doesn't take into account changes in the calculated name - self.load_display_name() - } - AnyRoomEvent::State(AnyStateEvent::RoomTopic(_)) => { - self.notify("topic"); + for event in &batch { + if let Some(event) = event.matrix_event() { + match event { + AnySyncRoomEvent::State(AnySyncStateEvent::RoomMember(ref event)) => { + self.update_member_for_member_event(event) + } + AnySyncRoomEvent::State(AnySyncStateEvent::RoomAvatar(event)) => { + self.avatar().set_url(event.content.url.to_owned()); + } + AnySyncRoomEvent::State(AnySyncStateEvent::RoomName(_)) => { + // FIXME: this doesn't take into account changes in the calculated name + self.load_display_name() + } + AnySyncRoomEvent::State(AnySyncStateEvent::RoomTopic(_)) => { + self.notify("topic"); + } + _ => {} } - _ => {} } } @@ -583,7 +584,7 @@ impl Room { } /// Updates a room member based on the room member state event - fn update_member_for_member_event(&self, event: &StateEvent) { + fn update_member_for_member_event(&self, event: &SyncStateEvent) { let priv_ = imp::Room::from_instance(self); let mut room_members = priv_.room_members.borrow_mut(); let user_id = &event.sender; @@ -636,49 +637,46 @@ impl Room { } pub fn send_text_message(&self, body: &str, markdown_enabled: bool) { - if let MatrixRoom::Joined(matrix_room) = self.matrix_room() { - let content = if let Some(body) = body.strip_prefix("/me ") { - let emote = if markdown_enabled { - EmoteMessageEventContent::markdown(body) - } else { - EmoteMessageEventContent::plain(body) - }; - MessageEventContent::new(MessageType::Emote(emote)) + let content = if let Some(body) = body.strip_prefix("/me ") { + let emote = if markdown_enabled { + EmoteMessageEventContent::markdown(body) } else { - let text = if markdown_enabled { - TextMessageEventContent::markdown(body) - } else { - TextMessageEventContent::plain(body) - }; - MessageEventContent::new(MessageType::Text(text)) + EmoteMessageEventContent::plain(body) + }; + MessageEventContent::new(MessageType::Emote(emote)) + } else { + let text = if markdown_enabled { + TextMessageEventContent::markdown(body) + } else { + TextMessageEventContent::plain(body) }; + MessageEventContent::new(MessageType::Text(text)) + }; - let txn_id = Uuid::new_v4(); + let txn_id = Uuid::new_v4(); - let pending_event = AnyMessageEvent::RoomMessage(MessageEvent { - content, - event_id: EventId::try_from(format!("${}:fractal.gnome.org", txn_id)).unwrap(), - sender: self.session().user().user_id().clone(), - origin_server_ts: MilliSecondsSinceUnixEpoch::now(), - room_id: matrix_room.room_id().clone(), - unsigned: Unsigned::default(), - }); + let pending_event = AnySyncMessageEvent::RoomMessage(SyncMessageEvent { + content, + event_id: EventId::try_from(format!("${}:fractal.gnome.org", txn_id)).unwrap(), + sender: self.session().user().user_id().clone(), + origin_server_ts: MilliSecondsSinceUnixEpoch::now(), + unsigned: Unsigned::default(), + }); - self.send_message(txn_id, pending_event); - } + self.send_message(txn_id, pending_event); } - pub fn send_message(&self, txn_id: Uuid, event: AnyMessageEvent) { + pub fn send_message(&self, txn_id: Uuid, event: AnySyncMessageEvent) { let priv_ = imp::Room::from_instance(self); let content = event.content(); if let MatrixRoom::Joined(matrix_room) = self.matrix_room() { let pending_id = event.event_id().clone(); - priv_ - .timeline - .get() - .unwrap() - .append_pending(AnyRoomEvent::Message(event)); + let json = serde_json::to_string(&AnySyncRoomEvent::Message(event)).unwrap(); + let raw_event: Raw = + Raw::from_json(RawValue::from_string(json).unwrap()); + let event = Event::new(raw_event.into(), self); + priv_.timeline.get().unwrap().append_pending(event); do_async( glib::PRIORITY_DEFAULT_IDLE, @@ -686,10 +684,7 @@ impl Room { clone!(@weak self as obj => move |result| async move { // FIXME: We should retry the request if it fails match result { - Ok(result) => { - let priv_ = imp::Room::from_instance(&obj); - priv_.timeline.get().unwrap().set_event_id_for_pending(pending_id, result.event_id) - }, + Ok(result) => obj.timeline().set_event_id_for_pending(pending_id, result.event_id), Err(error) => error!("Couldn’t send message: {}", error), }; }), @@ -760,22 +755,12 @@ impl Room { pub fn handle_left_response(&self, response_room: LeftRoom) { self.set_matrix_room(self.session().client().get_room(self.room_id()).unwrap()); - let room_id = self.room_id(); - self.append_events( response_room .timeline .events .into_iter() - .filter_map(|raw_event| { - if let Ok(event) = raw_event.event.deserialize() { - Some((event, raw_event.event)) - } else { - error!("Couldn’t deserialize event: {:?}", raw_event); - None - } - }) - .map(|(event, source)| (event_from_sync_event!(event, room_id), source)) + .map(|event| Event::new(event, self)) .collect(), ); } @@ -792,22 +777,12 @@ impl Room { self.load_category(); } - let room_id = self.room_id(); - self.append_events( response_room .timeline .events .into_iter() - .filter_map(|raw_event| { - if let Ok(event) = raw_event.event.deserialize() { - Some((event, raw_event.event)) - } else { - error!("Couldn’t deserialize event: {:?}", raw_event); - None - } - }) - .map(|(event, source)| (event_from_sync_event!(event, room_id), source)) + .map(|event| Event::new(event, self)) .collect(), ); } diff --git a/src/session/room/timeline.rs b/src/session/room/timeline.rs index 9206dfea..43298ad8 100644 --- a/src/session/room/timeline.rs +++ b/src/session/room/timeline.rs @@ -1,12 +1,6 @@ use gtk::{gio, glib, prelude::*, subclass::prelude::*}; -use matrix_sdk::ruma::{ - events::{exports::serde::de::DeserializeOwned, AnyRoomEvent}, - identifiers::EventId, - serde::Raw, -}; -use serde_json::{to_string_pretty as to_json_string_pretty, to_value as to_json_value}; - -use crate::fn_event; +use matrix_sdk::ruma::identifiers::EventId; + use crate::session::room::{Event, Item, Room}; mod imp { @@ -268,7 +262,7 @@ impl Timeline { /// Append the new events // TODO: This should be lazy, for inspiration see: https://blogs.gnome.org/ebassi/documentation/lazy-loading/ - pub fn append(&self, batch: Vec<(AnyRoomEvent, Raw)>) { + pub fn append(&self, batch: Vec) { let priv_ = imp::Timeline::from_instance(self); if batch.is_empty() { @@ -286,24 +280,22 @@ impl Timeline { let mut pending_events = priv_.pending_events.borrow_mut(); - 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(); + for event in batch.into_iter() { + let event_id = event.matrix_event_id(); 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); - } + let mut event_map = priv_.event_map.borrow_mut(); + + if let Some(pending_event) = event_map.remove(&pending_id) { + pending_event.set_matrix_pure_event(event.matrix_pure_event()); + event_map.insert(event_id, pending_event); + }; added -= 1; } else { - let event = Event::new(&event, &source, &user); - - priv_.event_map.borrow_mut().insert(event_id, event.clone()); + priv_ + .event_map + .borrow_mut() + .insert(event_id.to_owned(), event.clone()); if event.is_hidden_event() { self.add_hidden_event(event); added -= 1; @@ -320,17 +312,18 @@ impl Timeline { } /// Append an event that wasn't yet fully sent and received via a sync - pub fn append_pending(&self, event: AnyRoomEvent) { + pub fn append_pending(&self, event: Event) { let priv_ = imp::Timeline::from_instance(self); + priv_ + .event_map + .borrow_mut() + .insert(event.matrix_event_id(), event.clone()); + let index = { let mut list = priv_.list.borrow_mut(); let index = list.len(); - let user = self.room().member_by_id(fn_event!(event, sender)); - 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); None @@ -363,7 +356,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<(AnyRoomEvent, Raw)>) { + pub fn prepend(&self, batch: Vec) { let priv_ = imp::Timeline::from_instance(self); let mut added = batch.len(); @@ -371,15 +364,11 @@ impl Timeline { // Extend the size of the list so that rust doesn't need to reallocate memory multiple times priv_.list.borrow_mut().reserve(added); - 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 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()); + for event in batch { + priv_ + .event_map + .borrow_mut() + .insert(event.matrix_event_id(), event.clone()); if event.is_hidden_event() { self.add_hidden_event(event); diff --git a/src/session/user.rs b/src/session/user.rs index 3e75d374..c5ecb3a8 100644 --- a/src/session/user.rs +++ b/src/session/user.rs @@ -3,7 +3,7 @@ use gtk::{glib, prelude::*, subclass::prelude::*}; use crate::session::Session; use matrix_sdk::{ ruma::{ - events::{room::member::MemberEventContent, StateEvent, StrippedStateEvent}, + events::{room::member::MemberEventContent, StrippedStateEvent, SyncStateEvent}, identifiers::UserId, }, RoomMember, @@ -181,7 +181,7 @@ impl User { } /// Update the user based on the the room member state event - pub fn update_from_member_event(&self, event: &StateEvent) { + pub fn update_from_member_event(&self, event: &SyncStateEvent) { let changed = { let priv_ = imp::User::from_instance(&self); let user_id = priv_.user_id.get().unwrap();