fractal/session_view/
invite_request.rs1use 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 #[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 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 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 #[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 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 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 pub fn header_bar(&self) -> &adw::HeaderBar {
199 &self.imp().header_bar
200 }
201}