diff --git a/Cargo.lock b/Cargo.lock index cbbd6bc..cde8216 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2142,6 +2142,7 @@ dependencies = [ name = "reminder_web" version = "0.1.0" dependencies = [ + "base64", "chrono", "chrono-tz 0.5.3", "lazy_static", diff --git a/web/Cargo.toml b/web/Cargo.toml index 1c34311..89fccfa 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -17,3 +17,4 @@ chrono = "0.4" chrono-tz = "0.5" lazy_static = "1.4.0" rand = "0.7" +base64 = "0.13" diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index fb5c712..7047015 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -1,3 +1,4 @@ +use base64; use chrono::Utc; use rocket::{ http::CookieJar, @@ -7,7 +8,10 @@ use rocket::{ use serde::Serialize; use serenity::{ client::Context, - model::id::{ChannelId, GuildId}, + model::{ + channel::GuildChannel, + id::{ChannelId, GuildId}, + }, }; use sqlx::{MySql, Pool}; @@ -19,8 +23,8 @@ use crate::{ MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL, }, routes::dashboard::{ - create_database_channel, generate_uid, name_default, DeleteReminder, PatchReminder, - Reminder, + create_database_channel, generate_uid, name_default, DeleteReminder, JsonReminder, + PatchReminder, Reminder, }, }; @@ -45,12 +49,16 @@ pub async fn get_guild_channels( Some(guild) => { let mut channel_info = vec![]; - for (channel_id, channel) in guild + let mut channels = guild .channels .iter() - .filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id, c))) + .filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id.to_owned(), c))) .filter(|(_, channel)| channel.is_text_based()) - { + .collect::>(); + + channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position)); + + for (channel_id, channel) in channels { let mut ch = ChannelInfo { name: channel.name.to_string(), id: channel_id.to_string(), @@ -125,7 +133,7 @@ pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State/reminders", data = "")] pub async fn create_reminder( id: u64, - reminder: Json, + reminder: Json, cookies: &CookieJar<'_>, serenity_context: &State, pool: &State>, @@ -213,12 +221,16 @@ pub async fn create_reminder( } } + // base64 decode error dropped here + let attachment_data = reminder.attachment.as_ref().map(|s| base64::decode(s).ok()).flatten(); let name = if reminder.name.is_empty() { name_default() } else { reminder.name.clone() }; // write to db match sqlx::query!( "INSERT INTO reminders ( uid, + attachment, + attachment_name, channel_id, avatar, content, @@ -241,8 +253,10 @@ pub async fn create_reminder( tts, username, `utc_time` - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", generate_uid(), + attachment_data, + reminder.attachment_name, channel, reminder.avatar, reminder.content, diff --git a/web/src/routes/dashboard/mod.rs b/web/src/routes/dashboard/mod.rs index 5577102..998806a 100644 --- a/web/src/routes/dashboard/mod.rs +++ b/web/src/routes/dashboard/mod.rs @@ -26,6 +26,45 @@ fn channel_default() -> u64 { 0 } +#[derive(Serialize, Deserialize)] +pub struct EmbedField { + title: String, + value: String, +} + +#[derive(Serialize, Deserialize)] +pub struct JsonReminder { + attachment: Option, + attachment_name: Option, + avatar: Option, + #[serde(with = "string")] + channel: u64, + content: String, + embed_author: String, + embed_author_url: Option, + embed_color: u32, + embed_description: String, + embed_footer: String, + embed_footer_url: Option, + embed_image_url: Option, + embed_thumbnail_url: Option, + embed_title: String, + embed_fields: Vec, + enabled: bool, + expires: Option, + interval_seconds: Option, + interval_months: Option, + #[serde(default = "name_default")] + name: String, + pin: bool, + restartable: bool, + tts: bool, + #[serde(default)] + uid: String, + username: Option, + utc_time: NaiveDateTime, +} + #[derive(Serialize, Deserialize)] pub struct Reminder { attachment: Option>, @@ -62,7 +101,7 @@ pub struct Reminder { pub struct PatchReminder { uid: String, #[serde(default)] - attachment: Unset>>, + attachment: Unset>, #[serde(default)] attachment_name: Unset>, #[serde(default)] @@ -91,6 +130,8 @@ pub struct PatchReminder { #[serde(default)] embed_title: Unset, #[serde(default)] + embed_fields: Unset, + #[serde(default)] enabled: Unset, #[serde(default)] expires: Unset>, diff --git a/web/static/js/main.js b/web/static/js/main.js index 8ed6166..f2dc7eb 100644 --- a/web/static/js/main.js +++ b/web/static/js/main.js @@ -262,6 +262,13 @@ document.addEventListener("remindersLoaded", (event) => { node.querySelector('input[name="time"]').value ).setZone("UTC"); + let fields = node.querySelectorAll(".embed-field-box", (el) => { + return { + title: el.querySelector('input[name="embed_field_title[]"]').value, + value: el.querySelector('input[name="embed_field_value[]"]').value, + }; + }); + let reminder = { uid: node.closest(".reminderContent").dataset["uid"], avatar: has_source(node.querySelector("img.discord-avatar").src), @@ -286,6 +293,7 @@ document.addEventListener("remindersLoaded", (event) => { node.querySelector("img.embed_thumbnail_url").src ), embed_title: node.querySelector('textarea[name="embed_title"]').value, + embed_fields: fields, expires: null, interval_seconds: seconds, interval_months: months, @@ -493,77 +501,99 @@ function has_source(string) { let $createReminder = document.querySelector("#reminderCreator"); -$createReminder.querySelector("button#createReminder").addEventListener("click", () => { - // create reminder object - let seconds = - parseInt($createReminder.querySelector('input[name="interval_seconds"]').value) || - null; - let months = - parseInt($createReminder.querySelector('input[name="interval_months"]').value) || - null; +$createReminder + .querySelector("button#createReminder") + .addEventListener("click", async () => { + // create reminder object + let seconds = + parseInt( + $createReminder.querySelector('input[name="interval_seconds"]').value + ) || null; + let months = + parseInt( + $createReminder.querySelector('input[name="interval_months"]').value + ) || null; - let rgb_color = window.getComputedStyle( - $createReminder.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 rgb_color = window.getComputedStyle( + $createReminder.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( - $createReminder.querySelector('input[name="time"]').value - ).setZone("UTC"); + let utc_time = luxon.DateTime.fromISO( + $createReminder.querySelector('input[name="time"]').value + ).setZone("UTC"); - let reminder = { - avatar: has_source($createReminder.querySelector("img.discord-avatar").src), - channel: $createReminder.querySelector("select.channel-selector").value, - content: $createReminder.querySelector("textarea#messageContent").value, - embed_author_url: has_source( - $createReminder.querySelector("img.embed_author_url").src - ), - embed_author: $createReminder.querySelector("textarea#embedAuthor").value, - embed_color: color, - embed_description: $createReminder.querySelector("textarea#embedDescription") - .value, - embed_footer: $createReminder.querySelector("textarea#embedFooter").value, - embed_footer_url: has_source( - $createReminder.querySelector("img.embed_footer_url").src - ), - embed_image_url: has_source( - $createReminder.querySelector("img.embed_image_url").src - ), - embed_thumbnail_url: has_source( - $createReminder.querySelector("img.embed_thumbnail_url").src - ), - embed_title: $createReminder.querySelector("textarea#embedTitle").value, - enabled: true, - expires: null, - interval_seconds: seconds, - interval_months: months, - name: $createReminder.querySelector('input[name="name"]').value, - pin: $createReminder.querySelector('input[name="pin"]').checked, - restartable: false, - tts: $createReminder.querySelector('input[name="tts"]').checked, - username: $createReminder.querySelector("input#reminderUsername").value, - utc_time: utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"), - }; + let attachment = null; + let attachment_name = null; - // send to server - let guild = document.querySelector(".guildList a.is-active").dataset["guild"]; + if ($createReminder.querySelector('input[name="attachment"]').files.length > 0) { + let file = $createReminder.querySelector('input[name="attachment"]').files[0]; - fetch(`/dashboard/api/guild/${guild}/reminders`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(reminder), - }) - .then((response) => response.json()) - .then((data) => console.log(data)); + attachment = await new Promise((resolve) => { + let fileReader = new FileReader(); + fileReader.onload = (e) => resolve(fileReader.result); + fileReader.readAsDataURL(file); + }); + attachment = attachment.split(",")[1]; + attachment_name = file.name; + } - // process response - fetch_reminders(guild); + let reminder = { + attachment: attachment, + attachment_name: attachment_name, + avatar: has_source($createReminder.querySelector("img.discord-avatar").src), + channel: $createReminder.querySelector("select.channel-selector").value, + content: $createReminder.querySelector("textarea#messageContent").value, + embed_author_url: has_source( + $createReminder.querySelector("img.embed_author_url").src + ), + embed_author: $createReminder.querySelector("textarea#embedAuthor").value, + embed_color: color, + embed_description: $createReminder.querySelector("textarea#embedDescription") + .value, + embed_footer: $createReminder.querySelector("textarea#embedFooter").value, + embed_footer_url: has_source( + $createReminder.querySelector("img.embed_footer_url").src + ), + embed_image_url: has_source( + $createReminder.querySelector("img.embed_image_url").src + ), + embed_thumbnail_url: has_source( + $createReminder.querySelector("img.embed_thumbnail_url").src + ), + embed_title: $createReminder.querySelector("textarea#embedTitle").value, + embed_fields: [], + enabled: true, + expires: null, + interval_seconds: seconds, + interval_months: months, + name: $createReminder.querySelector('input[name="name"]').value, + pin: $createReminder.querySelector('input[name="pin"]').checked, + restartable: false, + tts: $createReminder.querySelector('input[name="tts"]').checked, + username: $createReminder.querySelector("input#reminderUsername").value, + utc_time: utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"), + }; - // reset inputs -}); + // send to server + let guild = document.querySelector(".guildList a.is-active").dataset["guild"]; + + fetch(`/dashboard/api/guild/${guild}/reminders`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(reminder), + }) + .then((response) => response.json()) + .then((data) => console.log(data)); + + // process response + fetch_reminders(guild); + + // reset inputs + }); document.querySelectorAll("textarea.autoresize").forEach((element) => { element.addEventListener("input", () => { diff --git a/web/templates/reminder_dashboard/guild_reminder.html.tera b/web/templates/reminder_dashboard/guild_reminder.html.tera index afd7a39..acd7b5b 100644 --- a/web/templates/reminder_dashboard/guild_reminder.html.tera +++ b/web/templates/reminder_dashboard/guild_reminder.html.tera @@ -178,7 +178,7 @@