fractal/session/view/content/room_details/
mod.rs1#![allow(deprecated)]
4
5use adw::{prelude::*, subclass::prelude::*};
6use gettextrs::gettext;
7use gtk::{CompositeTemplate, glib, glib::clone};
8use ruma::UserId;
9
10mod addresses_subpage;
11mod edit_details_subpage;
12mod general_page;
13mod history_viewer;
14mod invite_subpage;
15mod member_row;
16mod members_page;
17mod membership_lists;
18mod membership_subpage_item;
19mod permissions;
20mod room_upgrade_dialog;
21
22use self::{
23 addresses_subpage::AddressesSubpage,
24 edit_details_subpage::EditDetailsSubpage,
25 general_page::GeneralPage,
26 history_viewer::{
27 AudioHistoryViewer, FileHistoryViewer, HistoryViewerTimeline, VisualMediaHistoryViewer,
28 },
29 invite_subpage::InviteSubpage,
30 member_row::MemberRow,
31 members_page::MembersPage,
32 membership_lists::MembershipLists,
33 membership_subpage_item::MembershipSubpageItem,
34 permissions::PermissionsSubpage,
35};
36use crate::{components::UserPage, session::model::Room, toast};
37
38#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::Variant)]
40pub(crate) enum SubpageName {
41 EditDetails,
43 Members,
45 Invite,
47 VisualMediaHistory,
49 FileHistory,
51 AudioHistory,
53 Addresses,
55 Permissions,
57}
58
59mod imp {
60 use std::{
61 cell::{OnceCell, RefCell},
62 collections::HashMap,
63 };
64
65 use glib::subclass::InitializingObject;
66
67 use super::*;
68
69 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
70 #[template(resource = "/org/gnome/Fractal/ui/session/view/content/room_details/mod.ui")]
71 #[properties(wrapper_type = super::RoomDetails)]
72 pub struct RoomDetails {
73 #[property(get, set = Self::set_room, construct_only)]
75 room: OnceCell<Room>,
76 #[property(get)]
78 membership_lists: MembershipLists,
79 #[property(get)]
81 timeline: OnceCell<HistoryViewerTimeline>,
82 general_page: OnceCell<GeneralPage>,
84 subpages: RefCell<HashMap<SubpageName, adw::NavigationPage>>,
89 }
90
91 #[glib::object_subclass]
92 impl ObjectSubclass for RoomDetails {
93 const NAME: &'static str = "RoomDetails";
94 type Type = super::RoomDetails;
95 type ParentType = adw::PreferencesWindow;
96
97 fn class_init(klass: &mut Self::Class) {
98 Self::bind_template(klass);
99
100 klass.install_action(
101 "details.show-subpage",
102 Some(&String::static_variant_type()),
103 |obj, _, param| {
104 let subpage = param
105 .and_then(glib::Variant::get::<SubpageName>)
106 .expect("The parameter should be a valid subpage name");
107
108 obj.imp().show_subpage(subpage, false);
109 },
110 );
111
112 klass.install_action(
113 "details.show-member",
114 Some(&String::static_variant_type()),
115 |obj, _, param| {
116 let Some(user_id) = param
117 .and_then(glib::Variant::get::<String>)
118 .and_then(|s| UserId::parse(s).ok())
119 else {
120 return;
121 };
122
123 let member = obj.membership_lists().members().get_or_create(user_id);
124 let user_page = UserPage::new(&member);
125 user_page.connect_close(clone!(
126 #[weak]
127 obj,
128 move |_| {
129 obj.pop_subpage();
130 toast!(
131 obj,
132 gettext("The user is not in the room members list anymore"),
133 );
134 }
135 ));
136
137 obj.push_subpage(&user_page);
138 },
139 );
140
141 klass.install_action("win.toggle-fullscreen", None, |obj, _, _| {
142 if obj.is_fullscreen() {
143 obj.unfullscreen();
144 } else {
145 obj.fullscreen();
146 }
147 });
148 }
149
150 fn instance_init(obj: &InitializingObject<Self>) {
151 obj.init_template();
152 }
153 }
154
155 #[glib::derived_properties]
156 impl ObjectImpl for RoomDetails {}
157
158 impl WidgetImpl for RoomDetails {
159 fn map(&self) {
160 self.parent_map();
161
162 self.general_page
163 .get()
164 .expect("general page is initialized")
165 .unselect_topic();
166 }
167 }
168
169 impl WindowImpl for RoomDetails {}
170 impl AdwWindowImpl for RoomDetails {}
171 impl PreferencesWindowImpl for RoomDetails {}
172
173 impl RoomDetails {
174 fn set_room(&self, room: Room) {
176 let room = self.room.get_or_init(|| room);
177
178 self.timeline
180 .set(HistoryViewerTimeline::new(room))
181 .expect("timeline is uninitialized");
182
183 self.membership_lists
185 .set_members(room.get_or_create_members());
186
187 let general_page = self
189 .general_page
190 .get_or_init(|| GeneralPage::new(room, &self.membership_lists));
191 self.obj().add(general_page);
192 }
193
194 fn timeline(&self) -> &HistoryViewerTimeline {
196 self.timeline.get().expect("timeline is initialized")
197 }
198
199 pub(super) fn show_subpage(&self, name: SubpageName, is_initial: bool) {
201 let room = self.room.get().expect("room is initialized");
202
203 let mut subpages = self.subpages.borrow_mut();
204 let subpage = subpages.entry(name).or_insert_with(|| match name {
205 SubpageName::EditDetails => EditDetailsSubpage::new(room).upcast(),
206 SubpageName::Members => MembersPage::new(room, &self.membership_lists).upcast(),
207 SubpageName::Invite => InviteSubpage::new(room).upcast(),
208 SubpageName::VisualMediaHistory => {
209 VisualMediaHistoryViewer::new(self.timeline()).upcast()
210 }
211 SubpageName::FileHistory => FileHistoryViewer::new(self.timeline()).upcast(),
212 SubpageName::AudioHistory => AudioHistoryViewer::new(self.timeline()).upcast(),
213 SubpageName::Addresses => AddressesSubpage::new(room).upcast(),
214 SubpageName::Permissions => PermissionsSubpage::new(&room.permissions()).upcast(),
215 });
216
217 if is_initial {
218 subpage.set_can_pop(false);
219 }
220
221 self.obj().push_subpage(subpage);
222 }
223 }
224}
225
226glib::wrapper! {
227 pub struct RoomDetails(ObjectSubclass<imp::RoomDetails>)
229 @extends gtk::Widget, gtk::Window, adw::Window, gtk::Root, adw::PreferencesWindow,
230 @implements gtk::Accessible;
231}
232
233impl RoomDetails {
234 pub fn new(parent_window: Option<>k::Window>, room: &Room) -> Self {
237 glib::Object::builder()
238 .property("transient-for", parent_window)
239 .property("room", room)
240 .build()
241 }
242
243 pub(crate) fn show_initial_subpage(&self, name: SubpageName) {
245 self.imp().show_subpage(name, true);
246 }
247}