fractal/identity_verification_view/
accept_request_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/accept_request_page.ui"
19    )]
20    #[properties(wrapper_type = super::AcceptRequestPage)]
21    pub struct AcceptRequestPage {
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 decline_btn: TemplateChild<LoadingButton>,
32        #[template_child]
33        pub accept_btn: TemplateChild<LoadingButton>,
34    }
35
36    #[glib::object_subclass]
37    impl ObjectSubclass for AcceptRequestPage {
38        const NAME: &'static str = "IdentityVerificationAcceptRequestPage";
39        type Type = super::AcceptRequestPage;
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 AcceptRequestPage {
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 AcceptRequestPage {
64        fn grab_focus(&self) -> bool {
65            self.accept_btn.grab_focus()
66        }
67    }
68
69    impl BinImpl for AcceptRequestPage {}
70
71    impl AcceptRequestPage {
72        /// Set the current identity verification.
73        fn set_verification(&self, verification: Option<&IdentityVerification>) {
74            let prev_verification = self.verification.upgrade();
75
76            if prev_verification.as_ref() == verification {
77                return;
78            }
79            let obj = self.obj();
80
81            obj.reset();
82
83            if let Some(verification) = prev_verification {
84                if let Some(handler) = self.display_name_handler.take() {
85                    verification.user().disconnect(handler);
86                }
87            }
88
89            if let Some(verification) = verification {
90                let display_name_handler = verification.user().connect_display_name_notify(clone!(
91                    #[weak]
92                    obj,
93                    move |_| {
94                        obj.update_labels();
95                    }
96                ));
97                self.display_name_handler
98                    .replace(Some(display_name_handler));
99            }
100
101            self.verification.set(verification);
102            obj.update_labels();
103            obj.notify_verification();
104        }
105    }
106}
107
108glib::wrapper! {
109    /// A page to accept or decline a new identity verification request.
110    pub struct AcceptRequestPage(ObjectSubclass<imp::AcceptRequestPage>)
111        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
112}
113
114#[gtk::template_callbacks]
115impl AcceptRequestPage {
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.title
129                .set_label(&gettext("Login Request From Another Session"));
130            imp.instructions
131                .set_label(&gettext("Verify the new session from the current session."));
132        } else {
133            let name = verification.user().display_name();
134            imp.title.set_markup(&gettext("Verification Request"));
135            imp.instructions
136                // Translators: Do NOT translate the content between '{' and '}', this is a
137                // variable name.
138                .set_markup(&gettext_f("{user} asked to be verified. Verifying a user increases the security of the conversation.", &[("user", &format!("<b>{name}</b>"))]));
139        }
140    }
141
142    /// Reset the UI to its initial state.
143    pub fn reset(&self) {
144        let imp = self.imp();
145        imp.accept_btn.set_is_loading(false);
146        imp.decline_btn.set_is_loading(false);
147        self.set_sensitive(true);
148    }
149
150    /// Decline the verification request.
151    #[template_callback]
152    async fn decline(&self) {
153        let Some(verification) = self.verification() else {
154            return;
155        };
156
157        self.imp().decline_btn.set_is_loading(true);
158        self.set_sensitive(false);
159
160        if verification.cancel().await.is_err() {
161            toast!(self, gettext("Could not decline verification request"));
162            self.reset();
163        }
164    }
165
166    /// Accept the verification request.
167    #[template_callback]
168    async fn accept(&self) {
169        let Some(verification) = self.verification() else {
170            return;
171        };
172
173        self.imp().accept_btn.set_is_loading(true);
174        self.set_sensitive(false);
175
176        if verification.accept().await.is_err() {
177            toast!(self, gettext("Could not accept verification request"));
178            self.reset();
179        }
180    }
181}
182
183impl Default for AcceptRequestPage {
184    fn default() -> Self {
185        Self::new()
186    }
187}