Increase the size of reminder names. Restyle.

This commit is contained in:
jude 2023-07-22 15:06:53 +01:00
parent 88c4830209
commit 8799089b2d
8 changed files with 319 additions and 250 deletions

2
Cargo.lock generated
View File

@ -2217,7 +2217,7 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]] [[package]]
name = "reminder-rs" name = "reminder-rs"
version = "1.6.19" version = "1.6.20"
dependencies = [ dependencies = [
"base64 0.21.2", "base64 0.21.2",
"chrono", "chrono",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "reminder-rs" name = "reminder-rs"
version = "1.6.19" version = "1.6.20"
authors = ["Jude Southworth <judesouthworth@pm.me>"] authors = ["Jude Southworth <judesouthworth@pm.me>"]
edition = "2021" edition = "2021"
license = "AGPL-3.0 only" license = "AGPL-3.0 only"

View File

@ -0,0 +1,2 @@
ALTER TABLE reminders MODIFY `name` VARCHAR(100) NOT NULL DEFAULT 'Reminder';
ALTER TABLE reminder_template MODIFY `name` VARCHAR(100) NOT NULL DEFAULT 'Reminder';

View File

@ -2,6 +2,7 @@ pub const DISCORD_OAUTH_TOKEN: &'static str = "https://discord.com/api/oauth2/to
pub const DISCORD_OAUTH_AUTHORIZE: &'static str = "https://discord.com/api/oauth2/authorize"; pub const DISCORD_OAUTH_AUTHORIZE: &'static str = "https://discord.com/api/oauth2/authorize";
pub const DISCORD_API: &'static str = "https://discord.com/api"; pub const DISCORD_API: &'static str = "https://discord.com/api";
pub const MAX_NAME_LENGTH: usize = 100;
pub const MAX_CONTENT_LENGTH: usize = 2000; pub const MAX_CONTENT_LENGTH: usize = 2000;
pub const MAX_EMBED_DESCRIPTION_LENGTH: usize = 4096; pub const MAX_EMBED_DESCRIPTION_LENGTH: usize = 4096;
pub const MAX_EMBED_TITLE_LENGTH: usize = 256; pub const MAX_EMBED_TITLE_LENGTH: usize = 256;

View File

@ -18,7 +18,7 @@ use crate::{
CHARACTERS, DAY, DEFAULT_AVATAR, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, CHARACTERS, DAY, DEFAULT_AVATAR, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH,
MAX_EMBED_DESCRIPTION_LENGTH, MAX_EMBED_FIELDS, MAX_EMBED_FIELD_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_EMBED_FIELD_VALUE_LENGTH, MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_LENGTH,
MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL, MAX_NAME_LENGTH, MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL,
}, },
routes::JsonResult, routes::JsonResult,
Database, Error, Database, Error,
@ -400,6 +400,7 @@ pub async fn create_reminder(
let channel = channel.unwrap(); let channel = channel.unwrap();
// validate lengths // validate lengths
check_length!(MAX_NAME_LENGTH, reminder.name);
check_length!(MAX_CONTENT_LENGTH, reminder.content); check_length!(MAX_CONTENT_LENGTH, reminder.content);
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder.embed_description); check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder.embed_description);
check_length!(MAX_EMBED_TITLE_LENGTH, reminder.embed_title); check_length!(MAX_EMBED_TITLE_LENGTH, reminder.embed_title);

View File

@ -11,7 +11,7 @@ div.reminderContent.is-collapsed .column.discord-frame {
display: none; display: none;
} }
div.reminderContent.is-collapsed .collapses { div.reminderContent.is-collapsed .column.settings {
display: none; display: none;
} }
@ -23,42 +23,42 @@ div.reminderContent .invert-collapses {
display: none; display: none;
} }
div.reminderContent.is-collapsed .settings {
display: flex;
flex-direction: row;
padding-bottom: 0;
}
div.reminderContent.is-collapsed .channel-field {
display: inline-flex;
order: 1;
}
div.reminderContent.is-collapsed .reminder-topbar {
display: inline-flex;
margin-bottom: 0px;
flex-grow: 1;
order: 2;
}
div.reminderContent.is-collapsed input[name="name"] { div.reminderContent.is-collapsed input[name="name"] {
display: inline-flex; display: inline-flex;
flex-grow: 1; flex-grow: 1;
border: none; border: none;
font-weight: 700;
background: none; background: none;
box-shadow: none;
opacity: 1;
} }
div.reminderContent.is-collapsed button.hide-box { div.reminderContent.is-collapsed .hide-box {
display: inline-flex; display: inline-flex;
} }
div.reminderContent.is-collapsed button.hide-box i { div.reminderContent.is-collapsed .hide-box i {
transform: rotate(90deg); transform: rotate(90deg);
} }
/* END */ /* END */
/* dashboard styles */ /* dashboard styles */
.hide-box {
border: none;
background: none;
}
.hide-box:focus {
outline: none;
box-shadow: none !important;
}
.channel-bar {
display: flex;
justify-content: center;
flex-direction: column;
font-weight: bold;
}
button.inline-btn { button.inline-btn {
height: 100%; height: 100%;
padding: 5px; padding: 5px;
@ -85,11 +85,47 @@ div.discord-embed {
position: relative; position: relative;
} }
div.split-controls {
display: flex;
flex-direction: column;
justify-content: space-between;
flex-grow: 2;
}
.reminder-topbar > div {
padding-left: 6px;
padding-right: 6px;
}
.settings {
display: flex;
flex-direction: column;
}
.name-bar {
flex-grow: 1;
flex-shrink: 0;
}
.hide-button-bar {
flex-grow: 0;
flex-shrink: 0;
}
.patreon-only {
padding-bottom: 16px;
}
.reminder-topbar {
display: flex;
}
div.reminderContent { div.reminderContent {
padding: 2px; margin-top: 10px;
margin-bottom: 10px;
padding: 14px;
background-color: #f5f5f5; background-color: #f5f5f5;
border-radius: 8px; border-radius: 8px;
margin: 8px;
} }
div.interval-group > button { div.interval-group > button {

View File

@ -56,6 +56,12 @@ function switch_pane(selector) {
} }
function update_select(sel) { function update_select(sel) {
let channelDisplay = sel.closest("div.reminderContent").querySelector(".channel-bar");
if (channelDisplay !== null) {
channelDisplay.textContent = `#${sel.selectedOptions[0].textContent}`;
}
if (sel.selectedOptions[0].dataset["webhookAvatar"]) { if (sel.selectedOptions[0].dataset["webhookAvatar"]) {
sel.closest("div.reminderContent").querySelector("img.discord-avatar").src = sel.closest("div.reminderContent").querySelector("img.discord-avatar").src =
sel.selectedOptions[0].dataset["webhookAvatar"]; sel.selectedOptions[0].dataset["webhookAvatar"];
@ -231,6 +237,11 @@ async function serialize_reminder(node, mode) {
} }
} }
let name = node.querySelector('input[name="name"]').value;
if (name.length > 100) {
return { error: "Name exceeds maximum length (100)." };
}
let rgb_color = window.getComputedStyle( let rgb_color = window.getComputedStyle(
node.querySelector("div.discord-embed") node.querySelector("div.discord-embed")
).borderLeftColor; ).borderLeftColor;
@ -366,6 +377,7 @@ function deserialize_reminder(reminder, frame, mode) {
} }
update_interval(frame); update_interval(frame);
update_select(frame.querySelector(".channel-selector"));
const lastChild = frame.querySelector("div.embed-multifield-box .embed-field-box"); const lastChild = frame.querySelector("div.embed-multifield-box .embed-field-box");
@ -451,6 +463,12 @@ document.addEventListener("channelsLoaded", () => {
document.addEventListener("remindersLoaded", (event) => { document.addEventListener("remindersLoaded", (event) => {
const guild = guildId(); const guild = guildId();
document.querySelectorAll("select.channel-selector").forEach((el) => {
el.addEventListener("change", (e) => {
update_select(e.target);
});
});
for (let reminder of event.detail) { for (let reminder of event.detail) {
let node = reminder.node; let node = reminder.node;

View File

@ -1,251 +1,262 @@
<div class="columns reminderContent {% if creating %}creator{% endif %}"> <div class="reminderContent {% if creating %}creator{% endif %}">
<div class="column discord-frame"> <div class="columns column reminder-topbar">
<article class="media"> {% if not creating %}
<figure class="media-left"> <div class="invert-collapses channel-bar">
<p class="image is-32x32 customizable"> #channel
<a> </div>
<img class="is-rounded discord-avatar" src="/static/img/bg.webp" alt="Image for discord avatar"> {% endif %}
</a> <div class="name-bar">
</p> <div class="field">
</figure> <div class="control">
<div class="media-content"> <label class="label sr-only">Reminder Name</label>
<div class="content"> <input class="input" type="text" name="name" placeholder="Reminder Name" maxlength="100">
<div class="discord-message-header"> </div>
<label class="is-sr-only">Username Override</label> </div>
<input class="discord-username message-input" placeholder="Username Override" </div>
maxlength="32" name="username"> <div class="hide-button-bar">
</div> <button class="button hide-box">
<label class="is-sr-only">Message</label> <span class="is-sr-only">Hide reminder</span><i class="fas fa-chevron-down"></i>
<textarea class="message-input autoresize discord-content" </button>
placeholder="Message Content..." </div>
maxlength="2000" name="content" rows="1"></textarea> </div>
<div class="columns">
<div class="column discord-frame">
<article class="media">
<figure class="media-left">
<p class="image is-32x32 customizable">
<a>
<img class="is-rounded discord-avatar" src="/static/img/bg.webp" alt="Image for discord avatar">
</a>
</p>
</figure>
<div class="media-content">
<div class="content">
<div class="discord-message-header">
<label class="is-sr-only">Username Override</label>
<input class="discord-username message-input" placeholder="Username Override"
maxlength="32" name="username">
</div>
<label class="is-sr-only">Message</label>
<textarea class="message-input autoresize discord-content"
placeholder="Message Content..."
maxlength="2000" name="content" rows="1"></textarea>
<div class="discord-embed"> <div class="discord-embed">
<div class="embed-body"> <div class="embed-body">
<button class="change-color button is-rounded is-small"> <button class="change-color button is-rounded is-small">
<span class="is-sr-only">Choose embed color</span><i class="fas fa-eye-dropper"></i> <span class="is-sr-only">Choose embed color</span><i class="fas fa-eye-dropper"></i>
</button> </button>
<div class="a"> <div class="a">
<div class="embed-author-box"> <div class="embed-author-box">
<div class="a"> <div class="a">
<p class="image is-24x24 customizable"> <p class="image is-24x24 customizable">
<a> <a>
<img class="is-rounded embed_author_url" src="/static/img/bg.webp" alt="Image for embed author"> <img class="is-rounded embed_author_url" src="/static/img/bg.webp" alt="Image for embed author">
</a> </a>
</p> </p>
</div>
<div class="b">
<label class="is-sr-only" for="embedAuthor">Embed Author</label>
<textarea
class="discord-embed-author message-input autoresize"
placeholder="Embed Author..." rows="1" maxlength="256"
name="embed_author"></textarea>
</div>
</div>
<label class="is-sr-only" for="embedTitle">Embed Title</label>
<textarea class="discord-title message-input autoresize"
placeholder="Embed Title..."
maxlength="256" rows="1"
name="embed_title"></textarea>
<br>
<label class="is-sr-only" for="embedDescription">Embed Description</label>
<textarea class="discord-description message-input autoresize "
placeholder="Embed Description..."
maxlength="4096" name="embed_description"
rows="1"></textarea>
<br>
<div class="embed-multifield-box">
<div data-inlined="1" class="embed-field-box">
<label class="is-sr-only" for="embedFieldTitle">Field Title</label>
<div class="is-flex">
<textarea class="discord-field-title field-input message-input autoresize"
placeholder="Field Title..." rows="1"
maxlength="256" name="embed_field_title[]"></textarea>
<button class="button is-small inline-btn">
<span class="is-sr-only">Toggle field inline</span><i class="fas fa-arrows-h"></i>
</button>
</div> </div>
<label class="is-sr-only" for="embedFieldValue">Field Value</label> <div class="b">
<textarea <label class="is-sr-only" for="embedAuthor">Embed Author</label>
class="discord-field-value field-input message-input autoresize " <textarea
placeholder="Field Value..." class="discord-embed-author message-input autoresize"
maxlength="1024" name="embed_field_value[]" placeholder="Embed Author..." rows="1" maxlength="256"
rows="1"></textarea> name="embed_author"></textarea>
</div>
</div> </div>
<label class="is-sr-only" for="embedTitle">Embed Title</label>
<textarea class="discord-title message-input autoresize"
placeholder="Embed Title..."
maxlength="256" rows="1"
name="embed_title"></textarea>
<br>
<label class="is-sr-only" for="embedDescription">Embed Description</label>
<textarea class="discord-description message-input autoresize "
placeholder="Embed Description..."
maxlength="4096" name="embed_description"
rows="1"></textarea>
<br>
<div class="embed-multifield-box">
<div data-inlined="1" class="embed-field-box">
<label class="is-sr-only" for="embedFieldTitle">Field Title</label>
<div class="is-flex">
<textarea class="discord-field-title field-input message-input autoresize"
placeholder="Field Title..." rows="1"
maxlength="256" name="embed_field_title[]"></textarea>
<button class="button is-small inline-btn">
<span class="is-sr-only">Toggle field inline</span><i class="fas fa-arrows-h"></i>
</button>
</div>
<label class="is-sr-only" for="embedFieldValue">Field Value</label>
<textarea
class="discord-field-value field-input message-input autoresize "
placeholder="Field Value..."
maxlength="1024" name="embed_field_value[]"
rows="1"></textarea>
</div>
</div>
</div>
<div class="b">
<p class="image thumbnail customizable">
<a>
<img class="embed_thumbnail_url" src="/static/img/bg.webp" alt="Square thumbnail embedded image">
</a>
</p>
</div> </div>
</div> </div>
<div class="b"> <p class="image is-400x300 customizable">
<p class="image thumbnail customizable">
<a>
<img class="embed_thumbnail_url" src="/static/img/bg.webp" alt="Square thumbnail embedded image">
</a>
</p>
</div>
</div>
<p class="image is-400x300 customizable">
<a>
<img class="embed_image_url" src="/static/img/bg.webp" alt="Large embedded image">
</a>
</p>
<div class="embed-footer-box">
<p class="image is-20x20 customizable">
<a> <a>
<img class="is-rounded embed_footer_url" src="/static/img/bg.webp" alt="Footer profile-like image"> <img class="embed_image_url" src="/static/img/bg.webp" alt="Large embedded image">
</a> </a>
</p> </p>
<label class="is-sr-only" for="embedFooter">Embed Footer text</label> <div class="embed-footer-box">
<textarea class="discord-embed-footer message-input autoresize " <p class="image is-20x20 customizable">
placeholder="Embed Footer..." <a>
maxlength="2048" name="embed_footer" rows="1"></textarea> <img class="is-rounded embed_footer_url" src="/static/img/bg.webp" alt="Footer profile-like image">
</div> </a>
</div> </p>
</div>
</div>
</article>
</div>
<div class="column settings">
<div class="columns is-mobile reminder-topbar">
<div class="column">
<div class="field">
<div class="control">
<label class="label sr-only">Reminder Name</label>
<input class="input" type="text" name="name" placeholder="Reminder Name">
</div>
</div>
</div>
<div class="column is-narrow">
<button class="button is-rounded hide-box">
<span class="is-sr-only">Hide reminder</span><i class="fas fa-chevron-down"></i>
</button>
</div>
</div>
<div class="columns"> <label class="is-sr-only" for="embedFooter">Embed Footer text</label>
<div class="column"> <textarea class="discord-embed-footer message-input autoresize "
<div class="field channel-field"> placeholder="Embed Footer..."
<div class="collapses"> maxlength="2048" name="embed_footer" rows="1"></textarea>
<label class="label" for="channelOption">Channel*</label> </div>
</div>
<div class="control has-icons-left">
<div class="select">
<select name="channel" class="channel-selector">
</select>
</div>
<div class="icon is-small is-left">
<i class="fas fa-hashtag"></i>
</div> </div>
</div> </div>
</div> </div>
</div> </article>
<div class="column">
<div class="field">
<div class="control">
<label class="label collapses">
Time*
<input class="input" type="datetime-local" step="1" name="time">
</label>
</div>
</div>
</div>
</div> </div>
<div class="column settings">
<div class="columns">
<div class="column">
<div class="field channel-field">
<div class="collapses">
<label class="label" for="channelOption">Channel*</label>
</div>
<div class="control has-icons-left">
<div class="select">
<select name="channel" class="channel-selector">
</select>
</div>
<div class="icon is-small is-left">
<i class="fas fa-hashtag"></i>
</div>
</div>
</div>
</div>
<div class="column">
<div class="field">
<div class="control">
<label class="label collapses">
Time*
<input class="input" type="datetime-local" step="1" name="time">
</label>
</div>
</div>
</div>
</div>
<div class="collapses"> <div class="collapses split-controls">
<div class="patreon-only"> <div>
<div class="field"> <div class="patreon-only">
<label class="label">Interval <a class="foreground" href="/help/intervals"><i class="fas fa-question-circle"></i></a></label> <div class="field">
<div class="control intervalSelector" style="min-width: 400px;" > <label class="label">Interval <a class="foreground" href="/help/intervals"><i class="fas fa-question-circle"></i></a></label>
<div class="input interval-group"> <div class="control intervalSelector" style="min-width: 400px;" >
<div class="interval-group-left"> <div class="input interval-group">
<label> <div class="interval-group-left">
<span class="is-sr-only">Interval months</span> <label>
<input class="w2" type="text" pattern="\d*" name="interval_months" maxlength="2" placeholder=""> <span class="half-rem"></span> months, <span class="half-rem"></span> <span class="is-sr-only">Interval months</span>
</label> <input class="w2" type="text" pattern="\d*" name="interval_months" maxlength="2" placeholder=""> <span class="half-rem"></span> months, <span class="half-rem"></span>
<label> </label>
<span class="is-sr-only">Interval days</span> <label>
<input class="w3" type="text" pattern="\d*" name="interval_days" maxlength="4" placeholder=""> <span class="half-rem"></span> days, <span class="half-rem"></span> <span class="is-sr-only">Interval days</span>
</label> <input class="w3" type="text" pattern="\d*" name="interval_days" maxlength="4" placeholder=""> <span class="half-rem"></span> days, <span class="half-rem"></span>
<label> </label>
<span class="is-sr-only">Interval hours</span> <label>
<input class="w2" type="text" pattern="\d*" name="interval_hours" maxlength="2" placeholder="HH">: <span class="is-sr-only">Interval hours</span>
</label> <input class="w2" type="text" pattern="\d*" name="interval_hours" maxlength="2" placeholder="HH">:
<label> </label>
<span class="is-sr-only">Interval minutes</span> <label>
<input class="w2" type="text" pattern="\d*" name="interval_minutes" maxlength="2" placeholder="MM">: <span class="is-sr-only">Interval minutes</span>
</label> <input class="w2" type="text" pattern="\d*" name="interval_minutes" maxlength="2" placeholder="MM">:
<label> </label>
<span class="is-sr-only">Interval seconds</span> <label>
<input class="w2" type="text" pattern="\d*" name="interval_seconds" maxlength="2" placeholder="SS"> <span class="is-sr-only">Interval seconds</span>
<input class="w2" type="text" pattern="\d*" name="interval_seconds" maxlength="2" placeholder="SS">
</label>
</div>
<button class="clear"><span class="is-sr-only">Clear interval</span><span class="icon"><i class="fas fa-trash"></i></span></button>
</div>
</div>
</div>
<div class="field">
<div class="control">
<label class="label">
Expiration
<input class="input" type="datetime-local" step="1" name="expiration">
</label>
</div>
</div>
</div>
<div class="columns">
<div class="column has-text-centered">
<div class="is-boxed">
<label class="label">Enable TTS <input type="checkbox" name="tts"></label>
</div>
</div>
<div class="column has-text-centered">
<div class="file is-small is-boxed">
<label class="file-label">
<input class="file-input" type="file" name="attachment">
<span class="file-cta">
<span class="file-label">
Add Attachment
</span>
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
</span>
</label> </label>
</div> </div>
<button class="clear"><span class="is-sr-only">Clear interval</span><span class="icon"><i class="fas fa-trash"></i></span></button>
</div> </div>
</div> </div>
</div> </div>
<div class="field"> <div>
<div class="control"> <span class="pad-left"></span>
<label class="label"> {% if creating %}
Expiration <button class="button is-success" id="createReminder">
<input class="input" type="datetime-local" step="1" name="expiration"> <span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
</label> </button>
</div> <button class="button is-success is-outlined" id="createTemplate">
<span>Create Template</span> <span class="icon"><i class="fas fa-file-spreadsheet"></i></span>
</button>
<button class="button is-outlined show-modal is-pulled-right" data-modal="chooseTemplateModal">
Load Template
</button>
{% else %}
<button class="button is-success save-btn">
<span>Save</span> <span class="icon"><i class="fas fa-save"></i></span>
</button>
<button class="button is-warning disable-enable">
</button>
<button class="button is-danger delete-reminder">
Delete
</button>
<button class="button is-info demo-reminder">
Demo
</button>
{% endif %}
</div> </div>
</div> </div>
<div class="columns">
<div class="column has-text-centered">
<div class="is-boxed">
<label class="label">Enable TTS <input type="checkbox" name="tts"></label>
</div>
</div>
<div class="column has-text-centered">
<div class="file is-small is-boxed">
<label class="file-label">
<input class="file-input" type="file" name="attachment">
<span class="file-cta">
<span class="file-label">
Add Attachment
</span>
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
</span>
</label>
</div>
</div>
</div>
<div>
<span class="pad-left"></span>
{% if creating %}
<button class="button is-success" id="createReminder">
<span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
</button>
<button class="button is-success is-outlined" id="createTemplate">
<span>Create Template</span> <span class="icon"><i class="fas fa-file-spreadsheet"></i></span>
</button>
<button class="button is-outlined show-modal is-pulled-right" data-modal="chooseTemplateModal">
Load Template
</button>
{% else %}
<button class="button is-success save-btn">
<span>Save</span> <span class="icon"><i class="fas fa-save"></i></span>
</button>
<button class="button is-warning disable-enable">
</button>
<button class="button is-danger delete-reminder">
Delete
</button>
{% endif %}
</div>
</div> </div>
</div> </div>
</div> </div>