fields are now json and work. fix for intervals. moved some code together
This commit is contained in:
parent
d946ef1dca
commit
85d27c5bba
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1943,6 +1943,8 @@ dependencies = [
|
||||
"log",
|
||||
"num-integer",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serenity",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
@ -2153,6 +2155,7 @@ dependencies = [
|
||||
"rocket",
|
||||
"rocket_dyn_templates",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serenity",
|
||||
"sqlx",
|
||||
]
|
||||
@ -2761,6 +2764,8 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"rsa",
|
||||
"rustls 0.19.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha-1 0.9.8",
|
||||
"sha2 0.9.9",
|
||||
"smallvec",
|
||||
@ -2786,6 +2791,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
"sqlx-core",
|
||||
"sqlx-rt",
|
||||
|
@ -29,3 +29,5 @@ CREATE TABLE reminder_template (
|
||||
|
||||
FOREIGN KEY (`guild_id`) REFERENCES channels (`id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE reminders ADD COLUMN embed_fields JSON;
|
||||
|
@ -12,7 +12,9 @@ chrono = "0.4"
|
||||
chrono-tz = { version = "0.5", features = ["serde"] }
|
||||
lazy_static = "1.4"
|
||||
num-integer = "0.1"
|
||||
sqlx = { version = "0.5.10", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]}
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
sqlx = { version = "0.5.10", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "json"]}
|
||||
|
||||
[dependencies.serenity]
|
||||
git = "https://github.com/serenity-rs/serenity"
|
||||
|
@ -4,6 +4,7 @@ use lazy_static::lazy_static;
|
||||
use log::{error, info, warn};
|
||||
use num_integer::Integer;
|
||||
use regex::{Captures, Regex};
|
||||
use serde::Deserialize;
|
||||
use serenity::{
|
||||
builder::CreateEmbed,
|
||||
http::{CacheHttp, Http, StatusCode},
|
||||
@ -15,7 +16,10 @@ use serenity::{
|
||||
Error, Result,
|
||||
};
|
||||
use sqlx::{
|
||||
types::chrono::{NaiveDateTime, Utc},
|
||||
types::{
|
||||
chrono::{NaiveDateTime, Utc},
|
||||
Json,
|
||||
},
|
||||
Executor,
|
||||
};
|
||||
|
||||
@ -94,11 +98,6 @@ pub fn substitute(string: &str) -> String {
|
||||
}
|
||||
|
||||
struct Embed {
|
||||
inner: EmbedInner,
|
||||
fields: Vec<EmbedField>,
|
||||
}
|
||||
|
||||
struct EmbedInner {
|
||||
title: String,
|
||||
description: String,
|
||||
image_url: Option<String>,
|
||||
@ -108,8 +107,10 @@ struct EmbedInner {
|
||||
author: String,
|
||||
author_url: Option<String>,
|
||||
color: u32,
|
||||
fields: Json<Vec<EmbedField>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EmbedField {
|
||||
title: String,
|
||||
value: String,
|
||||
@ -121,9 +122,9 @@ impl Embed {
|
||||
pool: impl Executor<'_, Database = Database> + Copy,
|
||||
id: u32,
|
||||
) -> Option<Self> {
|
||||
let mut inner = sqlx::query_as_unchecked!(
|
||||
EmbedInner,
|
||||
"
|
||||
let mut embed = sqlx::query_as!(
|
||||
Self,
|
||||
r#"
|
||||
SELECT
|
||||
`embed_title` AS title,
|
||||
`embed_description` AS description,
|
||||
@ -133,64 +134,42 @@ SELECT
|
||||
`embed_footer_url` AS footer_url,
|
||||
`embed_author` AS author,
|
||||
`embed_author_url` AS author_url,
|
||||
`embed_color` AS color
|
||||
FROM
|
||||
reminders
|
||||
WHERE
|
||||
`id` = ?
|
||||
",
|
||||
`embed_color` AS color,
|
||||
IFNULL(`embed_fields`, '[]') AS "fields:_"
|
||||
FROM reminders
|
||||
WHERE `id` = ?"#,
|
||||
id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
inner.title = substitute(&inner.title);
|
||||
inner.description = substitute(&inner.description);
|
||||
inner.footer = substitute(&inner.footer);
|
||||
embed.title = substitute(&embed.title);
|
||||
embed.description = substitute(&embed.description);
|
||||
embed.footer = substitute(&embed.footer);
|
||||
|
||||
let mut fields = sqlx::query_as_unchecked!(
|
||||
EmbedField,
|
||||
"
|
||||
SELECT
|
||||
title,
|
||||
value,
|
||||
inline
|
||||
FROM
|
||||
embed_fields
|
||||
WHERE
|
||||
reminder_id = ?
|
||||
",
|
||||
id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
fields.iter_mut().for_each(|mut field| {
|
||||
embed.fields.iter_mut().for_each(|mut field| {
|
||||
field.title = substitute(&field.title);
|
||||
field.value = substitute(&field.value);
|
||||
});
|
||||
|
||||
let e = Embed { inner, fields };
|
||||
|
||||
if e.has_content() {
|
||||
Some(e)
|
||||
if embed.has_content() {
|
||||
Some(embed)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_content(&self) -> bool {
|
||||
if self.inner.title.is_empty()
|
||||
&& self.inner.description.is_empty()
|
||||
&& self.inner.image_url.is_none()
|
||||
&& self.inner.thumbnail_url.is_none()
|
||||
&& self.inner.footer.is_empty()
|
||||
&& self.inner.footer_url.is_none()
|
||||
&& self.inner.author.is_empty()
|
||||
&& self.inner.author_url.is_none()
|
||||
&& self.fields.is_empty()
|
||||
if self.title.is_empty()
|
||||
&& self.description.is_empty()
|
||||
&& self.image_url.is_none()
|
||||
&& self.thumbnail_url.is_none()
|
||||
&& self.footer.is_empty()
|
||||
&& self.footer_url.is_none()
|
||||
&& self.author.is_empty()
|
||||
&& self.author_url.is_none()
|
||||
&& self.fields.0.is_empty()
|
||||
{
|
||||
false
|
||||
} else {
|
||||
@ -203,37 +182,37 @@ impl Into<CreateEmbed> for Embed {
|
||||
fn into(self) -> CreateEmbed {
|
||||
let mut c = CreateEmbed::default();
|
||||
|
||||
c.title(&self.inner.title)
|
||||
.description(&self.inner.description)
|
||||
.color(self.inner.color)
|
||||
c.title(&self.title)
|
||||
.description(&self.description)
|
||||
.color(self.color)
|
||||
.author(|a| {
|
||||
a.name(&self.inner.author);
|
||||
a.name(&self.author);
|
||||
|
||||
if let Some(author_icon) = &self.inner.author_url {
|
||||
if let Some(author_icon) = &self.author_url {
|
||||
a.icon_url(author_icon);
|
||||
}
|
||||
|
||||
a
|
||||
})
|
||||
.footer(|f| {
|
||||
f.text(&self.inner.footer);
|
||||
f.text(&self.footer);
|
||||
|
||||
if let Some(footer_icon) = &self.inner.footer_url {
|
||||
if let Some(footer_icon) = &self.footer_url {
|
||||
f.icon_url(footer_icon);
|
||||
}
|
||||
|
||||
f
|
||||
});
|
||||
|
||||
for field in &self.fields {
|
||||
for field in &self.fields.0 {
|
||||
c.field(&field.title, &field.value, field.inline);
|
||||
}
|
||||
|
||||
if let Some(image_url) = &self.inner.image_url {
|
||||
if let Some(image_url) = &self.image_url {
|
||||
c.image(image_url);
|
||||
}
|
||||
|
||||
if let Some(thumbnail_url) = &self.inner.thumbnail_url {
|
||||
if let Some(thumbnail_url) = &self.thumbnail_url {
|
||||
c.thumbnail(thumbnail_url);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,8 @@ oauth2 = "4"
|
||||
log = "0.4"
|
||||
reqwest = "0.11"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono"] }
|
||||
serde_json = "1.0"
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
||||
chrono = "0.4"
|
||||
chrono-tz = "0.5"
|
||||
lazy_static = "1.4.0"
|
||||
|
@ -10,9 +10,9 @@ pub const MAX_EMBED_AUTHOR_LENGTH: usize = 256;
|
||||
pub const MAX_EMBED_FOOTER_LENGTH: usize = 2048;
|
||||
pub const MAX_URL_LENGTH: usize = 512;
|
||||
pub const MAX_USERNAME_LENGTH: usize = 100;
|
||||
pub const MAX_EMBED_FIELDS: usize = 25;
|
||||
pub const MAX_EMBED_FIELD_TITLE_LENGTH: usize = 256;
|
||||
pub const MAX_EMBED_FIELD_VALUE_LENGTH: usize = 1024;
|
||||
pub const MAX_EMBED_FIELDS: usize = 25;
|
||||
|
||||
pub const MINUTE: usize = 60;
|
||||
pub const HOUR: usize = 60 * MINUTE;
|
||||
|
@ -9,7 +9,11 @@ mod routes;
|
||||
use std::{collections::HashMap, env};
|
||||
|
||||
use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
|
||||
use rocket::{fs::FileServer, tokio::sync::broadcast::Sender};
|
||||
use rocket::{
|
||||
fs::FileServer,
|
||||
serde::json::{json, Json, Value as JsonValue},
|
||||
tokio::sync::broadcast::Sender,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use serenity::{
|
||||
client::Context,
|
||||
@ -46,6 +50,11 @@ async fn not_found() -> Template {
|
||||
Template::render("errors/404", &map)
|
||||
}
|
||||
|
||||
#[catch(422)]
|
||||
async fn unprocessable_entity() -> JsonValue {
|
||||
json!({"error": "Invalid request.", "errors": ["Invalid request."]})
|
||||
}
|
||||
|
||||
#[catch(500)]
|
||||
async fn internal_server_error() -> Template {
|
||||
let map: HashMap<String, String> = HashMap::new();
|
||||
@ -69,7 +78,16 @@ pub async fn initialize(
|
||||
|
||||
rocket::build()
|
||||
.attach(Template::fairing())
|
||||
.register("/", catchers![not_authorized, forbidden, not_found, internal_server_error])
|
||||
.register(
|
||||
"/",
|
||||
catchers![
|
||||
not_authorized,
|
||||
forbidden,
|
||||
not_found,
|
||||
internal_server_error,
|
||||
unprocessable_entity
|
||||
],
|
||||
)
|
||||
.manage(oauth2_client)
|
||||
.manage(reqwest_client)
|
||||
.manage(serenity_context)
|
||||
@ -105,10 +123,6 @@ pub async fn initialize(
|
||||
routes::dashboard::user::get_user_info,
|
||||
routes::dashboard::user::update_user_info,
|
||||
routes::dashboard::user::get_user_guilds,
|
||||
routes::dashboard::user::create_reminder,
|
||||
routes::dashboard::user::get_reminders,
|
||||
routes::dashboard::user::overwrite_reminder,
|
||||
routes::dashboard::user::delete_reminder,
|
||||
routes::dashboard::guild::get_guild_channels,
|
||||
routes::dashboard::guild::get_guild_roles,
|
||||
routes::dashboard::guild::create_reminder,
|
||||
|
@ -19,12 +19,13 @@ use crate::{
|
||||
check_guild_subscription, check_subscription,
|
||||
consts::{
|
||||
DAY, DISCORD_CDN, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH,
|
||||
MAX_EMBED_DESCRIPTION_LENGTH, MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_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, generate_uid, name_default, DeleteReminder, JsonReminder,
|
||||
PatchReminder, Reminder,
|
||||
create_database_channel, generate_uid, name_default, DeleteReminder, PatchReminder,
|
||||
Reminder,
|
||||
},
|
||||
};
|
||||
|
||||
@ -133,7 +134,7 @@ pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State<Conte
|
||||
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||
pub async fn create_reminder(
|
||||
id: u64,
|
||||
reminder: Json<JsonReminder>,
|
||||
reminder: Json<Reminder>,
|
||||
cookies: &CookieJar<'_>,
|
||||
serenity_context: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
@ -180,6 +181,13 @@ pub async fn create_reminder(
|
||||
check_length!(MAX_EMBED_TITLE_LENGTH, reminder.embed_title);
|
||||
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder.embed_author);
|
||||
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder.embed_footer);
|
||||
check_length_opt!(MAX_EMBED_FIELDS, reminder.embed_fields);
|
||||
if let Some(fields) = &reminder.embed_fields {
|
||||
for field in &fields.0 {
|
||||
check_length!(MAX_EMBED_FIELD_VALUE_LENGTH, field.value);
|
||||
check_length!(MAX_EMBED_FIELD_TITLE_LENGTH, field.title);
|
||||
}
|
||||
}
|
||||
check_length_opt!(MAX_USERNAME_LENGTH, reminder.username);
|
||||
check_length_opt!(
|
||||
MAX_URL_LENGTH,
|
||||
@ -245,6 +253,7 @@ pub async fn create_reminder(
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
enabled,
|
||||
expires,
|
||||
interval_seconds,
|
||||
@ -255,7 +264,7 @@ pub async fn create_reminder(
|
||||
tts,
|
||||
username,
|
||||
`utc_time`
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
new_uid,
|
||||
attachment_data,
|
||||
reminder.attachment_name,
|
||||
@ -271,6 +280,7 @@ pub async fn create_reminder(
|
||||
reminder.embed_image_url,
|
||||
reminder.embed_thumbnail_url,
|
||||
reminder.embed_title,
|
||||
reminder.embed_fields,
|
||||
reminder.enabled,
|
||||
reminder.expires,
|
||||
reminder.interval_seconds,
|
||||
@ -302,6 +312,7 @@ pub async fn create_reminder(
|
||||
reminders.embed_image_url,
|
||||
reminders.embed_thumbnail_url,
|
||||
reminders.embed_title,
|
||||
reminders.embed_fields,
|
||||
reminders.enabled,
|
||||
reminders.expires,
|
||||
reminders.interval_seconds,
|
||||
@ -324,7 +335,7 @@ pub async fn create_reminder(
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("Failed to complete SQL query: {:?}", e);
|
||||
|
||||
json!({"error": "Could not load reminders"})
|
||||
json!({"error": "Could not load reminder"})
|
||||
}),
|
||||
|
||||
Err(e) => {
|
||||
@ -365,6 +376,7 @@ pub async fn get_reminders(id: u64, ctx: &State<Context>, pool: &State<Pool<MySq
|
||||
reminders.embed_image_url,
|
||||
reminders.embed_thumbnail_url,
|
||||
reminders.embed_title,
|
||||
reminders.embed_fields,
|
||||
reminders.enabled,
|
||||
reminders.expires,
|
||||
reminders.interval_seconds,
|
||||
@ -421,6 +433,7 @@ pub async fn edit_reminder(
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
enabled,
|
||||
expires,
|
||||
interval_seconds,
|
||||
@ -507,6 +520,7 @@ pub async fn edit_reminder(
|
||||
reminders.embed_image_url,
|
||||
reminders.embed_thumbnail_url,
|
||||
reminders.embed_title,
|
||||
reminders.embed_fields,
|
||||
reminders.enabled,
|
||||
reminders.expires,
|
||||
reminders.interval_seconds,
|
||||
|
@ -6,7 +6,7 @@ use rocket::{http::CookieJar, response::Redirect};
|
||||
use rocket_dyn_templates::Template;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serenity::{http::Http, model::id::ChannelId};
|
||||
use sqlx::Executor;
|
||||
use sqlx::{types::Json, Executor};
|
||||
|
||||
use crate::{
|
||||
consts::{CHARACTERS, DEFAULT_AVATAR},
|
||||
@ -30,39 +30,7 @@ fn channel_default() -> u64 {
|
||||
pub struct EmbedField {
|
||||
title: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JsonReminder {
|
||||
attachment: Option<String>,
|
||||
attachment_name: Option<String>,
|
||||
avatar: Option<String>,
|
||||
#[serde(with = "string")]
|
||||
channel: u64,
|
||||
content: String,
|
||||
embed_author: String,
|
||||
embed_author_url: Option<String>,
|
||||
embed_color: u32,
|
||||
embed_description: String,
|
||||
embed_footer: String,
|
||||
embed_footer_url: Option<String>,
|
||||
embed_image_url: Option<String>,
|
||||
embed_thumbnail_url: Option<String>,
|
||||
embed_title: String,
|
||||
embed_fields: Vec<EmbedField>,
|
||||
enabled: bool,
|
||||
expires: Option<NaiveDateTime>,
|
||||
interval_seconds: Option<u32>,
|
||||
interval_months: Option<u32>,
|
||||
#[serde(default = "name_default")]
|
||||
name: String,
|
||||
pin: bool,
|
||||
restartable: bool,
|
||||
tts: bool,
|
||||
#[serde(default)]
|
||||
uid: String,
|
||||
username: Option<String>,
|
||||
utc_time: NaiveDateTime,
|
||||
inline: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@ -82,6 +50,7 @@ pub struct Reminder {
|
||||
embed_image_url: Option<String>,
|
||||
embed_thumbnail_url: Option<String>,
|
||||
embed_title: String,
|
||||
embed_fields: Option<Json<Vec<EmbedField>>>,
|
||||
enabled: bool,
|
||||
expires: Option<NaiveDateTime>,
|
||||
interval_seconds: Option<u32>,
|
||||
@ -130,7 +99,7 @@ pub struct PatchReminder {
|
||||
#[serde(default)]
|
||||
embed_title: Unset<String>,
|
||||
#[serde(default)]
|
||||
embed_fields: Unset<EmbedField>,
|
||||
embed_fields: Unset<Json<Vec<EmbedField>>>,
|
||||
#[serde(default)]
|
||||
enabled: Unset<bool>,
|
||||
#[serde(default)]
|
||||
|
@ -11,14 +11,13 @@ use serde::{Deserialize, Serialize};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
model::{
|
||||
id::{GuildId, RoleId, UserId},
|
||||
id::{GuildId, RoleId},
|
||||
permissions::Permissions,
|
||||
},
|
||||
};
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
use super::Reminder;
|
||||
use crate::{consts::DISCORD_API, routes::dashboard::DeleteReminder};
|
||||
use crate::consts::DISCORD_API;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UserInfo {
|
||||
@ -164,241 +163,3 @@ pub async fn get_user_guilds(cookies: &CookieJar<'_>, reqwest_client: &State<Cli
|
||||
json!({"error": "Not authorized"})
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/api/user/reminders", data = "<reminder>")]
|
||||
pub async fn create_reminder(
|
||||
reminder: Json<Reminder>,
|
||||
_ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonValue {
|
||||
match sqlx::query!(
|
||||
"INSERT INTO reminders (
|
||||
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`
|
||||
) VALUES (
|
||||
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` = ?
|
||||
)",
|
||||
reminder.avatar,
|
||||
reminder.content,
|
||||
reminder.embed_author,
|
||||
reminder.embed_author_url,
|
||||
reminder.embed_color,
|
||||
reminder.embed_description,
|
||||
reminder.embed_footer,
|
||||
reminder.embed_footer_url,
|
||||
reminder.embed_image_url,
|
||||
reminder.embed_thumbnail_url,
|
||||
reminder.embed_title,
|
||||
reminder.enabled,
|
||||
reminder.expires,
|
||||
reminder.interval_seconds,
|
||||
reminder.interval_months,
|
||||
reminder.name,
|
||||
reminder.pin,
|
||||
reminder.restartable,
|
||||
reminder.tts,
|
||||
reminder.username,
|
||||
reminder.utc_time,
|
||||
)
|
||||
.execute(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
json!({})
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error in `create_reminder`: {:?}", e);
|
||||
|
||||
json!({"error": "Could not create reminder"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/user/reminders")]
|
||||
pub async fn get_reminders(
|
||||
pool: &State<Pool<MySql>>,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
) -> JsonValue {
|
||||
if let Some(user_id) =
|
||||
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten()
|
||||
{
|
||||
let query_res = sqlx::query!(
|
||||
"SELECT channel FROM channels INNER JOIN users ON users.dm_channel = channels.id WHERE users.user = ?",
|
||||
user_id
|
||||
)
|
||||
.fetch_one(pool.inner())
|
||||
.await;
|
||||
|
||||
let dm_channel = if let Ok(query) = query_res {
|
||||
Some(query.channel)
|
||||
} else {
|
||||
if let Ok(dm_channel) = UserId(user_id).create_dm_channel(&ctx.inner()).await {
|
||||
Some(dm_channel.id.as_u64().to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(channel_id) = dm_channel {
|
||||
let reminders = sqlx::query_as!(
|
||||
Reminder,
|
||||
r#"SELECT
|
||||
reminders.attachment,
|
||||
reminders.attachment_name,
|
||||
reminders.avatar,
|
||||
channels.channel,
|
||||
reminders.content,
|
||||
reminders.embed_author,
|
||||
reminders.embed_author_url,
|
||||
reminders.embed_color,
|
||||
reminders.embed_description,
|
||||
reminders.embed_footer,
|
||||
reminders.embed_footer_url,
|
||||
reminders.embed_image_url,
|
||||
reminders.embed_thumbnail_url,
|
||||
reminders.embed_title,
|
||||
reminders.enabled as "enabled:_",
|
||||
reminders.expires,
|
||||
reminders.interval_seconds,
|
||||
reminders.interval_months,
|
||||
reminders.name,
|
||||
reminders.pin as "pin:_",
|
||||
reminders.restartable as "restartable:_",
|
||||
reminders.tts as "tts:_",
|
||||
reminders.uid,
|
||||
reminders.username,
|
||||
reminders.utc_time
|
||||
FROM reminders INNER JOIN channels ON channels.id = reminders.channel_id WHERE channels.channel = ?"#,
|
||||
channel_id
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
.unwrap_or(vec![]);
|
||||
|
||||
json!(reminders)
|
||||
} else {
|
||||
json!({"error": "User's DM channel could not be determined"})
|
||||
}
|
||||
} else {
|
||||
json!({"error": "Not authorized"})
|
||||
}
|
||||
}
|
||||
|
||||
#[put("/api/user/reminders", data = "<reminder>")]
|
||||
pub async fn overwrite_reminder(reminder: Json<Reminder>, pool: &State<Pool<MySql>>) -> JsonValue {
|
||||
match sqlx::query!(
|
||||
"UPDATE reminders SET
|
||||
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` = ?
|
||||
WHERE uid = ?",
|
||||
reminder.avatar,
|
||||
reminder.content,
|
||||
reminder.embed_author,
|
||||
reminder.embed_author_url,
|
||||
reminder.embed_color,
|
||||
reminder.embed_description,
|
||||
reminder.embed_footer,
|
||||
reminder.embed_footer_url,
|
||||
reminder.embed_image_url,
|
||||
reminder.embed_thumbnail_url,
|
||||
reminder.embed_title,
|
||||
reminder.enabled,
|
||||
reminder.expires,
|
||||
reminder.interval_seconds,
|
||||
reminder.interval_months,
|
||||
reminder.name,
|
||||
reminder.pin,
|
||||
reminder.restartable,
|
||||
reminder.tts,
|
||||
reminder.username,
|
||||
reminder.utc_time,
|
||||
reminder.uid
|
||||
)
|
||||
.execute(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
json!({})
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error in `overwrite_reminder`: {:?}", e);
|
||||
|
||||
json!({"error": "Could not modify reminder"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[delete("/api/user/reminders", data = "<reminder>")]
|
||||
pub async fn delete_reminder(
|
||||
reminder: Json<DeleteReminder>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonValue {
|
||||
if sqlx::query!("DELETE FROM reminders WHERE uid = ?", reminder.uid)
|
||||
.execute(pool.inner())
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
json!({})
|
||||
} else {
|
||||
json!({"error": "Could not delete reminder"})
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ function get_interval(element) {
|
||||
let minutes = element.querySelector('input[name="interval_minutes"]').value;
|
||||
let seconds = element.querySelector('input[name="interval_seconds"]').value;
|
||||
|
||||
console.log(minutes);
|
||||
|
||||
return {
|
||||
months: parseInt(months) || null,
|
||||
seconds:
|
||||
@ -53,7 +51,7 @@ function update_interval(element) {
|
||||
}
|
||||
}
|
||||
|
||||
let $intervalGroup = document.querySelector(".interval-group");
|
||||
const $intervalGroup = document.querySelector(".interval-group");
|
||||
|
||||
document.querySelector(".interval-group").addEventListener(
|
||||
"blur",
|
||||
@ -73,9 +71,13 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
for (reminder of event.detail) {
|
||||
let $intervalGroup = reminder.node.querySelector(".interval-group");
|
||||
|
||||
$intervalGroup.addEventListener("blur", (ev) => {
|
||||
$intervalGroup.addEventListener(
|
||||
"blur",
|
||||
(ev) => {
|
||||
if (ev.target.nodeName !== "BUTTON") update_interval($intervalGroup);
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
$intervalGroup.querySelector("button.clear").addEventListener("click", () => {
|
||||
$intervalGroup.querySelectorAll("input").forEach((el) => {
|
||||
|
@ -5,10 +5,10 @@ const $colorPickerModal = document.querySelector("div#pickColorModal");
|
||||
const $colorPickerInput = $colorPickerModal.querySelector("input");
|
||||
const $deleteReminderBtn = document.querySelector("#delete-reminder-confirm");
|
||||
const $reminderTemplate = document.querySelector("template#guildReminder");
|
||||
const $embedFieldTemplate = document.querySelector("template#embedFieldTemplate");
|
||||
|
||||
let channels;
|
||||
let roles;
|
||||
let guild_id;
|
||||
|
||||
function colorToInt(r, g, b) {
|
||||
return (r << 16) + (g << 8) + b;
|
||||
@ -118,10 +118,10 @@ async function fetch_reminders(guild_id) {
|
||||
for (let reminder of data) {
|
||||
let newFrame = $reminderTemplate.content.cloneNode(true);
|
||||
|
||||
newFrame.querySelector(".reminderContent").dataset.uid =
|
||||
newFrame.querySelector(".reminderContent").dataset["uid"] =
|
||||
reminder["uid"];
|
||||
|
||||
render_reminder(reminder, newFrame);
|
||||
deserialize_reminder(reminder, newFrame);
|
||||
|
||||
$reminderBox.appendChild(newFrame);
|
||||
|
||||
@ -137,7 +137,87 @@ async function fetch_reminders(guild_id) {
|
||||
});
|
||||
}
|
||||
|
||||
function render_reminder(reminder, frame) {
|
||||
async function serialize_reminder(node) {
|
||||
let interval = get_interval(node);
|
||||
|
||||
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");
|
||||
|
||||
if (utc_time.invalid) {
|
||||
return { error: "Time provided invalid." };
|
||||
}
|
||||
|
||||
let fields = [
|
||||
...node.querySelectorAll("div.embed-multifield-box div.embed-field-box"),
|
||||
]
|
||||
.map((el) => {
|
||||
return {
|
||||
title: el.querySelector("textarea#embedFieldTitle").value,
|
||||
value: el.querySelector("textarea#embedFieldValue").value,
|
||||
inline: el.dataset["inlined"] === "1",
|
||||
};
|
||||
})
|
||||
.filter(({ title, value, inline }) => title.length + value.length > 0);
|
||||
|
||||
let attachment = null;
|
||||
let attachment_name = null;
|
||||
|
||||
if (node.querySelector('input[name="attachment"]').files.length > 0) {
|
||||
let file = node.querySelector('input[name="attachment"]').files[0];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const reminderContent = node.closest(".reminderContent");
|
||||
|
||||
return {
|
||||
// if we're creating a reminder, ignore this field
|
||||
uid: reminderContent !== null ? reminderContent.dataset["uid"] : "",
|
||||
// if we're editing a reminder, ignore this field
|
||||
enabled: reminderContent !== null ? null : true,
|
||||
restartable: false,
|
||||
attachment: attachment,
|
||||
attachment_name: attachment_name,
|
||||
avatar: has_source(node.querySelector("img.discord-avatar").src),
|
||||
channel: node.querySelector("select.channel-selector").value,
|
||||
content: node.querySelector('textarea[name="content"]').value,
|
||||
embed_author_url: has_source(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: has_source(node.querySelector("img.embed_footer_url").src),
|
||||
embed_image_url: has_source(node.querySelector("img.embed_image_url").src),
|
||||
embed_thumbnail_url: has_source(
|
||||
node.querySelector("img.embed_thumbnail_url").src
|
||||
),
|
||||
embed_title: node.querySelector('textarea[name="embed_title"]').value,
|
||||
embed_fields: fields,
|
||||
expires: null,
|
||||
interval_seconds: interval.seconds,
|
||||
interval_months: interval.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"),
|
||||
};
|
||||
}
|
||||
|
||||
function deserialize_reminder(reminder, frame) {
|
||||
// populate channels
|
||||
set_channels(frame.querySelector("select.channel-selector"));
|
||||
|
||||
@ -161,10 +241,25 @@ function render_reminder(reminder, frame) {
|
||||
}
|
||||
}
|
||||
|
||||
const lastChild = frame.querySelector("div.embed-multifield-box .embed-field-box");
|
||||
|
||||
for (let field of reminder["embed_fields"]) {
|
||||
let embed_field = $embedFieldTemplate.content.cloneNode(true);
|
||||
embed_field.querySelector("textarea.discord-field-title").value = field["title"];
|
||||
embed_field.querySelector("textarea.discord-field-value").value = field["value"];
|
||||
embed_field.querySelector(".embed-field-box").dataset["inlined"] = field["inline"]
|
||||
? "1"
|
||||
: "0";
|
||||
|
||||
frame
|
||||
.querySelector("div.embed-multifield-box")
|
||||
.insertBefore(embed_field, lastChild);
|
||||
}
|
||||
|
||||
if (reminder["interval_seconds"] !== null) update_interval(frame);
|
||||
|
||||
let $enableBtn = frame.querySelector(".disable-enable");
|
||||
$enableBtn.dataset.action = reminder["enabled"] ? "disable" : "enable";
|
||||
$enableBtn.dataset["action"] = reminder["enabled"] ? "disable" : "enable";
|
||||
|
||||
let timeInput = frame.querySelector('input[name="time"]');
|
||||
let localTime = luxon.DateTime.fromISO(reminder["utc_time"], { zone: "UTC" }).setZone(
|
||||
@ -223,7 +318,7 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
|
||||
const enableBtn = node.querySelector(".disable-enable");
|
||||
enableBtn.addEventListener("click", () => {
|
||||
let enable = enableBtn.dataset.action === "enable";
|
||||
let enable = enableBtn.dataset["action"] === "enable";
|
||||
|
||||
fetch(`/dashboard/api/guild/${guild}/reminders`, {
|
||||
method: "PATCH",
|
||||
@ -237,7 +332,9 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
if (data.error) {
|
||||
show_error(data.error);
|
||||
} else {
|
||||
enableBtn.dataset.action = data["enabled"] ? "enable" : "disable";
|
||||
enableBtn.dataset["action"] = data["enabled"]
|
||||
? "enable"
|
||||
: "disable";
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -249,66 +346,17 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
|
||||
const $saveBtn = node.querySelector("button.save-btn");
|
||||
|
||||
$saveBtn.addEventListener("click", (event) => {
|
||||
$saveBtn.addEventListener("click", async (event) => {
|
||||
$saveBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-spinner fa-spin",
|
||||
];
|
||||
|
||||
let interval = get_interval(node);
|
||||
let reminder = await serialize_reminder(node);
|
||||
if (reminder.error) {
|
||||
show_error(reminder.error);
|
||||
return;
|
||||
}
|
||||
|
||||
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 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),
|
||||
channel: node.querySelector("select.channel-selector").value,
|
||||
content: node.querySelector('textarea[name="content"]').value,
|
||||
embed_author_url: has_source(
|
||||
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: has_source(
|
||||
node.querySelector("img.embed_footer_url").src
|
||||
),
|
||||
embed_image_url: has_source(
|
||||
node.querySelector("img.embed_image_url").src
|
||||
),
|
||||
embed_thumbnail_url: has_source(
|
||||
node.querySelector("img.embed_thumbnail_url").src
|
||||
),
|
||||
embed_title: node.querySelector('textarea[name="embed_title"]').value,
|
||||
embed_fields: fields,
|
||||
expires: null,
|
||||
interval_seconds: interval.seconds,
|
||||
interval_months: interval.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`, {
|
||||
@ -319,7 +367,9 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
body: JSON.stringify(reminder),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => console.log(data));
|
||||
.then((data) => {
|
||||
for (let error of data.errors) show_error(error);
|
||||
});
|
||||
|
||||
$saveBtn.querySelector("span.icon > i").classList = ["fas fa-check"];
|
||||
|
||||
@ -384,7 +434,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
document.querySelectorAll(".navbar-burger").forEach((el) => {
|
||||
el.addEventListener("click", () => {
|
||||
const target = el.dataset.target;
|
||||
const target = el.dataset["target"];
|
||||
const $target = document.getElementById(target);
|
||||
|
||||
el.classList.toggle("is-active");
|
||||
@ -434,8 +484,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
$anchor.addEventListener("click", async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
guild_id = guild.id;
|
||||
|
||||
const event = new CustomEvent("guildSwitched", {
|
||||
detail: {
|
||||
guild_name: guild.name,
|
||||
@ -488,35 +536,6 @@ let $createBtn = $createReminder.querySelector("button#createReminder");
|
||||
$createBtn.addEventListener("click", async () => {
|
||||
$createBtn.querySelector("span.icon > i").classList = ["fas fa-spinner fa-spin"];
|
||||
|
||||
let interval = get_interval($createReminder);
|
||||
|
||||
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");
|
||||
|
||||
if (utc_time.invalid) {
|
||||
show_error("Time provided invalid.");
|
||||
$createBtn.querySelector("span.icon > i").classList = ["fas fa-sparkles"];
|
||||
return;
|
||||
}
|
||||
|
||||
let fields = [
|
||||
...$createReminder.querySelectorAll(
|
||||
"div.embed-multifield-box div.embed-field-box"
|
||||
),
|
||||
].map((el) => {
|
||||
return {
|
||||
title: el.querySelector("textarea#embedFieldTitle").value,
|
||||
value: el.querySelector("textarea#embedFieldValue").value,
|
||||
};
|
||||
});
|
||||
|
||||
let attachment = null;
|
||||
let attachment_name = null;
|
||||
|
||||
@ -532,44 +551,12 @@ $createBtn.addEventListener("click", async () => {
|
||||
attachment_name = file.name;
|
||||
}
|
||||
|
||||
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: fields,
|
||||
enabled: true,
|
||||
expires: null,
|
||||
interval_seconds: interval.seconds,
|
||||
interval_months: interval.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 reminder = await serialize_reminder($createReminder);
|
||||
if (reminder.error) {
|
||||
show_error(reminder.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// send to server
|
||||
let guild = document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||
|
||||
fetch(`/dashboard/api/guild/${guild}/reminders`, {
|
||||
@ -589,7 +576,7 @@ $createBtn.addEventListener("click", async () => {
|
||||
|
||||
newFrame.querySelector(".reminderContent").dataset["uid"] = data["uid"];
|
||||
|
||||
render_reminder(data, newFrame);
|
||||
deserialize_reminder(data, newFrame);
|
||||
|
||||
$reminderBox.appendChild(newFrame);
|
||||
|
||||
@ -682,19 +669,10 @@ document.addEventListener("remindersLoaded", () => {
|
||||
window.getComputedStyle($discordFrame).borderLeftColor;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(".embed-field-box button.inline-btn").forEach((el) => {
|
||||
el.addEventListener("click", (ev) => {
|
||||
let inlined = ev.target.closest(".embed-field-box").dataset["inlined"];
|
||||
ev.target.closest(".embed-field-box").dataset["inlined"] =
|
||||
inlined === "1" ? "0" : "1";
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function check_embed_fields() {
|
||||
document.querySelectorAll(".embed-field-box").forEach((element) => {
|
||||
const $template = document.querySelector("template#embedFieldTemplate");
|
||||
const $titleInput = element.querySelector(".discord-field-title");
|
||||
const $valueInput = element.querySelector(".discord-field-value");
|
||||
|
||||
@ -726,16 +704,7 @@ function check_embed_fields() {
|
||||
$valueInput.value !== "" &&
|
||||
element.nextElementSibling === null
|
||||
) {
|
||||
const $clone = $template.content.cloneNode(true);
|
||||
$clone
|
||||
.querySelector(".embed-field-box button.inline-btn")
|
||||
.addEventListener("click", (ev) => {
|
||||
let inlined =
|
||||
ev.target.closest(".embed-field-box").dataset["inlined"];
|
||||
ev.target.closest(".embed-field-box").dataset["inlined"] =
|
||||
inlined == "1" ? "0" : "1";
|
||||
});
|
||||
|
||||
const $clone = $embedFieldTemplate.content.cloneNode(true);
|
||||
element.parentElement.append($clone);
|
||||
}
|
||||
});
|
||||
@ -746,16 +715,7 @@ function check_embed_fields() {
|
||||
$valueInput.value !== "" &&
|
||||
element.nextElementSibling === null
|
||||
) {
|
||||
const $clone = $template.content.cloneNode(true);
|
||||
$clone
|
||||
.querySelector(".embed-field-box button.inline-btn")
|
||||
.addEventListener("click", (ev) => {
|
||||
let inlined =
|
||||
ev.target.closest(".embed-field-box").dataset["inlined"];
|
||||
ev.target.closest(".embed-field-box").dataset["inlined"] =
|
||||
inlined == "1" ? "0" : "1";
|
||||
});
|
||||
|
||||
const $clone = $embedFieldTemplate.content.cloneNode(true);
|
||||
element.parentElement.append($clone);
|
||||
}
|
||||
});
|
||||
@ -780,3 +740,11 @@ document.addEventListener("DOMNodeInserted", () => {
|
||||
check_embed_fields();
|
||||
resize_textareas();
|
||||
});
|
||||
|
||||
document.addEventListener("click", (ev) => {
|
||||
if (ev.target.closest("button.inline-btn") !== null) {
|
||||
let inlined = ev.target.closest(".embed-field-box").dataset["inlined"];
|
||||
ev.target.closest(".embed-field-box").dataset["inlined"] =
|
||||
inlined == "1" ? "0" : "1";
|
||||
}
|
||||
});
|
||||
|
@ -140,6 +140,23 @@
|
||||
<button class="modal-close is-large close-modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="dataManagerModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<label class="modal-card-title" for="urlInput">Import/Export Manager <a href="/help/iemanager"><span><i class="fa fa-question-circle"></i></span></a></label>
|
||||
<button class="delete close-modal" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="has-text-centered">
|
||||
<button class="button is-success is-outlined" id="import-data">Import Data</button>
|
||||
<button class="button is-success" id="export-data">Export Data</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<button class="modal-close is-large close-modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="deleteReminderModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
@ -182,12 +199,15 @@
|
||||
<ul class="menu-list guildList">
|
||||
|
||||
</ul>
|
||||
<div class="aside-footer" style="position: fixed; bottom: 0;">
|
||||
<div class="aside-footer" style="position: fixed; bottom: 0; width: 226px;">
|
||||
<p class="menu-label">
|
||||
Settings
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a class="show-modal" data-modal="dataManagerModal">
|
||||
<span class="icon"><i class="fas fa-exchange"></i></span> Import/Export
|
||||
</a>
|
||||
<a class="show-modal" data-modal="chooseTimezoneModal">
|
||||
<span class="icon"><i class="fas fa-map-marked"></i></span> Timezone
|
||||
</a>
|
||||
@ -211,16 +231,6 @@
|
||||
</g>
|
||||
</svg>
|
||||
<aside class="menu" style="display: flex; flex-direction: column; flex-grow: 1;">
|
||||
<p class="menu-label">
|
||||
Personal
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a class="switch-pane" data-pane="personal">
|
||||
<span class="icon"><i class="fas fa-map-pin"></i></span> @%username%
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="menu-label">
|
||||
Servers
|
||||
</p>
|
||||
@ -233,6 +243,9 @@
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a class="show-modal" data-modal="dataManagerModal">
|
||||
<span class="icon"><i class="fas fa-exchange"></i></span> Import/Export
|
||||
</a>
|
||||
<a class="show-modal" data-modal="chooseTimezoneModal">
|
||||
<span class="icon"><i class="fas fa-map-marked"></i></span> Timezone
|
||||
</a>
|
||||
|
Loading…
Reference in New Issue
Block a user