fractal/identity_verification_view/
wait_for_other_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/wait_for_other_page.ui"
19    )]
20    #[properties(wrapper_type = super::WaitForOtherPage)]
21    pub struct WaitForOtherPage {
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 title: TemplateChild<gtk::Label>,
28        #[template_child]
29        pub instructions: TemplateChild<gtk::Label>,
30        #[template_child]
31        pub trust: TemplateChild<gtk::Label>,
32        #[template_child]
33        pub cancel_btn: TemplateChild<LoadingButton>,
34    }
35
36    #[glib::object_subclass]
37    impl ObjectSubclass for WaitForOtherPage {
38        const NAME: &'static str = "IdentityVerificationWaitForOtherPage";
39        type Type = super::WaitForOtherPage;
40        type ParentType = adw::Bin;
41
42        fn class_init(klass: &mut Self::Class) {
43            Self::bind_template(klass);
44            Self::Type::bind_template_callbacks(klass);
45        }
46
47        fn instance_init(obj: &InitializingObject<Self>) {
48            obj.init_template();
49        }
50    }
51
52    #[glib::derived_properties]
53    impl ObjectImpl for WaitForOtherPage {
54        fn dispose(&self) {
55            if let Some(verification) = self.verification.upgrade()
56                && let Some(handler) = self.display_name_handler.take()
57            {
58                verification.user().disconnect(handler);
59            }
60        }
61    }
62
63    impl WidgetImpl for WaitForOtherPage {}
64    impl BinImpl for WaitForOtherPage {}
65
66    impl WaitForOtherPage {
67        /// Set the current identity verification.
68        fn set_verification(&self, verification: Option<&IdentityVerification>) {
69            let prev_verification = self.verification.upgrade();
70
71            if prev_verification.as_ref() == verification {
72                return;
73            }
74            let obj = self.obj();
75
76            if let Some(verification) = prev_verification
77                && let Some(handler) = self.display_name_handler.take()
78            {
79                verification.user().disconnect(handler);
80            }
81
82            if let Some(verification) = verification {
83                let display_name_handler = verification.user().connect_display_name_notify(clone!(
84                    #[weak]
85                    obj,
86                    move |_| {
87                        obj.update_labels();
88                    }
89                ));
90                self.display_name_handler
91                    .replace(Some(display_name_handler));
92            }
93
94            self.verification.set(verification);
95
96            obj.update_labels();
97        }
98    }
99}
100
101glib::wrapper! {
102    /// A page instructing the user to wait for the other party.
103    pub struct WaitForOtherPage(ObjectSubclass<imp::WaitForOtherPage>)
104        @extends gtk::Widget, adw::Bin,
105        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
106}
107
108#[gtk::template_callbacks]
109impl WaitForOtherPage {
110    pub fn new(verification: &IdentityVerification) -> Self {
111        glib::Object::builder()
112            .property("verification", verification)
113            .build()
114    }
115
116    /// Update the labels for the current verification.
117    fn update_labels(&self) {
118        let Some(verification) = self.verification() else {
119            return;
120        };
121        let imp = self.imp();
122
123        if verification.is_self_verification() {
124            imp.title.set_label(&gettext("Get Another Device"));
125            imp.instructions.set_label(&gettext(
126                "Accept the verification request from another session or device.",
127            ));
128            imp.trust.set_visible(false);
129        } else {
130            let name = verification.user().display_name();
131            imp.title.set_markup(&gettext_f(
132                // Translators: Do NOT translate the content between '{' and '}', this is a
133                // variable name.
134                "Waiting for {user}",
135                &[("user", &name)],
136            ));
137            imp.instructions.set_markup(&gettext_f(
138                // Translators: Do NOT translate the content between '{' and '}', this is a
139                // variable name.
140                "Ask {user} to accept the verification request.",
141                &[("user", &format!("<b>{name}</b>"))],
142            ));
143            imp.trust.set_visible(true);
144        }
145    }
146
147    /// Reset the UI to its initial state.
148    pub fn reset(&self) {
149        self.imp().cancel_btn.set_is_loading(false);
150        self.set_sensitive(true);
151    }
152
153    /// Cancel the verification.
154    #[template_callback]
155    async fn cancel(&self) {
156        let Some(verification) = self.verification() else {
157            return;
158        };
159
160        self.imp().cancel_btn.set_is_loading(true);
161        self.set_sensitive(false);
162
163        if verification.cancel().await.is_err() {
164            toast!(self, gettext("Could not cancel the verification"));
165            self.reset();
166        }
167    }
168}