From 765a749959f117b2d87c609a29dab561f5d2e381 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 19 Jun 2023 11:58:09 +0100 Subject: [PATCH] discord: modularize user-facing code will be done for others too, code for discord account linking in form and userpage is now in ts/modules/account-linking.ts as a configurable class. --- ts/form.ts | 78 ++++++------------ ts/modules/account-linking.ts | 144 ++++++++++++++++++++++++++++++++++ ts/modules/common.ts | 5 -- ts/user.ts | 79 ++++--------------- 4 files changed, 185 insertions(+), 121 deletions(-) create mode 100644 ts/modules/account-linking.ts diff --git a/ts/form.ts b/ts/form.ts index b3dbc25..01b8d87 100644 --- a/ts/form.ts +++ b/ts/form.ts @@ -1,8 +1,9 @@ import { Modal } from "./modules/modal.js"; import { notificationBox, whichAnimationEvent } from "./modules/common.js"; -import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString, DiscordInvite } from "./modules/common.js"; +import { _get, _post, toggleLoader, addLoader, removeLoader, toDateString } from "./modules/common.js"; import { loadLangSelector } from "./modules/lang.js"; import { initValidator } from "./modules/validator.js"; +import { Discord, DiscordConfiguration } from "./modules/account-linking.js"; interface formWindow extends Window { invalidPassword: string; @@ -95,61 +96,32 @@ var discordVerified = false; if (window.discordEnabled) { window.discordModal = new Modal(document.getElementById("modal-discord"), window.discordRequired); const discordButton = document.getElementById("link-discord") as HTMLSpanElement; - if (window.discordInviteLink) { - _get("/invite/" + window.code + "/discord/invite", null, (req: XMLHttpRequest) => { - if (req.readyState == 4) { - if (req.status != 200) { - return; - } - const inv = req.response as DiscordInvite; - const link = document.getElementById("discord-invite") as HTMLAnchorElement; - link.classList.add("subheading", "link-center"); - link.href = inv.invite; - link.target = "_blank"; - link.innerHTML = `${window.discordServerName}`; + + const discordConf: DiscordConfiguration = { + modal: window.discordModal as Modal, + pin: window.discordPIN, + inviteURL: window.discordInviteLink ? ("/invite/" + window.code + "/discord/invite") : "", + pinURL: "", + verifiedURL: "/invite/" + window.code + "/discord/verified/", + invalidCodeError: window.messages["errorInvalidCode"], + accountLinkedError: window.messages["errorAccountLinked"], + successError: window.messages["verified"], + successFunc: (modalClosed: boolean) => { + if (!modalClosed) { + discordButton.classList.add("unfocused"); + document.getElementById("contact-via").classList.remove("unfocused"); + document.getElementById("contact-via-email").parentElement.classList.remove("unfocused"); + const radio = document.getElementById("contact-via-discord") as HTMLInputElement; + radio.parentElement.classList.remove("unfocused") + radio.checked = true; + validatorFunc(); } - }); - } - discordButton.onclick = () => { - const waiting = document.getElementById("discord-waiting") as HTMLSpanElement; - toggleLoader(waiting); - window.discordModal.show(); - let modalClosed = false; - window.discordModal.onclose = () => { - modalClosed = true; - toggleLoader(waiting); } - const checkVerified = () => _get("/invite/" + window.code + "/discord/verified/" + window.discordPIN, null, (req: XMLHttpRequest) => { - if (req.readyState == 4) { - if (req.status == 401) { - window.discordModal.close(); - window.notifications.customError("invalidCodeError", window.messages["errorInvalidCode"]); - return; - } else if (req.status == 400) { - window.discordModal.close(); - window.notifications.customError("accountLinkedError", window.messages["errorAccountLinked"]); - } else if (req.status == 200) { - if (req.response["success"] as boolean) { - discordVerified = true; - waiting.classList.add("~positive"); - waiting.classList.remove("~info"); - window.notifications.customPositive("discordVerified", "", window.messages["verified"]); - setTimeout(window.discordModal.close, 2000); - discordButton.classList.add("unfocused"); - document.getElementById("contact-via").classList.remove("unfocused"); - document.getElementById("contact-via-email").parentElement.classList.remove("unfocused"); - const radio = document.getElementById("contact-via-discord") as HTMLInputElement; - radio.parentElement.classList.remove("unfocused") - radio.checked = true; - validatorFunc(); - } else if (!modalClosed) { - setTimeout(checkVerified, 1500); - } - } - } - }); - checkVerified(); }; + + const discord = new Discord(discordConf); + + discordButton.onclick = discord.onclick; } var matrixVerified = false; diff --git a/ts/modules/account-linking.ts b/ts/modules/account-linking.ts new file mode 100644 index 0000000..fce2ec8 --- /dev/null +++ b/ts/modules/account-linking.ts @@ -0,0 +1,144 @@ +import { Modal } from "../modules/modal.js"; +import { _get, _post, toggleLoader } from "../modules/common.js"; + +interface formWindow extends Window { + invalidPassword: string; + successModal: Modal; + telegramModal: Modal; + discordModal: Modal; + matrixModal: Modal; + confirmationModal: Modal; + redirectToJellyfin: boolean; + code: string; + messages: { [key: string]: string }; + confirmation: boolean; + telegramRequired: boolean; + telegramPIN: string; + discordRequired: boolean; + discordPIN: string; + discordStartCommand: string; + discordInviteLink: boolean; + discordServerName: string; + matrixRequired: boolean; + matrixUserID: string; + userExpiryEnabled: boolean; + userExpiryMonths: number; + userExpiryDays: number; + userExpiryHours: number; + userExpiryMinutes: number; + userExpiryMessage: string; + emailRequired: boolean; + captcha: boolean; + reCAPTCHA: boolean; + reCAPTCHASiteKey: string; +} + +declare var window: formWindow; + +export interface DiscordConfiguration { + modal: Modal; + pin: string; + inviteURL: string; + pinURL: string; + verifiedURL: string; + invalidCodeError: string; + accountLinkedError: string; + successError: string; + successFunc: (modalClosed: boolean) => void; +}; + +export interface DiscordInvite { + invite: string; + icon: string; +} + +export class Discord { + private _conf: DiscordConfiguration; + private _pinAcquired = false; + private _modalClosed = false; + private _waiting = document.getElementById("discord-waiting") as HTMLSpanElement; + private _verified = false; + + get verified(): boolean { return this._verified; } + + constructor(conf: DiscordConfiguration) { + this._conf = conf; + this._conf.modal.onclose = () => { + this._modalClosed = true; + toggleLoader(this._waiting); + }; + } + + private _getInviteURL = () => _get(this._conf.inviteURL, null, (req: XMLHttpRequest) => { + if (req.readyState != 4) return; + const inv = req.response as DiscordInvite; + const link = document.getElementById("discord-invite") as HTMLAnchorElement; + link.href = inv.invite; + link.target = "_blank"; + link.innerHTML = `${window.discordServerName}`; + }); + + private _checkVerified = () => { + if (this._modalClosed) return; + if (!this._pinAcquired) { + setTimeout(this._checkVerified, 1500); + return; + } + _get(this._conf.verifiedURL + this._conf.pin, null, (req: XMLHttpRequest) => { + if (req.readyState != 4) return; + if (req.status == 401) { + this._conf.modal.close(); + window.notifications.customError("invalidCodeError", this._conf.invalidCodeError); + } else if (req.status == 400) { + this._conf.modal.close(); + window.notifications.customError("accountLinkedError", this._conf.accountLinkedError); + } else if (req.status == 200) { + if (req.response["success"] as boolean) { + this._verified = true; + this._waiting.classList.add("~positive"); + this._waiting.classList.remove("~info"); + window.notifications.customPositive("discordVerified", "", this._conf.successError); + if (this._conf.successFunc) { + this._conf.successFunc(false); + } + setTimeout(() => { + this._conf.modal.close(); + if (this._conf.successFunc) { + this._conf.successFunc(true); + } + }, 2000); + + } else if (!this._modalClosed) { + setTimeout(this._checkVerified, 1500); + } + } + }); + }; + + onclick = () => { + if (this._conf.inviteURL != "") { + this._getInviteURL(); + } + + toggleLoader(this._waiting); + + this._pinAcquired = false; + if (this._conf.pin) { + this._pinAcquired = true; + this._conf.modal.modal.querySelector(".pin").textContent = this._conf.pin; + } else { + _get(this._conf.pinURL, null, (req: XMLHttpRequest) => { + if (req.readyState == 4 && req.status == 200) { + this._conf.pin = req.response["pin"]; + this._conf.modal.modal.querySelector(".pin").textContent = this._conf.pin; + this._pinAcquired = true; + } + }); + } + + this._modalClosed = false; + this._conf.modal.show(); + + this._checkVerified(); + } +} diff --git a/ts/modules/common.ts b/ts/modules/common.ts index 6ae320d..4687cc8 100644 --- a/ts/modules/common.ts +++ b/ts/modules/common.ts @@ -221,8 +221,3 @@ export function insertText(textarea: HTMLTextAreaElement, text: string) { textarea.focus(); } } - -export interface DiscordInvite { - invite: string; - icon: string; -} diff --git a/ts/user.ts b/ts/user.ts index ac34517..465dfd9 100644 --- a/ts/user.ts +++ b/ts/user.ts @@ -1,8 +1,9 @@ import { ThemeManager } from "./modules/theme.js"; import { lang, LangFile, loadLangSelector } from "./modules/lang.js"; import { Modal } from "./modules/modal.js"; -import { _get, _post, notificationBox, whichAnimationEvent, toDateString, toggleLoader, DiscordInvite } from "./modules/common.js"; +import { _get, _post, notificationBox, whichAnimationEvent, toDateString, toggleLoader } from "./modules/common.js"; import { Login } from "./modules/login.js"; +import { Discord, DiscordConfiguration } from "./modules/account-linking.js"; interface userWindow extends Window { jellyfinID: string; @@ -278,70 +279,22 @@ const addEditEmail = (add: boolean): void => { window.modals.email.show(); } -let discordModalClosed = false; -let discordPIN = ""; -const addEditDiscord = (add: boolean): void => { - if (window.discordInviteLink) { - _get("/my/discord/invite", null, (req: XMLHttpRequest) => { - if (req.readyState == 4) { - if (req.status != 200) return; - const inv = req.response as DiscordInvite; - const link = document.getElementById("discord-invite") as HTMLAnchorElement; - link.href = inv.invite; - link.target = "_blank"; - link.innerHTML = `${window.discordServerName}`; - } - }); +const discordConf: DiscordConfiguration = { + modal: window.modals.discord as Modal, + pin: "", + inviteURL: window.discordInviteLink ? "/my/discord/invite" : "", + pinURL: "/my/pin/discord", + verifiedURL: "/my/discord/verified/", + invalidCodeError: window.lang.notif("errorInvalidCode"), + accountLinkedError: window.lang.notif("errorAccountLinked"), + successError: window.lang.notif("verified"), + successFunc: (modalClosed: boolean) => { + if (modalClosed) window.location.reload(); } - - _get("/my/pin/discord", null, (req: XMLHttpRequest) => { - if (req.readyState == 4 && req.status == 200) { - discordPIN = req.response["pin"]; - window.modals.discord.modal.querySelector(".pin").textContent = discordPIN; - } - }); - - const waiting = document.getElementById("discord-waiting") as HTMLSpanElement; - toggleLoader(waiting); - window.modals.discord.show(); - discordModalClosed = false; - window.modals.discord.onclose = () => { - discordModalClosed = true; - toggleLoader(waiting); - } - const checkVerified = () => { - if (discordPIN == "") { - setTimeout(checkVerified, 1500); - } - if (discordModalClosed) return; - _get("/my/discord/verified/" + discordPIN, null, (req: XMLHttpRequest) => { - if (req.readyState != 4) return; - if (req.status == 401) { - window.modals.discord.close(); - window.notifications.customError("invalidCodeError", window.lang.notif("errorInvalidCode")); - return; - } else if (req.status == 400) { - window.modals.discord.close(); - window.notifications.customError("accountLinkedError", window.lang.notif("errorAccountLinked")); - } else if (req.status == 200) { - if (req.response["success"] as boolean) { - waiting.classList.add("~positive"); - waiting.classList.remove("~info"); - window.notifications.customPositive("discordVerified", "", window.lang.notif("verified")); - setTimeout(() => { - window.modals.discord.close; - window.location.reload(); - }, 2000); - } else if (!discordModalClosed) { - setTimeout(checkVerified, 1500); - } - } - }); - }; - - checkVerified(); }; +let discord = new Discord(discordConf); + document.addEventListener("details-reload", () => { _get("/my/details", null, (req: XMLHttpRequest) => { if (req.readyState == 4) { @@ -368,7 +321,7 @@ document.addEventListener("details-reload", () => { const contactMethods: { name: string, icon: string, f: (add: boolean) => void }[] = [ {name: "email", icon: ``, f: addEditEmail}, - {name: "discord", icon: ``, f: addEditDiscord}, + {name: "discord", icon: ``, f: discord.onclick}, {name: "telegram", icon: ``, f: null}, {name: "matrix", icon: `[m]`, f: null} ];