parent
56f6b8edb4
commit
9683dcb81c
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: web/src/endpoints/user/put_receive_notifications.rs
|
||||
expression: subscribe_body
|
||||
snapshot_kind: text
|
||||
---
|
||||
<input
|
||||
hx-put="/users/1/unsubscribeNotifications"
|
||||
type="checkbox" hx-swap="outerHTML" checked>
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: web/src/endpoints/user/put_receive_notifications.rs
|
||||
expression: unsubscribe_body
|
||||
snapshot_kind: text
|
||||
---
|
||||
<input
|
||||
hx-put="/users/1/subscribeNotifications"
|
||||
type="checkbox" hx-swap="outerHTML" >
|
@ -49,6 +49,8 @@ pub fn init(cfg: &mut ServiceConfig) {
|
||||
cfg.service(user::get_register::get);
|
||||
cfg.service(user::post_register::post);
|
||||
cfg.service(user::post_resend_registration::post);
|
||||
cfg.service(user::put_receive_notifications::put_subscribe);
|
||||
cfg.service(user::put_receive_notifications::put_unsubscribe);
|
||||
|
||||
cfg.service(availability::delete::delete);
|
||||
cfg.service(availability::get_new::get);
|
||||
|
@ -69,17 +69,6 @@ pub async fn post(
|
||||
id = user.id));
|
||||
}
|
||||
}
|
||||
"receiveNotifications" => {
|
||||
User::update_receive_notifications(
|
||||
pool.get_ref(),
|
||||
user.id,
|
||||
!user.receive_notifications,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
_ => return HttpResponse::BadRequest().body("Other PATCH paths are not supported!"),
|
||||
};
|
||||
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
|
115
web/src/endpoints/user/put_receive_notifications.rs
Normal file
115
web/src/endpoints/user/put_receive_notifications.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use askama::Template;
|
||||
use serde_json::json;
|
||||
use sqlx::PgPool;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
endpoints::IdPath,
|
||||
filters,
|
||||
models::User,
|
||||
utils::{ApplicationError, TemplateResponse},
|
||||
};
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "user/profile.html", block = "notificationinput")]
|
||||
struct ProfilePartialNotificationInputTemplate {
|
||||
user: User,
|
||||
}
|
||||
|
||||
#[actix_web::put("/users/{id}/unsubscribeNotifications")]
|
||||
pub async fn put_unsubscribe(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
handle_subscription_to_notifications(user, pool, path, false).await
|
||||
}
|
||||
|
||||
#[actix_web::put("/users/{id}/subscribeNotifications")]
|
||||
pub async fn put_subscribe(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
handle_subscription_to_notifications(user, pool, path, true).await
|
||||
}
|
||||
|
||||
async fn handle_subscription_to_notifications(
|
||||
user: web::ReqData<User>,
|
||||
pool: web::Data<PgPool>,
|
||||
path: web::Path<IdPath>,
|
||||
subscription_state: bool,
|
||||
) -> Result<impl Responder, ApplicationError> {
|
||||
if user.id != path.id {
|
||||
return Err(ApplicationError::Unauthorized);
|
||||
}
|
||||
|
||||
let Some(user_in_db) = User::read_by_id(pool.get_ref(), path.id).await? else {
|
||||
error!(user = user.id, "Logged in user does not exist in database!");
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
};
|
||||
|
||||
let mut user = user.into_inner();
|
||||
if user_in_db.receive_notifications != subscription_state {
|
||||
User::update_receive_notifications(pool.get_ref(), user_in_db.id, subscription_state)
|
||||
.await?;
|
||||
user.receive_notifications = subscription_state;
|
||||
}
|
||||
|
||||
let trigger = json!({
|
||||
"showToast": {
|
||||
"type": "success",
|
||||
"message": "Benachrichtigungseinstellung gespeichert!"
|
||||
}
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let template = ProfilePartialNotificationInputTemplate { user };
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.insert_header(("HX-TRIGGER", trigger))
|
||||
.body(template.to_response()?.into_body()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::test_helper::{
|
||||
assert_snapshot, read_body, test_put, DbTestContext, RequestConfig, StatusCode,
|
||||
};
|
||||
use brass_macros::db_test;
|
||||
|
||||
#[db_test]
|
||||
async fn user_can_toggle_subscription_for_himself(context: &DbTestContext) {
|
||||
let app = context.app().await;
|
||||
|
||||
let unsubscribe_config = RequestConfig::new("/users/1/unsubscribeNotifications");
|
||||
let unsubscribe_response =
|
||||
test_put::<_, _, String>(&context.db_pool, &app, &unsubscribe_config, None).await;
|
||||
|
||||
assert_eq!(StatusCode::OK, unsubscribe_response.status());
|
||||
|
||||
let unsubscribe_body = read_body(unsubscribe_response).await;
|
||||
assert_snapshot!(unsubscribe_body);
|
||||
|
||||
let subscribe_config = RequestConfig::new("/users/1/subscribeNotifications");
|
||||
let subscribe_response =
|
||||
test_put::<_, _, String>(&context.db_pool, &app, &subscribe_config, None).await;
|
||||
|
||||
assert_eq!(StatusCode::OK, subscribe_response.status());
|
||||
|
||||
let subscribe_body = read_body(subscribe_response).await;
|
||||
assert_snapshot!(subscribe_body);
|
||||
}
|
||||
|
||||
#[db_test]
|
||||
async fn user_cant_toggle_subscription_for_someone_else(context: &DbTestContext) {
|
||||
let app = context.app().await;
|
||||
|
||||
let unsubscribe_config = RequestConfig::new("/users/3/unsubscribeNotifications");
|
||||
let unsubscribe_response =
|
||||
test_put::<_, _, String>(&context.db_pool, &app, &unsubscribe_config, None).await;
|
||||
|
||||
assert_eq!(StatusCode::UNAUTHORIZED, unsubscribe_response.status());
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ mod test_context;
|
||||
mod test_requests;
|
||||
pub use test_context::{setup, teardown, DbTestContext};
|
||||
pub use test_requests::RequestConfig;
|
||||
pub use test_requests::{read_body, test_delete, test_get, test_post};
|
||||
pub use test_requests::{read_body, test_delete, test_get, test_post, test_put};
|
||||
|
||||
pub use actix_http::StatusCode;
|
||||
|
||||
@ -25,5 +25,5 @@ macro_rules! assert_mail_snapshot {
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use assert_snapshot;
|
||||
pub(crate) use assert_mail_snapshot;
|
||||
pub(crate) use assert_snapshot;
|
||||
|
@ -59,7 +59,7 @@ where
|
||||
let login_form = LoginForm {
|
||||
email: "abc".to_string(),
|
||||
password: "abc".to_string(),
|
||||
next: None
|
||||
next: None,
|
||||
};
|
||||
|
||||
let login_req = test::TestRequest::post()
|
||||
@ -119,6 +119,28 @@ where
|
||||
test::call_service(&app, post_request).await
|
||||
}
|
||||
|
||||
pub async fn test_put<T, R, F>(
|
||||
pool: &Pool<Postgres>,
|
||||
app: &T,
|
||||
config: &RequestConfig,
|
||||
form: Option<F>,
|
||||
) -> ServiceResponse<R>
|
||||
where
|
||||
T: Service<Request, Response = ServiceResponse<R>, Error = Error>,
|
||||
R: MessageBody,
|
||||
F: Serialize,
|
||||
{
|
||||
let cookie = create_user_and_get_login_cookie(pool, app, config).await;
|
||||
|
||||
let put_request = test::TestRequest::put()
|
||||
.uri(&config.uri)
|
||||
.cookie(cookie)
|
||||
.set_form(form)
|
||||
.to_request();
|
||||
|
||||
test::call_service(app, put_request).await
|
||||
}
|
||||
|
||||
pub async fn test_delete<T, R>(
|
||||
pool: &Pool<Postgres>,
|
||||
app: T,
|
||||
|
@ -62,7 +62,7 @@
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
{{ user.function|show_tree|safe }}
|
||||
{{ user.function|show_tree|safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -88,19 +88,12 @@
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input hx-post="/users/{{ user.id }}/toggle?field=receiveNotifications" type="checkbox"
|
||||
hx-on::before-request="document.getElementById('success').classList.remove('fadeout')"
|
||||
hx-on::after-request="document.getElementById('success').classList.add('fadeout')"
|
||||
checked="{{ user.receive_notifications }}">
|
||||
{% block notificationinput %}
|
||||
<input
|
||||
hx-put="/users/{{ user.id }}/{%- if user.receive_notifications -%}un{%- endif -%}subscribeNotifications"
|
||||
type="checkbox" hx-swap="outerHTML" {{ user.receive_notifications|cond_show("checked") }}>
|
||||
{% endblock %}
|
||||
Ich möchte E-Mail Benachrichtigungen zu neuen Brasiwa-Einteilungen erhalten.
|
||||
<span id="success" class="result">
|
||||
<svg class="icon mr-2">
|
||||
<use href="/static/feather-sprite.svg#check" />
|
||||
</svg>
|
||||
<span>
|
||||
gespeichert
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user