diff --git a/public/static/cookies.js b/public/static/cookies.js index 6b55e02..3525c5b 100644 --- a/public/static/cookies.js +++ b/public/static/cookies.js @@ -1,3 +1,62 @@ +/** + * This functions gets the saved cookies if it is present on the user's machine If it + * is available then it is parsed and converted to an object which is then used to + * retrieve the preferences that the user had selected previously and is then loaded + * and used for displaying the user provided settings by setting them as the selected + * options in the settings page. + * + * @function + * @param {string} cookie - It takes the client settings cookie as a string. + * @returns {void} + */ +function setClientSettingsOnPage(cookie) { + let cookie_value = cookie + .split(';') + .map((item) => item.split('=')) + .reduce((acc, [_, v]) => (acc = JSON.parse(v)) && acc, {}) + + // Loop through all select tags and add their values to the cookie dictionary + document.querySelectorAll('select').forEach((select_tag) => { + switch (select_tag.name) { + case 'themes': + select_tag.value = cookie_value['theme'] + break + case 'colorschemes': + select_tag.value = cookie_value['colorscheme'] + break + case 'animations': + select_tag.value = cookie_value['animation'] + break + case 'safe_search_levels': + select_tag.value = cookie_value['safe_search_level'] + break + } + }) + let engines = document.querySelectorAll('.engine') + let engines_cookie = cookie_value['engines'] + + if (engines_cookie.length === engines.length) { + document.querySelector('.select_all').checked = true + engines.forEach((engine_checkbox) => { + engine_checkbox.checked = true + }) + } else { + engines.forEach((engines_checkbox) => { + engines_checkbox.checked = false + }) + engines_cookie.forEach((engine_name) => { + engines.forEach((engine_checkbox) => { + if ( + engine_checkbox.parentNode.parentNode.innerText.trim() === + engine_name.trim() + ) { + engine_checkbox.checked = true + } + }) + }) + } +} + /** * This function is executed when any page on the website finishes loading and * this function retrieves the cookies if it is present on the user's machine. @@ -16,9 +75,14 @@ document.addEventListener( let cookie = decodeURIComponent(document.cookie) // Set the value of the input field to the decoded cookie value if it is not empty // Otherwise, display a message indicating that no cookies have been saved on the user's system - document.querySelector('.cookies input').value = cookie.length - ? cookie - : 'No cookies have been saved on your system' + if (cookie.length) { + document.querySelector('.cookies input').value = cookie + // This function displays the user provided settings on the settings page. + setClientSettingsOnPage(cookie) + } else { + document.querySelector('.cookies input').value = + 'No cookies have been saved on your system' + } } catch (error) { // If there is an error decoding the cookie, log the error to the console // and display an error message in the input field diff --git a/public/static/themes/simple.css b/public/static/themes/simple.css index aab743e..ff05d70 100644 --- a/public/static/themes/simple.css +++ b/public/static/themes/simple.css @@ -600,12 +600,20 @@ footer div { text-transform: capitalize; } -.settings_container .tab .description { +.settings_container .tab .description, +.settings_container .tab .admin_warning { font-size: 1.5rem; margin-bottom: 0.5rem; +} + +.settings_container .tab .description { color: var(--foreground-color); } +.settings_container .tab .admin_warning { + color: var(--color-two); +} + .settings_container .user_interface select, .settings_container .general select { margin: 0.7rem 0; diff --git a/src/server/router.rs b/src/server/router.rs index 0fb37d1..74b8bfa 100644 --- a/src/server/router.rs +++ b/src/server/router.rs @@ -75,13 +75,11 @@ pub async fn settings( .content_type("text/html; charset=utf-8") .body( crate::templates::views::settings::settings( + config.safe_search, &config.style.colorscheme, &config.style.theme, &config.style.animation, - &config - .upstream_search_engines - .keys() - .collect::>(), + &config.upstream_search_engines, )? .0, )) diff --git a/src/templates/partials/settings_tabs/engines.rs b/src/templates/partials/settings_tabs/engines.rs index 5394178..748cc07 100644 --- a/src/templates/partials/settings_tabs/engines.rs +++ b/src/templates/partials/settings_tabs/engines.rs @@ -1,17 +1,20 @@ //! A module that handles the engines tab for setting page view in the `websurfx` frontend. +use std::collections::HashMap; + use maud::{html, Markup}; /// A functions that handles the html code for the engines tab for the settings page for the search page. /// /// # Arguments /// -/// * `engine_names` - It takes the list of all available engine names as an argument. +/// * `engine_names` - It takes the key value pair list of all available engine names and there corresponding +/// selected (enabled/disabled) value as an argument. /// /// # Returns /// /// It returns the compiled html markup code for the engines tab. -pub fn engines(engine_names: &[&String]) -> Markup { +pub fn engines(engine_names: &HashMap) -> Markup { html!( div class="engines tab"{ h1{"Engines"} @@ -20,21 +23,49 @@ pub fn engines(engine_names: &[&String]) -> Markup { "Select the search engines from the list of engines that you want results from" } .engine_selection{ - .toggle_btn{ - label class="switch"{ - input type="checkbox" class="select_all" onchange="toggleAllSelection()"; - span class="slider round"{} - } - "Select All" + // Checks whether all the engines are selected or not if they are then the + // checked `select_all` button is rendered otherwise the unchecked version + // is rendered. + @if engine_names.values().all(|selected| *selected){ + .toggle_btn{ + label class="switch"{ + input type="checkbox" class="select_all" onchange="toggleAllSelection()" checked; + span class="slider round"{} + } + "Select All" + } + } + @else{ + .toggle_btn { + label class="switch"{ + input type="checkbox" class="select_all" onchange="toggleAllSelection()"; + span class="slider round"{} + } + "Select All" + } } hr; - @for engine_name in engine_names{ - .toggle_btn{ - label class="switch"{ - input type="checkbox" class="engine"; - span class="slider round"{} + @for (engine_name, selected) in engine_names{ + // Checks whether the `engine_name` is selected or not if they are then the + // checked `engine` button is rendered otherwise the unchecked version is + // rendered. + @if *selected { + .toggle_btn{ + label class="switch"{ + input type="checkbox" class="engine" checked; + span class="slider round"{} + } + (format!("{}{}",engine_name[..1].to_uppercase().to_owned(), engine_name[1..].to_owned())) + } + } + @else { + .toggle_btn { + label class="switch"{ + input type="checkbox" class="engine"; + span class="slider round"{} + } + (format!("{}{}",engine_name[..1].to_uppercase().to_owned(), engine_name[1..].to_owned())) } - (format!("{}{}",engine_name[..1].to_uppercase().to_owned(), engine_name[1..].to_owned())) } } } diff --git a/src/templates/partials/settings_tabs/general.rs b/src/templates/partials/settings_tabs/general.rs index 4b0043d..7736667 100644 --- a/src/templates/partials/settings_tabs/general.rs +++ b/src/templates/partials/settings_tabs/general.rs @@ -7,10 +7,14 @@ const SAFE_SEARCH_LEVELS: [(u8, &str); 3] = [(0, "None"), (1, "Low"), (2, "Moder /// A functions that handles the html code for the general tab for the settings page for the search page. /// +/// # Arguments +/// +/// * `safe_search_level` - It takes the safe search level as an argument. +/// /// # Returns /// /// It returns the compiled html markup code for the general tab. -pub fn general() -> Markup { +pub fn general(safe_search_level: u8) -> Markup { html!( div class="general tab active"{ h1{"General"} @@ -18,9 +22,19 @@ pub fn general() -> Markup { p class="description"{ "Select a safe search level from the menu below to filter content based on the level." } - select name="safe_search_levels"{ - @for (k,v) in SAFE_SEARCH_LEVELS{ - option value=(k){(v)} + @if safe_search_level < 3 { + select name="safe_search_levels" { + // Sets the user selected safe_search_level name from the config file as the first option in the selection list. + option value=(safe_search_level){(SAFE_SEARCH_LEVELS.iter().find(|level| level.0 == safe_search_level).unwrap().1)} + @for (k,v) in SAFE_SEARCH_LEVELS.iter().filter(|level| level.0 != safe_search_level){ + option value=(k){(v)} + } + } + } + @else { + p class="admin_warning" {"⚠️ This setting is being managed by the server administrator."} + select name="safe_search_levels" disabled { + option value=(SAFE_SEARCH_LEVELS[2].0){(SAFE_SEARCH_LEVELS[2].1)} } } } diff --git a/src/templates/partials/settings_tabs/user_interface.rs b/src/templates/partials/settings_tabs/user_interface.rs index d94465b..6916b26 100644 --- a/src/templates/partials/settings_tabs/user_interface.rs +++ b/src/templates/partials/settings_tabs/user_interface.rs @@ -4,13 +4,16 @@ use crate::handler::{file_path, FileType}; use maud::{html, Markup}; use std::fs::read_dir; -/// A helper function that helps in building the list of all available colorscheme/theme names -/// present in the colorschemes and themes folder respectively. +/// A helper function that helps in building the list of all available colorscheme/theme/animation +/// names present in the colorschemes, animations and themes folder respectively by excluding the +/// ones that have already been selected via the config file. /// /// # Arguments /// /// * `style_type` - It takes the style type of the values `theme` and `colorscheme` as an /// argument. +/// * `selected_style` - It takes the currently selected style value provided via the config file +/// as an argument. /// /// # Error /// @@ -18,7 +21,8 @@ use std::fs::read_dir; /// returns a standard error message. fn style_option_list( style_type: &str, -) -> Result, Box> { + selected_style: &str, +) -> Result, Box> { let mut style_option_names: Vec<(String, String)> = Vec::new(); for file in read_dir(format!( "{}static/{}/", @@ -26,7 +30,13 @@ fn style_option_list( style_type, ))? { let style_name = file?.file_name().to_str().unwrap().replace(".css", ""); - style_option_names.push((style_name.clone(), style_name.replace('-', " "))); + if selected_style != style_name { + style_option_names.push((style_name.clone(), style_name.replace('-', " "))); + } + } + + if style_type == "animations" { + style_option_names.push(("".to_owned(), "none".to_owned())) } Ok(style_option_names) @@ -38,7 +48,11 @@ fn style_option_list( /// /// It returns the compiled html markup code for the user interface tab on success otherwise /// returns a standard error message. -pub fn user_interface() -> Result> { +pub fn user_interface( + theme: &str, + colorscheme: &str, + animation: &Option, +) -> Result> { Ok(html!( div class="user_interface tab"{ h1{"User Interface"} @@ -47,7 +61,9 @@ pub fn user_interface() -> Result> { "Select the theme from the available themes to be used in user interface" } select name="themes"{ - @for (k,v) in style_option_list("themes")?{ + // Sets the user selected theme name from the config file as the first option in the selection list. + option value=(theme){(theme.replace('-', " "))} + @for (k,v) in style_option_list("themes", theme)?{ option value=(k){(v)} } } @@ -56,7 +72,9 @@ pub fn user_interface() -> Result> { "Select the color scheme for your theme to be used in user interface" } select name="colorschemes"{ - @for (k,v) in style_option_list("colorschemes")?{ + // Sets the user selected colorscheme name from the config file as the first option in the selection list. + option value=(colorscheme){(colorscheme.replace('-', " "))} + @for (k,v) in style_option_list("colorschemes", colorscheme)?{ option value=(k){(v)} } } @@ -65,12 +83,12 @@ pub fn user_interface() -> Result> { "Select the animation for your theme to be used in user interface" } select name="animations"{ - option value=""{"none"} - @for (k,v) in style_option_list("animations")?{ + // Sets the user selected animation name from the config file as the first option in the selection list. + option value=(animation.as_ref().unwrap_or(&"".to_owned())){(animation.as_ref().unwrap_or(&"".to_owned()).replace('-'," "))} + @for (k,v) in style_option_list("animations", animation.as_ref().unwrap_or(&"".to_owned()))?{ option value=(k){(v)} } } - } )) } diff --git a/src/templates/views/settings.rs b/src/templates/views/settings.rs index 6da5202..4f0878a 100644 --- a/src/templates/views/settings.rs +++ b/src/templates/views/settings.rs @@ -1,5 +1,7 @@ //! A module that handles the view for the settings page in the `websurfx` frontend. +use std::collections::HashMap; + use maud::{html, Markup}; use crate::templates::partials::{ @@ -14,8 +16,10 @@ use crate::templates::partials::{ /// /// # Arguments /// +/// * `safe_search_level` - It takes the safe search level as an argument. /// * `colorscheme` - It takes the colorscheme name as an argument. /// * `theme` - It takes the theme name as an argument. +/// * `animation` - It takes the animation name as an argument. /// * `engine_names` - It takes a list of engine names as an argument. /// /// # Error @@ -23,10 +27,11 @@ use crate::templates::partials::{ /// This function returns a compiled html markup code on success otherwise returns a standard error /// message. pub fn settings( + safe_search_level: u8, colorscheme: &str, theme: &str, animation: &Option, - engine_names: &[&String], + engine_names: &HashMap, ) -> Result> { Ok(html!( (header(colorscheme, theme, animation)) @@ -41,8 +46,8 @@ pub fn settings( .btn onclick="setActiveTab(this)"{"cookies"} } .main_container{ - (general()) - (user_interface()?) + (general(safe_search_level)) + (user_interface(theme, colorscheme, animation)?) (engines(engine_names)) (cookies()) p class="message"{}