refactor: Remove JQuery from account-settings (@Leonabcd123, @fehmer) (#7219)

### Description

Also modifies `swapElements` to accept `ElementWithUtils`.

---------

Co-authored-by: Christian Fehmer <fehmer@users.noreply.github.com>
Co-authored-by: Christian Fehmer <cfe@sexy-developer.com>
This commit is contained in:
Leonabcd123 2025-12-14 15:29:05 +02:00 committed by GitHub
parent 331ca1a26f
commit 38f3fc251e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 90 additions and 107 deletions

View file

@ -65,15 +65,11 @@ export function update(): void {
accountButtonAndMenuEl
.qs(".menu .items .goToProfile")
?.setAttribute("href", `/profile/${name}`);
void Misc.swapElements(
loginButtonEl.native,
accountButtonAndMenuEl.native,
250,
);
void Misc.swapElements(loginButtonEl, accountButtonAndMenuEl, 250);
} else {
void Misc.swapElements(
accountButtonAndMenuEl.native,
loginButtonEl.native,
accountButtonAndMenuEl,
loginButtonEl,
250,
async () => {
updateName("");

View file

@ -13,6 +13,7 @@ import * as ActivePage from "../../states/active-page";
import { CustomThemeColors, ThemeName } from "@monkeytype/schemas/configs";
import { captureException } from "../../sentry";
import { ThemesListSorted } from "../../constants/themes";
import { qs } from "../../utils/dom";
function updateActiveButton(): void {
let activeThemeName: string = Config.theme;
@ -310,22 +311,14 @@ export function updateActiveTab(): void {
if (Config.customTheme) {
void Misc.swapElements(
document.querySelector(
'.pageSettings [tabContent="preset"]',
) as HTMLElement,
document.querySelector(
'.pageSettings [tabContent="custom"]',
) as HTMLElement,
qs('.pageSettings [tabContent="preset"]'),
qs('.pageSettings [tabContent="custom"]'),
250,
);
} else {
void Misc.swapElements(
document.querySelector(
'.pageSettings [tabContent="custom"]',
) as HTMLElement,
document.querySelector(
'.pageSettings [tabContent="preset"]',
) as HTMLElement,
qs('.pageSettings [tabContent="custom"]'),
qs('.pageSettings [tabContent="preset"]'),
250,
);
}

View file

@ -12,9 +12,9 @@ import * as BlockedUserTable from "../elements/account-settings/blocked-user-tab
import * as Notifications from "../elements/notifications";
import { z } from "zod";
import * as AuthEvent from "../observables/auth-event";
import { qsr } from "../utils/dom";
import { qs, qsr, onDocumentReady } from "../utils/dom";
const pageElement = $(".page.pageAccountSettings");
const pageElement = qsr(".page.pageAccountSettings");
const StateSchema = z.object({
tab: z.enum([
@ -34,9 +34,9 @@ const state: State = {
};
function updateAuthenticationSections(): void {
pageElement.find(".section.passwordAuthSettings button").addClass("hidden");
pageElement.find(".section.googleAuthSettings button").addClass("hidden");
pageElement.find(".section.githubAuthSettings button").addClass("hidden");
pageElement.qs(".section.passwordAuthSettings button")?.addClass("hidden");
pageElement.qs(".section.googleAuthSettings button")?.addClass("hidden");
pageElement.qs(".section.githubAuthSettings button")?.addClass("hidden");
const user = getAuthenticatedUser();
if (user === null) return;
@ -53,134 +53,128 @@ function updateAuthenticationSections(): void {
if (passwordProvider) {
pageElement
.find(".section.passwordAuthSettings #emailPasswordAuth")
.removeClass("hidden");
.qs(".section.passwordAuthSettings #emailPasswordAuth")
?.removeClass("hidden");
pageElement
.find(".section.passwordAuthSettings #passPasswordAuth")
.removeClass("hidden");
.qs(".section.passwordAuthSettings #passPasswordAuth")
?.removeClass("hidden");
if (googleProvider || githubProvider) {
pageElement
.find(".section.passwordAuthSettings #removePasswordAuth")
.removeClass("hidden");
.qs(".section.passwordAuthSettings #removePasswordAuth")
?.removeClass("hidden");
}
} else {
pageElement
.find(".section.passwordAuthSettings #addPasswordAuth")
.removeClass("hidden");
.qs(".section.passwordAuthSettings #addPasswordAuth")
?.removeClass("hidden");
}
if (googleProvider) {
pageElement
.find(".section.googleAuthSettings #removeGoogleAuth")
.removeClass("hidden");
.qs(".section.googleAuthSettings #removeGoogleAuth")
?.removeClass("hidden");
if (passwordProvider || githubProvider) {
pageElement
.find(".section.googleAuthSettings #removeGoogleAuth")
.removeClass("disabled");
.qs(".section.googleAuthSettings #removeGoogleAuth")
?.removeClass("disabled");
} else {
pageElement
.find(".section.googleAuthSettings #removeGoogleAuth")
.addClass("disabled");
.qs(".section.googleAuthSettings #removeGoogleAuth")
?.addClass("disabled");
}
} else {
pageElement
.find(".section.googleAuthSettings #addGoogleAuth")
.removeClass("hidden");
.qs(".section.googleAuthSettings #addGoogleAuth")
?.removeClass("hidden");
}
if (githubProvider) {
pageElement
.find(".section.githubAuthSettings #removeGithubAuth")
.removeClass("hidden");
.qs(".section.githubAuthSettings #removeGithubAuth")
?.removeClass("hidden");
if (passwordProvider || googleProvider) {
pageElement
.find(".section.githubAuthSettings #removeGithubAuth")
.removeClass("disabled");
.qs(".section.githubAuthSettings #removeGithubAuth")
?.removeClass("disabled");
} else {
pageElement
.find(".section.githubAuthSettings #removeGithubAuth")
.addClass("disabled");
.qs(".section.githubAuthSettings #removeGithubAuth")
?.addClass("disabled");
}
} else {
pageElement
.find(".section.githubAuthSettings #addGithubAuth")
.removeClass("hidden");
.qs(".section.githubAuthSettings #addGithubAuth")
?.removeClass("hidden");
}
}
function updateIntegrationSections(): void {
//no code and no discord
if (!isAuthenticated()) {
pageElement.find(".section.discordIntegration").addClass("hidden");
pageElement.qs(".section.discordIntegration")?.addClass("hidden");
} else {
if (!getSnapshot()) return;
pageElement.find(".section.discordIntegration").removeClass("hidden");
pageElement.qs(".section.discordIntegration")?.removeClass("hidden");
if (getSnapshot()?.discordId === undefined) {
//show button
pageElement
.find(".section.discordIntegration .buttons")
.removeClass("hidden");
pageElement.find(".section.discordIntegration .info").addClass("hidden");
.qs(".section.discordIntegration .buttons")
?.removeClass("hidden");
pageElement.qs(".section.discordIntegration .info")?.addClass("hidden");
} else {
pageElement
.find(".section.discordIntegration .buttons")
.addClass("hidden");
.qs(".section.discordIntegration .buttons")
?.addClass("hidden");
pageElement
.find(".section.discordIntegration .info")
.removeClass("hidden");
.qs(".section.discordIntegration .info")
?.removeClass("hidden");
}
}
}
function updateTabs(): void {
void swapElements(
pageElement.find(".tab.active")[0] as HTMLElement,
pageElement.find(`.tab[data-tab="${state.tab}"]`)[0] as HTMLElement,
pageElement.qs(".tab.active"),
pageElement.qs(`.tab[data-tab="${state.tab}"]`),
250,
async () => {
//
},
async () => {
pageElement.find(".tab").removeClass("active");
pageElement.find(`.tab[data-tab="${state.tab}"]`).addClass("active");
pageElement.qs(".tab")?.removeClass("active");
pageElement.qs(`.tab[data-tab="${state.tab}"]`)?.addClass("active");
},
);
pageElement.find("button").removeClass("active");
pageElement.find(`button[data-tab="${state.tab}"]`).addClass("active");
pageElement.qs("button")?.removeClass("active");
pageElement.qs(`button[data-tab="${state.tab}"]`)?.addClass("active");
}
function updateAccountSections(): void {
pageElement.qs(".section.optOutOfLeaderboards .optedOut")?.addClass("hidden");
pageElement
.find(".section.optOutOfLeaderboards .optedOut")
.addClass("hidden");
.qs(".section.optOutOfLeaderboards .buttons")
?.removeClass("hidden");
pageElement.qs(".section.setStreakHourOffset .info")?.addClass("hidden");
pageElement
.find(".section.optOutOfLeaderboards .buttons")
.removeClass("hidden");
pageElement.find(".section.setStreakHourOffset .info").addClass("hidden");
pageElement
.find(".section.setStreakHourOffset .buttons")
.removeClass("hidden");
.qs(".section.setStreakHourOffset .buttons")
?.removeClass("hidden");
const snapshot = getSnapshot();
if (snapshot?.lbOptOut === true) {
pageElement
.find(".section.optOutOfLeaderboards .optedOut")
.removeClass("hidden");
.qs(".section.optOutOfLeaderboards .optedOut")
?.removeClass("hidden");
pageElement
.find(".section.optOutOfLeaderboards .buttons")
.addClass("hidden");
.qs(".section.optOutOfLeaderboards .buttons")
?.addClass("hidden");
}
if (snapshot?.streakHourOffset !== undefined) {
pageElement
.find(".section.setStreakHourOffset .info")
.removeClass("hidden");
pageElement.qs(".section.setStreakHourOffset .info")?.removeClass("hidden");
const sign = snapshot?.streakHourOffset > 0 ? "+" : "";
pageElement
.find(".section.setStreakHourOffset .info span")
.text(sign + snapshot?.streakHourOffset);
pageElement
.find(".section.setStreakHourOffset .buttons")
.addClass("hidden");
.qs(".section.setStreakHourOffset .info span")
?.setText(sign + snapshot?.streakHourOffset);
pageElement.qs(".section.setStreakHourOffset .buttons")?.addClass("hidden");
}
}
@ -195,15 +189,17 @@ export function updateUI(): void {
page.setUrlParams(state);
}
$(".page.pageAccountSettings").on("click", ".tabs button", (event) => {
state.tab = $(event.target).data("tab") as State["tab"];
qs(".page.pageAccountSettings")?.onChild("click", ".tabs button", (event) => {
state.tab = (event.target as HTMLElement).getAttribute(
"data-tab",
) as State["tab"];
updateTabs();
page.setUrlParams(state);
});
$(
qs(
".page.pageAccountSettings .section.discordIntegration .getLinkAndGoToOauth",
).on("click", () => {
)?.on("click", () => {
Loader.show();
void Ape.users.getDiscordOAuth().then((response) => {
if (response.status === 200) {
@ -217,7 +213,7 @@ $(
});
});
$(".page.pageAccountSettings #setStreakHourOffset").on("click", () => {
qs(".page.pageAccountSettings #setStreakHourOffset")?.on("click", () => {
StreakHourOffsetModal.show();
});
@ -230,7 +226,7 @@ AuthEvent.subscribe((event) => {
export const page = new PageWithUrlParams({
id: "accountSettings",
display: "Account Settings",
element: qsr(".page.pageAccountSettings"),
element: pageElement,
path: "/account-settings",
urlParamsSchema: UrlParameterSchema,
afterHide: async (): Promise<void> => {
@ -241,11 +237,11 @@ export const page = new PageWithUrlParams({
state.tab = options.urlParams.tab;
}
Skeleton.append("pageAccountSettings", "main");
pageElement.find(`.tab[data-tab="${state.tab}"]`).addClass("active");
pageElement.qs(`.tab[data-tab="${state.tab}"]`)?.addClass("active");
updateUI();
},
});
$(() => {
onDocumentReady(() => {
Skeleton.save("pageAccountSettings");
});

View file

@ -7,6 +7,7 @@ import { Result } from "@monkeytype/schemas/results";
import { RankAndCount } from "@monkeytype/schemas/users";
import { roundTo2 } from "@monkeytype/util/numbers";
import { animate, AnimationParams } from "animejs";
import { ElementWithUtils } from "./dom";
export function whorf(speed: number, wordlen: number): number {
return Math.min(
@ -187,8 +188,8 @@ type LastIndex = {
export const trailingComposeChars = /[\u02B0-\u02FF`´^¨~]+$|⎄.*$/;
export async function swapElements(
el1: HTMLElement,
el2: HTMLElement,
el1: ElementWithUtils | null,
el2: ElementWithUtils | null,
totalDuration: number,
callback = async function (): Promise<void> {
return Promise.resolve();
@ -203,38 +204,35 @@ export async function swapElements(
totalDuration = applyReducedMotion(totalDuration);
if (
(el1.classList.contains("hidden") && !el2.classList.contains("hidden")) ||
(!el1.classList.contains("hidden") && el2.classList.contains("hidden"))
(el1.hasClass("hidden") && !el2.hasClass("hidden")) ||
(!el1.hasClass("hidden") && el2.hasClass("hidden"))
) {
//one of them is hidden and the other is visible
if (el1.classList.contains("hidden")) {
if (el1.hasClass("hidden")) {
await middleCallback();
await callback();
return false;
}
el1.classList.remove("hidden");
await promiseAnimate(el1, {
el1.show();
await el1.promiseAnimate({
opacity: [1, 0],
duration: totalDuration / 2,
});
el1.classList.add("hidden");
el1.hide();
await middleCallback();
el2.classList.remove("hidden");
await promiseAnimate(el2, {
el2.show();
await el2.promiseAnimate({
opacity: [0, 1],
duration: totalDuration / 2,
});
await callback();
} else if (
el1.classList.contains("hidden") &&
el2.classList.contains("hidden")
) {
} else if (el1.hasClass("hidden") && el2.hasClass("hidden")) {
//both are hidden, only fade in the second
await middleCallback();
el2.classList.remove("hidden");
await promiseAnimate(el2, {
el2.show();
await el2.promiseAnimate({
opacity: [0, 1],
duration: totalDuration / 2,
});