diff --git a/apps/static/js/home/content.js b/apps/static/js/home/content.js
index d4b946f..aa94517 100644
--- a/apps/static/js/home/content.js
+++ b/apps/static/js/home/content.js
@@ -77,16 +77,14 @@ async function initContentTable() {
data: "creation_datetime",
className: "text-nowrap",
render: function(data, type) {
- // return new Date(data).toISOString().split("T")[0];
let dateTime = new Date(data);
- let dateTimeString = formatDate(dateTime);
return $(`
- ${dateTime.toISOString().split("T")[0]}
+ data-bs-html="true"
+ data-bs-custom-class="text-center"
+ data-bs-toggle="popover"
+ data-bs-content="${formatStringDate(dateTime, "%a, %D %B, %Y
%H:%M:%S")}">
+ ${formatStringDate(dateTime, "%D, %b %Y")}
`).popover()[0];
}
diff --git a/apps/static/js/home/index.js b/apps/static/js/home/index.js
index 1460901..4ddecd6 100644
--- a/apps/static/js/home/index.js
+++ b/apps/static/js/home/index.js
@@ -21,42 +21,6 @@ $(document).on("selectedServerChange", function() {
$("#subscriptionsTab").click();
});
-function formatDate(date) {
-
- // Array of weekday names
- const weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
-
- // Array of month names
- const months = [
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
- ];
-
- // Get individual components
- let hours = String(date.getHours()).padStart(2, '0');
- let minutes = String(date.getMinutes()).padStart(2, '0');
- let seconds = String(date.getSeconds()).padStart(2, '0');
- let dayOfWeek = weekdays[date.getDay()];
- let dayOfMonth = date.getDate();
- let month = months[date.getMonth()];
- let year = date.getFullYear();
-
- // Format day with ordinal suffix
- let dayOfMonthSuffix;
- if (dayOfMonth % 10 === 1 && dayOfMonth !== 11) {
- dayOfMonthSuffix = dayOfMonth + "st";
- } else if (dayOfMonth % 10 === 2 && dayOfMonth !== 12) {
- dayOfMonthSuffix = dayOfMonth + "nd";
- } else if (dayOfMonth % 10 === 3 && dayOfMonth !== 13) {
- dayOfMonthSuffix = dayOfMonth + "rd";
- } else {
- dayOfMonthSuffix = dayOfMonth + "th";
- }
-
- // `${hours}:${minutes}:${seconds} ยท `
- return `${dayOfWeek}, ${dayOfMonthSuffix} ${month}, ${year}
${hours}:${minutes}:${seconds}`;
-}
-
function genHexString(len) {
let output = '';
for (let i = 0; i < len; ++i) {
@@ -65,6 +29,61 @@ function genHexString(len) {
return output;
}
+// my clone of python's datetime.strftime
+function formatStringDate(date, format) {
+ const padZero = (num, len) => String(num).padStart(len, "0");
+ const abbreviate = (str) => str.slice(0, 3);
+ const ordSuffix = day => [,"st","nd","rd"][day % 10] || "th";
+
+ const formatters = {
+ "%a": date => abbreviate(date.toLocaleString("en-GB", { weekday: "short" })),
+ "%A": date => date.toLocaleString("en-GB", { weekday: "long" }),
+ "%w": date => date.getDay(),
+ "%d": date => padZero(date.getDate(), 2),
+ "%-d": date => date.getDate(),
+ "%b": date => abbreviate(date.toLocaleString("en-GB", { month: "short" })),
+ "%B": date => date.toLocaleString("en-GB", { month: "long" }),
+ "%m": date => padZero(date.getMonth() + 1, 2),
+ "%-m": date => date.getMonth() + 1,
+ "%y": date => padZero(date.getFullYear() % 100, 2),
+ "%-y": date => date.getFullYear() % 100,
+ "%Y": date => date.getFullYear(),
+ "%H": date => padZero(date.getHours(), 2),
+ "%-H": date => date.getHours(),
+ "%I": date => padZero(date.getHours() % 12 || 12, 2),
+ "%-I": date => date.getHours() % 12 || 12,
+ "%p": date => date.getHours() >= 12 ? "PM" : "AM",
+ "%M": date => padZero(date.getMinutes(), 2),
+ "%-M": date => date.getMinutes(),
+ "%S": date => padZero(date.getSeconds(), 2),
+ "%-S": date => date.getSeconds(),
+ "%f": date => padZero(date.getMilliseconds() * 1000, 6),
+ "%z": date => {
+ const offset = date.getTimezoneOffset();
+ const sign = offset > 0 ? "-" : "+";
+ const absOffset = Math.abs(offset);
+ const hours = padZero(Math.floor(absOffset / 60), 2);
+ const minutes = padZero(absOffset % 60, 2);
+ return `${sign}${hours}${minutes}`;
+ },
+ "%Z": date => {
+ const match = date.toTimeString().match(/\((.*)\)/);
+ return match ? match[1] : "";
+ },
+ "%j": date => padZero(Math.ceil((date - new Date(date.getFullYear(), 0, 1)) / 86400000) + 1, 3),
+ "%-j": date => Math.ceil((date - new Date(date.getFullYear(), 0, 1)) / 86400000) + 1,
+ "%U": date => padZero(Math.floor((date - new Date(date.getFullYear(), 0, 1) + (86400000 * (date.getDay() || 7 - 1))) / (86400000 * 7)), 2),
+ "%W": date => padZero(Math.floor((date - new Date(date.getFullYear(), 0, 1) + (86400000 * (date.getDay() || 7))) / (86400000 * 7)), 2),
+ "%c": date => date.toLocaleString(),
+ "%x": date => date.toLocaleDateString(),
+ "%X": date => date.toLocaleTimeString(),
+ "%D": date => date.getDate() + ordSuffix(date.getDate()),
+ "%%": () => "%"
+ };
+
+ return format.replace(/%[a-zA-Z%-]/g, match => formatters[match] ? formatters[match](date) : match);
+}
+
// #region Colour Controls
$(".colour-control-picker").on("change", function() {
$(this).closest(".colour-control-group").find(".colour-control-text").val($(this).val());
@@ -123,4 +142,5 @@ $(document).ready(function() {
updateColourInput(id, "#" + defaultColour)
$(`#${id} [data-bs-toggle="tooltip"]`).tooltip();
});
-});
\ No newline at end of file
+});
+
diff --git a/apps/static/js/home/subscriptions.js b/apps/static/js/home/subscriptions.js
index eb022a7..058d9ff 100644
--- a/apps/static/js/home/subscriptions.js
+++ b/apps/static/js/home/subscriptions.js
@@ -57,6 +57,7 @@ async function initSubscriptionTable() {
{
title: "Channels",
data: "channels_count",
+ className: "text-center",
render: function(data) {
return `${data}`;
}
@@ -66,14 +67,13 @@ async function initSubscriptionTable() {
data: "creation_datetime",
render: function(data, type) {
let dateTime = new Date(data);
- let dateTimeString = formatDate(dateTime);
return $(`
- ${dateTime.toISOString().split("T")[0]}
+ data-bs-content="${formatStringDate(dateTime, "%a, %D %B, %Y
%H:%M:%S")}">
+ ${formatStringDate(dateTime, "%D, %b %Y")}
`).popover()[0];
}
@@ -431,7 +431,6 @@ async function loadChannelOptions(guildId) {
$("#subChannels").val("").change();
try {
const channels = await loadChannels(guildId);
- channels.sort((a, b) => a.position - b.position);
// If we have reached the discord API rate limit
if (channels.message && channels.message.includes("rate limit")) {
@@ -457,6 +456,9 @@ async function loadChannelOptions(guildId) {
);
}
+ // Sort by the specified position of each channel object
+ channels.sort((a, b) => a.position - b.position);
+
discordChannels = [];
channels.forEach(channel => {