fractal/identity_verification_view/
no_supported_methods_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    utils::BoundObjectWeakRef,
8};
9
10mod imp {
11    use std::cell::RefCell;
12
13    use glib::subclass::InitializingObject;
14
15    use super::*;
16
17    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
18    #[template(
19        resource = "/org/gnome/Fractal/ui/identity_verification_view/no_supported_methods_page.ui"
20    )]
21    #[properties(wrapper_type = super::NoSupportedMethodsPage)]
22    pub struct NoSupportedMethodsPage {
23        /// The current identity verification.
24        #[property(get, set = Self::set_verification, explicit_notify, nullable)]
25        pub verification: BoundObjectWeakRef<IdentityVerification>,
26        pub display_name_handler: RefCell<Option<glib::SignalHandlerId>>,
27        #[template_child]
28        pub message: TemplateChild<gtk::Label>,
29        #[template_child]
30        pub instructions: TemplateChild<gtk::Label>,
31        #[template_child]
32        pub cancel_btn: TemplateChild<LoadingButton>,
33    }
34
35    #[glib::object_subclass]
36    impl ObjectSubclass for NoSupportedMethodsPage {
37        const NAME: &'static str = "IdentityVerificationNoSupportedMethodsPage";
38        type Type = super::NoSupportedMethodsPage;
39        type ParentType = adw::Bin;
40
41        fn class_init(klass: &mut Self::Class) {
42            Self::bind_template(klass);
43            Self::Type::bind_template_callbacks(klass);
44        }
45
46        fn instance_init(obj: &InitializingObject<Self>) {
47            obj.init_template();
48        }
49    }
50
51    #[glib::derived_properties]
52    impl ObjectImpl for NoSupportedMethodsPage {
53        fn dispose(&self) {
54            if let Some(verification) = self.verification.obj()
55                && let Some(handler) = self.display_name_handler.take()
56            {
57                verification.user().disconnect(handler);
58            }
59        }
60    }
61
62    impl WidgetImpl for NoSupportedMethodsPage {
63        fn grab_focus(&self) -> bool {
64            self.cancel_btn.grab_focus()
65        }
66    }
67
68    impl BinImpl for NoSupportedMethodsPage {}
69
70    impl NoSupportedMethodsPage {
71        /// Set the current identity verification.
72        fn set_verification(&self, verification: Option<&IdentityVerification>) {
73            let prev_verification = self.verification.obj();
74
75            if prev_verification.as_ref() == verification {
76                return;
77            }
78            let obj = self.obj();
79
80            if let Some(verification) = prev_verification
81                && let Some(handler) = self.display_name_handler.take()
82            {
83                verification.user().disconnect(handler);
84            }
85            self.verification.disconnect_signals();
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_page();
93                    }
94                ));
95                self.display_name_handler
96                    .replace(Some(display_name_handler));
97
98                let was_accepted_handler = verification.connect_was_accepted_notify(clone!(
99                    #[weak]
100                    obj,
101                    move |_| {
102                        obj.update_page();
103                    }
104                ));
105
106                self.verification
107                    .set(verification, vec![was_accepted_handler]);
108            }
109
110            obj.update_page();
111            obj.notify_verification();
112        }
113    }
114}
115
116glib::wrapper! {
117    /// A page to show when a verification request was received with no methods that Fractal supports.
118    pub struct NoSupportedMethodsPage(ObjectSubclass<imp::NoSupportedMethodsPage>)
119        @extends gtk::Widget, adw::Bin,
120        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
121}
122
123#[gtk::template_callbacks]
124impl NoSupportedMethodsPage {
125    pub fn new() -> Self {
126        glib::Object::new()
127    }
128
129    /// Update the page for the current verification.
130    fn update_page(&self) {
131        let Some(verification) = self.verification() else {
132            return;
133        };
134        let imp = self.imp();
135        let was_accepted = verification.was_accepted();
136
137        let message = if verification.is_self_verification() {
138            if was_accepted {
139                gettext("None of the methods offered by the other client are supported by Fractal.")
140            } else {
141                gettext(
142                    "A login request was received, but none of the methods offered by the other client are supported by Fractal.",
143                )
144            }
145        } else {
146            let name = verification.user().display_name();
147            if was_accepted {
148                gettext_f(
149                    // Translators: Do NOT translate the content between '{' and '}', this is a
150                    // variable name.
151                    "None of the methods offered by {user}’s client are supported by Fractal.",
152                    &[("user", &format!("<b>{name}</b>"))],
153                )
154            } else {
155                gettext_f(
156                    // Translators: Do NOT translate the content between '{' and '}', this is a
157                    // variable name.
158                    "{user} sent a verification request, but none of the methods offered by the other client are supported by Fractal.",
159                    &[("user", &format!("<b>{name}</b>"))],
160                )
161            }
162        };
163        imp.message.set_markup(&message);
164
165        imp.instructions.set_visible(!was_accepted);
166
167        let cancel_label = if was_accepted {
168            gettext("Cancel Verification")
169        } else {
170            gettext("Decline Verification")
171        };
172        imp.cancel_btn.set_content_label(cancel_label);
173    }
174
175    /// Reset the UI to its initial state.
176    pub fn reset(&self) {
177        self.imp().cancel_btn.set_is_loading(false);
178        self.set_sensitive(true);
179    }
180
181    /// Decline the verification.
182    #[template_callback]
183    async fn cancel(&self) {
184        let Some(verification) = self.verification() else {
185            return;
186        };
187
188        self.imp().cancel_btn.set_is_loading(true);
189        self.set_sensitive(false);
190
191        if verification.cancel().await.is_err() {
192            toast!(self, gettext("Could not decline the verification"));
193            self.reset();
194        }
195    }
196}