fractal/identity_verification_view/
confirm_qr_code_page.rs1use adw::{prelude::*, subclass::prelude::*};
2use gettextrs::gettext;
3use gtk::{glib, glib::clone};
4
5use crate::{
6 components::LoadingButton, gettext_f, prelude::*, session::IdentityVerification, toast,
7};
8
9mod imp {
10 use std::cell::RefCell;
11
12 use glib::subclass::InitializingObject;
13
14 use super::*;
15
16 #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
17 #[template(
18 resource = "/org/gnome/Fractal/ui/identity_verification_view/confirm_qr_code_page.ui"
19 )]
20 #[properties(wrapper_type = super::ConfirmQrCodePage)]
21 pub struct ConfirmQrCodePage {
22 #[property(get, set = Self::set_verification, explicit_notify, nullable)]
24 pub verification: glib::WeakRef<IdentityVerification>,
25 pub display_name_handler: RefCell<Option<glib::SignalHandlerId>>,
26 #[template_child]
27 pub question: TemplateChild<gtk::Label>,
28 #[template_child]
29 pub confirm_btn: TemplateChild<LoadingButton>,
30 #[template_child]
31 pub cancel_btn: TemplateChild<LoadingButton>,
32 }
33
34 #[glib::object_subclass]
35 impl ObjectSubclass for ConfirmQrCodePage {
36 const NAME: &'static str = "IdentityVerificationConfirmQrCodePage";
37 type Type = super::ConfirmQrCodePage;
38 type ParentType = adw::Bin;
39
40 fn class_init(klass: &mut Self::Class) {
41 Self::bind_template(klass);
42 Self::Type::bind_template_callbacks(klass);
43 }
44
45 fn instance_init(obj: &InitializingObject<Self>) {
46 obj.init_template();
47 }
48 }
49
50 #[glib::derived_properties]
51 impl ObjectImpl for ConfirmQrCodePage {
52 fn dispose(&self) {
53 if let Some(verification) = self.verification.upgrade()
54 && let Some(handler) = self.display_name_handler.take()
55 {
56 verification.user().disconnect(handler);
57 }
58 }
59 }
60
61 impl WidgetImpl for ConfirmQrCodePage {
62 fn grab_focus(&self) -> bool {
63 self.confirm_btn.grab_focus()
64 }
65 }
66
67 impl BinImpl for ConfirmQrCodePage {}
68
69 impl ConfirmQrCodePage {
70 fn set_verification(&self, verification: Option<&IdentityVerification>) {
72 let prev_verification = self.verification.upgrade();
73
74 if prev_verification.as_ref() == verification {
75 return;
76 }
77 let obj = self.obj();
78
79 obj.reset();
80
81 if let Some(verification) = prev_verification
82 && let Some(handler) = self.display_name_handler.take()
83 {
84 verification.user().disconnect(handler);
85 }
86
87 if let Some(verification) = verification {
88 let display_name_handler = verification.user().connect_display_name_notify(clone!(
89 #[weak]
90 obj,
91 move |_| {
92 obj.update_labels();
93 }
94 ));
95 self.display_name_handler
96 .replace(Some(display_name_handler));
97 }
98
99 self.verification.set(verification);
100
101 obj.update_labels();
102 obj.notify_verification();
103 }
104 }
105}
106
107glib::wrapper! {
108 pub struct ConfirmQrCodePage(ObjectSubclass<imp::ConfirmQrCodePage>)
110 @extends gtk::Widget, adw::Bin,
111 @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
112}
113
114#[gtk::template_callbacks]
115impl ConfirmQrCodePage {
116 pub fn new() -> Self {
117 glib::Object::new()
118 }
119
120 fn update_labels(&self) {
122 let Some(verification) = self.verification() else {
123 return;
124 };
125 let imp = self.imp();
126
127 if verification.is_self_verification() {
128 imp.question
129 .set_label(&gettext("Does the other session show a confirmation?"));
130 } else {
131 let name = verification.user().display_name();
132 imp.question.set_markup(&gettext_f(
133 "Does {user} see a confirmation on their session?",
136 &[("user", &format!("<b>{name}</b>"))],
137 ));
138 }
139 }
140
141 pub fn reset(&self) {
143 let imp = self.imp();
144
145 imp.confirm_btn.set_is_loading(false);
146 imp.cancel_btn.set_is_loading(false);
147 self.set_sensitive(true);
148 }
149
150 #[template_callback]
152 async fn confirm_scanned(&self) {
153 let Some(verification) = self.verification() else {
154 return;
155 };
156
157 self.imp().confirm_btn.set_is_loading(true);
158 self.set_sensitive(false);
159
160 if verification.confirm_qr_code_scanned().await.is_err() {
161 toast!(self, gettext("Could not confirm the scan of the QR Code"));
162 self.reset();
163 }
164 }
165
166 #[template_callback]
168 async fn cancel(&self) {
169 let Some(verification) = self.verification() else {
170 return;
171 };
172
173 self.imp().cancel_btn.set_is_loading(true);
174 self.set_sensitive(false);
175
176 if verification.cancel().await.is_err() {
177 toast!(self, gettext("Could not cancel the verification"));
178 self.reset();
179 }
180 }
181}