Compare commits

..

No commits in common. "1f18ba54d305e681fbe9d1c33a19f88681b756c1" and "2489fed697e0b4e9f1cbbc76e563b82c38833862" have entirely different histories.

8 changed files with 46 additions and 93 deletions

View File

@ -9,8 +9,8 @@ use oswilno_contract::sea_orm_active_enums::ParkingSpaceState;
use oswilno_contract::{accounts, parking_space_locations}; use oswilno_contract::{accounts, parking_space_locations};
use oswilno_session::{Authenticated, MaybeAuthenticated}; use oswilno_session::{Authenticated, MaybeAuthenticated};
use oswilno_view::{ use oswilno_view::{
filters, is_partial, Blank, Errors, HelperContext, Lang, Layout, Main, MainOpts, SearchOpts, filters, is_partial, Blank, Errors, Lang, Layout, Main, MainOpts, SearchOpts, SessionOpts,
SessionOpts, TranslationStorage, TranslationStorage,
}; };
use sea_orm::prelude::*; use sea_orm::prelude::*;
use sea_orm::ActiveValue::{NotSet, Set}; use sea_orm::ActiveValue::{NotSet, Set};
@ -67,7 +67,6 @@ struct AllPartialParkingSpace {
location_by_id: BTreeMap<i32, Rc<parking_space_locations::Model>>, location_by_id: BTreeMap<i32, Rc<parking_space_locations::Model>>,
session: Option<SessionOpts>, session: Option<SessionOpts>,
account_id: Option<i32>, account_id: Option<i32>,
hcx: HelperContext,
} }
#[autometrics] #[autometrics]
@ -76,7 +75,6 @@ async fn all_parking_spaces(
req: HttpRequest, req: HttpRequest,
db: Data<sea_orm::DatabaseConnection>, db: Data<sea_orm::DatabaseConnection>,
session: MaybeAuthenticated, session: MaybeAuthenticated,
hcx: HelperContext,
) -> HttpResponse { ) -> HttpResponse {
let db = db.into_inner(); let db = db.into_inner();
let session = session.into_option(); let session = session.into_option();
@ -85,7 +83,7 @@ async fn all_parking_spaces(
tracing::debug!("session {session:?}"); tracing::debug!("session {session:?}");
let mut parking_spaces = load_parking_spaces(db, account_id, htx).await; let mut parking_spaces = load_parking_spaces(db, account_id).await;
parking_spaces.session = session.clone(); parking_spaces.session = session.clone();
let main = Main { let main = Main {
body: parking_spaces, body: parking_spaces,
@ -119,7 +117,6 @@ async fn search_parking_spaces() -> HttpResponse {
async fn load_parking_spaces( async fn load_parking_spaces(
db: Arc<DatabaseConnection>, db: Arc<DatabaseConnection>,
account_id: Option<i32>, account_id: Option<i32>,
hcx: HelperContext,
) -> AllPartialParkingSpace { ) -> AllPartialParkingSpace {
let rents = parking_space_rents::Entity::find().all(&*db).await.unwrap(); let rents = parking_space_rents::Entity::find().all(&*db).await.unwrap();
let ids = rents let ids = rents
@ -183,7 +180,6 @@ async fn load_parking_spaces(
locations, locations,
location_by_id, location_by_id,
session: None, session: None,
hcx,
} }
} }
@ -203,7 +199,8 @@ async fn load_locations(db: Arc<DatabaseConnection>) -> Vec<parking_space_locati
async fn form_show( async fn form_show(
req: HttpRequest, req: HttpRequest,
session: Authenticated, session: Authenticated,
hcx: HelperContext, t: Data<TranslationStorage>,
lang: Lang,
db: Data<DatabaseConnection>, db: Data<DatabaseConnection>,
) -> HttpResponse { ) -> HttpResponse {
let db = db.into_inner(); let db = db.into_inner();
@ -211,8 +208,9 @@ async fn form_show(
let body = ParkingSpaceFormPartial { let body = ParkingSpaceFormPartial {
form: Default::default(), form: Default::default(),
locations: load_locations(db.clone()).await, locations: load_locations(db.clone()).await,
lang,
errors: Default::default(), errors: Default::default(),
hcx, t: t.into_inner(),
}; };
let main = Main { let main = Main {
body, body,
@ -237,9 +235,10 @@ async fn form_show(
#[template(path = "../templates/parking-spaces/form-partial.html")] #[template(path = "../templates/parking-spaces/form-partial.html")]
struct ParkingSpaceFormPartial { struct ParkingSpaceFormPartial {
form: CreateParkingSpace, form: CreateParkingSpace,
lang: Lang,
errors: Errors, errors: Errors,
t: Arc<TranslationStorage>,
locations: Vec<parking_space_locations::Model>, locations: Vec<parking_space_locations::Model>,
hcx: HelperContext,
} }
#[derive(Debug, Default, serde::Deserialize)] #[derive(Debug, Default, serde::Deserialize)]
@ -255,7 +254,8 @@ async fn create(
db: Data<sea_orm::DatabaseConnection>, db: Data<sea_orm::DatabaseConnection>,
p: Form<CreateParkingSpace>, p: Form<CreateParkingSpace>,
session: Authenticated, session: Authenticated,
hcx: HelperContext, t: Data<TranslationStorage>,
lang: Lang,
) -> HttpResponse { ) -> HttpResponse {
use oswilno_contract::parking_spaces::*; use oswilno_contract::parking_spaces::*;
let CreateParkingSpace { let CreateParkingSpace {
@ -280,7 +280,8 @@ async fn create(
spot, spot,
id: None, id: None,
}, },
hcx, t: t.into_inner(),
lang,
errors: Default::default(), errors: Default::default(),
locations: load_locations(db.clone()).await, locations: load_locations(db.clone()).await,
} }

View File

@ -1,14 +1,14 @@
{% match parking_space.state %} {% match parking_space.state %}
{% when ParkingSpaceState::Pending %} {% when ParkingSpaceState::Pending %}
<div class="text-center p-1 text-sm text-yellow-400 bg-transparent rounded-sm hover:bg-yellow-200 hover:text-yellow-900 dark:hover:bg-yellow-800 dark:hover:text-yellow-300"> <div class="text-center p-1 text-sm text-yellow-400 bg-transparent rounded-sm hover:bg-yellow-200 hover:text-yellow-900 dark:hover:bg-yellow-800 dark:hover:text-yellow-300">
{{"Pending"|t(lang,t)}} Pending
</div> </div>
{% when ParkingSpaceState::Verified %} {% when ParkingSpaceState::Verified %}
<div class="text-center p-1 text-sm text-blue-400 bg-transparent rounded-sm hover:bg-blue-200 hover:text-blue-900 dark:hover:bg-blue-800 dark:hover:text-blue-300"> <div class="text-center p-1 text-sm text-blue-400 bg-transparent rounded-sm hover:bg-blue-200 hover:text-blue-900 dark:hover:bg-blue-800 dark:hover:text-blue-300">
{{"Accepted"|t(lang,t)}} Accepted
</div> </div>
{% when ParkingSpaceState::Banned %} {% when ParkingSpaceState::Banned %}
<div class="text-center p-1 text-sm text-red-400 bg-transparent rounded-sm hover:bg-red-200 hover:text-red-900 dark:hover:bg-red-800 dark:hover:text-red-300"> <div class="text-center p-1 text-sm text-red-400 bg-transparent rounded-sm hover:bg-red-200 hover:text-red-900 dark:hover:bg-red-800 dark:hover:text-red-300">
{{"Rejected"|t(lang,t)}} Rejected
</div> </div>
{% endmatch %} {% endmatch %}

View File

@ -7,9 +7,7 @@ use actix_web::{get, post, HttpRequest, HttpResponse};
use askama_actix::Template; use askama_actix::Template;
use autometrics::autometrics; use autometrics::autometrics;
use garde::Validate; use garde::Validate;
use oswilno_view::{ use oswilno_view::{Blank, Errors, Lang, Layout, Main, MainOpts, TranslationStorage};
Blank, Errors, HelperContext, Lang, Layout, Main, MainOpts, TranslationStorage,
};
use sea_orm::DatabaseConnection; use sea_orm::DatabaseConnection;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -150,7 +148,8 @@ impl SessionConfigurator {
#[template(path = "./sign-in/partial.html")] #[template(path = "./sign-in/partial.html")]
struct SignInPartialTemplate { struct SignInPartialTemplate {
form: SignInPayload, form: SignInPayload,
hcx: HelperContext, lang: Lang,
t: Arc<TranslationStorage>,
errors: Errors, errors: Errors,
} }
@ -161,14 +160,15 @@ pub struct SignInPayload {
} }
#[get("/login")] #[get("/login")]
async fn login_view(req: HttpRequest, hcx: HelperContext) -> HttpResponse { async fn login_view(req: HttpRequest, t: Data<TranslationStorage>) -> HttpResponse {
HttpResponse::Ok().body( HttpResponse::Ok().body(
if oswilno_view::is_partial(&req) { if oswilno_view::is_partial(&req) {
Main { Main {
body: SignInPartialTemplate { body: SignInPartialTemplate {
form: SignInPayload::default(), form: SignInPayload::default(),
lang: Lang::Pl,
t: t.into_inner(),
errors: Errors::default(), errors: Errors::default(),
hcx,
}, },
opts: MainOpts { opts: MainOpts {
show: true, show: true,
@ -183,8 +183,9 @@ async fn login_view(req: HttpRequest, hcx: HelperContext) -> HttpResponse {
main: Main { main: Main {
body: SignInPartialTemplate { body: SignInPartialTemplate {
form: SignInPayload::default(), form: SignInPayload::default(),
lang: Lang::Pl,
t: t.into_inner(),
errors: Errors::default(), errors: Errors::default(),
hcx,
}, },
opts: MainOpts { opts: MainOpts {
show: true, show: true,
@ -207,8 +208,10 @@ async fn login(
db: Data<DatabaseConnection>, db: Data<DatabaseConnection>,
redis: Data<SessionStorage>, redis: Data<SessionStorage>,
payload: Form<SignInPayload>, payload: Form<SignInPayload>,
hcx: HelperContext, t: Data<oswilno_view::TranslationStorage>,
lang: Lang,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let t = t.into_inner();
let mut errors = Errors::default(); let mut errors = Errors::default();
let form = payload.into_inner(); let form = payload.into_inner();
match login_inner( match login_inner(
@ -223,9 +226,14 @@ async fn login(
{ {
Some(res) => Ok(res), Some(res) => Ok(res),
None => Ok(HttpResponse::Ok().body( None => Ok(HttpResponse::Ok().body(
(SignInPartialTemplate { form, errors, hcx }) (SignInPartialTemplate {
.render() form,
.unwrap(), lang,
t,
errors,
})
.render()
.unwrap(),
)), )),
} }
} }
@ -344,7 +352,6 @@ async fn find_account(
async fn refresh_token( async fn refresh_token(
refresh_token: actix_jwt_session::Authenticated<RefreshToken>, refresh_token: actix_jwt_session::Authenticated<RefreshToken>,
storage: Data<SessionStorage>, storage: Data<SessionStorage>,
_hcx: HelperContext,
) -> HttpResponse { ) -> HttpResponse {
let s = storage.into_inner(); let s = storage.into_inner();
let pair = match s.refresh::<Claims>(refresh_token.access_jti()).await { let pair = match s.refresh::<Claims>(refresh_token.access_jti()).await {

View File

@ -1,10 +1,15 @@
use crate::HelperContext; use crate::{Lang, TranslationStorage};
use askama::Result; use askama::Result;
use std::sync::Arc;
#[tracing::instrument(skip(htx))] #[tracing::instrument]
pub fn t<T: std::fmt::Display + std::fmt::Debug>(s: T, htx: &HelperContext) -> Result<String> { pub fn t<T: std::fmt::Display + std::fmt::Debug>(
s: T,
lang: &Lang,
t: &Arc<TranslationStorage>,
) -> Result<String> {
// tracing::debug!("translating {s:?} to lang {lang:?} with {t:?}"); // tracing::debug!("translating {s:?} to lang {lang:?} with {t:?}");
Ok(htx.t.to_lang(htx.lang, &s.to_string())) Ok(t.to_lang(*lang, &s.to_string()))
} }
pub fn skip_if<T: std::fmt::Display + PartialEq + Copy>(n: &T, skip: T) -> Result<String> { pub fn skip_if<T: std::fmt::Display + PartialEq + Copy>(n: &T, skip: T) -> Result<String> {

View File

@ -1,57 +0,0 @@
use crate::Lang;
use crate::{ExtractLangFuture, TranslationStorage};
use actix_web::error::Error;
use actix_web::web::Data;
use actix_web::HttpRequest;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
#[derive(Clone, Debug)]
pub struct HelperContext {
pub t: Data<TranslationStorage>,
pub lang: Lang,
}
impl actix_web::FromRequest for HelperContext {
type Error = actix_web::error::Error;
type Future = ExtractFuture;
fn from_request(
req: &actix_web::HttpRequest,
_payload: &mut actix_web::dev::Payload,
) -> Self::Future {
ExtractFuture {
req: Some(req.clone()),
}
}
}
pub struct ExtractFuture {
req: Option<HttpRequest>,
}
impl Future for ExtractFuture {
type Output = Result<HelperContext, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let lang = Pin::new(&mut ExtractLangFuture {
req: this.req.clone(),
})
.poll(cx);
let lang = match lang {
Poll::Ready(Ok(lang)) => lang,
_ => panic!("{lang:?}"),
};
let t = this
.req
.as_ref()
.unwrap()
.app_data::<Data<TranslationStorage>>()
.unwrap()
.clone();
Poll::Ready(Ok(HelperContext { t, lang }))
}
}

View File

@ -24,7 +24,7 @@ impl FromStr for Lang {
} }
pub struct ExtractLangFuture { pub struct ExtractLangFuture {
pub req: Option<HttpRequest>, req: Option<HttpRequest>,
} }
impl Future for ExtractLangFuture { impl Future for ExtractLangFuture {

View File

@ -4,12 +4,9 @@ use std::sync::{Arc, RwLock};
use actix_web::http::header::ContentType; use actix_web::http::header::ContentType;
use actix_web::web::ServiceConfig; use actix_web::web::ServiceConfig;
use actix_web::{get, HttpRequest, HttpResponse}; use actix_web::{get, HttpRequest, HttpResponse};
pub use helper_context::*;
pub use lang::*; pub use lang::*;
pub mod filters; pub mod filters;
pub mod helper_context;
pub mod lang; pub mod lang;
pub fn mount(config: &mut ServiceConfig) { pub fn mount(config: &mut ServiceConfig) {

View File

@ -21,7 +21,7 @@
hx-target="main" hx-target="main"
class="block py-2 pl-3 pr-4 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700" class="block py-2 pl-3 pr-4 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
> >
Targowisko About
</a> </a>
</li> </li>
<li> <li>