Update dependencies, add translation endpoint, and display a user message when logged in
This commit is contained in:
parent
6b043620b8
commit
348b0f24e9
19 changed files with 170 additions and 34 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
|
@ -166,7 +166,7 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"winnow 0.7.8",
|
"winnow 0.7.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -499,9 +499,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.20"
|
version = "1.2.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
|
checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
@ -592,13 +592,14 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"ron",
|
"ron",
|
||||||
"serde",
|
"serde",
|
||||||
|
"strum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "comrak"
|
name = "comrak"
|
||||||
version = "0.38.0"
|
version = "0.39.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f690706b5db081dccea6206d7f6d594bb9895599abea9d1a0539f13888781ae8"
|
checksum = "d5c834ca54c5a20588b358f34d1533b4b498ddb5fd979cec6b22d0e8867a2449"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bon",
|
"bon",
|
||||||
"caseless",
|
"caseless",
|
||||||
|
|
@ -1836,9 +1837,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.13"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
|
checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
|
|
@ -2536,7 +2537,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
|
@ -2548,9 +2548,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.11"
|
version = "0.5.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
|
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.0",
|
||||||
]
|
]
|
||||||
|
|
@ -3268,6 +3268,9 @@ name = "strum"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum_macros"
|
name = "strum_macros"
|
||||||
|
|
@ -4222,9 +4225,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.8"
|
version = "0.7.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e27d6ad3dac991091e4d35de9ba2d2d00647c5d0fc26c5496dee55984ae111b"
|
checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,12 @@ sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio", "chrono"] }
|
||||||
async-compression = { version = "0.4", features = ["tokio", "gzip"] }
|
async-compression = { version = "0.4", features = ["tokio", "gzip"] }
|
||||||
|
|
||||||
askama = "0.14"
|
askama = "0.14"
|
||||||
comrak = "0.38"
|
comrak = "0.39"
|
||||||
|
|
||||||
argon2 = { version = "0.5", features = ["default", "std"] }
|
argon2 = { version = "0.5", features = ["default", "std"] }
|
||||||
rand_core = { version = "0.9", features = ["std"] }
|
rand_core = { version = "0.9", features = ["std"] }
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
strum = "0.27"
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
strum_macros = "0.27"
|
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|
||||||
lettre = { version = "0.11", default-features = false, features = [
|
lettre = { version = "0.11", default-features = false, features = [
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,7 @@ pub fn make_service(
|
||||||
"/shopping_list/checked",
|
"/shopping_list/checked",
|
||||||
patch(services::ron::shopping_list::set_entry_checked),
|
patch(services::ron::shopping_list::set_entry_checked),
|
||||||
)
|
)
|
||||||
|
.route("/translation", get(services::ron::get_translation))
|
||||||
.fallback(services::ron::not_found);
|
.fallback(services::ron::not_found);
|
||||||
|
|
||||||
let fragments_routes = Router::new().route(
|
let fragments_routes = Router::new().route(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use chrono::{Duration, prelude::*};
|
use chrono::{Duration, prelude::*};
|
||||||
use rand::distr::{Alphanumeric, SampleString};
|
use rand::distr::{Alphanumeric, SampleString};
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
use strum_macros::Display;
|
use strum::Display;
|
||||||
|
|
||||||
use super::{Connection, DBError, Result};
|
use super::{Connection, DBError, Result};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
debug_handler,
|
debug_handler,
|
||||||
extract::{Extension, State},
|
extract::{Extension, Query, State},
|
||||||
http::{HeaderMap, StatusCode},
|
http::{HeaderMap, StatusCode},
|
||||||
response::{IntoResponse, Result},
|
response::{IntoResponse, Result},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::cookie::{Cookie, CookieJar, SameSite};
|
use axum_extra::extract::cookie::{Cookie, CookieJar, SameSite};
|
||||||
|
use common::ron_api;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::Context, consts, data::db, data::model, ron_extractor::ExtractRon, ron_utils::ron_error,
|
app::Context,
|
||||||
|
consts,
|
||||||
|
data::{db, model},
|
||||||
|
ron_extractor::ExtractRon,
|
||||||
|
ron_utils::{ron_error, ron_response_ok},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
|
|
@ -36,6 +41,16 @@ pub async fn set_lang(
|
||||||
Ok((jar, StatusCode::OK))
|
Ok((jar, StatusCode::OK))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn get_translation(
|
||||||
|
Extension(context): Extension<Context>,
|
||||||
|
translation_id: Query<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
Ok(ron_response_ok(ron_api::Value {
|
||||||
|
value: context.tr.t_from_id(translation_id.id),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/*** 404 ***/
|
/*** 404 ***/
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ pub async fn get(
|
||||||
pub async fn set_entry_checked(
|
pub async fn set_entry_checked(
|
||||||
State(connection): State<db::Connection>,
|
State(connection): State<db::Connection>,
|
||||||
Extension(context): Extension<Context>,
|
Extension(context): Extension<Context>,
|
||||||
ExtractRon(ron): ExtractRon<ron_api::Value<bool>>,
|
ExtractRon(ron): ExtractRon<ron_api::KeyValue<bool>>,
|
||||||
) -> Result<impl IntoResponse> {
|
) -> Result<impl IntoResponse> {
|
||||||
check_user_rights_shopping_list_entry(&connection, &context.user, ron.id).await?;
|
check_user_rights_shopping_list_entry(&connection, &context.user, ron.id).await?;
|
||||||
Ok(ron_response_ok(
|
Ok(ron_response_ok(
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,15 @@ pub async fn sign_in_post(
|
||||||
.same_site(cookie::SameSite::Strict);
|
.same_site(cookie::SameSite::Strict);
|
||||||
Ok((
|
Ok((
|
||||||
jar.add(cookie),
|
jar.add(cookie),
|
||||||
Redirect::to(&format!("/{}/", context.tr.current_lang_code())).into_response(),
|
Redirect::to(&format!(
|
||||||
|
"/{}/?{}={}&{}={}",
|
||||||
|
context.tr.current_lang_code(),
|
||||||
|
common::consts::GET_PARAMETER_USER_MESSAGE,
|
||||||
|
Sentence::SignInSuccess as i64,
|
||||||
|
common::consts::GET_PARAMETER_USER_MESSAGE_LEVEL,
|
||||||
|
common::toast::Level::Success as usize
|
||||||
|
))
|
||||||
|
.into_response(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ use common::utils;
|
||||||
use ron::de::from_reader;
|
use ron::de::from_reader;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use strum::EnumCount;
|
use strum::EnumCount;
|
||||||
use strum_macros::EnumCount;
|
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::consts;
|
use crate::consts;
|
||||||
|
|
||||||
|
#[repr(i64)]
|
||||||
#[derive(Debug, Clone, EnumCount, Deserialize)]
|
#[derive(Debug, Clone, EnumCount, Deserialize)]
|
||||||
pub enum Sentence {
|
pub enum Sentence {
|
||||||
MainTitle = 0,
|
MainTitle = 0,
|
||||||
|
|
@ -34,6 +34,7 @@ pub enum Sentence {
|
||||||
SignInMenu,
|
SignInMenu,
|
||||||
SignInTitle,
|
SignInTitle,
|
||||||
SignInButton,
|
SignInButton,
|
||||||
|
SignInSuccess,
|
||||||
WrongEmailOrPassword,
|
WrongEmailOrPassword,
|
||||||
|
|
||||||
// Sign up page.
|
// Sign up page.
|
||||||
|
|
@ -188,6 +189,11 @@ impl Tr {
|
||||||
self.lang.get(sentence)
|
self.lang.get(sentence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn t_from_id(&self, sentence_id: i64) -> &'static str {
|
||||||
|
self.lang.get_from_id(sentence_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translate a sentence with parameters.
|
||||||
pub fn tp(&self, sentence: Sentence, params: &[Box<dyn ToString + Send>]) -> String {
|
pub fn tp(&self, sentence: Sentence, params: &[Box<dyn ToString + Send>]) -> String {
|
||||||
let text = self.lang.get(sentence);
|
let text = self.lang.get(sentence);
|
||||||
let params_as_string: Vec<String> = params.iter().map(|p| p.to_string()).collect();
|
let params_as_string: Vec<String> = params.iter().map(|p| p.to_string()).collect();
|
||||||
|
|
@ -246,6 +252,7 @@ impl Tr {
|
||||||
| "VE" // Venezuela.
|
| "VE" // Venezuela.
|
||||||
| "ZW" // Zimbabwe.
|
| "ZW" // Zimbabwe.
|
||||||
=> Weekday::Sun,
|
=> Weekday::Sun,
|
||||||
|
|
||||||
"AF" // Afghanistan.
|
"AF" // Afghanistan.
|
||||||
| "DZ" // Algeria.
|
| "DZ" // Algeria.
|
||||||
| "BH" // Bahrain.
|
| "BH" // Bahrain.
|
||||||
|
|
@ -262,6 +269,7 @@ impl Tr {
|
||||||
| "AE" // United Arab Emirates.
|
| "AE" // United Arab Emirates.
|
||||||
| "YE" // Yemen.
|
| "YE" // Yemen.
|
||||||
=> Weekday::Sat,
|
=> Weekday::Sat,
|
||||||
|
|
||||||
_ => Weekday::Mon,
|
_ => Weekday::Mon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -320,14 +328,17 @@ impl Language {
|
||||||
T: Borrow<Sentence>,
|
T: Borrow<Sentence>,
|
||||||
{
|
{
|
||||||
let sentence_cloned: Sentence = sentence.borrow().clone();
|
let sentence_cloned: Sentence = sentence.borrow().clone();
|
||||||
|
self.get_from_id(sentence_cloned as i64)
|
||||||
|
}
|
||||||
|
|
||||||
let text: &str = match self.translation.get(sentence_cloned as usize) {
|
pub fn get_from_id(&'static self, sentence_id: i64) -> &'static str {
|
||||||
|
let text: &str = match self.translation.get(sentence_id as usize) {
|
||||||
None => UNABLE_TO_FIND_TRANSLATION_MESSAGE,
|
None => UNABLE_TO_FIND_TRANSLATION_MESSAGE,
|
||||||
Some(text) => text,
|
Some(text) => text,
|
||||||
};
|
};
|
||||||
|
|
||||||
if text.is_empty() && self.code != DEFAULT_LANGUAGE_CODE {
|
if text.is_empty() && self.code != DEFAULT_LANGUAGE_CODE {
|
||||||
return get_language_translation(DEFAULT_LANGUAGE_CODE).get(sentence);
|
return get_language_translation(DEFAULT_LANGUAGE_CODE).get_from_id(sentence_id);
|
||||||
}
|
}
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@
|
||||||
(SignInMenu, "Sign in"),
|
(SignInMenu, "Sign in"),
|
||||||
(SignInTitle, "Sign in"),
|
(SignInTitle, "Sign in"),
|
||||||
(SignInButton, "Sign in"),
|
(SignInButton, "Sign in"),
|
||||||
|
(SignInSuccess, "Sign in successful"),
|
||||||
(WrongEmailOrPassword, "Wrong email or password"),
|
(WrongEmailOrPassword, "Wrong email or password"),
|
||||||
|
|
||||||
(AccountMustBeValidatedFirst, "This account must be validated first"),
|
(AccountMustBeValidatedFirst, "This account must be validated first"),
|
||||||
(InvalidEmail, "Invalid email"),
|
(InvalidEmail, "Invalid email"),
|
||||||
(PasswordDontMatch, "Passwords don't match"),
|
(PasswordDontMatch, "Passwords don't match"),
|
||||||
|
|
@ -175,7 +177,9 @@
|
||||||
(SignInMenu, "Se connecter"),
|
(SignInMenu, "Se connecter"),
|
||||||
(SignInTitle, "Se connecter"),
|
(SignInTitle, "Se connecter"),
|
||||||
(SignInButton, "Se connecter"),
|
(SignInButton, "Se connecter"),
|
||||||
|
(SignInSuccess, "Connexion réussie"),
|
||||||
(WrongEmailOrPassword, "Mot de passe ou email invalide"),
|
(WrongEmailOrPassword, "Mot de passe ou email invalide"),
|
||||||
|
|
||||||
(AccountMustBeValidatedFirst, "Ce compte doit d'abord être validé"),
|
(AccountMustBeValidatedFirst, "Ce compte doit d'abord être validé"),
|
||||||
(InvalidEmail, "Adresse email invalide"),
|
(InvalidEmail, "Adresse email invalide"),
|
||||||
(PasswordDontMatch, "Les mots de passe ne correspondent pas"),
|
(PasswordDontMatch, "Les mots de passe ne correspondent pas"),
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@ edition = "2024"
|
||||||
ron = "0.10"
|
ron = "0.10"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
pub const MIN_PASSWORD_SIZE: usize = 8;
|
pub const MIN_PASSWORD_SIZE: usize = 8;
|
||||||
pub const COOKIE_DARK_THEME: &str = "dark_theme";
|
pub const COOKIE_DARK_THEME: &str = "dark_theme";
|
||||||
|
|
||||||
|
pub const GET_PARAMETER_USER_MESSAGE: &str = "user_message";
|
||||||
|
pub const GET_PARAMETER_USER_MESSAGE_LEVEL: &str = "user_message_icon";
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod ron_api;
|
pub mod ron_api;
|
||||||
|
pub mod toast;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,15 @@ pub struct Id {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A value associated with an id.
|
// A simple value.
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct Value<T> {
|
pub struct Value<T> {
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A value associated with an id.
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct KeyValue<T> {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
pub value: T,
|
pub value: T,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
common/src/toast.rs
Normal file
10
common/src/toast.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
use strum::FromRepr;
|
||||||
|
|
||||||
|
#[derive(FromRepr)]
|
||||||
|
pub enum Level {
|
||||||
|
Success,
|
||||||
|
Error,
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
@ -36,6 +36,7 @@ web-sys = { version = "0.3", features = [
|
||||||
"DataTransfer",
|
"DataTransfer",
|
||||||
"DomRect",
|
"DomRect",
|
||||||
"KeyboardEvent",
|
"KeyboardEvent",
|
||||||
|
"History",
|
||||||
"Element",
|
"Element",
|
||||||
"DomStringMap",
|
"DomStringMap",
|
||||||
"HtmlDocument",
|
"HtmlDocument",
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,44 @@ pub fn main() -> Result<(), JsValue> {
|
||||||
_ => log!("Path unknown: ", location),
|
_ => log!("Path unknown: ", location),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User message handling.
|
||||||
|
let user_message_params = utils::extract_get_parameters(&[
|
||||||
|
common::consts::GET_PARAMETER_USER_MESSAGE,
|
||||||
|
common::consts::GET_PARAMETER_USER_MESSAGE_LEVEL,
|
||||||
|
]);
|
||||||
|
if let Some(mess_id) = user_message_params.iter().find_map(|(k, v)| {
|
||||||
|
if k == common::consts::GET_PARAMETER_USER_MESSAGE {
|
||||||
|
v.parse::<i64>().ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
let level_id = user_message_params.iter().find_map(|(k, v)| {
|
||||||
|
if k == common::consts::GET_PARAMETER_USER_MESSAGE_LEVEL {
|
||||||
|
v.parse::<usize>().ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Request the message to display.
|
||||||
|
spawn_local(async move {
|
||||||
|
let translation: ron_api::Value<String> =
|
||||||
|
request::get("translation", ron_api::Id { id: mess_id })
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
if let Some(level_id) = level_id {
|
||||||
|
toast::show_message_level(
|
||||||
|
common::toast::Level::from_repr(level_id)
|
||||||
|
.unwrap_or(common::toast::Level::Unknown),
|
||||||
|
&translation.value,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toast::show_message(&translation.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Language handling.
|
// Language handling.
|
||||||
let select_language: HtmlSelectElement = by_id("select-website-language");
|
let select_language: HtmlSelectElement = by_id("select-website-language");
|
||||||
EventListener::new(&select_language.clone(), "input", move |_event| {
|
EventListener::new(&select_language.clone(), "input", move |_event| {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ impl ShoppingList {
|
||||||
} else {
|
} else {
|
||||||
request::patch(
|
request::patch(
|
||||||
"shopping_list/checked",
|
"shopping_list/checked",
|
||||||
ron_api::Value {
|
ron_api::KeyValue {
|
||||||
id: item_id,
|
id: item_id,
|
||||||
value: is_checked,
|
value: is_checked,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,9 @@
|
||||||
|
pub use common::toast::Level;
|
||||||
use gloo::{events::EventListener, timers::callback::Timeout};
|
use gloo::{events::EventListener, timers::callback::Timeout};
|
||||||
use web_sys::{Element, HtmlElement, HtmlImageElement};
|
use web_sys::{Element, HtmlElement, HtmlImageElement};
|
||||||
|
|
||||||
use crate::utils::{SelectorExt, by_id, selector_and_clone};
|
use crate::utils::{SelectorExt, by_id, selector_and_clone};
|
||||||
|
|
||||||
pub enum Level {
|
|
||||||
Success,
|
|
||||||
Error,
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
const TIME_ANIMATION: u32 = 500; // [ms].
|
const TIME_ANIMATION: u32 = 500; // [ms].
|
||||||
const TIME_DISPLAYED: u32 = 5_000; // [ms].
|
const TIME_DISPLAYED: u32 = 5_000; // [ms].
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::Locale;
|
use chrono::Locale;
|
||||||
use gloo::utils::document;
|
use gloo::utils::{document, window};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::Element;
|
use web_sys::Element;
|
||||||
|
|
||||||
|
|
@ -133,3 +133,45 @@ pub fn get_locale() -> Locale {
|
||||||
.replace("-", "_");
|
.replace("-", "_");
|
||||||
Locale::from_str(&lang_and_territory).unwrap_or_default()
|
Locale::from_str(&lang_and_territory).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts and remove some URL parameters from the current URL.
|
||||||
|
pub fn extract_get_parameters(names: &[&str]) -> Vec<(String, String)> {
|
||||||
|
let mut param_values = vec![];
|
||||||
|
let location = window().location();
|
||||||
|
|
||||||
|
if let Ok(search) = location.search() {
|
||||||
|
if !search.is_empty() && search.starts_with('?') {
|
||||||
|
let mut search_chars = search.chars();
|
||||||
|
search_chars.next(); // To remove the first character which is '?'.
|
||||||
|
let mut new_search = String::with_capacity(search.len());
|
||||||
|
for kv in search_chars.as_str().split('&') {
|
||||||
|
match kv.split('=').collect::<Vec<&str>>()[..] {
|
||||||
|
[key, value] if names.contains(&key) => {
|
||||||
|
param_values.push((key.to_string(), value.to_string()));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if !new_search.is_empty() {
|
||||||
|
new_search.push('&');
|
||||||
|
}
|
||||||
|
new_search.push_str(kv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !param_values.is_empty() {
|
||||||
|
let mut new_url = location.pathname().unwrap();
|
||||||
|
if !new_search.is_empty() {
|
||||||
|
new_url.push('?');
|
||||||
|
new_url.push_str(&new_search);
|
||||||
|
}
|
||||||
|
window()
|
||||||
|
.history()
|
||||||
|
.unwrap()
|
||||||
|
.push_state_with_url(&JsValue::null(), "", Some(&new_url))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
param_values
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue