diff --git a/web/src/lib.rs b/web/src/lib.rs index ff93af0..274a796 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -122,7 +122,12 @@ pub async fn initialize( warn!("Exiting rocket runtime"); // distribute kill signal - kill_channel.send(()); + match kill_channel.send(()) { + Ok(_) => {} + Err(e) => { + error!("Failed to issue kill signal: {:?}", e); + } + } Ok(()) } diff --git a/web/src/macros.rs b/web/src/macros.rs index e14d3a4..8b33616 100644 --- a/web/src/macros.rs +++ b/web/src/macros.rs @@ -79,3 +79,41 @@ macro_rules! check_authorization { } } } + +macro_rules! update_field { + ($pool:expr, $error:ident, $reminder:ident.[$field:ident]) => { + if let Some(value) = &$reminder.$field { + match sqlx::query(concat!( + "UPDATE reminders SET `", + stringify!($field), + "` = ? WHERE uid = ?" + )) + .bind(value) + .bind(&$reminder.uid) + .execute($pool) + .await + { + Ok(_) => {} + Err(e) => { + warn!( + concat!( + "Error in `update_field!(", + stringify!($pool), + stringify!($reminder), + stringify!($field), + ")': {:?}" + ), + e + ); + + $error.push(format!("Error setting field {}", stringify!($field))); + } + } + } + }; + + ($pool:expr, $error:ident, $reminder:ident.[$field:ident, $($fields:ident),+]) => { + update_field!($pool, $error, $reminder.[$field]); + update_field!($pool, $error, $reminder.[$($fields),+]); + }; +} diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index b2c5243..a610172 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -294,42 +294,35 @@ pub async fn get_reminders(id: u64, ctx: &State, pool: &State, pool: &State>, ) -> JsonValue { - if let Some(enabled) = reminder.enabled { - sqlx::query!("UPDATE reminders SET enabled = ? WHERE uid = ?", enabled, reminder.uid) - .execute(pool.inner()) - .await; + let mut error = vec![]; + + update_field!(pool.inner(), error, reminder.[ + attachment, + attachment_name, + avatar, + content, + embed_author, + embed_author_url, + embed_color, + embed_description, + embed_footer, + embed_footer_url, + embed_image_url, + embed_thumbnail_url, + embed_title, + enabled, + expires, + interval_seconds, + interval_months, + name, + pin, + restartable, + tts, + username, + utc_time + ]); + + if reminder.channel > 0 { + let channel = ChannelId(reminder.channel).to_channel_cached(&serenity_context.inner()); + match channel { + Some(channel) => { + let channel_matches_guild = channel.guild().map_or(false, |c| c.guild_id.0 == id); + + if !channel_matches_guild { + warn!( + "Error in `edit_reminder`: channel {:?} not found for guild {}", + reminder.channel, id + ); + + return json!({"error": "Channel not found"}); + } + + let channel = create_database_channel( + serenity_context.inner(), + ChannelId(reminder.channel), + pool.inner(), + ) + .await; + + if let Err(e) = channel { + warn!("`create_database_channel` returned an error code: {:?}", e); + + return json!({"error": "Failed to configure channel for reminders. Please check the bot permissions"}); + } + + let channel = channel.unwrap(); + + match sqlx::query!( + "UPDATE reminders SET channel_id = ? WHERE uid = ?", + channel, + reminder.uid + ) + .execute(pool.inner()) + .await + { + Ok(_) => {} + Err(e) => { + warn!("Error setting channel: {:?}", e); + + error.push("Couldn't set channel".to_string()) + } + } + } + + None => { + warn!( + "Error in `edit_reminder`: channel {:?} not found for guild {}", + reminder.channel, id + ); + + return json!({"error": "Channel not found"}); + } + } } match sqlx::query_as_unchecked!( @@ -397,12 +470,12 @@ pub async fn edit_reminder( .fetch_one(pool.inner()) .await { - Ok(reminder) => json!(reminder), + Ok(reminder) => json!({"reminder": reminder, "errors": error}), Err(e) => { warn!("Error exiting `edit_reminder': {:?}", e); - json!({"error": "Unknown error"}) + json!({"reminder": Option::::None, "errors": vec!["Unknown error"]}) } } } diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index 2e130a5..5577102 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -22,6 +22,10 @@ fn name_default() -> String { "Reminder".to_string() } +fn channel_default() -> u64 { + 0 +} + #[derive(Serialize, Deserialize)] pub struct Reminder { attachment: Option>, @@ -63,6 +67,9 @@ pub struct PatchReminder { attachment_name: Unset>, #[serde(default)] avatar: Unset>, + #[serde(default = "channel_default")] + #[serde(with = "string")] + channel: u64, #[serde(default)] content: Unset, #[serde(default)] diff --git a/web/static/img/logo_flat.webp b/web/static/img/logo_flat.webp new file mode 100644 index 0000000..35f4a26 Binary files /dev/null and b/web/static/img/logo_flat.webp differ diff --git a/web/static/js/main.js b/web/static/js/main.js index 80e31f7..2bb5778 100644 --- a/web/static/js/main.js +++ b/web/static/js/main.js @@ -12,6 +12,10 @@ function colorToInt(r, g, b) { return (r << 16) + (g << 8) + b; } +function intToColor(i) { + return `#${i.toString(16)}`; +} + function resize_textareas() { document.querySelectorAll("textarea.autoresize").forEach((element) => { element.style.height = ""; @@ -134,19 +138,23 @@ async function fetch_reminders(guild_id) { document.dispatchEvent(remindersLoadedEvent); } }); - - register_interval_hide(); } document.addEventListener("remindersLoaded", (event) => { const guild = document.querySelector(".guildList a.is-active").dataset["guild"]; for (let reminder of event.detail) { - reminder.node.querySelector("button.hide-box").addEventListener("click", () => { - reminder.node.closest(".reminderContent").classList.toggle("is-collapsed"); + let node = reminder.node; + + node.querySelector("button.hide-box").addEventListener("click", () => { + node.closest(".reminderContent").classList.toggle("is-collapsed"); }); - const enableBtn = reminder.node.querySelector(".disable-enable"); + node.querySelector("div.discord-embed").style.borderLeftColor = intToColor( + reminder.embed_color + ); + + const enableBtn = node.querySelector(".disable-enable"); enableBtn.addEventListener("click", () => { let enable = enableBtn.dataset.action === "enable"; @@ -167,14 +175,68 @@ document.addEventListener("remindersLoaded", (event) => { }); }); - reminder.node - .querySelector("button.delete-reminder") - .addEventListener("click", () => { - let uid = reminder.node.closest(".reminderContent").dataset.uid; + node.querySelector("button.delete-reminder").addEventListener("click", () => { + $deleteReminderBtn.dataset["uid"] = reminder["uid"]; + $deleteReminderBtn.closest(".modal").classList.toggle("is-active"); + }); - $deleteReminderBtn.dataset["uid"] = uid; - $deleteReminderBtn.closest(".modal").classList.toggle("is-active"); - }); + node.querySelector("button.save-btn").addEventListener("click", (event) => { + let seconds = + parseInt(node.querySelector('input[name="interval_seconds"]').value) || + null; + let months = + parseInt(node.querySelector('input[name="interval_months"]').value) || + null; + + let rgb_color = window.getComputedStyle( + node.querySelector("div.discord-embed") + ).borderLeftColor; + let rgb = rgb_color.match(/\d+/g); + let color = colorToInt(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2])); + + let utc_time = luxon.DateTime.fromISO( + node.querySelector('input[name="time"]').value + ).setZone("UTC"); + + let reminder = { + uid: node.closest(".reminderContent").dataset["uid"], + avatar: node.querySelector("img.discord-avatar").src, + channel: node.querySelector("select.channel-selector").value, + content: node.querySelector('textarea[name="content"]').value, + embed_author_url: node.querySelector("img.embed_author_url").src, + embed_author: node.querySelector('textarea[name="embed_author"]').value, + embed_color: color, + embed_description: node.querySelector( + 'textarea[name="embed_description"]' + ).value, + embed_footer: node.querySelector('textarea[name="embed_footer"]').value, + embed_footer_url: node.querySelector("img.embed_footer_url").src, + embed_image_url: node.querySelector("img.embed_image_url").src, + embed_thumbnail_url: node.querySelector("img.embed_thumbnail_url").src, + embed_title: node.querySelector('textarea[name="embed_title"]').value, + expires: null, + interval_seconds: seconds, + interval_months: months, + name: node.querySelector('input[name="name"]').value, + pin: node.querySelector('input[name="pin"]').checked, + tts: node.querySelector('input[name="tts"]').checked, + username: node.querySelector('input[name="username"]').value, + utc_time: utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"), + }; + + // send to server + let guild = document.querySelector(".guildList a.is-active").dataset["guild"]; + + fetch(`/dashboard/api/guild/${guild}/reminders`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(reminder), + }) + .then((response) => response.json()) + .then((data) => console.log(data)); + }); } }); @@ -214,19 +276,6 @@ colorPicker.on("color:change", function (color) { $colorPickerInput.value = color.hexString; }); -document.querySelectorAll(".change-color").forEach((element) => { - element.addEventListener("click", (e) => { - e.preventDefault(); - - $discordFrame = element - .closest("div.reminderContent") - .querySelector("div.discord-embed"); - $colorPickerModal.classList.toggle("is-active"); - colorPicker.color.rgbString = - window.getComputedStyle($discordFrame).borderLeftColor; - }); -}); - $colorPickerModal.querySelector("button.is-success").addEventListener("click", () => { $discordFrame.style.borderLeftColor = colorPicker.color.rgbString; @@ -464,7 +513,7 @@ let $img; const $urlModal = document.querySelector("div#addImageModal"); const $urlInput = $urlModal.querySelector("input"); -$urlModal.querySelector("button.is-success").addEventListener("click", () => { +$urlModal.querySelector("button#setImgUrl").addEventListener("click", () => { $img.src = $urlInput.value; $urlInput.value = ""; @@ -481,26 +530,29 @@ document.querySelectorAll("button.close-modal").forEach((element) => { }); }); -document.querySelectorAll(".customizable").forEach((element) => { - element.querySelector("a").addEventListener("click", (e) => { - e.preventDefault(); +document.addEventListener("remindersLoaded", () => { + document.querySelectorAll(".customizable").forEach((element) => { + element.querySelector("a").addEventListener("click", (e) => { + e.preventDefault(); - $img = element.querySelector("img"); + $img = element.querySelector("img"); - $urlModal.classList.toggle("is-active"); + $urlModal.classList.toggle("is-active"); + }); }); -}); -document.querySelectorAll("a.icon-toggle").forEach((element) => { - element.addEventListener("click", (e) => { - e.preventDefault(); + const fileInput = document.querySelectorAll("input[type=file]"); - element.classList.toggle("is-active"); + fileInput.forEach((element) => { + element.addEventListener("change", () => { + if (element.files.length > 0) { + const fileName = element.parentElement.querySelector(".file-label"); + fileName.textContent = element.files[0].name; + } + }); }); -}); -function register_interval_hide() { - let $showInterval = document.querySelectorAll("a.intervalLabel"); + const $showInterval = document.querySelectorAll("a.intervalLabel"); $showInterval.forEach((element) => { element.addEventListener("click", () => { @@ -509,16 +561,18 @@ function register_interval_hide() { element.nextElementSibling.classList.toggle("is-hidden"); }); }); -} -const fileInput = document.querySelectorAll("input[type=file]"); + document.querySelectorAll(".change-color").forEach((element) => { + element.addEventListener("click", (e) => { + e.preventDefault(); -fileInput.forEach((element) => { - element.addEventListener("change", () => { - if (element.files.length > 0) { - const fileName = element.parentElement.querySelector(".file-label"); - fileName.textContent = element.files[0].name; - } + $discordFrame = element + .closest("div.reminderContent") + .querySelector("div.discord-embed"); + $colorPickerModal.classList.toggle("is-active"); + colorPicker.color.rgbString = + window.getComputedStyle($discordFrame).borderLeftColor; + }); }); }); diff --git a/web/templates/base.html.tera b/web/templates/base.html.tera index 788ad7f..ecad959 100644 --- a/web/templates/base.html.tera +++ b/web/templates/base.html.tera @@ -36,7 +36,7 @@