fractal/identity_verification_view/
confirm_qr_code_page.rs

1use 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        /// The current identity verification.
23        #[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        /// Set the current identity verification.
71        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    /// A page to confirm whether the QR Code was scanned successfully by the other party.
109    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    /// Update the labels for the current verification.
120    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                // Translators: Do NOT translate the content between '{' and '}', this is a
133                // variable name.
134                "Does {user} see a confirmation on their session?",
135                &[("user", &format!("<b>{name}</b>"))],
136            ));
137        }
138    }
139
140    /// Reset the UI to its initial state.
141    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    /// Confirm that the QR Code was successfully scanned.
150    #[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    /// Cancel the verification.
166    #[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}