fractal/session/view/content/room_history/
verification_info_bar.rs1use adw::subclass::prelude::*;
2use gettextrs::gettext;
3use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
4
5use crate::{
6 components::LoadingButton,
7 gettext_f,
8 prelude::*,
9 session::model::{IdentityVerification, VerificationState},
10 toast,
11 utils::BoundObjectWeakRef,
12 Window,
13};
14
15mod imp {
16 use std::cell::RefCell;
17
18 use glib::subclass::InitializingObject;
19
20 use super::*;
21
22 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
23 #[template(
24 resource = "/org/gnome/Fractal/ui/session/view/content/room_history/verification_info_bar.ui"
25 )]
26 #[properties(wrapper_type = super::VerificationInfoBar)]
27 pub struct VerificationInfoBar {
28 #[template_child]
29 revealer: TemplateChild<gtk::Revealer>,
30 #[template_child]
31 label: TemplateChild<gtk::Label>,
32 #[template_child]
33 accept_btn: TemplateChild<LoadingButton>,
34 #[template_child]
35 cancel_btn: TemplateChild<LoadingButton>,
36 #[property(get, set = Self::set_verification, explicit_notify)]
38 verification: BoundObjectWeakRef<IdentityVerification>,
39 user_handler: RefCell<Option<glib::SignalHandlerId>>,
40 }
41
42 #[glib::object_subclass]
43 impl ObjectSubclass for VerificationInfoBar {
44 const NAME: &'static str = "ContentVerificationInfoBar";
45 type Type = super::VerificationInfoBar;
46 type ParentType = adw::Bin;
47
48 fn class_init(klass: &mut Self::Class) {
49 Self::bind_template(klass);
50
51 klass.set_css_name("infobar");
52 klass.set_accessible_role(gtk::AccessibleRole::Group);
53
54 klass.install_action_async("verification.accept", None, |obj, _, _| async move {
55 let Some(window) = obj.root().and_downcast::<Window>() else {
56 return;
57 };
58 let Some(verification) = obj.verification() else {
59 return;
60 };
61 let imp = obj.imp();
62
63 if verification.state() == VerificationState::Requested {
64 imp.accept_btn.set_is_loading(true);
65
66 if verification.accept().await.is_err() {
67 toast!(obj, gettext("Could not accept verification"));
68 imp.accept_btn.set_is_loading(false);
69 return;
70 }
71 }
72
73 window
74 .session_view()
75 .select_identity_verification(verification);
76 imp.accept_btn.set_is_loading(false);
77 });
78
79 klass.install_action_async("verification.decline", None, |obj, _, _| async move {
80 let Some(verification) = obj.verification() else {
81 return;
82 };
83 let imp = obj.imp();
84
85 imp.cancel_btn.set_is_loading(true);
86
87 if verification.cancel().await.is_err() {
88 toast!(obj, gettext("Could not decline verification"));
89 }
90
91 imp.cancel_btn.set_is_loading(false);
92 });
93 }
94
95 fn instance_init(obj: &InitializingObject<Self>) {
96 obj.init_template();
97 }
98 }
99
100 #[glib::derived_properties]
101 impl ObjectImpl for VerificationInfoBar {
102 fn dispose(&self) {
103 if let Some(verification) = self.verification.obj() {
104 if let Some(handler) = self.user_handler.take() {
105 verification.user().disconnect(handler);
106 }
107 }
108 }
109 }
110
111 impl WidgetImpl for VerificationInfoBar {}
112 impl BinImpl for VerificationInfoBar {}
113
114 impl VerificationInfoBar {
115 fn set_verification(&self, verification: Option<&IdentityVerification>) {
117 let prev_verification = self.verification.obj();
118
119 if prev_verification.as_ref() == verification {
120 return;
121 }
122
123 if let Some(verification) = prev_verification {
124 if let Some(handler) = self.user_handler.take() {
125 verification.user().disconnect(handler);
126 }
127 }
128 self.verification.disconnect_signals();
129
130 if let Some(verification) = verification {
131 let user_handler = verification.user().connect_display_name_notify(clone!(
132 #[weak(rename_to = imp)]
133 self,
134 move |_| {
135 imp.update_bar();
136 }
137 ));
138 self.user_handler.replace(Some(user_handler));
139
140 let state_handler = verification.connect_state_notify(clone!(
141 #[weak(rename_to = imp)]
142 self,
143 move |_| {
144 imp.update_bar();
145 }
146 ));
147
148 self.verification.set(verification, vec![state_handler]);
149 }
150
151 self.update_bar();
152 self.obj().notify_verification();
153 }
154
155 fn update_bar(&self) {
157 let Some(verification) = self.verification.obj().filter(|v| !v.is_finished()) else {
158 self.revealer.set_reveal_child(false);
159 return;
160 };
161
162 if matches!(verification.state(), VerificationState::Requested) {
163 self.label.set_markup(&gettext_f(
164 "{user_name} wants to be verified",
167 &[(
168 "user_name",
169 &format!("<b>{}</b>", verification.user().display_name()),
170 )],
171 ));
172 self.accept_btn.set_label(&gettext("Verify"));
173 self.cancel_btn.set_label(&gettext("Decline"));
174 } else {
175 self.label.set_label(&gettext("Verification in progress"));
176 self.accept_btn.set_label(&gettext("Continue"));
177 self.cancel_btn.set_label(&gettext("Cancel"));
178 }
179
180 self.revealer.set_reveal_child(true);
181 }
182 }
183}
184
185glib::wrapper! {
186 pub struct VerificationInfoBar(ObjectSubclass<imp::VerificationInfoBar>)
188 @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
189}
190
191impl VerificationInfoBar {
192 pub fn new() -> Self {
193 glib::Object::new()
194 }
195}