fractal/session/view/
event_details_dialog.rs1use adw::{prelude::*, subclass::prelude::*};
2use gettextrs::gettext;
3use gtk::{glib, CompositeTemplate};
4use sourceview::prelude::*;
5
6use crate::{
7 components::{CopyableRow, ToastableDialog, UserProfileDialog},
8 prelude::*,
9 session::model::Event,
10 toast, utils,
11 utils::TemplateCallbacks,
12};
13
14mod imp {
15 use std::cell::RefCell;
16
17 use glib::subclass::InitializingObject;
18
19 use super::*;
20
21 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
22 #[template(resource = "/org/gnome/Fractal/ui/session/view/event_details_dialog.ui")]
23 #[properties(wrapper_type = super::EventDetailsDialog)]
24 pub struct EventDetailsDialog {
25 #[property(get, construct_only)]
27 event: RefCell<Option<Event>>,
28 #[template_child]
29 navigation_view: TemplateChild<adw::NavigationView>,
30 #[template_child]
31 source_page: TemplateChild<adw::NavigationPage>,
32 #[template_child]
33 source_view: TemplateChild<sourceview::View>,
34 }
35
36 #[glib::object_subclass]
37 impl ObjectSubclass for EventDetailsDialog {
38 const NAME: &'static str = "EventDetailsDialog";
39 type Type = super::EventDetailsDialog;
40 type ParentType = ToastableDialog;
41
42 fn class_init(klass: &mut Self::Class) {
43 CopyableRow::ensure_type();
44
45 Self::bind_template(klass);
46 Self::bind_template_callbacks(klass);
47 TemplateCallbacks::bind_template_callbacks(klass);
48 }
49
50 fn instance_init(obj: &InitializingObject<Self>) {
51 obj.init_template();
52 }
53 }
54
55 #[glib::derived_properties]
56 impl ObjectImpl for EventDetailsDialog {
57 fn constructed(&self) {
58 self.parent_constructed();
59
60 let json_lang = sourceview::LanguageManager::default().language("json");
61
62 let buffer = self
63 .source_view
64 .buffer()
65 .downcast::<sourceview::Buffer>()
66 .unwrap();
67 buffer.set_language(json_lang.as_ref());
68 utils::sourceview::setup_style_scheme(&buffer);
69 }
70 }
71
72 impl WidgetImpl for EventDetailsDialog {}
73 impl AdwDialogImpl for EventDetailsDialog {}
74 impl ToastableDialogImpl for EventDetailsDialog {}
75
76 #[gtk::template_callbacks]
77 impl EventDetailsDialog {
78 fn show_source(&self, title: &str, source: &str) {
80 self.source_view.buffer().set_text(source);
81 self.source_page.set_title(title);
82 self.navigation_view.push_by_tag("source");
83 }
84
85 #[template_callback]
87 fn open_sender_profile(&self) {
88 let Some(sender) = self.event.borrow().as_ref().map(Event::sender) else {
89 return;
90 };
91
92 let dialog = UserProfileDialog::new();
93 dialog.set_room_member(sender);
94 dialog.present(Some(&*self.obj()));
95 }
96
97 #[template_callback]
99 fn show_original_source(&self) {
100 let Some(event) = self.event.borrow().clone() else {
101 return;
102 };
103
104 if let Some(source) = event.source() {
105 let title = if event.is_edited() {
106 gettext("Original Event Source")
107 } else {
108 gettext("Event Source")
109 };
110 self.show_source(&title, &source);
111 }
112 }
113
114 #[template_callback]
116 fn show_edit_source(&self) {
117 let Some(event) = self.event.borrow().clone() else {
118 return;
119 };
120
121 let source = event.latest_edit_source();
122 let title = gettext("Latest Edit Source");
123 self.show_source(&title, &source);
124 }
125
126 #[template_callback]
128 fn copy_source(&self) {
129 let obj = self.obj();
130
131 let buffer = self.source_view.buffer();
132 let (start_iter, end_iter) = buffer.bounds();
133 obj.clipboard()
134 .set_text(&buffer.text(&start_iter, &end_iter, true));
135
136 toast!(obj, gettext("Source copied to clipboard"));
137 }
138 }
139}
140
141glib::wrapper! {
142 pub struct EventDetailsDialog(ObjectSubclass<imp::EventDetailsDialog>)
144 @extends gtk::Widget, adw::Dialog, ToastableDialog, @implements gtk::Accessible;
145}
146
147impl EventDetailsDialog {
148 pub fn new(event: &Event) -> Self {
149 glib::Object::builder().property("event", event).build()
150 }
151}