diff --git a/Cargo.lock b/Cargo.lock index b6ef5d1..5fe86b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2272,6 +2272,7 @@ dependencies = [ "lettre", "log", "openssl", + "regex", "rocket", "rocket_dyn_templates", "serde", diff --git a/Cargo.toml b/Cargo.toml index 9c950fd..4eea07c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ csv = "1.3" itertools = "0.12" job_scheduler_ng = "2.0" ureq = { version = "2.9", features = ["json"] } +regex = "1.10" [target.'cfg(not(windows))'.dependencies] openssl = { version = "0.10", features = [ "vendored" ] } diff --git a/migration.sql b/migration.sql index c530488..574288c 100644 --- a/migration.sql +++ b/migration.sql @@ -160,6 +160,7 @@ CREATE TABLE IF NOT EXISTS "notification" ( "read_at" DATETIME, "created_at" DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, "category" TEXT NOT NULL, + "action_after_reading" TEXT, "link" TEXT ); diff --git a/src/model/boatdamage.rs b/src/model/boatdamage.rs index a262410..e7dc017 100644 --- a/src/model/boatdamage.rs +++ b/src/model/boatdamage.rs @@ -137,7 +137,7 @@ ORDER BY created_at DESC if !was_unusable_before && boat.is_locked(db).await { let cox = Role::find_by_name(db, "cox").await.unwrap(); - Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, bitte beachten, dass {} bis auf weiteres aufgrund von Reparaturarbeiten gesperrt ist.", boat.name), "Boot gesperrt", None).await; + Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, bitte beachten, dass {} bis auf weiteres aufgrund von Reparaturarbeiten gesperrt ist.", boat.name), "Boot gesperrt", None, None).await; } let technicals = @@ -158,6 +158,7 @@ ORDER BY created_at DESC ), "Neuer Bootsschaden angelegt", None, + None, ) .await; } @@ -178,6 +179,7 @@ ORDER BY created_at DESC ), "Neuer Bootsschaden angelegt", None, + None, ) .await; @@ -235,7 +237,7 @@ ORDER BY created_at DESC boat.name, ), "Bootsschaden repariert", - None, + None,None ) .await; } @@ -261,7 +263,7 @@ ORDER BY created_at DESC boat_damage.desc, boat.name, ), "Bootsschaden repariert", - None, + None,None ) .await; } @@ -320,6 +322,7 @@ ORDER BY created_at DESC ), "Bootsschaden repariert & verifiziert", None, + None ) .await; } else { @@ -333,13 +336,14 @@ ORDER BY created_at DESC ), "Bootsschaden verifiziert", None, + None ).await; } } if was_unusable_before && !boat.is_locked(db).await { let cox = Role::find_by_name(db, "cox").await.unwrap(); - Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, {} wurde repariert und freut sich ab sofort wieder gerudert zu werden :-)", boat.name), "Boot repariert", None).await; + Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, {} wurde repariert und freut sich ab sofort wieder gerudert zu werden :-)", boat.name), "Boot repariert", None, None).await; } Ok(()) diff --git a/src/model/boatreservation.rs b/src/model/boatreservation.rs index d3d56f0..8a2a2b9 100644 --- a/src/model/boatreservation.rs +++ b/src/model/boatreservation.rs @@ -175,7 +175,7 @@ WHERE end_date >= CURRENT_DATE ORDER BY end_date boatreservation.usage ), "Neue Bootsreservierung", - None, + None,None ) .await; } diff --git a/src/model/logbook.rs b/src/model/logbook.rs index 66b7a4e..09713f2 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -549,6 +549,7 @@ ORDER BY departure DESC ), "Neuer Logbucheintrag", None, + None, ) .await; } @@ -578,7 +579,7 @@ ORDER BY departure DESC &vorstand, &format!("'{}' hat eine mehrtägige Ausfahrt vom {} bis {} eingetragen ({} km; Ziel: {}; Anmerkungen: {}). Falls das nicht stimmen sollte, bitte nachhaken.",user.name,log.departure, log.arrival, log.distance_in_km, log.destination, log.comments.clone().unwrap_or("".into())), "Mehrtägige Ausfahrt eingetragen", - None, + None,None ).await; } @@ -590,7 +591,7 @@ ORDER BY departure DESC &vorstand, &format!("'{}' hat eine Ausfahrt mit externem Boot '{}' am {} eingetragen ({} km; Ziel: {}; Anmerkungen: {}). Falls das nicht stimmen sollte, bitte nachhaken.",user.name,boat.name,log.departure,log.distance_in_km, log.destination, log.comments.unwrap_or("".into())), "Ausfahrt mit externem Boot eingetragen", - None, + None,None, ).await; } diff --git a/src/model/notification.rs b/src/model/notification.rs index 3a29e49..bf920cb 100644 --- a/src/model/notification.rs +++ b/src/model/notification.rs @@ -1,6 +1,7 @@ use std::ops::DerefMut; use chrono::NaiveDateTime; +use regex::Regex; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; @@ -15,6 +16,7 @@ pub struct Notification { pub created_at: NaiveDateTime, pub category: String, pub link: Option, + pub action_after_reading: Option, } impl Notification { @@ -30,13 +32,15 @@ impl Notification { message: &str, category: &str, link: Option<&str>, + action_after_reading: Option<&str>, ) { sqlx::query!( - "INSERT INTO notification(user_id, message, category, link) VALUES (?, ?, ?, ?)", + "INSERT INTO notification(user_id, message, category, link, action_after_reading) VALUES (?, ?, ?, ?, ?)", user.id, message, category, - link + link, + action_after_reading ) .execute(db.deref_mut()) .await @@ -49,9 +53,10 @@ impl Notification { message: &str, category: &str, link: Option<&str>, + action_after_reading: Option<&str>, ) { let mut tx = db.begin().await.unwrap(); - Self::create_with_tx(&mut tx, user, message, category, link).await; + Self::create_with_tx(&mut tx, user, message, category, link, action_after_reading).await; tx.commit().await.unwrap(); } @@ -61,11 +66,12 @@ impl Notification { message: &str, category: &str, link: Option<&str>, + action_after_reading: Option<&str>, ) { let users = User::all_with_role_tx(db, role).await; for user in users { - Self::create_with_tx(db, &user, message, category, link).await; + Self::create_with_tx(db, &user, message, category, link, action_after_reading).await; } } @@ -75,16 +81,18 @@ impl Notification { message: &str, category: &str, link: Option<&str>, + action_after_reading: Option<&str>, ) { let mut tx = db.begin().await.unwrap(); - Self::create_for_role_tx(&mut tx, role, message, category, link).await; + Self::create_for_role_tx(&mut tx, role, message, category, link, action_after_reading) + .await; tx.commit().await.unwrap(); } pub async fn for_user(db: &SqlitePool, user: &User) -> Vec { let rows = sqlx::query!( " -SELECT id, user_id, message, read_at, datetime(created_at, 'localtime') as created_at, category, link FROM notification +SELECT id, user_id, message, read_at, datetime(created_at, 'localtime') as created_at, category, link, action_after_reading FROM notification WHERE user_id = ? AND ( @@ -113,6 +121,7 @@ ORDER BY read_at DESC, created_at DESC; .unwrap(), category: rec.category, link: rec.link, + action_after_reading: rec.action_after_reading, }) .collect() } @@ -125,5 +134,24 @@ ORDER BY read_at DESC, created_at DESC; .execute(db) .await .unwrap(); + + if let Some(action) = self.action_after_reading.as_ref() { + // User read notification about cancelled trip/event + let re = Regex::new(r"^remove_user_trip_with_trip_details_id:(\d+)$").unwrap(); + if let Some(caps) = re.captures(action) { + if let Some(matched) = caps.get(1) { + if let Ok(number) = matched.as_str().parse::() { + let _ = sqlx::query!( + "DELETE FROM user_trip WHERE user_id = ? AND trip_details_id = ?", + self.user_id, + number + ) + .execute(db) + .await + .unwrap(); + } + } + } + } } } diff --git a/src/model/trip.rs b/src/model/trip.rs index 407dded..2f95287 100644 --- a/src/model/trip.rs +++ b/src/model/trip.rs @@ -80,6 +80,7 @@ impl Trip { ), "Neue Ausfahrt zur selben Zeit", None, + None, ) .await; } @@ -209,10 +210,9 @@ WHERE day=? let rowers = TripWithUserAndType::from(db, trip.clone()).await.rower; for user in rowers { if let Some(user) = User::find_by_name(db, &user.name).await { - let notes = if let Some(notes) = notes { - format!(": {notes}") - } else { - String::from(".") + let notes = match notes { + Some(n) if !n.is_empty() => n, + _ => ".", }; Notification::create( @@ -224,6 +224,10 @@ WHERE day=? ), "Absage Ausfahrt", None, + Some(&format!( + "remove_user_trip_with_trip_details_id:{}", + trip_details_id + )), ) .await; } diff --git a/src/model/tripdetails.rs b/src/model/tripdetails.rs index 109eefe..d505dcb 100644 --- a/src/model/tripdetails.rs +++ b/src/model/tripdetails.rs @@ -120,7 +120,7 @@ WHERE day = ? AND planned_starting_time = ? continue; } - Notification::create(db, &user, &format!("Du hast dich als Ruderer bei der Ausfahrt von {} am {} um {} angemeldet. Bei allen Ausfahrten zu dieser Zeit sind nun alle Plätze ausgebucht. Damit noch mehr (Nicht-Steuerleute) mitfahren können, wäre es super, wenn du eine eigene Ausfahrt zur selben Zeit ausschreiben könntest.", cox.name, self.day, self.planned_starting_time), "Volle Ausfahrt", None).await; + Notification::create(db, &user, &format!("Du hast dich als Ruderer bei der Ausfahrt von {} am {} um {} angemeldet. Bei allen Ausfahrten zu dieser Zeit sind nun alle Plätze ausgebucht. Damit noch mehr (Nicht-Steuerleute) mitfahren können, wäre es super, wenn du eine eigene Ausfahrt zur selben Zeit ausschreiben könntest.", cox.name, self.day, self.planned_starting_time), "Volle Ausfahrt", None, None).await; } } } diff --git a/src/model/user.rs b/src/model/user.rs index d495cc2..a0a1c1b 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -202,7 +202,7 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100), self.name ), "Neues Scheckbuch", - None, + None,None ) .await; } @@ -246,6 +246,7 @@ ASKÖ Ruderverein Donau Linz", self.name), ), "Neues Vereinsmitglied", None, + None, ) .await; } diff --git a/src/model/usertrip.rs b/src/model/usertrip.rs index c37c834..440a6b4 100644 --- a/src/model/usertrip.rs +++ b/src/model/usertrip.rs @@ -76,6 +76,7 @@ impl UserTrip { ), "Registrierung bei deiner Ausfahrt", None, + None, ) .await; diff --git a/src/tera/admin/notification.rs b/src/tera/admin/notification.rs index a777a32..0eeb56c 100644 --- a/src/tera/admin/notification.rs +++ b/src/tera/admin/notification.rs @@ -76,7 +76,7 @@ async fn send_group( }; for user in User::all_with_role(db, &role).await { - Notification::create(db, &user, &d.message, &d.category, None).await; + Notification::create(db, &user, &d.message, &d.category, None, None).await; } Log::create(db, "Notification successfully sent".into()).await; Flash::success(