Skip to main content

fractal/session_view/
invite_request.rs

1use adw::{prelude::*, subclass::prelude::*};
2use gettextrs::gettext;
3use gtk::{glib, glib::clone};
4
5use crate::{
6    components::{Avatar, LoadingButton},
7    session::{Room, RoomCategory, TargetRoomCategory},
8    toast,
9    utils::matrix::MatrixIdUri,
10};
11
12mod imp {
13    use std::cell::RefCell;
14
15    use glib::subclass::InitializingObject;
16
17    use super::*;
18
19    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
20    #[template(resource = "/org/gnome/Fractal/ui/session_view/invite_request.ui")]
21    #[properties(wrapper_type = super::InviteRequest)]
22    pub struct InviteRequest {
23        #[template_child]
24        pub(super) header_bar: TemplateChild<adw::HeaderBar>,
25        #[template_child]
26        avatar: TemplateChild<Avatar>,
27        #[template_child]
28        room_alias: TemplateChild<gtk::Label>,
29        #[template_child]
30        room_topic: TemplateChild<gtk::Label>,
31        #[template_child]
32        retract_button: TemplateChild<LoadingButton>,
33        /// The room currently displayed.
34        #[property(get, set = Self::set_room, explicit_notify, nullable)]
35        room: RefCell<Option<Room>>,
36        category_handler: RefCell<Option<glib::SignalHandlerId>>,
37    }
38
39    #[glib::object_subclass]
40    impl ObjectSubclass for InviteRequest {
41        const NAME: &'static str = "ContentInviteRequest";
42        type Type = super::InviteRequest;
43        type ParentType = adw::Bin;
44
45        fn class_init(klass: &mut Self::Class) {
46            Self::bind_template(klass);
47            Self::bind_template_callbacks(klass);
48
49            klass.set_accessible_role(gtk::AccessibleRole::Group);
50        }
51
52        fn instance_init(obj: &InitializingObject<Self>) {
53            obj.init_template();
54        }
55    }
56
57    #[glib::derived_properties]
58    impl ObjectImpl for InviteRequest {
59        fn constructed(&self) {
60            self.parent_constructed();
61            let obj = self.obj();
62
63            self.room_alias.connect_label_notify(|room_alias| {
64                room_alias.set_visible(!room_alias.label().is_empty());
65            });
66            self.room_alias
67                .set_visible(!self.room_alias.label().is_empty());
68
69            self.room_topic.connect_label_notify(|room_topic| {
70                room_topic.set_visible(!room_topic.label().is_empty());
71            });
72            self.room_topic
73                .set_visible(!self.room_topic.label().is_empty());
74            self.room_topic.connect_activate_link(clone!(
75                #[weak]
76                obj,
77                #[upgrade_or]
78                glib::Propagation::Proceed,
79                move |_, uri| {
80                    if MatrixIdUri::parse(uri).is_ok() {
81                        let _ =
82                            obj.activate_action("session.show-matrix-uri", Some(&uri.to_variant()));
83                        glib::Propagation::Stop
84                    } else {
85                        glib::Propagation::Proceed
86                    }
87                }
88            ));
89        }
90
91        fn dispose(&self) {
92            self.disconnect_signals();
93        }
94    }
95
96    impl WidgetImpl for InviteRequest {
97        fn grab_focus(&self) -> bool {
98            self.retract_button.grab_focus()
99        }
100    }
101
102    impl BinImpl for InviteRequest {}
103
104    #[gtk::template_callbacks]
105    impl InviteRequest {
106        /// Set the room currently displayed.
107        fn set_room(&self, room: Option<Room>) {
108            if *self.room.borrow() == room {
109                return;
110            }
111
112            self.disconnect_signals();
113
114            if let Some(room) = &room {
115                let category_handler = room.connect_category_notify(clone!(
116                    #[weak(rename_to = imp)]
117                    self,
118                    move |room| {
119                        let category = room.category();
120
121                        if category == RoomCategory::Left {
122                            // We retracted the request or the request was denied, we should close
123                            // the room if it is opened.
124                            let Some(session) = room.session() else {
125                                return;
126                            };
127                            let selection = session.sidebar_list_model().selection_model();
128                            if selection
129                                .selected_item()
130                                .and_downcast::<Room>()
131                                .is_some_and(|selected_room| selected_room == *room)
132                            {
133                                selection.set_selected_item(None::<glib::Object>);
134                            }
135                        }
136
137                        if category != RoomCategory::Knocked {
138                            imp.retract_button.set_is_loading(false);
139
140                            if let Some(category_handler) = imp.category_handler.take() {
141                                room.disconnect(category_handler);
142                            }
143                        }
144                    }
145                ));
146                self.category_handler.replace(Some(category_handler));
147            }
148
149            self.room.replace(room);
150
151            self.obj().notify_room();
152        }
153
154        /// Retract the request.
155        #[template_callback]
156        async fn retract(&self) {
157            let Some(room) = self.room.borrow().clone() else {
158                return;
159            };
160
161            self.retract_button.set_is_loading(true);
162
163            if room
164                .change_category(TargetRoomCategory::Left)
165                .await
166                .is_err()
167            {
168                toast!(self.obj(), gettext("Could not retract invite request",),);
169
170                self.retract_button.set_is_loading(false);
171            }
172        }
173
174        /// Disconnect the signal handlers of this view.
175        fn disconnect_signals(&self) {
176            if let Some(room) = self.room.take()
177                && let Some(handler) = self.category_handler.take()
178            {
179                room.disconnect(handler);
180            }
181        }
182    }
183}
184
185glib::wrapper! {
186    /// A view presenting an invitate request to a room.
187    pub struct InviteRequest(ObjectSubclass<imp::InviteRequest>)
188        @extends gtk::Widget, adw::Bin,
189        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
190}
191
192impl InviteRequest {
193    pub fn new() -> Self {
194        glib::Object::new()
195    }
196
197    /// The header bar of the invite request.
198    pub fn header_bar(&self) -> &adw::HeaderBar {
199        &self.imp().header_bar
200    }
201}