fractal/identity_verification_view/
confirm_qr_code_page.rs1use adw::subclass::prelude::*;
2use gettextrs::gettext;
3use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
4
5use crate::{
6 components::LoadingButton, gettext_f, prelude::*, session::model::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, 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 if let Some(handler) = self.display_name_handler.take() {
55 verification.user().disconnect(handler);
56 }
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 if let Some(handler) = self.display_name_handler.take() {
83 verification.user().disconnect(handler);
84 }
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, @implements gtk::Accessible;
111}
112
113#[gtk::template_callbacks]
114impl ConfirmQrCodePage {
115 pub fn new() -> Self {
116 glib::Object::new()
117 }
118
119 fn update_labels(&self) {
121 let Some(verification) = self.verification() else {
122 return;
123 };
124 let imp = self.imp();
125
126 if verification.is_self_verification() {
127 imp.question
128 .set_label(&gettext("Does the other session show a confirmation?"));
129 } else {
130 let name = verification.user().display_name();
131 imp.question.set_markup(&gettext_f(
132 "Does {user} see a confirmation on their session?",
135 &[("user", &format!("<b>{name}</b>"))],
136 ));
137 }
138 }
139
140 pub fn reset(&self) {
142 let imp = self.imp();
143
144 imp.confirm_btn.set_is_loading(false);
145 imp.cancel_btn.set_is_loading(false);
146 self.set_sensitive(true);
147 }
148
149 #[template_callback]
151 async fn confirm_scanned(&self) {
152 let Some(verification) = self.verification() else {
153 return;
154 };
155
156 self.imp().confirm_btn.set_is_loading(true);
157 self.set_sensitive(false);
158
159 if verification.confirm_qr_code_scanned().await.is_err() {
160 toast!(self, gettext("Could not confirm the scan of the QR Code"));
161 self.reset();
162 }
163 }
164
165 #[template_callback]
167 async fn cancel(&self) {
168 let Some(verification) = self.verification() else {
169 return;
170 };
171
172 self.imp().cancel_btn.set_is_loading(true);
173 self.set_sensitive(false);
174
175 if verification.cancel().await.is_err() {
176 toast!(self, gettext("Could not cancel the verification"));
177 self.reset();
178 }
179 }
180}