fractal/session_list/
session_info.rs

1use gtk::{glib, prelude::*, subclass::prelude::*};
2use matrix_sdk::authentication::oauth::ClientId;
3use ruma::{OwnedDeviceId, OwnedUserId};
4use url::Url;
5
6use crate::{components::AvatarData, secret::StoredSession};
7
8mod imp {
9    use std::{cell::OnceCell, marker::PhantomData};
10
11    use super::*;
12
13    #[repr(C)]
14    pub struct SessionInfoClass {
15        parent_class: glib::object::ObjectClass,
16        pub(super) avatar_data: fn(&super::SessionInfo) -> AvatarData,
17    }
18
19    unsafe impl ClassStruct for SessionInfoClass {
20        type Type = SessionInfo;
21    }
22
23    pub(super) fn session_info_avatar_data(this: &super::SessionInfo) -> AvatarData {
24        let klass = this.class();
25        (klass.as_ref().avatar_data)(this)
26    }
27
28    #[derive(Debug, Default, glib::Properties)]
29    #[properties(wrapper_type = super::SessionInfo)]
30    pub struct SessionInfo {
31        /// The Matrix session's info.
32        #[property(get, construct_only)]
33        info: OnceCell<StoredSession>,
34        /// The Matrix session's user ID, as a string.
35        #[property(get = Self::user_id_string)]
36        user_id_string: PhantomData<String>,
37        /// The Matrix session's homeserver, as a string.
38        #[property(get = Self::homeserver_string)]
39        homeserver_string: PhantomData<String>,
40        /// The Matrix session's device ID, as a string.
41        #[property(get = Self::device_id_string)]
42        device_id_string: PhantomData<String>,
43        /// The local session's ID.
44        #[property(get = Self::session_id)]
45        session_id: PhantomData<String>,
46        /// The avatar data to represent this session.
47        #[property(get = Self::avatar_data)]
48        avatar_data: PhantomData<AvatarData>,
49    }
50
51    #[glib::object_subclass]
52    impl ObjectSubclass for SessionInfo {
53        const NAME: &'static str = "SessionInfo";
54        const ABSTRACT: bool = true;
55        type Type = super::SessionInfo;
56        type Class = SessionInfoClass;
57    }
58
59    #[glib::derived_properties]
60    impl ObjectImpl for SessionInfo {}
61
62    impl SessionInfo {
63        /// The Matrix session's info.
64        pub(super) fn info(&self) -> &StoredSession {
65            self.info.get().expect("info is initialized")
66        }
67
68        /// The Matrix session's user ID, as a string.
69        fn user_id_string(&self) -> String {
70            self.info().user_id.to_string()
71        }
72
73        /// The Matrix session's homeserver, as a string.
74        fn homeserver_string(&self) -> String {
75            self.info().homeserver.to_string()
76        }
77
78        /// The Matrix session's device ID, as a string.
79        fn device_id_string(&self) -> String {
80            self.info().device_id.to_string()
81        }
82
83        /// The local session's ID.
84        fn session_id(&self) -> String {
85            self.info().id.clone()
86        }
87
88        /// The avatar data to represent this session.
89        fn avatar_data(&self) -> AvatarData {
90            session_info_avatar_data(&self.obj())
91        }
92    }
93}
94
95glib::wrapper! {
96    /// Parent class of objects containing a Matrix session's info.
97    ///
98    /// Its main purpose is to be able to handle `Session`s that are being initialized, or where initialization failed.
99    pub struct SessionInfo(ObjectSubclass<imp::SessionInfo>);
100}
101
102/// Public trait containing implemented methods for everything that derives from
103/// `SessionInfo`.
104///
105/// To override the behavior of these methods, override the corresponding method
106/// of `SessionInfoImpl`.
107pub trait SessionInfoExt: 'static {
108    /// The Matrix session's info.
109    fn info(&self) -> &StoredSession;
110
111    /// The Matrix session's user ID.
112    fn user_id(&self) -> &OwnedUserId {
113        &self.info().user_id
114    }
115
116    /// The Matrix session's homeserver.
117    fn homeserver(&self) -> &Url {
118        &self.info().homeserver
119    }
120
121    /// The OAuth 2.0 client ID, if any.
122    fn client_id(&self) -> Option<&ClientId> {
123        self.info().client_id.as_ref()
124    }
125
126    /// Whether this session uses the OAuth 2.0 API.
127    fn uses_oauth_api(&self) -> bool {
128        self.client_id().is_some()
129    }
130
131    /// The Matrix session's device ID.
132    fn device_id(&self) -> &OwnedDeviceId {
133        &self.info().device_id
134    }
135
136    /// The local session's ID.
137    fn session_id(&self) -> &str {
138        &self.info().id
139    }
140
141    /// The avatar data to represent this session.
142    #[allow(dead_code)]
143    fn avatar_data(&self) -> AvatarData;
144}
145
146impl<O: IsA<SessionInfo>> SessionInfoExt for O {
147    fn info(&self) -> &StoredSession {
148        self.upcast_ref().imp().info()
149    }
150
151    fn avatar_data(&self) -> AvatarData {
152        imp::session_info_avatar_data(self.upcast_ref())
153    }
154}
155
156/// Public trait that must be implemented for everything that derives from
157/// `SessionInfo`.
158///
159/// Overriding a method from this Trait overrides also its behavior in
160/// `SessionInfoExt`.
161pub trait SessionInfoImpl: ObjectImpl {
162    fn avatar_data(&self) -> AvatarData;
163}
164
165// Make `SessionInfo` subclassable.
166unsafe impl<T> IsSubclassable<T> for SessionInfo
167where
168    T: SessionInfoImpl,
169    T::Type: IsA<SessionInfo>,
170{
171    fn class_init(class: &mut glib::Class<Self>) {
172        Self::parent_class_init::<T>(class.upcast_ref_mut());
173        let klass = class.as_mut();
174
175        klass.avatar_data = avatar_data_trampoline::<T>;
176    }
177}
178
179// Virtual method implementation trampolines.
180fn avatar_data_trampoline<T>(this: &SessionInfo) -> AvatarData
181where
182    T: ObjectSubclass + SessionInfoImpl,
183    T::Type: IsA<SessionInfo>,
184{
185    let this = this.downcast_ref::<T::Type>().unwrap();
186    this.imp().avatar_data()
187}