fractal/identity_verification_view/
confirm_qr_code_page.rs

1use 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        /// 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                && 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        /// 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                && 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    /// 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,
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    /// Update the labels for the current verification.
121    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                // Translators: Do NOT translate the content between '{' and '}', this is a
134                // variable name.
135                "Does {user} see a confirmation on their session?",
136                &[("user", &format!("<b>{name}</b>"))],
137            ));
138        }
139    }
140
141    /// Reset the UI to its initial state.
142    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    /// Confirm that the QR Code was successfully scanned.
151    #[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    /// Cancel the verification.
167    #[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}