Set user language to new recipe + cleaning
This commit is contained in:
parent
8b4b788562
commit
03ebbb74fa
12 changed files with 73 additions and 86 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use std::{sync::LazyLock, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
pub const FILE_CONF: &str = "conf.ron";
|
||||
pub const TRANSLATION_FILE: &str = "translation.ron";
|
||||
|
|
@ -23,10 +23,3 @@ pub const SEND_EMAIL_TIMEOUT: Duration = Duration::from_secs(60);
|
|||
pub const REVERSE_PROXY_IP_HTTP_FIELD: &str = "x-real-ip"; // Set by the reverse proxy (Nginx).
|
||||
|
||||
pub const MAX_DB_CONNECTION: u32 = 1; // To avoid database lock.
|
||||
|
||||
// TODO: remove, should be replaced by the translation module.
|
||||
pub static LANGUAGES: LazyLock<[(&str, &str); 2]> = LazyLock::new(|| {
|
||||
let mut langs = [("Français", "fr"), ("English", "en")];
|
||||
langs.sort();
|
||||
langs
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ pub enum DBError {
|
|||
)]
|
||||
UnsupportedVersion(u32),
|
||||
|
||||
#[error("Unknown language: {0}")]
|
||||
UnknownLanguage(String),
|
||||
|
||||
#[error("Unknown error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
use super::{Connection, DBError, Result};
|
||||
use crate::{consts, data::model};
|
||||
use crate::data::model;
|
||||
|
||||
use common::ron_api::Difficulty;
|
||||
|
||||
impl Connection {
|
||||
pub async fn get_all_published_recipe_titles(&self) -> Result<Vec<(i64, String)>> {
|
||||
/// Returns all the recipe titles where recipe is written in the given language.
|
||||
/// If a user_id is given, the language constraint is ignored for recipes owned by user_id.
|
||||
pub async fn get_all_published_recipe_titles(
|
||||
&self,
|
||||
lang: &str,
|
||||
user_id: Option<i64>,
|
||||
) -> Result<Vec<(i64, String)>> {
|
||||
if let Some(user_id) = user_id {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT [id], [title]
|
||||
FROM [Recipe]
|
||||
WHERE [is_published] = true
|
||||
WHERE [is_published] = true AND ([lang] = $1 OR [user_id] = $2)
|
||||
ORDER BY [title]
|
||||
"#,
|
||||
)
|
||||
.bind(lang)
|
||||
.bind(user_id)
|
||||
} else {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT [id], [title]
|
||||
FROM [Recipe]
|
||||
WHERE [is_published] = true AND [lang] = $1
|
||||
ORDER BY [title]
|
||||
"#,
|
||||
)
|
||||
.bind(lang)
|
||||
}
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DBError::from)
|
||||
|
|
@ -147,9 +167,16 @@ WHERE [Recipe].[user_id] = $1
|
|||
{
|
||||
Some(recipe_id) => Ok(recipe_id),
|
||||
None => {
|
||||
let db_result =
|
||||
sqlx::query("INSERT INTO [Recipe] ([user_id], [title]) VALUES ($1, '')")
|
||||
let lang: String = sqlx::query_scalar("SELECT [lang] FROM [User] WHERE [id] = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let db_result = sqlx::query(
|
||||
"INSERT INTO [Recipe] ([user_id], [lang], [title]) VALUES ($1, $2, '')",
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(lang)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
|
|
@ -345,9 +372,6 @@ WHERE [id] = $1 AND [id] NOT IN (
|
|||
}
|
||||
|
||||
pub async fn set_recipe_language(&self, recipe_id: i64, lang: &str) -> Result<()> {
|
||||
if !consts::LANGUAGES.iter().any(|(_, l)| *l == lang) {
|
||||
return Err(DBError::UnknownLanguage(lang.to_string()));
|
||||
}
|
||||
sqlx::query("UPDATE [Recipe] SET [lang] = $2 WHERE [id] = $1")
|
||||
.bind(recipe_id)
|
||||
.bind(lang)
|
||||
|
|
@ -598,24 +622,6 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_nonexistent_language() -> Result<()> {
|
||||
let connection = Connection::new_in_memory().await?;
|
||||
|
||||
let user_id = create_a_user(&connection).await?;
|
||||
let recipe_id = connection.create_recipe(user_id).await?;
|
||||
|
||||
match connection.set_recipe_language(recipe_id, "asdf").await {
|
||||
// Nominal case.
|
||||
Err(DBError::UnknownLanguage(message)) => {
|
||||
println!("Ok: {}", message);
|
||||
}
|
||||
other => panic!("Set an nonexistent language must fail: {:?}", other),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_a_user(connection: &Connection) -> Result<i64> {
|
||||
let user_id = 1;
|
||||
connection.execute_sql(
|
||||
|
|
|
|||
|
|
@ -133,13 +133,11 @@ pub struct RecipeEditTemplate {
|
|||
pub recipes: Recipes,
|
||||
|
||||
pub recipe: model::Recipe,
|
||||
pub languages: [(&'static str, &'static str); 2],
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "recipes_list_fragment.html")]
|
||||
pub struct RecipesListFragmentTemplate {
|
||||
pub user: Option<model::User>,
|
||||
pub tr: Tr,
|
||||
|
||||
pub recipes: Recipes,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use axum::{
|
|||
middleware::Next,
|
||||
response::{IntoResponse, Response, Result},
|
||||
};
|
||||
// use tracing::{event, Level};
|
||||
|
||||
use crate::{
|
||||
data::{db, model},
|
||||
|
|
@ -54,7 +53,9 @@ pub async fn home_page(
|
|||
Extension(tr): Extension<translation::Tr>,
|
||||
) -> Result<impl IntoResponse> {
|
||||
let recipes = Recipes {
|
||||
published: connection.get_all_published_recipe_titles().await?,
|
||||
published: connection
|
||||
.get_all_published_recipe_titles(tr.current_lang_code(), user.as_ref().map(|u| u.id))
|
||||
.await?,
|
||||
unpublished: if let Some(user) = user.as_ref() {
|
||||
connection
|
||||
.get_all_unpublished_recipe_titles(user.id)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ pub async fn recipes_list_fragments(
|
|||
Extension(tr): Extension<translation::Tr>,
|
||||
) -> Result<impl IntoResponse> {
|
||||
let recipes = Recipes {
|
||||
published: connection.get_all_published_recipe_titles().await?,
|
||||
published: connection
|
||||
.get_all_published_recipe_titles(tr.current_lang_code(), user.as_ref().map(|u| u.id))
|
||||
.await?,
|
||||
unpublished: if let Some(user) = user.as_ref() {
|
||||
connection
|
||||
.get_all_unpublished_recipe_titles(user.id)
|
||||
|
|
@ -35,5 +37,5 @@ pub async fn recipes_list_fragments(
|
|||
},
|
||||
current_id: current_recipe.current_recipe_id,
|
||||
};
|
||||
Ok(RecipesListFragmentTemplate { user, tr, recipes })
|
||||
Ok(RecipesListFragmentTemplate { tr, recipes })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use axum::{
|
|||
// use tracing::{event, Level};
|
||||
|
||||
use crate::{
|
||||
consts,
|
||||
data::{db, model},
|
||||
html_templates::*,
|
||||
translation::{self, Sentence},
|
||||
|
|
@ -37,21 +36,20 @@ pub async fn edit_recipe(
|
|||
if let Some(recipe) = connection.get_recipe(recipe_id, false).await? {
|
||||
if recipe.user_id == user.id {
|
||||
let recipes = Recipes {
|
||||
published: connection.get_all_published_recipe_titles().await?,
|
||||
published: connection
|
||||
.get_all_published_recipe_titles(tr.current_lang_code(), Some(user.id))
|
||||
.await?,
|
||||
unpublished: connection
|
||||
.get_all_unpublished_recipe_titles(user.id)
|
||||
.await?,
|
||||
current_id: Some(recipe_id),
|
||||
};
|
||||
|
||||
dbg!(&recipe);
|
||||
|
||||
Ok(RecipeEditTemplate {
|
||||
user: Some(user),
|
||||
tr,
|
||||
recipes,
|
||||
recipe,
|
||||
languages: *consts::LANGUAGES,
|
||||
}
|
||||
.into_response())
|
||||
} else {
|
||||
|
|
@ -89,7 +87,12 @@ pub async fn view(
|
|||
}
|
||||
|
||||
let recipes = Recipes {
|
||||
published: connection.get_all_published_recipe_titles().await?,
|
||||
published: connection
|
||||
.get_all_published_recipe_titles(
|
||||
tr.current_lang_code(),
|
||||
user.as_ref().map(|u| u.id),
|
||||
)
|
||||
.await?,
|
||||
unpublished: if let Some(user) = user.as_ref() {
|
||||
connection
|
||||
.get_all_unpublished_recipe_titles(user.id)
|
||||
|
|
|
|||
|
|
@ -249,6 +249,14 @@ pub async fn set_language(
|
|||
Extension(user): Extension<Option<model::User>>,
|
||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeLanguage>,
|
||||
) -> Result<StatusCode> {
|
||||
if !crate::translation::Tr::available_codes()
|
||||
.iter()
|
||||
.any(|&l| l == ron.lang)
|
||||
{
|
||||
// TODO: log?
|
||||
return Ok(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
||||
connection
|
||||
.set_recipe_language(ron.recipe_id, &ron.lang)
|
||||
|
|
|
|||
|
|
@ -59,12 +59,12 @@
|
|||
|
||||
<label for="select-language">{{ tr.t(Sentence::RecipeLanguage) }}</label>
|
||||
<select id="select-language">
|
||||
{% for lang in languages %}
|
||||
<option value="{{ lang.1 }}"
|
||||
{%+ if recipe.lang == lang.1 %}
|
||||
{% for lang in Tr::available_languages() %}
|
||||
<option value="{{ lang.0 }}"
|
||||
{%+ if recipe.lang == lang.0 %}
|
||||
selected
|
||||
{% endif %}
|
||||
>{{ lang.0 }}</option>
|
||||
>{{ lang.1 }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use gloo::{
|
||||
console::log,
|
||||
events::EventListener,
|
||||
net::http::Request,
|
||||
utils::{document, window},
|
||||
|
|
|
|||
|
|
@ -5,11 +5,7 @@ mod request;
|
|||
mod toast;
|
||||
mod utils;
|
||||
|
||||
use gloo::{
|
||||
console::log,
|
||||
events::EventListener,
|
||||
utils::{document, window},
|
||||
};
|
||||
use gloo::{events::EventListener, utils::window};
|
||||
use utils::by_id;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
|
@ -17,24 +13,10 @@ use web_sys::HtmlSelectElement;
|
|||
|
||||
use common::ron_api;
|
||||
|
||||
// #[wasm_bindgen]
|
||||
// extern "C" {
|
||||
// fn alert(s: &str);
|
||||
// }
|
||||
|
||||
// #[wasm_bindgen]
|
||||
// pub fn greet(name: &str) {
|
||||
// alert(&format!("Hello, {}!", name));
|
||||
// console::log_1(&"Hello bg".into());
|
||||
// }
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main() -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// let window = web_sys::window().expect("no global `window` exists");
|
||||
// let document = window.document().expect("should have a document on window");
|
||||
|
||||
let location = window().location().pathname()?;
|
||||
let path: Vec<&str> = location.split('/').skip(1).collect();
|
||||
|
||||
|
|
@ -56,8 +38,6 @@ pub fn main() -> Result<(), JsValue> {
|
|||
let _ = request::put::<(), _>("set_lang", body).await;
|
||||
let _ = window().location().reload();
|
||||
});
|
||||
|
||||
// log!(lang);
|
||||
})
|
||||
.forget();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use futures::channel::mpsc;
|
||||
use futures::stream::Stream;
|
||||
use gloo::{console::log, events::EventListener, net::http::Request, utils::document};
|
||||
use gloo::events::EventListener;
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue