fractal/components/dialogs/
user_profile.rs1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{glib, glib::clone};
3use ruma::OwnedUserId;
4
5use super::ToastableDialog;
6use crate::{
7 components::UserPage,
8 prelude::*,
9 session::{Member, Session, User},
10 utils::LoadingState,
11};
12
13mod imp {
14 use std::cell::RefCell;
15
16 use glib::subclass::InitializingObject;
17
18 use super::*;
19
20 #[derive(Debug, Default, gtk::CompositeTemplate)]
21 #[template(resource = "/org/gnome/Fractal/ui/components/dialogs/user_profile.ui")]
22 pub struct UserProfileDialog {
23 #[template_child]
24 stack: TemplateChild<gtk::Stack>,
25 #[template_child]
26 user_page: TemplateChild<UserPage>,
27 user_loading_handler: RefCell<Option<glib::SignalHandlerId>>,
28 }
29
30 #[glib::object_subclass]
31 impl ObjectSubclass for UserProfileDialog {
32 const NAME: &'static str = "UserProfileDialog";
33 type Type = super::UserProfileDialog;
34 type ParentType = ToastableDialog;
35
36 fn class_init(klass: &mut Self::Class) {
37 Self::bind_template(klass);
38 }
39
40 fn instance_init(obj: &InitializingObject<Self>) {
41 obj.init_template();
42 }
43 }
44
45 impl ObjectImpl for UserProfileDialog {
46 fn dispose(&self) {
47 self.reset();
48 }
49 }
50
51 impl WidgetImpl for UserProfileDialog {}
52 impl AdwDialogImpl for UserProfileDialog {}
53 impl ToastableDialogImpl for UserProfileDialog {}
54
55 impl UserProfileDialog {
56 fn show_details(&self) {
58 self.stack.set_visible_child_name("details");
59 }
60
61 pub(super) fn load_user(&self, session: &Session, user_id: OwnedUserId) {
63 self.reset();
64
65 let user = session.remote_cache().user(user_id);
66 self.user_page.set_user(Some(user.clone()));
67
68 if matches!(
69 user.loading_state(),
70 LoadingState::Initial | LoadingState::Loading
71 ) {
72 let user_loading_handler = user.connect_loading_state_notify(clone!(
73 #[weak(rename_to = imp)]
74 self,
75 move |user| {
76 if !matches!(
77 user.loading_state(),
78 LoadingState::Initial | LoadingState::Loading
79 ) && let Some(handler) = imp.user_loading_handler.take()
80 {
81 user.disconnect(handler);
82 imp.show_details();
83 }
84 }
85 ));
86 self.user_loading_handler
87 .replace(Some(user_loading_handler));
88 } else {
89 self.show_details();
90 }
91 }
92
93 pub(super) fn set_room_member(&self, member: Member) {
95 self.reset();
96
97 self.user_page.set_user(Some(member.upcast::<User>()));
98 self.show_details();
99 }
100
101 fn reset(&self) {
103 if let Some(handler) = self.user_loading_handler.take()
104 && let Some(user) = self.user_page.user()
105 {
106 user.disconnect(handler);
107 }
108 }
109 }
110}
111
112glib::wrapper! {
113 pub struct UserProfileDialog(ObjectSubclass<imp::UserProfileDialog>)
115 @extends gtk::Widget, adw::Dialog, ToastableDialog,
116 @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
117}
118
119impl UserProfileDialog {
120 pub fn new() -> Self {
122 glib::Object::new()
123 }
124
125 pub(crate) fn load_user(&self, session: &Session, user_id: OwnedUserId) {
127 self.imp().load_user(session, user_id);
128 }
129
130 pub(crate) fn set_room_member(&self, member: Member) {
132 self.imp().set_room_member(member);
133 }
134}