1 use fractal_api::reqwest::Error as ReqwestError;
2 use fractal_api::url::{Host, ParseError as UrlError, Url};
3 use std::convert::TryInto;
4
5 use crate::globals;
6
7 use crate::backend::HTTP_CLIENT;
8 use crate::util::cache_dir_path;
9
10 use crate::types::Room;
11 use fractal_api::r0::directory::post_public_rooms::request as post_public_rooms;
12 use fractal_api::r0::directory::post_public_rooms::Body as PublicRoomsBody;
13 use fractal_api::r0::directory::post_public_rooms::Filter as PublicRoomsFilter;
14 use fractal_api::r0::directory::post_public_rooms::Parameters as PublicRoomsParameters;
15 use fractal_api::r0::directory::post_public_rooms::Response as PublicRoomsResponse;
16 use fractal_api::r0::directory::post_public_rooms::ThirdPartyNetworks;
17 use fractal_api::r0::thirdparty::get_supported_protocols::request as get_supported_protocols;
18 use fractal_api::r0::thirdparty::get_supported_protocols::Parameters as SupportedProtocolsParameters;
19 use fractal_api::r0::thirdparty::get_supported_protocols::ProtocolInstance;
20 use fractal_api::r0::thirdparty::get_supported_protocols::Response as SupportedProtocolsResponse;
21 use fractal_api::r0::AccessToken;
22
23 use super::{dw_media, ContentType, HandleError};
24 use crate::app::App;
25 use crate::i18n::i18n;
26 use crate::APPOP;
27
28 #[derive(Debug)]
29 pub struct DirectoryProtocolsError;
30
31 impl From<ReqwestError> for DirectoryProtocolsError {
from(_: ReqwestError) -> Self32 fn from(_: ReqwestError) -> Self {
33 Self
34 }
35 }
36
37 impl HandleError for DirectoryProtocolsError {
handle_error(&self)38 fn handle_error(&self) {
39 let error = i18n("Error searching for rooms");
40 APPOP!(reset_directory_state);
41 APPOP!(show_error, (error));
42 }
43 }
44
protocols( base: Url, access_token: AccessToken, ) -> Result<Vec<ProtocolInstance>, DirectoryProtocolsError>45 pub fn protocols(
46 base: Url,
47 access_token: AccessToken,
48 ) -> Result<Vec<ProtocolInstance>, DirectoryProtocolsError> {
49 let params = SupportedProtocolsParameters { access_token };
50 let request = get_supported_protocols(base, ¶ms)?;
51 let response: SupportedProtocolsResponse = HTTP_CLIENT.get_client().execute(request)?.json()?;
52
53 Ok(response
54 .into_iter()
55 .flat_map(|(_, protocol)| protocol.instances.into_iter())
56 .collect())
57 }
58
59 #[derive(Debug)]
60 pub enum DirectorySearchError {
61 InvalidHomeserverUrl(UrlError),
62 Reqwest(ReqwestError),
63 ParseUrl(UrlError),
64 }
65
66 impl From<ReqwestError> for DirectorySearchError {
from(err: ReqwestError) -> Self67 fn from(err: ReqwestError) -> Self {
68 Self::Reqwest(err)
69 }
70 }
71
72 impl From<UrlError> for DirectorySearchError {
from(err: UrlError) -> Self73 fn from(err: UrlError) -> Self {
74 Self::ParseUrl(err)
75 }
76 }
77
78 impl HandleError for DirectorySearchError {
handle_error(&self)79 fn handle_error(&self) {
80 let error = i18n("Error searching for rooms");
81 APPOP!(reset_directory_state);
82 APPOP!(show_error, (error));
83 }
84 }
85
room_search( base: Url, access_token: AccessToken, homeserver: String, generic_search_term: String, third_party: String, rooms_since: Option<String>, ) -> Result<(Vec<Room>, Option<String>), DirectorySearchError>86 pub fn room_search(
87 base: Url,
88 access_token: AccessToken,
89 homeserver: String, // TODO: Option<Use HostAndPort>?
90 generic_search_term: String,
91 third_party: String,
92 rooms_since: Option<String>,
93 ) -> Result<(Vec<Room>, Option<String>), DirectorySearchError> {
94 let homeserver = Some(homeserver).filter(|hs| !hs.is_empty());
95 let generic_search_term = Some(generic_search_term).filter(|q| !q.is_empty());
96 let third_party = Some(third_party).filter(|tp| !tp.is_empty());
97
98 let server = homeserver
99 .map(|hs| {
100 Url::parse(&hs)
101 .ok()
102 .as_ref()
103 .and_then(Url::host)
104 .as_ref()
105 .map(Host::to_owned)
106 .map(Ok)
107 .unwrap_or_else(|| Host::parse(&hs))
108 // Remove the url::Host enum, we only need the domain string
109 .map(|host| host.to_string())
110 .map(Some)
111 })
112 .unwrap_or(Ok(None))
113 .map_err(DirectorySearchError::InvalidHomeserverUrl)?;
114
115 let params = PublicRoomsParameters {
116 access_token,
117 server,
118 };
119
120 let body = PublicRoomsBody {
121 limit: Some(globals::ROOM_DIRECTORY_LIMIT),
122 filter: Some(PublicRoomsFilter {
123 generic_search_term,
124 }),
125 since: rooms_since,
126 third_party_networks: third_party
127 .map(ThirdPartyNetworks::Only)
128 .unwrap_or_default(),
129 };
130
131 let request = post_public_rooms(base.clone(), ¶ms, &body)?;
132 let response: PublicRoomsResponse = HTTP_CLIENT.get_client().execute(request)?.json()?;
133
134 let since = response.next_batch;
135 let rooms = response
136 .chunk
137 .into_iter()
138 .map(TryInto::try_into)
139 .inspect(|r: &Result<Room, _>| {
140 if let Ok(room) = r {
141 if let Some(avatar) = room.avatar.clone() {
142 if let Ok(dest) = cache_dir_path(None, &room.id.to_string()) {
143 let _ = dw_media(base.clone(), &avatar, ContentType::Download, Some(dest));
144 }
145 }
146 }
147 })
148 .collect::<Result<_, UrlError>>()?;
149
150 Ok((rooms, since))
151 }
152