Ensure interval updating is performed properly

Validate patreon status. Validate interval length against minimum. Update the reminder pane to reflect changes that were made. Properly deserialize.
This commit is contained in:
jude 2022-12-11 10:09:26 +00:00
parent e9bcb1973f
commit 67a4db2e9a
3 changed files with 97 additions and 4 deletions

View File

@ -16,10 +16,12 @@ use serenity::{
use sqlx::{MySql, Pool};
use crate::{
check_guild_subscription, check_subscription,
consts::{
MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH,
MAX_EMBED_FIELDS, MAX_EMBED_FIELD_TITLE_LENGTH, MAX_EMBED_FIELD_VALUE_LENGTH,
MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_LENGTH, MAX_URL_LENGTH, MAX_USERNAME_LENGTH,
MIN_INTERVAL,
},
routes::dashboard::{
create_database_channel, create_reminder, template_name_default, DeleteReminder,
@ -375,9 +377,15 @@ pub async fn edit_reminder(
reminder: Json<PatchReminder>,
serenity_context: &State<Context>,
pool: &State<Pool<MySql>>,
cookies: &CookieJar<'_>,
) -> JsonResult {
check_authorization!(cookies, serenity_context.inner(), id);
let mut error = vec![];
let user_id =
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
update_field!(pool.inner(), error, reminder.[
attachment,
attachment_name,
@ -395,9 +403,6 @@ pub async fn edit_reminder(
embed_fields,
enabled,
expires,
interval_seconds,
interval_days,
interval_months,
name,
restartable,
tts,
@ -405,6 +410,69 @@ pub async fn edit_reminder(
utc_time
]);
if reminder.interval_days.is_some()
|| reminder.interval_months.is_some()
|| reminder.interval_seconds.is_some()
{
if check_guild_subscription(&serenity_context.inner(), id).await
|| check_subscription(&serenity_context.inner(), user_id).await
{
let new_interval_length = match reminder.interval_days {
Some(interval) => interval.unwrap_or(0),
None => sqlx::query!(
"SELECT interval_days AS days FROM reminders WHERE uid = ?",
reminder.uid
)
.fetch_one(pool.inner())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
})?
.days
.unwrap_or(0),
} + match reminder.interval_months {
Some(interval) => interval.unwrap_or(0),
None => sqlx::query!(
"SELECT interval_months AS months FROM reminders WHERE uid = ?",
reminder.uid
)
.fetch_one(pool.inner())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
})?
.months
.unwrap_or(0),
} + match reminder.interval_seconds {
Some(interval) => interval.unwrap_or(0),
None => sqlx::query!(
"SELECT interval_seconds AS seconds FROM reminders WHERE uid = ?",
reminder.uid
)
.fetch_one(pool.inner())
.await
.map_err(|e| {
warn!("Error updating reminder interval: {:?}", e);
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
})?
.seconds
.unwrap_or(0),
};
if new_interval_length < *MIN_INTERVAL {
error.push(String::from("New interval is too short."));
} else {
update_field!(pool.inner(), error, reminder.[
interval_days,
interval_months,
interval_seconds
]);
}
}
}
if reminder.channel > 0 {
let channel = ChannelId(reminder.channel).to_channel_cached(&serenity_context.inner());
match channel {

View File

@ -8,7 +8,7 @@ use rocket::{
serde::json::{json, Value as JsonValue},
};
use rocket_dyn_templates::Template;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serenity::{
client::Context,
http::Http,
@ -50,6 +50,14 @@ fn id_default() -> u32 {
0
}
fn deserialize_optional_field<'de, T, D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Ok(Some(Option::deserialize(deserializer)?))
}
#[derive(Serialize, Deserialize)]
pub struct ReminderTemplate {
#[serde(default = "id_default")]
@ -179,10 +187,13 @@ pub struct ReminderCsv {
pub struct PatchReminder {
uid: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
attachment: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
attachment_name: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
avatar: Unset<Option<String>>,
#[serde(default = "channel_default")]
#[serde(with = "string")]
@ -192,6 +203,7 @@ pub struct PatchReminder {
#[serde(default)]
embed_author: Unset<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
embed_author_url: Unset<Option<String>>,
#[serde(default)]
embed_color: Unset<u32>,
@ -200,10 +212,13 @@ pub struct PatchReminder {
#[serde(default)]
embed_footer: Unset<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
embed_footer_url: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
embed_image_url: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
embed_thumbnail_url: Unset<Option<String>>,
#[serde(default)]
embed_title: Unset<String>,
@ -212,12 +227,16 @@ pub struct PatchReminder {
#[serde(default)]
enabled: Unset<bool>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
expires: Unset<Option<NaiveDateTime>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
interval_seconds: Unset<Option<u32>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
interval_days: Unset<Option<u32>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
interval_months: Unset<Option<u32>>,
#[serde(default)]
name: Unset<String>,
@ -226,6 +245,7 @@ pub struct PatchReminder {
#[serde(default)]
tts: Unset<bool>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
username: Unset<Option<String>>,
#[serde(default)]
utc_time: Unset<NaiveDateTime>,

View File

@ -332,6 +332,9 @@ function deserialize_reminder(reminder, frame, mode) {
// populate channels
set_channels(frame.querySelector("select.channel-selector"));
frame.querySelector(`*[name="interval_hours"]`).value = 0;
frame.querySelector(`*[name="interval_minutes"]`).value = 0;
// populate majority of items
for (let prop in reminder) {
if (reminder.hasOwnProperty(prop) && reminder[prop] !== null) {
@ -498,6 +501,8 @@ document.addEventListener("remindersLoaded", (event) => {
.then((response) => response.json())
.then((data) => {
for (let error of data.errors) show_error(error);
deserialize_reminder(data.reminder, node, "reload");
});
$saveBtn.querySelector("span.icon > i").classList = ["fas fa-check"];