fractal/identity_verification_view/
wait_for_other_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/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                if let Some(handler) = self.display_name_handler.take() {
57                    verification.user().disconnect(handler);
58                }
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                if let Some(handler) = self.display_name_handler.take() {
78                    verification.user().disconnect(handler);
79                }
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, @implements gtk::Accessible;
105}
106
107#[gtk::template_callbacks]
108impl WaitForOtherPage {
109    pub fn new(verification: &IdentityVerification) -> Self {
110        glib::Object::builder()
111            .property("verification", verification)
112            .build()
113    }
114
115    /// Update the labels for the current verification.
116    fn update_labels(&self) {
117        let Some(verification) = self.verification() else {
118            return;
119        };
120        let imp = self.imp();
121
122        if verification.is_self_verification() {
123            imp.title.set_label(&gettext("Get Another Device"));
124            imp.instructions.set_label(&gettext(
125                "Accept the verification request from another session or device.",
126            ));
127            imp.trust.set_visible(false);
128        } else {
129            let name = verification.user().display_name();
130            imp.title.set_markup(&gettext_f(
131                // Translators: Do NOT translate the content between '{' and '}', this is a
132                // variable name.
133                "Waiting for {user}",
134                &[("user", &name)],
135            ));
136            imp.instructions.set_markup(&gettext_f(
137                // Translators: Do NOT translate the content between '{' and '}', this is a
138                // variable name.
139                "Ask {user} to accept the verification request.",
140                &[("user", &format!("<b>{name}</b>"))],
141            ));
142            imp.trust.set_visible(true);
143        }
144    }
145
146    /// Reset the UI to its initial state.
147    pub fn reset(&self) {
148        self.imp().cancel_btn.set_is_loading(false);
149        self.set_sensitive(true);
150    }
151
152    /// Cancel the verification.
153    #[template_callback]
154    async fn cancel(&self) {
155        let Some(verification) = self.verification() else {
156            return;
157        };
158
159        self.imp().cancel_btn.set_is_loading(true);
160        self.set_sensitive(false);
161
162        if verification.cancel().await.is_err() {
163            toast!(self, gettext("Could not cancel the verification"));
164            self.reset();
165        }
166    }
167}