use std::collections::HashMap; use maud::{html, Markup, PreEscaped}; use peach_network::{network, network::AccessPoint, NetworkError}; use rouille::Request; use crate::{ templates, utils::{flash::FlashRequest, theme}, WLAN_IFACE, }; // ROUTE: /settings/network/wifi? fn render_network_status_icon(ssid: &str, wlan_ssid: &str, ap_state: &str) -> Markup { let status_label_value = if ssid == wlan_ssid { "CONNECTED" } else if ap_state == "Available" { "AVAILABLE" } else { "NOT IN RANGE" }; html! { (PreEscaped("")) div class="grid-column-1" { img id="wifiIcon" class="center icon" src="/icons/wifi.svg" alt="WiFi icon"; label class="center label-small font-gray" for="wifiIcon" title="Access Point Status" { (status_label_value) } } } } fn render_network_detailed_info(ssid: &str, ap_protocol: &str, ap_signal: Option) -> Markup { let ap_signal_value = match ap_signal { Some(signal) => signal.to_string(), None => "Unknown".to_string(), }; html! { (PreEscaped("")) div class="grid-column-2" { label class="label-small font-gray" for="netSsid" title="WiFi network SSID" { "SSID" }; p id="netSsid" class="card-text" title="SSID" { (ssid) } label class="label-small font-gray" for="netSec" title="Security protocol" { "SECURITY" }; p id="netSec" class="card-text" title={ "Security protocol in use by " (ssid) } { (ap_protocol) } label class="label-small font-gray" for="netSig" title="Signal Strength" { "SIGNAL" }; p id="netSig" class="card-text" title="Signal strength of WiFi access point" { (ap_signal_value) } } } } fn render_disconnect_form(ssid: &str) -> Markup { html! { form id="wifiDisconnect" action="/settings/network/wifi/disconnect" method="post" { (PreEscaped("")) input id="disconnectSsid" name="ssid" type="text" value=(ssid) style="display: none;"; input id="disconnectWifi" class="button button-warning center" title="Disconnect from Network" type="submit" value="Disconnect"; } } } fn render_connect_form(ssid: &str) -> Markup { html! { form id="wifiConnect" action="/settings/network/wifi/connect" method="post" { (PreEscaped("")) input id="connectSsid" name="ssid" type="text" value=(ssid) style="display: none;"; input id="connectWifi" class="button button-primary center" title="Connect to Network" type="submit" value="Connect"; } } } fn render_forget_form(ssid: &str) -> Markup { html! { form id="wifiForget" action="/settings/network/wifi/forget" method="post" { (PreEscaped("")) input id="forgetSsid" name="ssid" type="text" value=(ssid) style="display: none;"; input id="forgetWifi" class="button button-warning center" title="Forget Network" type="submit" value="Forget"; } } } fn render_buttons( selected_ap: &str, wlan_ssid: &str, ap: &AccessPoint, saved_wifi_networks: Vec, ) -> Markup { html! { (PreEscaped("")) div id="buttons" { @if wlan_ssid == selected_ap { (render_disconnect_form(selected_ap)) } @if saved_wifi_networks.contains(&selected_ap.to_string()) { @if wlan_ssid != selected_ap && ap.state == "Available" { (render_connect_form(selected_ap)) } a class="button button-primary center" href={ "/settings/network/wifi/modify?ssid=" (selected_ap) } { "Modify" } (render_forget_form(selected_ap)) } @else { // display the Add button if AP creds not already in saved // networks list a class="button button-primary center" href={ "/settings/network/wifi/add?ssid=" (selected_ap) } { "Add" } } a class="button button-secondary center" href="/settings/network/wifi" title="Cancel" { "Cancel" } } } } /// Retrieve the list of all saved and in-range networks (including SSID and /// AP details for each network), the list of all saved networks (SSIDs only) /// and the SSID for the WiFi interface. fn retrieve_network_data() -> ( Result, NetworkError>, Vec, String, ) { let all_wifi_networks = network::all_networks(WLAN_IFACE); let saved_wifi_networks = match network::saved_networks() { Ok(Some(ssids)) => ssids, _ => Vec::new(), }; let wlan_ssid = match network::ssid(WLAN_IFACE) { Ok(Some(ssid)) => ssid, _ => String::from("Not connected"), }; (all_wifi_networks, saved_wifi_networks, wlan_ssid) } /// WiFi access point (AP) template builder. /// /// Render a UI card with details about the selected access point, including /// the connection state, security protocol being used, the SSID and the /// signal strength. Buttons are also rendering based on the state of the /// access point and whether or not credentials for the AP have previously /// been saved. /// /// If the AP is available (ie. in-range) then a Connect button is rendered. /// A Disconnect button is rendered if the WiFi client is currently /// connected to the AP. /// /// If credentials have not previously been saved for the AP, an Add button is /// rendered. Forget and Modify buttons are rendered if credentials for the AP /// have previously been saved. pub fn build_template(request: &Request, selected_ap: String) -> PreEscaped { let (flash_name, flash_msg) = request.retrieve_flash(); let (all_wifi_networks, saved_wifi_networks, wlan_ssid) = retrieve_network_data(); let network_info_box_class = if selected_ap == wlan_ssid { "two-grid capsule success-border" } else { "two-grid capsule" }; let network_list_template = html! { (PreEscaped("")) div class="card center" { @if let Ok(wlan_networks) = all_wifi_networks { // select only the access point we are interested in displaying @if let Some((ssid, ap)) = wlan_networks.get_key_value(&selected_ap) { @let ap_protocol = match &ap.detail { Some(detail) => detail.protocol.clone(), None => "None".to_string() }; (PreEscaped("")) div class=(network_info_box_class) title="PeachCloud network mode and status" { (PreEscaped("")) (render_network_status_icon(ssid, &wlan_ssid, &ap.state)) (PreEscaped("")) (render_network_detailed_info(ssid, &ap_protocol, ap.signal)) } (render_buttons(ssid, &wlan_ssid, ap, saved_wifi_networks)) } @else { p class="card-text list-item" { (selected_ap) " not found in saved or in-range networks" } } } @else { p class="card-text list-item" { "No saved or in-range networks found" } } @if let (Some(name), Some(msg)) = (flash_name, flash_msg) { (PreEscaped("")) (templates::flash::build_template(name, msg)) } } }; let body = templates::nav::build_template( network_list_template, "WiFi Networks", Some("/settings/network"), ); let theme = theme::get_theme(); templates::base::build_template(body, theme) }