fractal/identity_verification_view/
mod.rs1use adw::{prelude::*, subclass::prelude::*};
2use gettextrs::gettext;
3use gtk::{glib, glib::clone};
4
5mod accept_request_page;
6mod cancelled_page;
7mod choose_method_page;
8mod completed_page;
9mod confirm_qr_code_page;
10mod no_supported_methods_page;
11mod qr_code_scanned_page;
12mod room_left_page;
13mod sas_emoji;
14mod sas_page;
15mod scan_qr_code_page;
16mod wait_for_other_page;
17
18use self::{
19 accept_request_page::AcceptRequestPage, cancelled_page::CancelledPage,
20 choose_method_page::ChooseMethodPage, completed_page::CompletedPage,
21 confirm_qr_code_page::ConfirmQrCodePage, no_supported_methods_page::NoSupportedMethodsPage,
22 qr_code_scanned_page::QrCodeScannedPage, room_left_page::RoomLeftPage, sas_page::SasPage,
23 scan_qr_code_page::ScanQrCodePage, wait_for_other_page::WaitForOtherPage,
24};
25use crate::{
26 session::{IdentityVerification, VerificationState},
27 utils::BoundObject,
28};
29
30mod imp {
31 use glib::subclass::InitializingObject;
32
33 use super::*;
34
35 #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
36 #[template(resource = "/org/gnome/Fractal/ui/identity_verification_view/mod.ui")]
37 #[properties(wrapper_type = super::IdentityVerificationView)]
38 pub struct IdentityVerificationView {
39 #[property(get, set = Self::set_verification, explicit_notify, nullable)]
41 pub verification: BoundObject<IdentityVerification>,
42 #[template_child]
43 pub main_stack: TemplateChild<gtk::Stack>,
44 #[template_child]
45 pub accept_request_page: TemplateChild<AcceptRequestPage>,
46 #[template_child]
47 pub wait_for_other_page: TemplateChild<WaitForOtherPage>,
48 #[template_child]
49 pub no_supported_methods_page: TemplateChild<NoSupportedMethodsPage>,
50 #[template_child]
51 pub choose_method_page: TemplateChild<ChooseMethodPage>,
52 #[template_child]
53 pub qr_code_scanned_page: TemplateChild<QrCodeScannedPage>,
54 #[template_child]
55 pub confirm_qr_code_page: TemplateChild<ConfirmQrCodePage>,
56 #[template_child]
57 pub sas_page: TemplateChild<SasPage>,
58 #[template_child]
59 pub completed_page: TemplateChild<CompletedPage>,
60 #[template_child]
61 pub cancelled_page: TemplateChild<CancelledPage>,
62 #[template_child]
63 pub room_left_page: TemplateChild<RoomLeftPage>,
64 }
65
66 #[glib::object_subclass]
67 impl ObjectSubclass for IdentityVerificationView {
68 const NAME: &'static str = "IdentityVerificationView";
69 type Type = super::IdentityVerificationView;
70 type ParentType = adw::Bin;
71
72 fn class_init(klass: &mut Self::Class) {
73 Self::bind_template(klass);
74 Self::bind_template_callbacks(klass);
75 }
76
77 fn instance_init(obj: &InitializingObject<Self>) {
78 obj.init_template();
79 }
80 }
81
82 #[glib::derived_properties]
83 impl ObjectImpl for IdentityVerificationView {}
84
85 impl WidgetImpl for IdentityVerificationView {
86 fn grab_focus(&self) -> bool {
87 let Some(name) = self.main_stack.visible_child_name() else {
88 return false;
89 };
90
91 match name.as_str() {
92 "accept-request" => self.accept_request_page.grab_focus(),
93 "no-supported-methods" => self.no_supported_methods_page.grab_focus(),
94 "choose-method" => self.choose_method_page.grab_focus(),
95 "confirm-qr-code" => self.confirm_qr_code_page.grab_focus(),
96 "sas" => self.sas_page.grab_focus(),
97 "completed" => self.completed_page.grab_focus(),
98 "cancelled" => self.cancelled_page.grab_focus(),
99 "room-left" => self.room_left_page.grab_focus(),
100 _ => false,
101 }
102 }
103 }
104
105 impl BinImpl for IdentityVerificationView {}
106
107 #[gtk::template_callbacks]
108 impl IdentityVerificationView {
109 fn set_verification(&self, verification: Option<IdentityVerification>) {
111 let prev_verification = self.verification.obj();
112
113 if prev_verification == verification {
114 return;
115 }
116 let obj = self.obj();
117
118 self.verification.disconnect_signals();
119
120 if let Some(verification) = verification {
121 let state_handler = verification.connect_state_notify(clone!(
122 #[weak]
123 obj,
124 move |_| {
125 obj.update_view();
126 }
127 ));
128
129 verification.set_was_viewed(true);
130 self.verification.set(verification, vec![state_handler]);
131 }
132
133 obj.update_view();
134 obj.notify_verification();
135 }
136
137 #[template_callback]
138 fn handle_transition_running(&self) {
139 if !self.main_stack.is_transition_running() {
140 self.grab_focus();
142
143 if let Some(scan_qrcode_page) = self.main_stack.child_by_name("scan-qrcode")
146 && self.main_stack.visible_child_name().as_deref() != Some("scan-qrcode")
147 {
148 self.main_stack.remove(&scan_qrcode_page);
149 }
150 }
151 }
152 }
153}
154
155glib::wrapper! {
156 pub struct IdentityVerificationView(ObjectSubclass<imp::IdentityVerificationView>)
158 @extends gtk::Widget, adw::Bin,
159 @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
160}
161
162impl IdentityVerificationView {
163 pub fn new(verification: &IdentityVerification) -> Self {
164 glib::Object::builder()
165 .property("verification", verification)
166 .build()
167 }
168
169 fn update_view(&self) {
171 let Some(verification) = self.verification() else {
172 return;
173 };
174 let imp = self.imp();
175
176 match verification.state() {
177 VerificationState::Created => {
178 imp.wait_for_other_page.reset();
179 imp.main_stack
180 .set_visible_child_name("wait-for-other-party");
181 }
182 VerificationState::Requested => {
183 imp.accept_request_page.reset();
184 imp.main_stack.set_visible_child_name("accept-request");
185 }
186 VerificationState::NoSupportedMethods => {
187 imp.no_supported_methods_page.reset();
188 imp.main_stack
189 .set_visible_child_name("no-supported-methods");
190 }
191 VerificationState::Ready => {
192 imp.choose_method_page.reset();
193 imp.main_stack.set_visible_child_name("choose-method");
194 }
195 VerificationState::QrScan => {
196 let scan_qrcode_page = ScanQrCodePage::new(verification);
197 imp.main_stack.add_titled(
198 &scan_qrcode_page,
199 Some("scan-qrcode"),
200 &gettext("Scan QR Code"),
201 );
202 imp.main_stack.set_visible_child_name("scan-qrcode");
203 }
204 VerificationState::QrScanned => {
205 imp.qr_code_scanned_page.reset();
206 imp.main_stack.set_visible_child_name("qr-code-scanned");
207 }
208 VerificationState::QrConfirm => {
209 imp.confirm_qr_code_page.reset();
210 imp.main_stack.set_visible_child_name("confirm-qr-code");
211 }
212 VerificationState::SasConfirm => {
213 imp.sas_page.reset();
214 imp.main_stack.set_visible_child_name("sas");
215 }
216 VerificationState::Done => {
217 imp.main_stack.set_visible_child_name("completed");
218 }
219 VerificationState::Cancelled | VerificationState::Error => {
220 imp.cancelled_page.reset();
221 imp.main_stack.set_visible_child_name("cancelled");
222 }
223 VerificationState::RoomLeft => {
224 imp.main_stack.set_visible_child_name("room-left");
225 }
226 VerificationState::Dismissed => {}
228 }
229 }
230}