Skip to main content

fractal/session_view/explore/
server_list.rs

1use gtk::{gio, glib, glib::clone, prelude::*, subclass::prelude::*};
2use ruma::{OwnedServerName, ServerName, api::client::thirdparty::get_protocols};
3use tracing::error;
4
5use super::ExploreServer;
6use crate::{prelude::*, session::Session, spawn, spawn_tokio};
7
8mod imp {
9    use std::cell::RefCell;
10
11    use indexmap::IndexMap;
12
13    use super::*;
14
15    #[derive(Debug, Default, glib::Properties)]
16    #[properties(wrapper_type = super::ExploreServerList)]
17    pub struct ExploreServerList {
18        /// The current session.
19        #[property(get, set = Self::set_session)]
20        session: glib::WeakRef<Session>,
21        /// The item for our own server.
22        own_server: RefCell<Option<ExploreServer>>,
23        /// The list of third-party networks on our own server.
24        third_party_networks: RefCell<Vec<ExploreServer>>,
25        /// The list of custom homeservers.
26        custom_servers: RefCell<IndexMap<OwnedServerName, ExploreServer>>,
27    }
28
29    #[glib::object_subclass]
30    impl ObjectSubclass for ExploreServerList {
31        const NAME: &'static str = "ExploreServerList";
32        type Type = super::ExploreServerList;
33        type Interfaces = (gio::ListModel,);
34    }
35
36    #[glib::derived_properties]
37    impl ObjectImpl for ExploreServerList {}
38
39    impl ListModelImpl for ExploreServerList {
40        fn item_type(&self) -> glib::Type {
41            ExploreServer::static_type()
42        }
43
44        fn n_items(&self) -> u32 {
45            (usize::from(self.own_server.borrow().is_some())
46                + self.third_party_networks.borrow().len()
47                + self.custom_servers.borrow().len()) as u32
48        }
49
50        fn item(&self, position: u32) -> Option<glib::Object> {
51            let mut position = position as usize;
52
53            // We always have our own server if we have a session.
54            if position == 0 {
55                return self.own_server.borrow().clone().and_upcast();
56            }
57
58            position -= 1;
59
60            let third_party_len = self.third_party_networks.borrow().len();
61            if position < third_party_len {
62                return self
63                    .third_party_networks
64                    .borrow()
65                    .get(position)
66                    .cloned()
67                    .and_upcast();
68            }
69
70            position -= third_party_len;
71
72            self.custom_servers
73                .borrow()
74                .get_index(position)
75                .map(|(_, server)| server.clone().upcast())
76        }
77    }
78
79    impl ExploreServerList {
80        /// Set the current session.
81        fn set_session(&self, session: Option<&Session>) {
82            if self.session.upgrade().as_ref() == session {
83                return;
84            }
85
86            self.session.set(session);
87
88            self.load_servers();
89            self.obj().notify_session();
90        }
91
92        /// Load the servers.
93        fn load_servers(&self) {
94            let removed = self.n_items();
95
96            self.own_server.take();
97            self.third_party_networks.borrow_mut().clear();
98            self.custom_servers.borrow_mut().clear();
99
100            let Some(session) = self.session.upgrade() else {
101                self.obj().items_changed(0, removed, 0);
102                return;
103            };
104
105            // Add our own server.
106            let own_server =
107                ExploreServer::with_default_server(session.user_id().server_name().as_str());
108            self.own_server.replace(Some(own_server));
109
110            // Load the custom servers.
111            let custom_servers = session.settings().explore_custom_servers();
112            self.custom_servers.borrow_mut().extend(
113                custom_servers
114                    .into_iter()
115                    .map(|server| (server.clone(), ExploreServer::with_server(server))),
116            );
117
118            let added = self.n_items();
119            self.obj().items_changed(0, removed, added);
120
121            // Make a request to get the third-party networks.
122            spawn!(clone!(
123                #[weak(rename_to = imp)]
124                self,
125                async move {
126                    imp.load_third_party_networks().await;
127                }
128            ));
129        }
130
131        /// Load the list of third-party networks.
132        async fn load_third_party_networks(&self) {
133            let Some(session) = self.session.upgrade() else {
134                return;
135            };
136
137            let client = session.client();
138            let handle =
139                spawn_tokio!(async move { client.send(get_protocols::v3::Request::new()).await });
140
141            let protocols = match handle.await.expect("task was not aborted") {
142                Ok(response) => response.protocols,
143                Err(error) => {
144                    error!("Could not get third-party networks: {error}");
145                    Default::default()
146                }
147            };
148
149            let added = if protocols.is_empty() {
150                0
151            } else {
152                let mut third_party_networks = self.third_party_networks.borrow_mut();
153                third_party_networks.extend(protocols.iter().flat_map(
154                    |(protocol_id, protocol)| {
155                        protocol.instances.iter().filter_map(|instance| {
156                            instance.instance_id.as_deref().map(|instance_id| {
157                                ExploreServer::with_third_party_protocol(
158                                    &instance.desc,
159                                    protocol_id,
160                                    instance_id,
161                                )
162                            })
163                        })
164                    },
165                ));
166
167                third_party_networks.len()
168            };
169
170            self.obj().items_changed(1, 0, added as u32);
171        }
172
173        /// Whether this list contains the given Matrix server.
174        pub(super) fn contains_matrix_server(&self, server_name: &ServerName) -> bool {
175            self.own_server
176                .borrow()
177                .as_ref()
178                // The user's matrix server is a special case that doesn't have a "server", so
179                // compare to its name, which should be a server name.
180                .is_some_and(|server| server.name().as_str() == server_name)
181                || self.custom_servers.borrow().contains_key(server_name)
182        }
183
184        /// Add a custom Matrix server.
185        pub(super) fn add_custom_server(&self, server_name: OwnedServerName) {
186            let Some(session) = self.session.upgrade() else {
187                return;
188            };
189
190            let server = ExploreServer::with_server(server_name.clone());
191            if self
192                .custom_servers
193                .borrow_mut()
194                .insert(server_name.clone(), server)
195                .is_some()
196            {
197                // The server already existed, the list did not change.
198                return;
199            }
200
201            // Update the list in the settings.
202            let settings = session.settings();
203            let mut servers = settings.explore_custom_servers();
204            servers.insert(server_name);
205            settings.set_explore_custom_servers(servers);
206
207            self.obj().items_changed(self.n_items() - 1, 0, 1);
208        }
209
210        /// Remove a custom Matrix server.
211        pub(super) fn remove_custom_server(&self, server_name: &ServerName) {
212            let Some(session) = self.session.upgrade() else {
213                return;
214            };
215
216            let Some((pos, ..)) = self
217                .custom_servers
218                .borrow_mut()
219                .shift_remove_full(server_name)
220            else {
221                // The list did not change.
222                return;
223            };
224
225            // Update the list in the settings.
226            let settings = session.settings();
227            let mut servers = settings.explore_custom_servers();
228            servers.shift_remove(server_name);
229            settings.set_explore_custom_servers(servers);
230
231            let pos = self.third_party_networks.borrow().len() + pos + 1;
232            self.obj().items_changed(pos as u32, 1, 0);
233        }
234    }
235}
236
237glib::wrapper! {
238    /// The list of servers to explore.
239    pub struct ExploreServerList(ObjectSubclass<imp::ExploreServerList>)
240        @implements gio::ListModel;
241}
242
243impl ExploreServerList {
244    pub fn new() -> Self {
245        glib::Object::new()
246    }
247
248    /// Whether this list contains the given Matrix server.
249    pub(crate) fn contains_matrix_server(&self, server_name: &ServerName) -> bool {
250        self.imp().contains_matrix_server(server_name)
251    }
252
253    /// Add a custom Matrix server.
254    pub(crate) fn add_custom_server(&self, server_name: OwnedServerName) {
255        self.imp().add_custom_server(server_name);
256    }
257
258    /// Remove a custom Matrix server.
259    pub(crate) fn remove_custom_server(&self, server_name: &ServerName) {
260        self.imp().remove_custom_server(server_name);
261    }
262}
263
264impl Default for ExploreServerList {
265    fn default() -> Self {
266        Self::new()
267    }
268}