Skip to content

Commit 8b48497

Browse files
committed
Add flash message
1 parent 02dd80e commit 8b48497

File tree

6 files changed

+126
-55
lines changed

6 files changed

+126
-55
lines changed

Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ askama = "0.14.0"
1616
# askama_web::WebTemplate implements axum::IntoResponse
1717
askama_web = { version = "0.14.6", features = ["axum-0.8"] }
1818
axum = { version = "0.8.4", features = ["macros"] }
19+
axum-extra = { version = "0.12.1", features = ["cookie"] }
1920
# UTF-8 paths for easier String/PathBuf interop
2021
camino = { version = "1.1.12", features = ["serde1"] }
2122
# Date/time management
@@ -70,4 +71,4 @@ uucore = { version = "0.1.0", features = ["fsext"] }
7071
xdg = "3.0.0"
7172

7273
[dev-dependencies]
73-
async-tempfile = "0.7"
74+
async-tempfile = "0.7"

src/routes/category.rs

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ use askama::Template;
22
use askama_web::WebTemplate;
33
use axum::Form;
44
use axum::extract::{Path, State};
5-
use axum::response::IntoResponse;
5+
use axum::response::{IntoResponse, Redirect};
6+
use axum_extra::extract::CookieJar;
67
use serde::{Deserialize, Serialize};
78
use snafu::prelude::*;
89

910
use crate::database::category::CategoryError;
1011
use crate::database::{category, category::CategoryOperator};
1112
use crate::extractors::user::User;
13+
use crate::state::flash_message::{OperationStatus, get_cookie};
1214
use crate::state::{AppState, AppStateContext, error::*};
1315

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

20-
pub struct OperationStatus {
21-
/// Status of operation
22-
pub success: bool,
23-
/// Message for confirmation alert
24-
pub message: String,
25-
}
26-
2722
#[derive(Template, WebTemplate)]
2823
#[template(path = "categories/index.html")]
2924
pub struct CategoriesTemplate {
@@ -34,7 +29,7 @@ pub struct CategoriesTemplate {
3429
/// Logged-in user.
3530
pub user: Option<User>,
3631
/// Operation status for UI confirmation
37-
pub operation_status: Option<OperationStatus>,
32+
pub flash: Option<OperationStatus>,
3833
}
3934

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

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

77-
match deleted {
78-
Ok(name) => Ok(CategoriesTemplate {
79-
categories: categories.list().await.context(CategorySnafu)?,
80-
operation_status: Some(OperationStatus {
81-
success: true,
82-
message: format!("The category {} has been successfully deleted", name),
83-
}),
84-
state: app_state_context,
85-
user,
86-
}),
87-
Err(error) => Ok(CategoriesTemplate {
88-
categories: categories.list().await.context(CategorySnafu)?,
89-
operation_status: Some(OperationStatus {
90-
success: false,
91-
message: format!("{}", error),
92-
}),
93-
state: app_state_context,
94-
user,
95-
}),
96-
}
73+
let operation_status = match deleted {
74+
Ok(name) => OperationStatus {
75+
success: true,
76+
message: format!("The category {} has been successfully deleted", name),
77+
},
78+
Err(error) => OperationStatus {
79+
success: false,
80+
message: format!("{}", error),
81+
},
82+
};
83+
84+
let jar = operation_status.set_cookie(jar);
85+
86+
Ok((jar, Redirect::to("/categories")))
9787
}
9888

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

109100
match created {
110-
Ok(created) => Ok(CategoriesTemplate {
111-
categories: categories.list().await.context(CategorySnafu)?,
112-
state: app_state_context,
113-
user,
114-
operation_status: Some(OperationStatus {
101+
Ok(created) => {
102+
let operation_status = OperationStatus {
115103
success: true,
116104
message: format!(
117105
"The category {} has been successfully created (ID {})",
118106
created.name, created.id
119107
),
120-
}),
121-
}
122-
.into_response()),
123-
Err(error) => Ok(NewCategoryTemplate {
124-
state: app_state_context,
125-
user,
126-
category_form: Some(form),
127-
error: Some(error),
108+
};
109+
110+
let jar = operation_status.set_cookie(jar);
111+
112+
Ok((jar, Redirect::to("/categories").into_response()))
128113
}
129-
.into_response()),
114+
Err(error) => Ok((
115+
jar,
116+
NewCategoryTemplate {
117+
state: app_state_context,
118+
user,
119+
category_form: Some(form),
120+
error: Some(error),
121+
}
122+
.into_response(),
123+
)),
130124
}
131125
}
132126

133127
pub async fn index(
134128
State(app_state): State<AppState>,
135129
user: Option<User>,
136-
) -> Result<CategoriesTemplate, AppStateError> {
130+
jar: CookieJar,
131+
) -> Result<(CookieJar, CategoriesTemplate), AppStateError> {
137132
let app_state_context = app_state.context().await?;
138133
let categories = CategoryOperator::new(app_state.clone(), user.clone());
139134

140-
Ok(CategoriesTemplate {
141-
categories: categories.list().await.context(CategorySnafu)?,
142-
state: app_state_context,
143-
user,
144-
operation_status: None,
145-
})
135+
let (jar, operation_status) = get_cookie(jar);
136+
137+
Ok((
138+
jar,
139+
CategoriesTemplate {
140+
categories: categories.list().await.context(CategorySnafu)?,
141+
state: app_state_context,
142+
user,
143+
flash: operation_status,
144+
},
145+
))
146146
}

src/state/flash_message.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use axum_extra::extract::{CookieJar, cookie::Cookie};
2+
3+
pub struct OperationStatus {
4+
/// Status of operation
5+
pub success: bool,
6+
/// Message for confirmation alert
7+
pub message: String,
8+
}
9+
10+
impl OperationStatus {
11+
pub fn set_cookie(&self, jar: CookieJar) -> CookieJar {
12+
let mut cookie_operation_status_success =
13+
Cookie::new("operation_status_success", self.success.to_string());
14+
15+
let mut cookie_operation_status_message =
16+
Cookie::new("operation_status_message", self.message.clone());
17+
cookie_operation_status_success.set_path("/");
18+
cookie_operation_status_message.set_path("/");
19+
20+
jar.add(cookie_operation_status_success)
21+
.add(cookie_operation_status_message)
22+
}
23+
}
24+
25+
pub fn get_cookie(jar: CookieJar) -> (CookieJar, Option<OperationStatus>) {
26+
let operation_status = match (
27+
jar.get("operation_status_success"),
28+
jar.get("operation_status_message"),
29+
) {
30+
(Some(success), Some(message)) => Some(OperationStatus {
31+
success: if let Ok(success) = success.value().parse() {
32+
success
33+
} else {
34+
return (jar, None);
35+
},
36+
message: message.value().to_string(),
37+
}),
38+
_ => None,
39+
};
40+
41+
let jar = jar
42+
.remove(Cookie::from("operation_status_success"))
43+
.remove(Cookie::from("operation_status_message"));
44+
45+
(jar, operation_status)
46+
}

src/state/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use snafu::prelude::*;
77
use crate::config::AppConfig;
88

99
pub mod error;
10+
pub mod flash_message;
1011
pub mod free_space;
1112
pub mod logger;
1213

templates/shared/alert_operation_status.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
{% if let Some(operation_status) = operation_status %}
2-
<div class="alert {% if operation_status.success %}alert-success{% else %}alert-danger{% endif %} mt-4">
3-
<p class="mb-0">{{ operation_status.message }}</p>
1+
{% if let Some(flash) = flash %}
2+
<div class="alert {% if flash.success %}alert-success{% else %}alert-danger{% endif %} mt-4">
3+
<p class="mb-0">{{ flash.message }}</p>
44
</div>
55
{% endif %}
66

0 commit comments

Comments
 (0)