graceful shutdown
This commit is contained in:
		@@ -2,14 +2,31 @@ mod sender;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use std::env;
 | 
					use std::env;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use log::info;
 | 
					use log::{info, warn};
 | 
				
			||||||
use serenity::client::Context;
 | 
					use serenity::client::Context;
 | 
				
			||||||
use sqlx::{Executor, MySql};
 | 
					use sqlx::{Executor, MySql};
 | 
				
			||||||
use tokio::time::{sleep_until, Duration, Instant};
 | 
					use tokio::{
 | 
				
			||||||
 | 
					    sync::broadcast::Receiver,
 | 
				
			||||||
 | 
					    time::{sleep_until, Duration, Instant},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Database = MySql;
 | 
					type Database = MySql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn initialize(ctx: Context, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
					pub async fn initialize(
 | 
				
			||||||
 | 
					    mut kill: Receiver<()>,
 | 
				
			||||||
 | 
					    ctx: Context,
 | 
				
			||||||
 | 
					    pool: impl Executor<'_, Database = Database> + Copy,
 | 
				
			||||||
 | 
					) -> Result<(), &'static str> {
 | 
				
			||||||
 | 
					    tokio::select! {
 | 
				
			||||||
 | 
					        output = _initialize(ctx, pool) => Ok(output),
 | 
				
			||||||
 | 
					        _ = kill.recv() => {
 | 
				
			||||||
 | 
					            warn!("Received terminate signal. Goodbye");
 | 
				
			||||||
 | 
					            Err("Received terminate signal. Goodbye")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn _initialize(ctx: Context, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
				
			||||||
    let remind_interval = env::var("REMIND_INTERVAL")
 | 
					    let remind_interval = env::var("REMIND_INTERVAL")
 | 
				
			||||||
        .map(|inner| inner.parse::<u64>().ok())
 | 
					        .map(|inner| inner.parse::<u64>().ok())
 | 
				
			||||||
        .ok()
 | 
					        .ok()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
use std::{collections::HashMap, env, sync::atomic::Ordering};
 | 
					use std::{collections::HashMap, env, sync::atomic::Ordering};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use log::{info, warn};
 | 
					use log::{error, info, warn};
 | 
				
			||||||
use poise::{
 | 
					use poise::{
 | 
				
			||||||
    serenity::{model::interactions::Interaction, utils::shard_id},
 | 
					    serenity::{model::interactions::Interaction, utils::shard_id},
 | 
				
			||||||
    serenity_prelude as serenity,
 | 
					    serenity_prelude as serenity,
 | 
				
			||||||
@@ -15,10 +15,12 @@ pub async fn listener(
 | 
				
			|||||||
) -> Result<(), Error> {
 | 
					) -> Result<(), Error> {
 | 
				
			||||||
    match event {
 | 
					    match event {
 | 
				
			||||||
        poise::Event::CacheReady { .. } => {
 | 
					        poise::Event::CacheReady { .. } => {
 | 
				
			||||||
            info!("Cache Ready!");
 | 
					            info!("Cache Ready! Preparing extra processes");
 | 
				
			||||||
            info!("Preparing to send reminders");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if !data.is_loop_running.load(Ordering::Relaxed) {
 | 
					            if !data.is_loop_running.load(Ordering::Relaxed) {
 | 
				
			||||||
 | 
					                let kill_tx = data.broadcast.clone();
 | 
				
			||||||
 | 
					                let kill_recv = data.broadcast.subscribe();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let ctx1 = ctx.clone();
 | 
					                let ctx1 = ctx.clone();
 | 
				
			||||||
                let ctx2 = ctx.clone();
 | 
					                let ctx2 = ctx.clone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,7 +31,12 @@ pub async fn listener(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if !run_settings.contains("postman") {
 | 
					                if !run_settings.contains("postman") {
 | 
				
			||||||
                    tokio::spawn(async move {
 | 
					                    tokio::spawn(async move {
 | 
				
			||||||
                        postman::initialize(ctx1, &pool1).await;
 | 
					                        match postman::initialize(kill_recv, ctx1, &pool1).await {
 | 
				
			||||||
 | 
					                            Ok(_) => {}
 | 
				
			||||||
 | 
					                            Err(e) => {
 | 
				
			||||||
 | 
					                                error!("postman exiting: {}", e);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    warn!("Not running postman")
 | 
					                    warn!("Not running postman")
 | 
				
			||||||
@@ -37,7 +44,7 @@ pub async fn listener(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if !run_settings.contains("web") {
 | 
					                if !run_settings.contains("web") {
 | 
				
			||||||
                    tokio::spawn(async move {
 | 
					                    tokio::spawn(async move {
 | 
				
			||||||
                        reminder_web::initialize(ctx2, pool2).await.unwrap();
 | 
					                        reminder_web::initialize(kill_tx, ctx2, pool2).await.unwrap();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    warn!("Not running web")
 | 
					                    warn!("Not running web")
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										39
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -12,7 +12,13 @@ mod models;
 | 
				
			|||||||
mod time_parser;
 | 
					mod time_parser;
 | 
				
			||||||
mod utils;
 | 
					mod utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{collections::HashMap, env, fmt::Formatter, sync::atomic::AtomicBool};
 | 
					use std::{
 | 
				
			||||||
 | 
					    collections::HashMap,
 | 
				
			||||||
 | 
					    env,
 | 
				
			||||||
 | 
					    error::Error as StdError,
 | 
				
			||||||
 | 
					    fmt::{Debug, Display, Formatter},
 | 
				
			||||||
 | 
					    sync::atomic::AtomicBool,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use chrono_tz::Tz;
 | 
					use chrono_tz::Tz;
 | 
				
			||||||
use dotenv::dotenv;
 | 
					use dotenv::dotenv;
 | 
				
			||||||
@@ -21,7 +27,7 @@ use poise::serenity::model::{
 | 
				
			|||||||
    id::{GuildId, UserId},
 | 
					    id::{GuildId, UserId},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use sqlx::{MySql, Pool};
 | 
					use sqlx::{MySql, Pool};
 | 
				
			||||||
use tokio::sync::RwLock;
 | 
					use tokio::sync::{broadcast, broadcast::Sender, RwLock};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds},
 | 
					    commands::{info_cmds, moderation_cmds, reminder_cmds, todo_cmds},
 | 
				
			||||||
@@ -43,6 +49,7 @@ pub struct Data {
 | 
				
			|||||||
    recording_macros: RwLock<HashMap<(GuildId, UserId), CommandMacro<Data, Error>>>,
 | 
					    recording_macros: RwLock<HashMap<(GuildId, UserId), CommandMacro<Data, Error>>>,
 | 
				
			||||||
    popular_timezones: Vec<Tz>,
 | 
					    popular_timezones: Vec<Tz>,
 | 
				
			||||||
    is_loop_running: AtomicBool,
 | 
					    is_loop_running: AtomicBool,
 | 
				
			||||||
 | 
					    broadcast: Sender<()>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl std::fmt::Debug for Data {
 | 
					impl std::fmt::Debug for Data {
 | 
				
			||||||
@@ -51,8 +58,33 @@ impl std::fmt::Debug for Data {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Ended;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Debug for Ended {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        f.write_str("Process ended.")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Display for Ended {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        f.write_str("Process ended.")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl StdError for Ended {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[tokio::main]
 | 
					#[tokio::main]
 | 
				
			||||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
					async fn main() -> Result<(), Box<dyn StdError + Send + Sync>> {
 | 
				
			||||||
 | 
					    let (tx, mut rx) = broadcast::channel(16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tokio::select! {
 | 
				
			||||||
 | 
					        output = _main(tx) => output,
 | 
				
			||||||
 | 
					        _ = rx.recv() => Err(Box::new(Ended) as Box<dyn StdError + Send + Sync>)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
 | 
				
			||||||
    env_logger::init();
 | 
					    env_logger::init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dotenv()?;
 | 
					    dotenv()?;
 | 
				
			||||||
@@ -157,6 +189,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
				
			|||||||
                    popular_timezones,
 | 
					                    popular_timezones,
 | 
				
			||||||
                    recording_macros: Default::default(),
 | 
					                    recording_macros: Default::default(),
 | 
				
			||||||
                    is_loop_running: AtomicBool::new(false),
 | 
					                    is_loop_running: AtomicBool::new(false),
 | 
				
			||||||
 | 
					                    broadcast: tx,
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ mod routes;
 | 
				
			|||||||
use std::{collections::HashMap, env};
 | 
					use std::{collections::HashMap, env};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
 | 
					use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
 | 
				
			||||||
use rocket::fs::FileServer;
 | 
					use rocket::{fs::FileServer, tokio::sync::broadcast::Sender};
 | 
				
			||||||
use rocket_dyn_templates::Template;
 | 
					use rocket_dyn_templates::Template;
 | 
				
			||||||
use serenity::{
 | 
					use serenity::{
 | 
				
			||||||
    client::Context,
 | 
					    client::Context,
 | 
				
			||||||
@@ -53,6 +53,7 @@ async fn internal_server_error() -> Template {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn initialize(
 | 
					pub async fn initialize(
 | 
				
			||||||
 | 
					    kill_channel: Sender<()>,
 | 
				
			||||||
    serenity_context: Context,
 | 
					    serenity_context: Context,
 | 
				
			||||||
    db_pool: Pool<Database>,
 | 
					    db_pool: Pool<Database>,
 | 
				
			||||||
) -> Result<(), Box<dyn std::error::Error>> {
 | 
					) -> Result<(), Box<dyn std::error::Error>> {
 | 
				
			||||||
@@ -119,6 +120,10 @@ pub async fn initialize(
 | 
				
			|||||||
        .launch()
 | 
					        .launch()
 | 
				
			||||||
        .await?;
 | 
					        .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    warn!("Exiting rocket runtime");
 | 
				
			||||||
 | 
					    // distribute kill signal
 | 
				
			||||||
 | 
					    kill_channel.send(());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,14 @@ button.change-color {
 | 
				
			|||||||
    left: calc(-1rem - 40px);
 | 
					    left: calc(-1rem - 40px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button.disable-enable[data-action="enable"]:after {
 | 
				
			||||||
 | 
					    content: "Enable";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button.disable-enable[data-action="disable"]:after {
 | 
				
			||||||
 | 
					    content: "Disable";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.media-content {
 | 
					.media-content {
 | 
				
			||||||
    overflow-x: visible;
 | 
					    overflow-x: visible;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,10 +3,7 @@ let $discordFrame;
 | 
				
			|||||||
const $loader = document.querySelector("#loader");
 | 
					const $loader = document.querySelector("#loader");
 | 
				
			||||||
const $colorPickerModal = document.querySelector("div#pickColorModal");
 | 
					const $colorPickerModal = document.querySelector("div#pickColorModal");
 | 
				
			||||||
const $colorPickerInput = $colorPickerModal.querySelector("input");
 | 
					const $colorPickerInput = $colorPickerModal.querySelector("input");
 | 
				
			||||||
 | 
					const $deleteReminderBtn = document.querySelector("#delete-reminder-confirm");
 | 
				
			||||||
let timezone = luxon.DateTime.now().zone.name;
 | 
					 | 
				
			||||||
const browserTimezone = luxon.DateTime.now().zone.name;
 | 
					 | 
				
			||||||
let botTimezone = "UTC";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let channels;
 | 
					let channels;
 | 
				
			||||||
let roles;
 | 
					let roles;
 | 
				
			||||||
@@ -75,6 +72,8 @@ function fetch_roles(guild_id) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function fetch_reminders(guild_id) {
 | 
					async function fetch_reminders(guild_id) {
 | 
				
			||||||
 | 
					    document.dispatchEvent(new Event("remindersLoading"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const $reminderBox = document.querySelector("div#guildReminders");
 | 
					    const $reminderBox = document.querySelector("div#guildReminders");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // reset div contents
 | 
					    // reset div contents
 | 
				
			||||||
@@ -113,7 +112,6 @@ async function fetch_reminders(guild_id) {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let $enableBtn = newFrame.querySelector(".disable-enable");
 | 
					                    let $enableBtn = newFrame.querySelector(".disable-enable");
 | 
				
			||||||
                    $enableBtn.textContent = reminder["enabled"] ? "Disable" : "Enable";
 | 
					 | 
				
			||||||
                    $enableBtn.dataset.action = reminder["enabled"]
 | 
					                    $enableBtn.dataset.action = reminder["enabled"]
 | 
				
			||||||
                        ? "disable"
 | 
					                        ? "disable"
 | 
				
			||||||
                        : "enable";
 | 
					                        : "enable";
 | 
				
			||||||
@@ -164,14 +162,36 @@ document.addEventListener("remindersLoaded", (event) => {
 | 
				
			|||||||
                    if (data.error) {
 | 
					                    if (data.error) {
 | 
				
			||||||
                        show_error(data.error);
 | 
					                        show_error(data.error);
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        enableBtn.textContent = data["enabled"] ? "Disable" : "Enable";
 | 
					 | 
				
			||||||
                        enableBtn.dataset.action = data["enabled"] ? "enable" : "disable";
 | 
					                        enableBtn.dataset.action = data["enabled"] ? "enable" : "disable";
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reminder.node
 | 
				
			||||||
 | 
					            .querySelector("button.delete-reminder")
 | 
				
			||||||
 | 
					            .addEventListener("click", () => {
 | 
				
			||||||
 | 
					                let uid = reminder.node.closest(".reminderContent").dataset.uid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $deleteReminderBtn.dataset["uid"] = uid;
 | 
				
			||||||
 | 
					                $deleteReminderBtn.closest(".modal").classList.toggle("is-active");
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$deleteReminderBtn.addEventListener("click", () => {
 | 
				
			||||||
 | 
					    let guild = document.querySelector(".guildList a.is-active").dataset["guild"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fetch(`/dashboard/api/guild/${guild}/reminders`, {
 | 
				
			||||||
 | 
					        method: "DELETE",
 | 
				
			||||||
 | 
					        body: JSON.stringify({
 | 
				
			||||||
 | 
					            uid: $deleteReminderBtn.dataset["uid"],
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					    }).then(() => {
 | 
				
			||||||
 | 
					        document.querySelector("#deleteReminderModal").classList.remove("is-active");
 | 
				
			||||||
 | 
					        fetch_reminders(guild);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function show_error(error) {
 | 
					function show_error(error) {
 | 
				
			||||||
    document.getElementById("errors").querySelector("span.error-message").textContent =
 | 
					    document.getElementById("errors").querySelector("span.error-message").textContent =
 | 
				
			||||||
        error;
 | 
					        error;
 | 
				
			||||||
@@ -182,60 +202,6 @@ function show_error(error) {
 | 
				
			|||||||
    }, 5000);
 | 
					    }, 5000);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_times() {
 | 
					 | 
				
			||||||
    document.querySelectorAll("span.set-timezone").forEach((element) => {
 | 
					 | 
				
			||||||
        element.textContent = timezone;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelectorAll("span.set-time").forEach((element) => {
 | 
					 | 
				
			||||||
        element.textContent = luxon.DateTime.now().setZone(timezone).toFormat("HH:mm");
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelectorAll("span.browser-timezone").forEach((element) => {
 | 
					 | 
				
			||||||
        element.textContent = browserTimezone;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelectorAll("span.browser-time").forEach((element) => {
 | 
					 | 
				
			||||||
        element.textContent = luxon.DateTime.now().toFormat("HH:mm");
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelectorAll("span.bot-timezone").forEach((element) => {
 | 
					 | 
				
			||||||
        element.textContent = botTimezone;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelectorAll("span.bot-time").forEach((element) => {
 | 
					 | 
				
			||||||
        element.textContent = luxon.DateTime.now().setZone(botTimezone).toFormat("HH:mm");
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
window.setInterval(() => {
 | 
					 | 
				
			||||||
    update_times();
 | 
					 | 
				
			||||||
}, 30000);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
document.getElementById("set-bot-timezone").addEventListener("click", () => {
 | 
					 | 
				
			||||||
    timezone = botTimezone;
 | 
					 | 
				
			||||||
    update_times();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
document.getElementById("set-browser-timezone").addEventListener("click", () => {
 | 
					 | 
				
			||||||
    timezone = browserTimezone;
 | 
					 | 
				
			||||||
    update_times();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
document.getElementById("update-bot-timezone").addEventListener("click", () => {
 | 
					 | 
				
			||||||
    timezone = browserTimezone;
 | 
					 | 
				
			||||||
    fetch("/dashboard/api/user", {
 | 
					 | 
				
			||||||
        method: "PATCH",
 | 
					 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
            Accept: "application/json",
 | 
					 | 
				
			||||||
            "Content-Type": "application/json",
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        body: JSON.stringify({ timezone: timezone }),
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
        .then((response) => response.json())
 | 
					 | 
				
			||||||
        .then((data) => {
 | 
					 | 
				
			||||||
            if (data.error) {
 | 
					 | 
				
			||||||
                show_error(data.error);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                botTimezone = browserTimezone;
 | 
					 | 
				
			||||||
                update_times();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$colorPickerInput.value = colorPicker.color.hexString;
 | 
					$colorPickerInput.value = colorPicker.color.hexString;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$colorPickerInput.addEventListener("input", () => {
 | 
					$colorPickerInput.addEventListener("input", () => {
 | 
				
			||||||
@@ -482,6 +448,7 @@ $createReminder.querySelector("button#createReminder").addEventListener("click",
 | 
				
			|||||||
        .then((data) => console.log(data));
 | 
					        .then((data) => console.log(data));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // process response
 | 
					    // process response
 | 
				
			||||||
 | 
					    fetch_reminders(guild);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // reset inputs
 | 
					    // reset inputs
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								web/static/js/timezone.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								web/static/js/timezone.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					let timezone = luxon.DateTime.now().zone.name;
 | 
				
			||||||
 | 
					const browserTimezone = luxon.DateTime.now().zone.name;
 | 
				
			||||||
 | 
					let botTimezone = "UTC";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_times() {
 | 
				
			||||||
 | 
					    document.querySelectorAll("span.set-timezone").forEach((element) => {
 | 
				
			||||||
 | 
					        element.textContent = timezone;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelectorAll("span.set-time").forEach((element) => {
 | 
				
			||||||
 | 
					        element.textContent = luxon.DateTime.now().setZone(timezone).toFormat("HH:mm");
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelectorAll("span.browser-timezone").forEach((element) => {
 | 
				
			||||||
 | 
					        element.textContent = browserTimezone;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelectorAll("span.browser-time").forEach((element) => {
 | 
				
			||||||
 | 
					        element.textContent = luxon.DateTime.now().toFormat("HH:mm");
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelectorAll("span.bot-timezone").forEach((element) => {
 | 
				
			||||||
 | 
					        element.textContent = botTimezone;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelectorAll("span.bot-time").forEach((element) => {
 | 
				
			||||||
 | 
					        element.textContent = luxon.DateTime.now().setZone(botTimezone).toFormat("HH:mm");
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					window.setInterval(() => {
 | 
				
			||||||
 | 
					    update_times();
 | 
				
			||||||
 | 
					}, 30000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.getElementById("set-bot-timezone").addEventListener("click", () => {
 | 
				
			||||||
 | 
					    timezone = botTimezone;
 | 
				
			||||||
 | 
					    update_times();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					document.getElementById("set-browser-timezone").addEventListener("click", () => {
 | 
				
			||||||
 | 
					    timezone = browserTimezone;
 | 
				
			||||||
 | 
					    update_times();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					document.getElementById("update-bot-timezone").addEventListener("click", () => {
 | 
				
			||||||
 | 
					    timezone = browserTimezone;
 | 
				
			||||||
 | 
					    fetch("/dashboard/api/user", {
 | 
				
			||||||
 | 
					        method: "PATCH",
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					            Accept: "application/json",
 | 
				
			||||||
 | 
					            "Content-Type": "application/json",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        body: JSON.stringify({ timezone: timezone }),
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					        .then((response) => response.json())
 | 
				
			||||||
 | 
					        .then((data) => {
 | 
				
			||||||
 | 
					            if (data.error) {
 | 
				
			||||||
 | 
					                show_error(data.error);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                botTimezone = browserTimezone;
 | 
				
			||||||
 | 
					                update_times();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -140,6 +140,27 @@
 | 
				
			|||||||
    <button class="modal-close is-large close-modal" aria-label="close"></button>
 | 
					    <button class="modal-close is-large close-modal" aria-label="close"></button>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="modal" id="deleteReminderModal">
 | 
				
			||||||
 | 
					    <div class="modal-background"></div>
 | 
				
			||||||
 | 
					    <div class="modal-card">
 | 
				
			||||||
 | 
					        <header class="modal-card-head">
 | 
				
			||||||
 | 
					            <label class="modal-card-title" for="urlInput">Delete Reminder</label>
 | 
				
			||||||
 | 
					            <button class="delete close-modal" aria-label="close"></button>
 | 
				
			||||||
 | 
					        </header>
 | 
				
			||||||
 | 
					        <section class="modal-card-body">
 | 
				
			||||||
 | 
					            <p>
 | 
				
			||||||
 | 
					                This reminder will be permenantly deleted. Are you sure?
 | 
				
			||||||
 | 
					            </p>
 | 
				
			||||||
 | 
					            <br>
 | 
				
			||||||
 | 
					            <div class="has-text-centered">
 | 
				
			||||||
 | 
					                <button class="button is-danger" id="delete-reminder-confirm">Delete</button>
 | 
				
			||||||
 | 
					                <button class="button is-light close-modal">Cancel</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </section>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <button class="modal-close is-large close-modal" aria-label="close"></button>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="columns is-gapless dashboard-frame">
 | 
					<div class="columns is-gapless dashboard-frame">
 | 
				
			||||||
    <div class="column is-2 is-sidebar-menu dashboard-sidebar is-hidden-touch" style="display: flex; flex-direction: column;">
 | 
					    <div class="column is-2 is-sidebar-menu dashboard-sidebar is-hidden-touch" style="display: flex; flex-direction: column;">
 | 
				
			||||||
        <a href="/">
 | 
					        <a href="/">
 | 
				
			||||||
@@ -300,6 +321,7 @@
 | 
				
			|||||||
    {% include "reminder_dashboard/personal_reminder" %}
 | 
					    {% include "reminder_dashboard/personal_reminder" %}
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script src="/static/js/timezone.js"></script>
 | 
				
			||||||
<script src="/static/js/main.js"></script>
 | 
					<script src="/static/js/main.js"></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,9 +215,8 @@
 | 
				
			|||||||
                    Saved!
 | 
					                    Saved!
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
                <button class="button is-warning disable-enable">
 | 
					                <button class="button is-warning disable-enable">
 | 
				
			||||||
                    Disable
 | 
					 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
                <button class="button is-danger">
 | 
					                <button class="button is-danger delete-reminder">
 | 
				
			||||||
                    Delete
 | 
					                    Delete
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user