fractal/login/
local_server.rs1use gettextrs::gettext;
2use gtk::gio;
3use matrix_sdk::utils::local_server::{
4 LocalServerBuilder, LocalServerRedirectHandle, LocalServerResponse,
5};
6use tracing::error;
7use url::Url;
8
9use crate::{APP_NAME, spawn_tokio};
10
11const LOCAL_SERVER_LANDING_PAGE_TEMPLATE: &str = include_str!("local_server_landing_page.html");
13
14pub(super) async fn spawn_local_server() -> Result<(Url, LocalServerRedirectHandle), ()> {
16 spawn_tokio!(async move {
17 LocalServerBuilder::new()
18 .response(local_server_landing_page())
19 .spawn()
20 .await
21 })
22 .await
23 .expect("task was not aborted")
24 .map_err(|error| {
25 error!("Could not spawn local server: {error}");
26 })
27}
28
29fn local_server_landing_page() -> LocalServerResponse {
32 let mut html = LOCAL_SERVER_LANDING_PAGE_TEMPLATE.to_owned();
33
34 replace_html_variable(&mut html, "app_name", APP_NAME);
35 replace_html_variable(&mut html, "title", &gettext("Authorization Completed"));
36 replace_html_variable(
37 &mut html,
38 "message",
39 &gettext(
40 "The authorization step is complete. You can close this page and go back to Fractal.",
41 ),
42 );
43 replace_html_variable(&mut html, "icon", &svg_icon());
44
45 LocalServerResponse::Html(html)
46}
47
48fn replace_html_variable(html: &mut String, name: &str, value: &str) {
56 let pattern = format!("@{name}@");
57
58 assert!(
60 html.contains(&pattern),
61 "Variable `{pattern}` should be present in HTML template"
62 );
63
64 *html = html.replace(&pattern, value);
65}
66
67fn svg_icon() -> String {
71 let bytes = gio::resources_lookup_data(
73 "/org/gnome/Fractal/icons/scalable/apps/org.gnome.Fractal.svg",
74 gio::ResourceLookupFlags::NONE,
75 )
76 .expect("Application SVG icon should be present in GResources");
77
78 let icon = String::from_utf8(bytes.to_vec())
80 .expect("Application SVG icon content should be a UTF-8 string");
81
82 icon.trim()
84 .strip_prefix(r#"<?xml version="1.0" encoding="UTF-8"?>"#)
85 .expect("Application SVG icon should start with an XML prologue")
86 .to_owned()
87}
88
89#[cfg(test)]
90mod tests {
91 use assert_matches2::assert_matches;
92 use gtk::gio;
93 use matrix_sdk::utils::local_server::LocalServerResponse;
94
95 use super::local_server_landing_page;
96 use crate::config::tests::BUILD_DIR;
97
98 #[gtk::test]
99 fn generate_local_server_landing_page() {
100 let resources_file = format!("{BUILD_DIR}/data/resources/resources.gresource");
101 let res = gio::Resource::load(resources_file).expect("Could not load gresource file");
102 gio::resources_register(&res);
103
104 assert_matches!(local_server_landing_page(), LocalServerResponse::Html(html));
106 assert!(!html.is_empty());
107 assert!(!html.contains("@app_name@"));
108 assert!(!html.contains("@title@"));
109 assert!(!html.contains("@message@"));
110 assert!(!html.contains("@icon@"));
111 }
112}