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, &params)?;
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(), &params, &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