fractal/session_view/explore/
server_list.rs1use 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 #[property(get, set = Self::set_session)]
20 session: glib::WeakRef<Session>,
21 own_server: RefCell<Option<ExploreServer>>,
23 third_party_networks: RefCell<Vec<ExploreServer>>,
25 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 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 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 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 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 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 spawn!(clone!(
123 #[weak(rename_to = imp)]
124 self,
125 async move {
126 imp.load_third_party_networks().await;
127 }
128 ));
129 }
130
131 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 pub(super) fn contains_matrix_server(&self, server_name: &ServerName) -> bool {
175 self.own_server
176 .borrow()
177 .as_ref()
178 .is_some_and(|server| server.name().as_str() == server_name)
181 || self.custom_servers.borrow().contains_key(server_name)
182 }
183
184 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 return;
199 }
200
201 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 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 return;
223 };
224
225 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 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 pub(crate) fn contains_matrix_server(&self, server_name: &ServerName) -> bool {
250 self.imp().contains_matrix_server(server_name)
251 }
252
253 pub(crate) fn add_custom_server(&self, server_name: OwnedServerName) {
255 self.imp().add_custom_server(server_name);
256 }
257
258 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}