diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8efed..a67f287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Startup screen when loading messages. +- Clicking on a notification now brings up the channel the notification came from. ### Changed diff --git a/src/backend/channel.rs b/src/backend/channel.rs index 27d2e22..99739ae 100644 --- a/src/backend/channel.rs +++ b/src/backend/channel.rs @@ -591,6 +591,28 @@ impl Channel { }) .collect() } + + pub fn notification_variant(&self) -> (u8, Vec) { + let thread = self.thread(); + match thread { + Thread::Contact(u) => (0, u.service_id_binary()), + Thread::Group(g) => (1, g.into()), + } + } + + pub fn from_notification_variant((kind, data): (u8, Vec)) -> Thread { + match kind { + 0 => Thread::Contact( + ServiceId::parse_from_service_id_binary(&data) + .expect("Notification variant for contact to be a service ID"), + ), + 1 => Thread::Group( + data.try_into() + .expect("Notification variant for groups to have the correct number of bytes"), + ), + _ => unreachable!("Unexpected notification variant type"), + } + } } mod imp { diff --git a/src/backend/manager.rs b/src/backend/manager.rs index a6b8fc1..0c0bd2a 100644 --- a/src/backend/manager.rs +++ b/src/backend/manager.rs @@ -472,6 +472,11 @@ impl Manager { channel } + pub fn channel_from_thread(&self, thread: Thread) -> Option { + let known_channels = self.imp().channels.borrow_mut(); + known_channels.get(&thread).cloned() + } + #[cfg(not(feature = "screenshot"))] pub async fn list_contacts(&self) -> Vec { let store = self.store(); diff --git a/src/backend/message/display_message.rs b/src/backend/message/display_message.rs index 9a0953a..864ce6f 100644 --- a/src/backend/message/display_message.rs +++ b/src/backend/message/display_message.rs @@ -41,6 +41,10 @@ impl DisplayMessage { notification.set_body(Some(¬ification_body)); notification.set_icon(&icon); notification.set_category(Some("im.received")); + notification.set_default_action_and_target_value( + "app.notification-clicked", + Some(&channel.notification_variant().to_variant()), + ); let manager = self.manager(); let uid = self.uid(); diff --git a/src/backend/message/reaction_message.rs b/src/backend/message/reaction_message.rs index a8974b0..db50c1a 100644 --- a/src/backend/message/reaction_message.rs +++ b/src/backend/message/reaction_message.rs @@ -96,6 +96,10 @@ impl ReactionMessage { notification.set_body(Some(¬ification_body)); notification.set_icon(&icon); notification.set_category(Some("im.received")); + notification.set_default_action_and_target_value( + "app.notification-clicked", + Some(&channel.notification_variant().to_variant()), + ); let manager = self.manager(); let uid = self.uid(); diff --git a/src/gui/window.rs b/src/gui/window.rs index e77f40f..6335f3d 100644 --- a/src/gui/window.rs +++ b/src/gui/window.rs @@ -1,4 +1,7 @@ -use crate::prelude::*; +use gio::SimpleAction; +use presage::store::Thread; + +use crate::{backend::Channel, prelude::*}; glib::wrapper! { /// The main application window. @@ -24,9 +27,30 @@ impl Window { ); } app.set_accels_for_action("channel-list.toggle-search", &["f"]); - Object::builder::() + + let window = Object::builder::() .property("application", app) - .build() + .build(); + + let action_notification_clicked = SimpleAction::new( + "notification-clicked", + Some(&<(u8, Vec)>::static_variant_type()), + ); + action_notification_clicked.connect_activate(clone!( + #[weak] + window, + move |_, variant| { + let variant: (u8, Vec) = variant + .expect("Notification-clicked action to have attached data") + .get() + .expect("Variant of notification is of unexpected type"); + let thread = Channel::from_notification_variant(variant); + window.activate_channel(thread); + } + )); + app.add_action(&action_notification_clicked); + + window } // Closes the window, even if hide-on-close is set. @@ -69,6 +93,13 @@ impl Window { } } + fn activate_channel(&self, thread: Thread) { + let channel = self + .property::("manager") + .channel_from_thread(thread); + self.imp().channel_list.set_active_channel(channel); + } + pub(crate) fn settings(&self) -> gio::Settings { self.imp().settings.clone() }