Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ askama = "0.14.0"
# askama_web::WebTemplate implements axum::IntoResponse
askama_web = { version = "0.14.6", features = ["axum-0.8"] }
axum = { version = "0.8.4", features = ["macros"] }
axum-extra = { version = "0.12.1", features = ["cookie"] }
# UTF-8 paths for easier String/PathBuf interop
camino = { version = "1.1.12", features = ["serde1"] }
# Date/time management
Expand Down Expand Up @@ -70,4 +71,4 @@ uucore = { version = "0.1.0", features = ["fsext"] }
xdg = "3.0.0"

[dev-dependencies]
async-tempfile = "0.7"
async-tempfile = "0.7"
102 changes: 51 additions & 51 deletions src/routes/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use askama::Template;
use askama_web::WebTemplate;
use axum::Form;
use axum::extract::{Path, State};
use axum::response::IntoResponse;
use axum::response::{IntoResponse, Redirect};
use axum_extra::extract::CookieJar;
use serde::{Deserialize, Serialize};
use snafu::prelude::*;

use crate::database::category::CategoryError;
use crate::database::{category, category::CategoryOperator};
use crate::extractors::user::User;
use crate::state::flash_message::{OperationStatus, get_cookie};
use crate::state::{AppState, AppStateContext, error::*};

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand All @@ -17,13 +19,6 @@ pub struct CategoryForm {
pub path: String,
}

pub struct OperationStatus {
/// Status of operation
pub success: bool,
/// Message for confirmation alert
pub message: String,
}

#[derive(Template, WebTemplate)]
#[template(path = "categories/index.html")]
pub struct CategoriesTemplate {
Expand All @@ -34,7 +29,7 @@ pub struct CategoriesTemplate {
/// Logged-in user.
pub user: Option<User>,
/// Operation status for UI confirmation
pub operation_status: Option<OperationStatus>,
pub flash: Option<OperationStatus>,
}

#[derive(Template, WebTemplate)]
Expand Down Expand Up @@ -68,37 +63,33 @@ pub async fn delete(
State(app_state): State<AppState>,
user: Option<User>,
Path(id): Path<i32>,
jar: CookieJar,
) -> Result<impl axum::response::IntoResponse, AppStateError> {
let app_state_context = app_state.context().await?;
// let app_state_context = app_state.context().await?;
let categories = CategoryOperator::new(app_state.clone(), user.clone());

let deleted = categories.delete(id, user.clone()).await;

match deleted {
Ok(name) => Ok(CategoriesTemplate {
categories: categories.list().await.context(CategorySnafu)?,
operation_status: Some(OperationStatus {
success: true,
message: format!("The category {} has been successfully deleted", name),
}),
state: app_state_context,
user,
}),
Err(error) => Ok(CategoriesTemplate {
categories: categories.list().await.context(CategorySnafu)?,
operation_status: Some(OperationStatus {
success: false,
message: format!("{}", error),
}),
state: app_state_context,
user,
}),
}
let operation_status = match deleted {
Ok(name) => OperationStatus {
success: true,
message: format!("The category {} has been successfully deleted", name),
},
Err(error) => OperationStatus {
success: false,
message: format!("{}", error),
},
};

let jar = operation_status.set_cookie(jar);

Ok((jar, Redirect::to("/categories")))
}

pub async fn create(
State(app_state): State<AppState>,
user: Option<User>,
jar: CookieJar,
Form(form): Form<CategoryForm>,
) -> Result<impl axum::response::IntoResponse, AppStateError> {
let app_state_context = app_state.context().await?;
Expand All @@ -107,40 +98,49 @@ pub async fn create(
let created = categories.create(&form, user.clone()).await;

match created {
Ok(created) => Ok(CategoriesTemplate {
categories: categories.list().await.context(CategorySnafu)?,
state: app_state_context,
user,
operation_status: Some(OperationStatus {
Ok(created) => {
let operation_status = OperationStatus {
success: true,
message: format!(
"The category {} has been successfully created (ID {})",
created.name, created.id
),
}),
}
.into_response()),
Err(error) => Ok(NewCategoryTemplate {
state: app_state_context,
user,
category_form: Some(form),
error: Some(error),
};

let jar = operation_status.set_cookie(jar);

Ok((jar, Redirect::to("/categories").into_response()))
}
.into_response()),
Err(error) => Ok((
jar,
NewCategoryTemplate {
state: app_state_context,
user,
category_form: Some(form),
error: Some(error),
}
.into_response(),
)),
}
}

pub async fn index(
State(app_state): State<AppState>,
user: Option<User>,
) -> Result<CategoriesTemplate, AppStateError> {
jar: CookieJar,
) -> Result<(CookieJar, CategoriesTemplate), AppStateError> {
let app_state_context = app_state.context().await?;
let categories = CategoryOperator::new(app_state.clone(), user.clone());

Ok(CategoriesTemplate {
categories: categories.list().await.context(CategorySnafu)?,
state: app_state_context,
user,
operation_status: None,
})
let (jar, operation_status) = get_cookie(jar);

Ok((
jar,
CategoriesTemplate {
categories: categories.list().await.context(CategorySnafu)?,
state: app_state_context,
user,
flash: operation_status,
},
))
}
46 changes: 46 additions & 0 deletions src/state/flash_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use axum_extra::extract::{CookieJar, cookie::Cookie};

pub struct OperationStatus {
/// Status of operation
pub success: bool,
/// Message for confirmation alert
pub message: String,
}

impl OperationStatus {
pub fn set_cookie(&self, jar: CookieJar) -> CookieJar {
let mut cookie_operation_status_success =
Cookie::new("operation_status_success", self.success.to_string());

let mut cookie_operation_status_message =
Cookie::new("operation_status_message", self.message.clone());
cookie_operation_status_success.set_path("/");
cookie_operation_status_message.set_path("/");

jar.add(cookie_operation_status_success)
.add(cookie_operation_status_message)
}
}

pub fn get_cookie(jar: CookieJar) -> (CookieJar, Option<OperationStatus>) {
let operation_status = match (
jar.get("operation_status_success"),
jar.get("operation_status_message"),
) {
(Some(success), Some(message)) => Some(OperationStatus {
success: if let Ok(success) = success.value().parse() {
success
} else {
return (jar, None);
},
message: message.value().to_string(),
}),
_ => None,
};

let jar = jar
.remove(Cookie::from("operation_status_success"))
.remove(Cookie::from("operation_status_message"));

(jar, operation_status)
}
1 change: 1 addition & 0 deletions src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use snafu::prelude::*;
use crate::config::AppConfig;

pub mod error;
pub mod flash_message;
pub mod free_space;
pub mod logger;

Expand Down
6 changes: 3 additions & 3 deletions templates/shared/alert_operation_status.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% if let Some(operation_status) = operation_status %}
<div class="alert {% if operation_status.success %}alert-success{% else %}alert-danger{% endif %} mt-4">
<p class="mb-0">{{ operation_status.message }}</p>
{% if let Some(flash) = flash %}
<div class="alert {% if flash.success %}alert-success{% else %}alert-danger{% endif %} mt-4">
<p class="mb-0">{{ flash.message }}</p>
</div>
{% endif %}

Expand Down
Loading