Add overlay when data fetching

This commit is contained in:
jude 2024-02-24 17:23:31 +00:00
parent a8ef3d03f9
commit e6b4ff8719
13 changed files with 171 additions and 146 deletions

3
.gitignore vendored
View File

@ -2,7 +2,7 @@ target
.env .env
/venv /venv
.cargo .cargo
/.idea .idea
web/static/index.html web/static/index.html
web/static/assets web/static/assets
# Logs # Logs
@ -22,7 +22,6 @@ dist-ssr
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
.idea
.DS_Store .DS_Store
*.suo *.suo
*.ntvs* *.ntvs*

2
Cargo.lock generated
View File

@ -2358,7 +2358,7 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]] [[package]]
name = "reminder-rs" name = "reminder-rs"
version = "1.7.0-rc1" version = "1.7.0-rc2"
dependencies = [ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"chrono", "chrono",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "reminder-rs" name = "reminder-rs"
version = "1.7.0-rc1" version = "1.7.0-rc2"
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

@ -4,16 +4,16 @@ The re-re-rewrite of the dashboard.
## Why ## Why
The existing beta variant of the dashboard is written using vanilla JavaScript. This is fine, The existing beta variant of the dashboard is written using vanilla JavaScript. This is fine,
but annoying to update. This would've been okay if I was more dedicated to updating the vanilla but annoying to update. This would've been okay if I was more dedicated to updating the vanilla
JavaScript too, but I want to experiment with "new" things. JavaScript too, but I want to experiment with "new" things.
This also allows me to expand my frontend skills, which is relevant to part of my job. This also allows me to expand my frontend skills, which is relevant to part of my job.
## Developing ## Developing
1. Download the parent repo: https://gitea.jellypro.xyz/jude/reminder-bot 1. Run both `npm run dev` and `cargo run`
2. Initialise the submodules: `git pull --recurse-submodules` 2. Symlink assets: assuming cloned
3. Run both `npm run dev` and `cargo run` into `$HOME`, `ln -s $HOME/reminder-bot/reminder-dashboard/dist/index.html $HOME/reminder-bot/web/static/index.html`
4. Symlink assets: assuming cloned into `$HOME`, `ln -s $HOME/reminder-bot/reminder-dashboard/dist/index.html $HOME/reminder-bot/web/static/index.html` and and
`ln -s $HOME/reminder-bot/reminder-dashboard/dist/static/assets $HOME/reminder-bot/web/static/assets` `ln -s $HOME/reminder-bot/reminder-dashboard/dist/static/assets $HOME/reminder-bot/web/static/assets`

View File

@ -14,7 +14,7 @@ enum Sort {
export const GuildReminders = () => { export const GuildReminders = () => {
const { guild } = useParams(); const { guild } = useParams();
const { isSuccess, data: guildReminders } = useQuery(fetchGuildReminders(guild)); const { isSuccess, isFetching, data: guildReminders } = useQuery(fetchGuildReminders(guild));
const { data: channels } = useQuery(fetchGuildChannels(guild)); const { data: channels } = useQuery(fetchGuildChannels(guild));
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
@ -85,7 +85,7 @@ export const GuildReminders = () => {
</div> </div>
</div> </div>
<div id={"guildReminders"}> <div id={"guildReminders"} className={isFetching ? "loading" : ""}>
{isSuccess && {isSuccess &&
guildReminders guildReminders
.sort((r1, r2) => { .sort((r1, r2) => {

View File

@ -97,3 +97,15 @@ macro_rules! json_err {
Err(json!({ "error": $message })) Err(json!({ "error": $message }))
}; };
} }
macro_rules! path {
($path:expr) => {{
use rocket::fs::relative;
if Path::new(concat!("/lib/reminder-rs/", $path)).exists() {
concat!("/lib/reminder-rs/", $path)
} else {
relative!($path)
}
}};
}

View File

@ -2,12 +2,8 @@ use std::path::Path;
use chrono::{naive::NaiveDateTime, Utc}; use chrono::{naive::NaiveDateTime, Utc};
use rand::{rngs::OsRng, seq::IteratorRandom}; use rand::{rngs::OsRng, seq::IteratorRandom};
use rocket::{ use rocket::{fs::NamedFile, http::CookieJar, response::Redirect, serde::json::json};
fs::{relative, NamedFile}, use rocket_dyn_templates::Template;
http::CookieJar,
response::Redirect,
serde::json::json,
};
use secrecy::ExposeSecret; use secrecy::ExposeSecret;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serenity::{ use serenity::{
@ -19,6 +15,7 @@ use serenity::{
use sqlx::types::Json; use sqlx::types::Json;
use crate::{ use crate::{
catchers::internal_server_error,
check_guild_subscription, check_subscription, check_guild_subscription, check_subscription,
consts::{ consts::{
CHARACTERS, DAY, DEFAULT_AVATAR, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, CHARACTERS, DAY, DEFAULT_AVATAR, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH,
@ -672,28 +669,44 @@ async fn create_database_channel(
Ok(row.id) Ok(row.id)
} }
#[get("/")] #[derive(Responder)]
pub async fn dashboard_home(cookies: &CookieJar<'_>) -> Result<NamedFile, Redirect> { pub enum DashboardPage {
if cookies.get_private("userid").is_some() { #[response(status = 200)]
NamedFile::open(Path::new(relative!("static/index.html"))).await.map_err(|e| { Ok(NamedFile),
warn!("Couldn't render dashboard: {:?}", e); #[response(status = 200)]
Unauthorised(Redirect),
#[response(status = 500)]
NotConfigured(Template),
}
Redirect::to("/login/discord") #[get("/")]
}) pub async fn dashboard_home(cookies: &CookieJar<'_>) -> DashboardPage {
if cookies.get_private("userid").is_some() {
match NamedFile::open(Path::new(path!("static/index.html"))).await {
Ok(f) => DashboardPage::Ok(f),
Err(e) => {
warn!("Couldn't render dashboard: {:?}", e);
DashboardPage::NotConfigured(internal_server_error().await)
}
}
} else { } else {
Err(Redirect::to("/login/discord")) DashboardPage::Unauthorised(Redirect::to("/login/discord"))
} }
} }
#[get("/<_..>")] #[get("/<_..>")]
pub async fn dashboard(cookies: &CookieJar<'_>) -> Result<NamedFile, Redirect> { pub async fn dashboard(cookies: &CookieJar<'_>) -> DashboardPage {
if cookies.get_private("userid").is_some() { if cookies.get_private("userid").is_some() {
NamedFile::open(Path::new(relative!("static/index.html"))).await.map_err(|e| { match NamedFile::open(Path::new(path!("static/index.html"))).await {
warn!("Couldn't render dashboard: {:?}", e); Ok(f) => DashboardPage::Ok(f),
Err(e) => {
warn!("Couldn't render dashboard: {:?}", e);
Redirect::to("/login/discord") DashboardPage::NotConfigured(internal_server_error().await)
}) }
}
} else { } else {
Err(Redirect::to("/login/discord")) DashboardPage::Unauthorised(Redirect::to("/login/discord"))
} }
} }

View File

@ -1 +1 @@
/home/jude/reminder-dashboard/dist/static/assets /home/jude/reminder-bot/reminder-dashboard/dist/static/assets

View File

@ -55,6 +55,7 @@ div.reminderContent.is-collapsed .hide-box {
div.reminderContent.is-collapsed .hide-box i { div.reminderContent.is-collapsed .hide-box i {
transform: rotate(90deg); transform: rotate(90deg);
} }
/* END */ /* END */
/* dashboard styles */ /* dashboard styles */
@ -202,6 +203,7 @@ div.interval-group {
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
} }
/* !Interval inputs */ /* !Interval inputs */
.left-pad { .left-pad {
@ -239,7 +241,7 @@ span.spacer {
} }
nav .dashboard-button { nav .dashboard-button {
background: white ; background: white;
} }
span.patreon-color { span.patreon-color {
@ -694,7 +696,7 @@ li.highlight {
} }
} }
/* loader */ /* loaders */
#loader { #loader {
position: fixed; position: fixed;
top: 0; top: 0;
@ -707,6 +709,14 @@ li.highlight {
font-size: 6rem; font-size: 6rem;
} }
.loading {
position: absolute;
top: 0;
height: 100%;
width: 100%;
background-color: rgba(255, 255, 255, 0.8);
}
/* END */ /* END */
div.reminderError { div.reminderError {

View File

@ -1 +0,0 @@
/home/jude/reminder-dashboard/dist/index.html

View File

@ -32,27 +32,54 @@
</div> </div>
{% endif %} {% endif %}
<nav class="navbar is-dark is-spaced is-size-4" role="navigation" aria-label="main navigation"> <div style="min-height: 100vh;">
<div class="navbar-brand"> <nav class="navbar is-dark is-spaced is-size-4" role="navigation" aria-label="main navigation">
<a class="navbar-item" href="/"> <div class="navbar-brand">
<figure class="image"> <a class="navbar-item" href="/">
<img src="/static/img/logo_flat.webp" alt="Reminder Bot Logo" class="is-rounded" style="width: auto;"> <figure class="image">
</figure> <img src="/static/img/logo_flat.webp" alt="Reminder Bot Logo" class="is-rounded" style="width: auto;">
</a> </figure>
</a>
<a role="button" class="navbar-burger is-right" aria-label="menu" aria-expanded="false" data-target="pageNavbar"> <a role="button" class="navbar-burger is-right" aria-label="menu" aria-expanded="false" data-target="pageNavbar">
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
</a> </a>
</div> </div>
<div class="navbar-menu"> <div class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" href="https://invite.reminder-bot.com">
<i class="fas fa-plus"></i>
</a>
<a class="navbar-item" href="https://gitea.jellypro.xyz/jude">
<i class="fab fa-git-square"></i>
</a>
<a class="navbar-item" href="https://discord.jellywx.com">
<i class="fab fa-discord"></i>
</a>
<a class="navbar-item" href="/help">
<i class="fas fa-book"></i>
</a>
</div>
<div class="navbar-end">
<div class="navbar-item">
<a class="button is-rounded is-light" href="/dashboard">
<p>
<span>Dashboard</span> <span class="icon"><i class="fas fa-chevron-right"></i></span>
</p>
</a>
</div>
</div>
</div>
<div class="navbar-menu is-hidden-desktop" id="pageNavbar">
<a class="navbar-item" href="https://invite.reminder-bot.com"> <a class="navbar-item" href="https://invite.reminder-bot.com">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
</a> </a>
<a class="navbar-item" href="https://gitea.jellypro.xyz/jude"> <a class="navbar-item" href="https://github.com/jellywx">
<i class="fab fa-git-square"></i> <i class="fab fa-github"></i>
</a> </a>
<a class="navbar-item" href="https://discord.jellywx.com"> <a class="navbar-item" href="https://discord.jellywx.com">
<i class="fab fa-discord"></i> <i class="fab fa-discord"></i>
@ -60,92 +87,67 @@
<a class="navbar-item" href="/help"> <a class="navbar-item" href="/help">
<i class="fas fa-book"></i> <i class="fas fa-book"></i>
</a> </a>
</div>
<div class="navbar-end">
<div class="navbar-item"> <div class="navbar-item">
<a class="button is-rounded is-light" href="/dashboard"> <a href="/dashboard">
<p> <p>
<span>Dashboard</span> <span class="icon"><i class="fas fa-chevron-right"></i></span> Dashboard <span class="icon"><i class="fas fa-chevron-right"></i></span>
</p> </p>
</a> </a>
</div> </div>
</div> </div>
</div> </nav>
<div class="navbar-menu is-hidden-desktop" id="pageNavbar"> {% if not hide_title_block %}
<a class="navbar-item" href="https://invite.reminder-bot.com"> <section class="hero is-small is-dark">
<i class="fas fa-plus"></i> <div class="hero-body">
</a> <div class="">
<a class="navbar-item" href="https://github.com/jellywx"> <h1 class="title is-1">{{ page_title }}</h1>
<i class="fab fa-github"></i> <h2 class="subtitle is-3">{{ page_subtitle }}
</a> {% if page_emoji %}
<a class="navbar-item" href="https://discord.jellywx.com"> <span class="icon"><i class="fas {{ page_emoji }}"></i></span>
<i class="fab fa-discord"></i> {% endif %}
</a> </h2>
<a class="navbar-item" href="/help"> </div>
<i class="fas fa-book"></i>
</a>
<div class="navbar-item">
<a href="/dashboard">
<p>
Dashboard <span class="icon"><i class="fas fa-chevron-right"></i></span>
</p>
</a>
</div>
</div>
</nav>
{% if not hide_title_block %}
<section class="hero is-small is-dark">
<div class="hero-body">
<div class="">
<h1 class="title is-1">{{ page_title }}</h1>
<h2 class="subtitle is-3">{{ page_subtitle }}
{% if page_emoji %}
<span class="icon"><i class="fas {{ page_emoji }}"></i></span>
{% endif %}
</h2>
</div> </div>
</div>
{% if show_invite %} {% if show_invite %}
<div class="hero-foot has-text-centered"> <div class="hero-foot has-text-centered">
<a class="button is-size-4 is-rounded is-success" href="https://invite.reminder-bot.com"> <a class="button is-size-4 is-rounded is-success" href="https://invite.reminder-bot.com">
<p class="is-size-4"> <p class="is-size-4">
<span>Add to your Server</span> <span class="icon"><i class="fas fa-chevron-right"></i></span> <span>Add to your Server</span> <span class="icon"><i class="fas fa-chevron-right"></i></span>
</p> </p>
</a> </a>
</div> </div>
{% elif show_contact %} {% elif show_contact %}
<div class="hero-foot has-text-centered"> <div class="hero-foot has-text-centered">
<a class="button is-size-4 is-rounded is-primary" href="https://discord.jellywx.com"> <a class="button is-size-4 is-rounded is-primary" href="https://discord.jellywx.com">
<p class="is-size-4"> <p class="is-size-4">
<span>Join Discord</span> <span class="icon"><i class="fas fa-chevron-right"></i></span> <span>Join Discord</span> <span class="icon"><i class="fas fa-chevron-right"></i></span>
</p> </p>
</a> </a>
</div> </div>
{% elif show_login %} {% elif show_login %}
<div class="hero-foot has-text-centered"> <div class="hero-foot has-text-centered">
<a class="button is-size-4 is-rounded is-light" href="/login/discord"> <a class="button is-size-4 is-rounded is-light" href="/login/discord">
<p class="is-size-4"> <p class="is-size-4">
<span>Login with Discord</span> <span class="icon"><i class="fas fa-chevron-right"></i></span> <span>Login with Discord</span> <span class="icon"><i class="fas fa-chevron-right"></i></span>
</p> </p>
</a> </a>
</div> </div>
{% endif %} {% endif %}
</section> </section>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 160"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 160">
<path transform="scale(1, 0.5)" fill="#98dc9a" fill-opacity="1" d="M0,288L80,266.7C160,245,320,203,480,202.7C640,203,800,245,960,218.7C1120,192,1280,96,1360,48L1440,0L1440,0L1360,0C1280,0,1120,0,960,0C800,0,640,0,480,0C320,0,160,0,80,0L0,0Z"></path> <path transform="scale(1, 0.5)" fill="#98dc9a" fill-opacity="1" d="M0,288L80,266.7C160,245,320,203,480,202.7C640,203,800,245,960,218.7C1120,192,1280,96,1360,48L1440,0L1440,0L1360,0C1280,0,1120,0,960,0C800,0,640,0,480,0C320,0,160,0,80,0L0,0Z"></path>
<path transform="scale(1, 0.5)" fill="#363636" fill-opacity="1" d="M0,224L60,202.7C120,181,240,139,360,138.7C480,139,600,181,720,197.3C840,213,960,203,1080,176C1200,149,1320,107,1380,85.3L1440,64L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"> <path transform="scale(1, 0.5)" fill="#363636" fill-opacity="1" d="M0,224L60,202.7C120,181,240,139,360,138.7C480,139,600,181,720,197.3C840,213,960,203,1080,176C1200,149,1320,107,1380,85.3L1440,64L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z">
</path> </path>
</svg> </svg>
{% endif %} {% endif %}
{% block content %} {% block content %}
{% endblock %} {% endblock %}
</div>
<br> <br>
<footer class="footer"> <footer class="footer">
@ -165,25 +167,15 @@
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) { if ($navbarBurgers.length > 0) {
$navbarBurgers.forEach( el => {
el.addEventListener('click', () => {
const target = el.dataset.target;
const $target = document.getElementById(target);
// Add a click event on each of them el.classList.toggle('is-active');
$navbarBurgers.forEach( el => { $target.classList.toggle('is-active');
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target;
const $target = document.getElementById(target);
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active');
$target.classList.toggle('is-active');
}); });
}); });
} }

View File

@ -6,5 +6,5 @@
{% set show_contact = True %} {% set show_contact = True %}
{% set page_title = "Forbidden" %} {% set page_title = "Forbidden" %}
{% set page_subtitle = "You currently cannot access this page, if it exists. Sorry." %} {% set page_subtitle = "You currently cannot access this page, if it exists." %}
{% endblock %} {% endblock %}

View File

@ -6,5 +6,5 @@
{% set show_contact = True %} {% set show_contact = True %}
{% set page_title = "File Not Found" %} {% set page_title = "File Not Found" %}
{% set page_subtitle = "This page does not exist. Sorry." %} {% set page_subtitle = "This page does not exist." %}
{% endblock %} {% endblock %}