ts: format finally

formatted with biome, a config file is provided.
This commit is contained in:
Harvey Tindall
2025-12-08 20:38:30 +00:00
parent ca7c553147
commit 817107622a
29 changed files with 3956 additions and 2610 deletions
+183 -106
View File
@@ -1,7 +1,17 @@
import { ThemeManager } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, addLoader, removeLoader, toClipboard } from "./modules/common.js";
import {
_get,
_post,
_delete,
notificationBox,
whichAnimationEvent,
toDateString,
addLoader,
removeLoader,
toClipboard,
} from "./modules/common.js";
import { Login } from "./modules/login.js";
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
@@ -77,7 +87,7 @@ pages.setPage({
pages.setPage({
name: "reset",
title: document.title,
url: basePath+"/password/reset",
url: basePath + "/password/reset",
show: () => {
const usernameInput = document.getElementById("login-user") as HTMLInputElement;
const input = document.getElementById("pwr-address") as HTMLInputElement;
@@ -98,11 +108,11 @@ pages.setPage({
const resetButton = document.getElementById("modal-login-pwr");
resetButton.onclick = () => {
pages.load("reset");
}
};
}
})();
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
window.notifications = new notificationBox(document.getElementById("notification-box") as HTMLDivElement, 5);
if (window.pwrEnabled && window.linkResetEnabled) {
const submitButton = document.getElementById("pwr-submit");
@@ -113,7 +123,7 @@ if (window.pwrEnabled && window.linkResetEnabled) {
if (req.readyState != 4) return;
removeLoader(submitButton);
if (req.status != 204) {
window.notifications.customError("unkownError", window.lang.notif("errorUnknown"));;
window.notifications.customError("unkownError", window.lang.notif("errorUnknown"));
window.modals.pwr.close();
return;
}
@@ -168,9 +178,9 @@ interface ContactDTO {
class ContactMethods {
private _card: HTMLElement;
private _content: HTMLElement;
private _buttons: { [name: string]: { element: HTMLElement, details: MyDetailsContactMethod } };
private _buttons: { [name: string]: { element: HTMLElement; details: MyDetailsContactMethod } };
constructor (card: HTMLElement) {
constructor(card: HTMLElement) {
this._card = card;
this._content = this._card.querySelector(".content");
this._buttons = {};
@@ -179,9 +189,15 @@ class ContactMethods {
clear = () => {
this._content.textContent = "";
this._buttons = {};
}
};
append = (name: string, details: MyDetailsContactMethod, icon: string, addEditFunc?: (add: boolean) => void, required?: boolean) => {
append = (
name: string,
details: MyDetailsContactMethod,
icon: string,
addEditFunc?: (add: boolean) => void,
required?: boolean,
) => {
const row = document.createElement("div");
row.classList.add("flex", "flex-row", "justify-between", "gap-2", "flex-nowrap");
let innerHTML = `
@@ -191,7 +207,7 @@ class ContactMethods {
${icon}
</span>
</span>
<span class="font-bold text-ellipsis overflow-hidden">${(details.value == "") ? window.lang.strings("notSet") : details.value}</span>
<span class="font-bold text-ellipsis overflow-hidden">${details.value == "" ? window.lang.strings("notSet") : details.value}</span>
</div>
<div class="flex flex-col justify-center">
<div class="flex items-center flex-row gap-2">
@@ -224,12 +240,12 @@ class ContactMethods {
`;
row.innerHTML = innerHTML;
this._buttons[name] = {
element: row,
details: details
details: details,
};
const button = row.querySelector(".user-contact-enabled-disabled") as HTMLButtonElement;
const checkbox = button.querySelector("input[type=checkbox]") as HTMLInputElement;
const setButtonAppearance = () => {
@@ -260,13 +276,14 @@ class ContactMethods {
const addEditButton = row.querySelector(".user-contact-edit") as HTMLButtonElement;
addEditButton.onclick = () => addEditFunc(details.value == "");
}
if (!required && details.value != "") {
const deleteButton = row.querySelector(".user-contact-delete") as HTMLButtonElement;
deleteButton.onclick = () => _delete("/my/" + name, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
document.dispatchEvent(new CustomEvent("details-reload"));
});
deleteButton.onclick = () =>
_delete("/my/" + name, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
document.dispatchEvent(new CustomEvent("details-reload"));
});
}
this._content.appendChild(row);
@@ -305,46 +322,52 @@ class ReferralCard {
private _expiryEl: HTMLSpanElement;
private _descriptionEl: HTMLSpanElement;
get code(): string { return this._code; }
get code(): string {
return this._code;
}
set code(c: string) {
this._code = c;
// let u = new URL(window.location.href);
// const path = window.pages.Base + window.pages.Form + "/" + this._code;
//
//
// u.pathname = path;
// u.hash = "";
// u.search = "";
// this._url = u.toString();
this._url = generateCodeLink(this._code);
}
get remaining_uses(): number { return this._remainingUses; }
set remaining_uses(v: number) {
get remaining_uses(): number {
return this._remainingUses;
}
set remaining_uses(v: number) {
this._remainingUses = v;
if (v > 0 && !(this._noLimit))
this._remainingUsesEl.textContent = `${v}`;
if (v > 0 && !this._noLimit) this._remainingUsesEl.textContent = `${v}`;
}
get no_limit(): boolean { return this._noLimit; }
get no_limit(): boolean {
return this._noLimit;
}
set no_limit(v: boolean) {
this._noLimit = v;
if (v)
this._remainingUsesEl.textContent = ``;
else
this._remainingUsesEl.textContent = `${this._remainingUses}`;
if (v) this._remainingUsesEl.textContent = ``;
else this._remainingUsesEl.textContent = `${this._remainingUses}`;
}
get expiry(): Date { return this._expiry; };
get expiry(): Date {
return this._expiry;
}
set expiry(expiryUnix: number) {
this._expiryUnix = expiryUnix;
this._expiry = new Date(expiryUnix * 1000);
this._expiryEl.textContent = toDateString(this._expiry);
}
get use_expiry(): boolean { return this._useExpiry; }
get use_expiry(): boolean {
return this._useExpiry;
}
set use_expiry(v: boolean) {
this._useExpiry = v;
if (v) {
@@ -353,7 +376,7 @@ class ReferralCard {
this._descriptionEl.textContent = window.lang.strings("referralsDescription");
}
}
constructor(card: HTMLElement) {
this._card = card;
this._button = this._card.querySelector(".user-referrals-button") as HTMLButtonElement;
@@ -372,10 +395,10 @@ class ReferralCard {
<div>
</div>
`;
this._remainingUsesEl = this._infoArea.querySelector(".referral-remaining-uses") as HTMLSpanElement;
this._expiryEl = this._infoArea.querySelector(".referral-expiry") as HTMLSpanElement;
document.addEventListener("timefmt-change", () => {
this.expiry = this._expiryUnix;
});
@@ -432,25 +455,25 @@ class ExpiryCard {
let ymd = [0, 0, 0];
while (now.getFullYear() != this._expiry.getFullYear()) {
ymd[0] += 1;
now.setFullYear(now.getFullYear()+1);
now.setFullYear(now.getFullYear() + 1);
}
if (now.getMonth() > this._expiry.getMonth()) {
ymd[0] -=1;
now.setFullYear(now.getFullYear()-1);
ymd[0] -= 1;
now.setFullYear(now.getFullYear() - 1);
}
while (now.getMonth() != this._expiry.getMonth()) {
ymd[1] += 1;
now.setMonth(now.getMonth() + 1);
}
if (now.getDate() > this._expiry.getDate()) {
ymd[1] -=1;
now.setMonth(now.getMonth()-1);
ymd[1] -= 1;
now.setMonth(now.getMonth() - 1);
}
while (now.getDate() != this._expiry.getDate()) {
ymd[2] += 1;
now.setDate(now.getDate() + 1);
}
const langKeys = ["year", "month", "day"];
let innerHTML = ``;
for (let i = 0; i < langKeys.length; i++) {
@@ -467,7 +490,9 @@ class ExpiryCard {
this._countdown.innerHTML = innerHTML;
};
get expiry(): Date { return this._expiry; };
get expiry(): Date {
return this._expiry;
}
set expiry(expiryUnix: number) {
if (this._interval !== null) {
window.clearInterval(this._interval);
@@ -479,10 +504,12 @@ class ExpiryCard {
return;
}
this._expiry = new Date(expiryUnix * 1000);
this._aside.textContent = window.lang.strings("yourAccountIsValidUntil").replace("{date}", toDateString(this._expiry));
this._aside.textContent = window.lang
.strings("yourAccountIsValidUntil")
.replace("{date}", toDateString(this._expiry));
this._card.classList.remove("unfocused");
this._interval = window.setInterval(this._drawCountdown, 60*1000);
this._interval = window.setInterval(this._drawCountdown, 60 * 1000);
this._drawCountdown();
}
}
@@ -496,37 +523,45 @@ var contactMethodList = new ContactMethods(contactCard);
const addEditEmail = (add: boolean): void => {
const heading = window.modals.email.modal.querySelector(".heading");
heading.innerHTML = (add ? window.lang.strings("addContactMethod") : window.lang.strings("editContactMethod")) + `<span class="modal-close">&times;</span>`;
heading.innerHTML =
(add ? window.lang.strings("addContactMethod") : window.lang.strings("editContactMethod")) +
`<span class="modal-close">&times;</span>`;
const input = document.getElementById("modal-email-input") as HTMLInputElement;
input.value = "";
const confirmationRequired = window.modals.email.modal.querySelector(".confirmation-required");
confirmationRequired.classList.add("unfocused");
const content = window.modals.email.modal.querySelector(".content");
content.classList.remove("unfocused");
const submit = window.modals.email.modal.querySelector(".modal-submit") as HTMLButtonElement;
submit.onclick = () => {
addLoader(submit);
_post("/my/email", {"email": input.value}, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(submit);
if (req.status == 303 || req.status == 200) {
document.dispatchEvent(new CustomEvent("details-reload"));
window.modals.email.close();
}
}, true, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(submit);
if (req.status == 401) {
content.classList.add("unfocused");
confirmationRequired.classList.remove("unfocused");
}
});
}
_post(
"/my/email",
{ email: input.value },
(req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(submit);
if (req.status == 303 || req.status == 200) {
document.dispatchEvent(new CustomEvent("details-reload"));
window.modals.email.close();
}
},
true,
(req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(submit);
if (req.status == 401) {
content.classList.add("unfocused");
confirmationRequired.classList.remove("unfocused");
}
},
);
};
window.modals.email.show();
}
};
const discordConf: ServiceConfiguration = {
modal: window.modals.discord as Modal,
@@ -539,7 +574,7 @@ const discordConf: ServiceConfiguration = {
successError: window.lang.notif("verified"),
successFunc: (modalClosed: boolean) => {
if (modalClosed) document.dispatchEvent(new CustomEvent("details-reload"));
}
},
};
let discord: Discord;
@@ -555,7 +590,7 @@ const telegramConf: ServiceConfiguration = {
successError: window.lang.notif("verified"),
successFunc: (modalClosed: boolean) => {
if (modalClosed) document.dispatchEvent(new CustomEvent("details-reload"));
}
},
};
let telegram: Telegram;
@@ -571,13 +606,12 @@ const matrixConf: MatrixConfiguration = {
successError: window.lang.notif("verified"),
successFunc: () => {
setTimeout(() => document.dispatchEvent(new CustomEvent("details-reload")), 1200);
}
},
};
let matrix: Matrix;
if (window.matrixEnabled) matrix = new Matrix(matrixConf);
const oldPasswordField = document.getElementById("user-old-password") as HTMLInputElement;
const newPasswordField = document.getElementById("user-new-password") as HTMLInputElement;
const rePasswordField = document.getElementById("user-reenter-new-password") as HTMLInputElement;
@@ -592,7 +626,7 @@ let validatorConf: ValidatorConf = {
passwordField: newPasswordField,
rePasswordField: rePasswordField,
submitButton: changePasswordButton,
validatorFunc: baseValidator
validatorFunc: baseValidator,
};
let validator = new Validator(validatorConf);
@@ -601,25 +635,33 @@ let validator = new Validator(validatorConf);
oldPasswordField.addEventListener("keyup", validator.validate);
changePasswordButton.addEventListener("click", () => {
addLoader(changePasswordButton);
_post("/my/password", { old: oldPasswordField.value, new: newPasswordField.value }, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(changePasswordButton);
if (req.status == 400) {
window.notifications.customError("errorPassword", window.lang.notif("errorPassword"));
} else if (req.status == 500) {
window.notifications.customError("errorUnknown", window.lang.notif("errorUnknown"));
} else if (req.status == 204) {
window.notifications.customSuccess("passwordChanged", window.lang.notif("passwordChanged"));
setTimeout(() => { window.location.reload() }, 2000);
}
}, true, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(changePasswordButton);
if (req.status == 401) {
window.notifications.customError("oldPasswordError", window.lang.notif("errorOldPassword"));
return;
}
});
_post(
"/my/password",
{ old: oldPasswordField.value, new: newPasswordField.value },
(req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(changePasswordButton);
if (req.status == 400) {
window.notifications.customError("errorPassword", window.lang.notif("errorPassword"));
} else if (req.status == 500) {
window.notifications.customError("errorUnknown", window.lang.notif("errorUnknown"));
} else if (req.status == 204) {
window.notifications.customSuccess("passwordChanged", window.lang.notif("passwordChanged"));
setTimeout(() => {
window.location.reload();
}, 2000);
}
},
true,
(req: XMLHttpRequest) => {
if (req.readyState != 4) return;
removeLoader(changePasswordButton);
if (req.status == 401) {
window.notifications.customError("oldPasswordError", window.lang.notif("errorOldPassword"));
return;
}
},
);
});
document.addEventListener("details-reload", () => {
@@ -644,20 +686,56 @@ document.addEventListener("details-reload", () => {
rootCard.querySelector(".heading").innerHTML = innerHTML;
contactMethodList.clear();
contactMethodList.clear();
// Note the weird format of the functions for discord/telegram:
// "this" was being redefined within the onclick() method, so
// they had to be wrapped in an anonymous function.
const contactMethods: { name: string, icon: string, f: (add: boolean) => void, required: boolean, enabled: boolean }[] = [
{name: "email", icon: `<i class="ri-mail-fill ri-lg"></i>`, f: addEditEmail, required: true, enabled: true},
{name: "discord", icon: `<i class="ri-discord-fill ri-lg"></i>`, f: (add: boolean) => { discord.onclick(); }, required: window.discordRequired, enabled: window.discordEnabled},
{name: "telegram", icon: `<i class="ri-telegram-fill ri-lg"></i>`, f: (add: boolean) => { telegram.onclick() }, required: window.telegramRequired, enabled: window.telegramEnabled},
{name: "matrix", icon: `<span class="font-bold">[m]</span>`, f: (add: boolean) => { matrix.show(); }, required: window.matrixRequired, enabled: window.matrixEnabled}
const contactMethods: {
name: string;
icon: string;
f: (add: boolean) => void;
required: boolean;
enabled: boolean;
}[] = [
{
name: "email",
icon: `<i class="ri-mail-fill ri-lg"></i>`,
f: addEditEmail,
required: true,
enabled: true,
},
{
name: "discord",
icon: `<i class="ri-discord-fill ri-lg"></i>`,
f: (add: boolean) => {
discord.onclick();
},
required: window.discordRequired,
enabled: window.discordEnabled,
},
{
name: "telegram",
icon: `<i class="ri-telegram-fill ri-lg"></i>`,
f: (add: boolean) => {
telegram.onclick();
},
required: window.telegramRequired,
enabled: window.telegramEnabled,
},
{
name: "matrix",
icon: `<span class="font-bold">[m]</span>`,
f: (add: boolean) => {
matrix.show();
},
required: window.matrixRequired,
enabled: window.matrixEnabled,
},
];
for (let method of contactMethods) {
if (!(method.enabled)) continue;
if (!method.enabled) continue;
if (method.name in details) {
contactMethodList.append(method.name, details[method.name], method.icon, method.f, method.required);
}
@@ -671,7 +749,7 @@ document.addEventListener("details-reload", () => {
let messageCard = document.getElementById("card-message");
if (details.accounts_admin) {
adminBackButton.classList.remove("unfocused");
if (typeof(messageCard) == "undefined" || messageCard == null) {
if (typeof messageCard == "undefined" || messageCard == null) {
messageCard = document.createElement("div");
messageCard.classList.add("card", "@low", "dark:~d_neutral", "content");
messageCard.id = "card-message";
@@ -685,7 +763,7 @@ document.addEventListener("details-reload", () => {
}
}
if (typeof(messageCard) != "undefined" && messageCard != null) {
if (typeof messageCard != "undefined" && messageCard != null) {
messageCard.innerHTML = messageCard.innerHTML.replace(new RegExp("{username}", "g"), details.username);
// setBestRowSpan(messageCard, false);
// contactCard.querySelector(".content").classList.add("h-100");
@@ -696,7 +774,7 @@ document.addEventListener("details-reload", () => {
if (window.referralsEnabled) {
if (details.has_referrals) {
_get("/my/referral", null, (req: XMLHttpRequest) => {
if (req.readyState != 4 || req.status != 200) return;
if (req.readyState != 4 || req.status != 200) return;
const referral: MyReferral = req.response as MyReferral;
referralCard.update(referral);
setCardOrder(messageCard);
@@ -715,9 +793,9 @@ document.addEventListener("details-reload", () => {
const setCardOrder = (messageCard: HTMLElement) => {
const cards = document.getElementById("user-cardlist");
const children = Array.from(cards.children);
const idxs = [...Array(cards.childElementCount).keys()]
const idxs = [...Array(cards.childElementCount).keys()];
// The message card is the first element and should always be so, so remove it from the list.
const hasMessageCard = !(typeof(messageCard) == "undefined" || messageCard == null);
const hasMessageCard = !(typeof messageCard == "undefined" || messageCard == null);
if (hasMessageCard) idxs.shift();
const perms = generatePermutations(idxs);
let minHeight = 999999;
@@ -781,8 +859,7 @@ const setBestRowSpan = (el: HTMLElement, setOnParent: boolean) => {
let rowSpan = Math.ceil(computeRealHeight(el) / largestNonMessageCardHeight);
if (rowSpan > 0)
(setOnParent ? el.parentElement : el).style.gridRow = `span ${rowSpan}`;
if (rowSpan > 0) (setOnParent ? el.parentElement : el).style.gridRow = `span ${rowSpan}`;
};
const computeRealHeight = (el: HTMLElement): number => {
@@ -801,12 +878,12 @@ const computeRealHeight = (el: HTMLElement): number => {
}
}
return total;
}
};
const generatePermutations = (xs: number[]): [number[], number[]][] => {
const l = xs.length;
let out: [number[], number[]][] = [];
for (let i = 0; i < (l << 1); i++) {
for (let i = 0; i < l << 1; i++) {
let incl = [];
let excl = [];
for (let j = 0; j < l; j++) {
@@ -819,7 +896,7 @@ const generatePermutations = (xs: number[]): [number[], number[]][] => {
out.push([incl, excl]);
}
return out;
}
};
login.bindLogout(document.getElementById("logout-button"));