inital no-js frontend

This commit is contained in:
Milim 2024-08-15 21:11:14 +02:00
parent 38f04320f0
commit 253de6ff13
No known key found for this signature in database
17 changed files with 13 additions and 488 deletions

View file

@ -1,61 +0,0 @@
/**
* 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) {
// Loop through all select tags and add their values to the cookie dictionary
let engines = document.querySelectorAll('.engine')
document.querySelector('.select_all').checked = true
engines.forEach((engine) => {
engine.checked = cookie[engine.parentNode.parentNode.innerText.trim()];
if (!engine.checked) {
document.querySelector('.select_all').checked = false
}
})
}
/**
* 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.
* If it is available then the saved cookies is display in the cookies tab
* otherwise an appropriate message is displayed if it is not available.
*
* @function
* @listens DOMContentLoaded
* @returns {void}
*/
document.addEventListener(
'DOMContentLoaded',
() => {
try {
// Decode the cookie value
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
if (cookie.length) {
document.querySelector('.cookies input').value = cookie
// This function displays the user provided settings on the settings page.
setClientSettingsOnPage(JSON.parse(cookie.replace("appCookie=", "")))
} 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
console.error('Error decoding cookie:', error)
document.querySelector('.cookies input').value = 'Error decoding cookie'
}
},
false,
)

View file

@ -1,7 +0,0 @@
/**
* This function provides the ability for the button to toggle the dropdown error-box
* in the search page.
*/
function toggleErrorBox() {
document.querySelector('.dropdown_error_box').classList.toggle('show')
}

View file

@ -1,34 +0,0 @@
/**
* Selects the input element for the search box
* @type {HTMLInputElement}
*/
const searchBox = document.querySelector('input')
/**
* Redirects the user to the search results page with the query parameter
*/
function searchWeb() {
const query = searchBox.value.trim()
try {
let safeSearchLevel = document.querySelector('.search_options select').value
if (query) {
window.location.href = `search?q=${encodeURIComponent(
query,
)}&safesearch=${encodeURIComponent(safeSearchLevel)}`
}
} catch (error) {
if (query) {
window.location.href = `search?q=${encodeURIComponent(query)}`
}
}
}
/**
* Listens for the 'Enter' key press event on the search box and calls the searchWeb function
* @param {KeyboardEvent} e - The keyboard event object
*/
searchBox.addEventListener('keyup', (e) => {
if (e.key === 'Enter') {
searchWeb()
}
})

View file

@ -1,39 +0,0 @@
/**
* Navigates to the next page by incrementing the current page number in the URL query string.
* @returns {void}
*/
function navigate_forward() {
let url = new URL(window.location);
let searchParams = url.searchParams;
let q = searchParams.get('q');
let page = parseInt(searchParams.get('page'));
if (isNaN(page)) {
page = 1;
} else {
page++;
}
window.location.href = `${url.origin}${url.pathname}?q=${encodeURIComponent(q)}&page=${page}`;
}
/**
* Navigates to the previous page by decrementing the current page number in the URL query string.
* @returns {void}
*/
function navigate_backward() {
let url = new URL(window.location);
let searchParams = url.searchParams;
let q = searchParams.get('q');
let page = parseInt(searchParams.get('page'));
if (isNaN(page)) {
page = 0;
} else if (page > 0) {
page--;
}
window.location.href = `${url.origin}${url.pathname}?q=${encodeURIComponent(q)}&page=${page}`;
}

View file

@ -1,18 +0,0 @@
document.addEventListener(
'DOMContentLoaded',
() => {
let url = new URL(window.location)
let searchParams = url.searchParams
let safeSearchLevel = searchParams.get('safesearch')
if (
safeSearchLevel >= 0 &&
safeSearchLevel <= 2 &&
safeSearchLevel !== null
) {
document.querySelector('.search_options select').value = safeSearchLevel
}
},
false,
)

View file

@ -1,66 +0,0 @@
/**
* This function handles the toggling of selections of all upstream search engines
* options in the settings page under the tab engines.
*/
function toggleAllSelection() {
document
.querySelectorAll('.engine')
.forEach(
(engine_checkbox) =>
(engine_checkbox.checked =
document.querySelector('.select_all').checked),
)
}
/**
* This function adds the functionality to sidebar buttons to only show settings
* related to that tab.
* @param {HTMLElement} current_tab - The current tab that was clicked.
*/
function setActiveTab(current_tab) {
// Remove the active class from all tabs and buttons
document
.querySelectorAll('.tab')
.forEach((tab) => tab.classList.remove('active'))
document
.querySelectorAll('.btn')
.forEach((tab) => tab.classList.remove('active'))
// Add the active class to the current tab and its corresponding settings
current_tab.classList.add('active')
document
.querySelector(`.${current_tab.innerText.toLowerCase().replace(' ', '_')}`)
.classList.add('active')
}
/**
* This function adds the functionality to save all the user selected preferences
* to be saved in a cookie on the users machine.
*/
function setClientSettings() {
// Create an object to store the user's preferences
let cookie_dictionary = new Object()
document.querySelectorAll('.engine').forEach((engine_checkbox) => {
cookie_dictionary[engine_checkbox.parentNode.parentNode.innerText.trim()] = engine_checkbox.checked
})
// Set the expiration date for the cookie to 1 year from the current date
let expiration_date = new Date()
expiration_date.setFullYear(expiration_date.getFullYear() + 1)
// Save the cookie to the user's machine
document.cookie = `appCookie=${JSON.stringify(
cookie_dictionary,
)}; expires=${expiration_date.toUTCString()}`
// Display a success message to the user
document.querySelector('.message').innerText =
'✅ The settings have been saved sucessfully!!'
// Clear the success message after 10 seconds
setTimeout(() => {
document.querySelector('.message').innerText = ''
}, 10000)
}

View file

@ -86,7 +86,6 @@ async fn main() {
.service(router::index) // index page
.service(server::routes::search::search) // search page
.service(router::about) // about page
.service(router::settings) // settings page
.default_service(web::route().to(router::not_found)) // error page
})
// Start server on 127.0.0.1 with the user provided port number. for example 127.0.0.1:8080.

View file

@ -2,11 +2,8 @@
//! meta search engine website and provide appropriate response to each route/page
//! when requested.
use crate::{
config::Config,
handler::{file_path, FileType},
};
use actix_web::{get, http::header::ContentType, web, HttpRequest, HttpResponse};
use crate::handler::{file_path, FileType};
use actix_web::{get, http::header::ContentType, HttpRequest, HttpResponse};
use tokio::fs::read_to_string;
/// Handles the route of index page or main page of the `crabbysearch` meta search engine website.
@ -42,21 +39,3 @@ pub async fn about() -> Result<HttpResponse, Box<dyn std::error::Error>> {
.content_type(ContentType::html())
.body(crate::templates::views::about::about().0))
}
/// Handles the route of settings page of the `crabbysearch` meta search engine website.
#[get("/settings")]
pub async fn settings(
config: web::Data<Config>,
) -> Result<HttpResponse, Box<dyn std::error::Error>> {
Ok(HttpResponse::Ok().content_type(ContentType::html()).body(
crate::templates::views::settings::settings(
&config
.upstream_search_engines
.list()
.iter()
.map(|n| (*n, true))
.collect(),
)?
.0,
))
}

View file

@ -14,10 +14,15 @@ use maud::{html, Markup, PreEscaped};
/// It returns the compiled html code for the search bar as a result.
pub fn bar(query: &str) -> Markup {
html!(
(PreEscaped("<div class=\"search_bar\">"))
input type="search" name="search-box" value=(query) placeholder="Type to search";
button type="submit" onclick="searchWeb()" {
img src="./images/magnifying_glass.svg" alt="Info icon for error box";
.search_container {
(PreEscaped("<div class=\"search_bar\">"))
form action="/search" method="get" {
input type="search" name="query" value=(query) placeholder="Type to search";
input type="hidden" name="page" value="0";
button type="submit" {
img src="./images/magnifying_glass.svg" alt="Info icon for error box";
}
}
}
)
}

View file

@ -4,5 +4,3 @@ pub mod bar;
pub mod footer;
pub mod header;
pub mod navbar;
pub mod search_bar;
pub mod settings_tabs;

View file

@ -1,76 +0,0 @@
//! A module that handles `search bar` partial for the search page in the `crabbysearch` frontend.
use maud::{html, Markup, PreEscaped};
use crate::{models::aggregation_models::EngineErrorInfo, templates::partials::bar::bar};
/// A constant holding the named safe search level options for the corresponding values 0, 1 and 2.
const SAFE_SEARCH_LEVELS_NAME: [&str; 3] = ["None", "Low", "Moderate"];
/// A functions that handles the html code for the search bar for the search page.
///
/// # Arguments
///
/// * `engine_errors_info` - It takes the engine errors list containing errors for each upstream
/// search engine which failed to provide results as an argument.
/// * `safe_search_level` - It takes the safe search level with values from 0-2 as an argument.
/// * `query` - It takes the current search query provided by user as an argument.
///
/// # Returns
///
/// It returns the compiled html code for the search bar as a result.
pub fn search_bar(
engine_errors_info: &[EngineErrorInfo],
safe_search_level: u8,
query: &str,
) -> Markup {
html!(
.search_area{
(bar(query))
.error_box {
@if !engine_errors_info.is_empty(){
button onclick="toggleErrorBox()" class="error_box_toggle_button"{
img src="./images/warning.svg" alt="Info icon for error box";
}
.dropdown_error_box{
@for errors in engine_errors_info{
.error_item{
span class="engine_name"{(errors.engine)}
span class="engine_name"{(errors.error)}
span class="severity_color" style="background: {{{this.severity_color}}};"{}
}
}
}
}
@else {
button onclick="toggleErrorBox()" class="error_box_toggle_button"{
img src="./images/info.svg" alt="Warning icon for error box";
}
.dropdown_error_box {
.no_errors{
"Everything looks good 🙂!!"
}
}
}
}
(PreEscaped("</div>"))
.search_options {
@if safe_search_level >= 3 {
(PreEscaped("<select name=\"safe_search_levels\" disabled>"))
}
@else{
(PreEscaped("<select name=\"safe_search_levels\">"))
}
@for (idx, name) in SAFE_SEARCH_LEVELS_NAME.iter().enumerate() {
@if (safe_search_level as usize) == idx {
option value=(idx) selected {(format!("SafeSearch: {name}"))}
}
@else{
option value=(idx) {(format!("SafeSearch: {name}"))}
}
}
(PreEscaped("</select>"))
}
}
)
}

View file

@ -1,25 +0,0 @@
//! A module that handles the engines tab for setting page view in the `crabbysearch` frontend.
use maud::{html, Markup};
/// A functions that handles the html code for the cookies tab for the settings page for the search page.
///
/// # Returns
///
/// It returns the compiled html markup code for the cookies tab.
pub fn cookies() -> Markup {
html!(
div class="cookies tab"{
h1{"Cookies"}
p class="description"{
"This is the cookies are saved on your system and it contains the preferences
you chose in the settings page"
}
input type="text" name="cookie_field" value="" readonly;
p class="description"{
"The cookies stored are not used by us for any malicious intend or for
tracking you in any way."
}
}
)
}

View file

@ -1,72 +0,0 @@
//! A module that handles the engines tab for setting page view in the `crabbysearch` frontend.
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 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: &Vec<(&str, bool)>) -> Markup {
html!(
div class="engines tab"{
h1{"Engines"}
h3{"select search engines"}
p class="description"{
"Select the search engines from the list of engines that you want results from"
}
.engine_selection{
// 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.iter().all(|selected| selected.1){
.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, 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(), &engine_name[1..]))
}
}
@else {
.toggle_btn {
label class="switch"{
input type="checkbox" class="engine";
span class="slider round"{}
}
(format!("{}{}",&engine_name[..1], &engine_name[1..]))
}
}
}
}
}
)
}

View file

@ -1,5 +0,0 @@
//! This module provides other modules to handle the partials for the tabs for the settings page
//! view in the `crabbysearch` frontend.
pub mod cookies;
pub mod engines;

View file

@ -5,4 +5,3 @@ pub mod about;
pub mod index;
pub mod not_found;
pub mod search;
pub mod settings;

View file

@ -4,7 +4,7 @@ use maud::{html, Markup, PreEscaped};
use crate::{
models::aggregation_models::SearchResults,
templates::partials::{footer::footer, header::header, search_bar::search_bar},
templates::partials::{bar::bar, footer::footer, header::header},
};
/// A function that handles the html code for the search page view in the search engine frontend.
@ -23,7 +23,7 @@ pub fn search(query: &str, search_results: &SearchResults) -> Markup {
html!(
(header())
main class="results"{
(search_bar(&search_results.engine_errors_info, search_results.safe_search_level, query))
(bar(query))
.results_aggregated{
@if !search_results.results.is_empty() {
@for result in search_results.results.iter(){

View file

@ -1,52 +0,0 @@
//! A module that handles the view for the settings page in the `crabbysearch` frontend.
use maud::{html, Markup};
use crate::templates::partials::{
footer::footer,
header::header,
settings_tabs::{cookies::cookies, engines::engines},
};
/// A function that handles the html code for the settings page view in the search engine frontend.
///
/// # 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
///
/// This function returns a compiled html markup code on success otherwise returns a standard error
/// message.
pub fn settings(
engine_names: &Vec<(&'static str, bool)>,
) -> Result<Markup, Box<dyn std::error::Error>> {
Ok(html!(
(header())
main class="settings"{
h1{"Settings"}
hr;
.settings_container{
.sidebar{
div class="btn active" onclick="setActiveTab(this)"{"general"}
.btn onclick="setActiveTab(this)"{"user interface"}
.btn onclick="setActiveTab(this)"{"engines"}
.btn onclick="setActiveTab(this)"{"cookies"}
}
.main_container{
(engines(&engine_names))
(cookies())
p class="message"{}
button type="submit" onclick="setClientSettings()"{"Save"}
}
}
}
script src="static/settings.js"{}
script src="static/cookies.js"{}
(footer())
))
}