fractal/session/remote/
user.rs1use std::time::{Duration, Instant};
2
3use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
4use matrix_sdk::ruma::OwnedUserId;
5
6use crate::{
7 components::PillSource,
8 prelude::*,
9 session::{Session, User},
10 spawn,
11 utils::LoadingState,
12};
13
14const PROFILE_VALIDITY_DURATION: Duration = Duration::from_secs(60 * 60);
18
19mod imp {
20 use std::cell::Cell;
21
22 use super::*;
23
24 #[derive(Debug, Default, glib::Properties)]
25 #[properties(wrapper_type = super::RemoteUser)]
26 pub struct RemoteUser {
27 #[property(get, builder(LoadingState::default()))]
29 loading_state: Cell<LoadingState>,
30 last_request_time: Cell<Option<Instant>>,
32 }
33
34 #[glib::object_subclass]
35 impl ObjectSubclass for RemoteUser {
36 const NAME: &'static str = "RemoteUser";
37 type Type = super::RemoteUser;
38 type ParentType = User;
39 }
40
41 #[glib::derived_properties]
42 impl ObjectImpl for RemoteUser {}
43
44 impl PillSourceImpl for RemoteUser {
45 fn identifier(&self) -> String {
46 self.obj().upcast_ref::<User>().user_id_string()
47 }
48 }
49
50 impl RemoteUser {
51 pub(super) fn set_loading_state(&self, loading_state: LoadingState) {
53 if self.loading_state.get() == loading_state {
54 return;
55 }
56
57 self.loading_state.set(loading_state);
58
59 if loading_state == LoadingState::Error {
60 self.last_request_time.take();
62 }
63
64 self.obj().notify_loading_state();
65 }
66
67 pub(super) fn is_profile_stale(&self) -> bool {
69 self.last_request_time
70 .get()
71 .is_none_or(|last_time| last_time.elapsed() > PROFILE_VALIDITY_DURATION)
72 }
73
74 pub(super) fn update_last_request_time(&self) {
76 self.last_request_time.set(Some(Instant::now()));
77 }
78 }
79}
80
81glib::wrapper! {
82 pub struct RemoteUser(ObjectSubclass<imp::RemoteUser>) @extends PillSource, User;
84}
85
86impl RemoteUser {
87 pub(super) fn new(session: &Session, user_id: OwnedUserId) -> Self {
88 let obj = glib::Object::builder::<Self>()
89 .property("session", session)
90 .build();
91
92 obj.upcast_ref::<User>().imp().set_user_id(user_id);
93 obj.load_profile_if_stale();
94
95 obj
96 }
97
98 pub(super) fn load_profile_if_stale(&self) {
101 let imp = self.imp();
102
103 if !imp.is_profile_stale() {
104 return;
106 }
107
108 imp.update_last_request_time();
111
112 spawn!(clone!(
113 #[weak(rename_to = obj)]
114 self,
115 async move {
116 let imp = obj.imp();
117 imp.set_loading_state(LoadingState::Loading);
118
119 let loading_state = match obj.load_profile().await {
120 Ok(()) => LoadingState::Ready,
121 Err(()) => LoadingState::Error,
122 };
123 imp.set_loading_state(loading_state);
124 }
125 ));
126 }
127}