chore: add more eslint rules (#5687)

* duplicate

* no meaningless void

* no-unnecessary-boolean-literal-compare

* prefer includes

* fixes

* type cast

* ignore rule

* backend

* duplicate

* interface > type

* no-confusing-void-expression

* no-unnecessary-type-assertion

* extend plugin

* fix

* ignore
This commit is contained in:
Jack 2024-07-30 18:17:58 +02:00 committed by GitHub
parent 6b9f4a0f18
commit fe7a67d0fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 225 additions and 178 deletions

View file

@ -54,7 +54,7 @@ async function getOrCreateUser(
if (existingUser !== undefined && existingUser !== null) {
return existingUser;
} else if (createUser === false) {
} else if (!createUser) {
throw new MonkeyError(404, `User ${username} does not exist.`);
}
@ -256,7 +256,7 @@ async function updateUser(uid: string): Promise<void> {
timeTyping: timeTyping,
completedTests: completedTests,
startedTests: Math.round(completedTests * 1.25),
personalBests: personalBests as PersonalBests,
personalBests: personalBests,
lbPersonalBests: lbPersonalBests,
},
}

View file

@ -472,7 +472,7 @@ export async function addResult(
user.banned !== true &&
user.lbOptOut !== true &&
(isDevEnvironment() || (user.timeTyping ?? 0) > 7200) &&
completedEvent.stopOnLetter !== true;
!completedEvent.stopOnLetter;
const selectedBadgeId = user.inventory?.badges?.find((b) => b.selected)?.id;
const isPremium =

View file

@ -79,7 +79,10 @@ function applyTsRestApiRoutes(app: IRouter): void {
createExpressEndpoints(contract, router, app, {
jsonQuery: true,
requestValidationErrorHandler(err, req, res, next) {
if (err.body?.issues === undefined) return next();
if (err.body?.issues === undefined) {
next();
return;
}
const issues = err.body?.issues.map(prettyErrorMessage);
res.status(422).json({
message: "Invalid request data schema",
@ -101,7 +104,7 @@ function applyDevApiRoutes(app: Application): void {
//disable csp to allow assets to load from unsecured http
app.use((req, res, next) => {
res.setHeader("Content-Security-Policy", "");
return next();
next();
});
app.use("/configure", expressStatic(join(__dirname, "../../../private")));

View file

@ -970,7 +970,7 @@ export async function updateInbox(
//we don't need to read mails that are going to be deleted because
//Rewards will be claimed on unread mails on deletion
const readSet = [...new Set(mailToRead)].filter(
(it) => deleteSet.includes(it) === false
(it) => !deleteSet.includes(it)
);
const update = await getUsersCollection().updateOne({ uid }, [
@ -992,12 +992,12 @@ export async function updateInbox(
);
const toBeRead = inbox.filter(
(it) => readIds.includes(it.id) && it.read === false
(it) => readIds.includes(it.id) && !it.read
);
//flatMap rewards
const rewards: AllRewards[] = [...toBeRead, ...toBeDeleted]
.filter((it) => it.read === false)
.filter((it) => !it.read)
.reduce((arr, current) => {
return [...arr, ...current.rewards];
}, []);
@ -1126,7 +1126,7 @@ export async function checkIfUserIsPremium(
): Promise<boolean> {
const premiumFeaturesEnabled = (await getCachedConfiguration(true)).users
.premium.enabled;
if (premiumFeaturesEnabled !== true) {
if (!premiumFeaturesEnabled) {
return false;
}
const user =

View file

@ -50,7 +50,7 @@ export async function connect(): Promise<void> {
};
mongoClient = new MongoClient(
(DB_URI as string) ?? global.__MONGO_URI__, // Set in tests only
DB_URI ?? global.__MONGO_URI__, // Set in tests only
connectionOptions
);

View file

@ -112,7 +112,8 @@ async function _authenticateRequestInternal(
req
);
return next(error);
next(error);
return;
}
recordAuthTime(
token.type,
@ -345,7 +346,8 @@ export function authenticateGithubWebhook(): Handler {
}
}
} catch (e) {
return next(e);
next(e);
return;
}
next();

View file

@ -98,13 +98,14 @@ async function errorHandlingMiddleware(
delete monkeyResponse.data.errorId;
}
return handleMonkeyResponse(monkeyResponse, res);
handleMonkeyResponse(monkeyResponse, res);
return;
} catch (e) {
Logger.error("Error handling middleware failed.");
Logger.error(e);
}
return handleMonkeyResponse(
handleMonkeyResponse(
new MonkeyResponse(
"Something went really wrong, please contact support.",
undefined,

View file

@ -8,10 +8,12 @@ import { isDevEnvironment } from "../utils/misc";
const REQUEST_MULTIPLIER = isDevEnvironment() ? 100 : 1;
const getKey = (req: MonkeyTypes.Request, _res: Response): string => {
return ((req.headers["cf-connecting-ip"] as string) ||
return (
(req.headers["cf-connecting-ip"] as string) ||
(req.headers["x-forwarded-for"] as string) ||
(req.ip as string) ||
"255.255.255.255") as string;
"255.255.255.255"
);
};
const getKeyWithUid = (req: MonkeyTypes.Request, _res: Response): string => {
@ -61,7 +63,8 @@ export async function badAuthRateLimiterHandler(
const badAuthEnabled =
req?.ctx?.configuration?.rateLimiting?.badAuthentication?.enabled;
if (!badAuthEnabled) {
return next();
next();
return;
}
try {
@ -75,7 +78,8 @@ export async function badAuthRateLimiterHandler(
);
}
} catch (error) {
return next(error);
next(error);
return;
}
next();

View file

@ -28,7 +28,7 @@ export function asyncHandler(handler: AsyncHandler): RequestHandler {
) => {
try {
const handlerData = await handler(req, res);
return handleMonkeyResponse(handlerData, res);
handleMonkeyResponse(handlerData, res);
} catch (error) {
next(error);
}

View file

@ -35,7 +35,8 @@ export function handleMonkeyResponse(
//@ts-expect-error ignored so that we can see message in swagger stats
res.monkeyMessage = message;
if ([301, 302].includes(status)) {
return res.redirect(data);
res.redirect(data);
return;
}
res.json({ message, data });

View file

@ -102,7 +102,7 @@ export function getFontawesomeConfig(debug = false): FontawesomeConfig {
throw new Error("unknown icons: " + leftOvers);
}
if (debug === true) {
if (debug) {
console.debug(
"Make sure fontawesome modules are active: ",
Object.entries(modules2)

View file

@ -491,9 +491,7 @@ async function runActiveCommand(): Promise<void> {
updateInput(inputModeParams.value as string);
hideCommands();
} else if (command.subgroup) {
CommandlineLists.pushToStack(
command.subgroup as MonkeyTypes.CommandsSubgroup
);
CommandlineLists.pushToStack(command.subgroup);
updateInput("");
await filterSubgroup();
await showCommands();

View file

@ -246,9 +246,7 @@ export const commands: MonkeyTypes.CommandsSubgroup = {
icon: "fa-tint",
exec: ({ input }): void => {
if (input === undefined) return;
void UpdateConfig.setCustomLayoutfluid(
input as MonkeyTypes.CustomLayoutFluidSpaces
);
void UpdateConfig.setCustomLayoutfluid(input);
},
},

View file

@ -21,7 +21,7 @@ function update(themes: MonkeyTypes.Theme[]): void {
subgroup.list = [];
const favs: MonkeyTypes.Command[] = [];
themes.forEach((theme) => {
if ((Config.favThemes as string[]).includes(theme.name)) {
if (Config.favThemes.includes(theme.name)) {
favs.push({
id: "changeTheme" + capitalizeFirstLetterOfEachWord(theme.name),
display: theme.name.replace(/_/g, " "),

View file

@ -1875,10 +1875,7 @@ export async function setCustomLayoutfluid(
return false;
}
const customLayoutfluid = trimmed.replace(
/ /g,
"#"
) as ConfigSchemas.CustomLayoutFluid;
const customLayoutfluid = trimmed.replace(/ /g, "#");
config.customLayoutfluid = customLayoutfluid;
saveToLocalStorage("customLayoutfluid", nosave);

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
type Config = {
backendUrl: string;
isDevelopment: boolean;

View file

@ -215,10 +215,8 @@ export async function loadUser(user: UserType): Promise<void> {
const response = await Ape.results.save(TestLogic.notSignedInLastResult);
if (response.status !== 200) {
return Notifications.add(
"Failed to save last result: " + response.message,
-1
);
Notifications.add("Failed to save last result: " + response.message, -1);
return;
}
TestLogic.clearNotSignedInResult();
@ -491,7 +489,7 @@ async function signUp(): Promise<void> {
});
return;
}
await RegisterCaptchaModal.show();
RegisterCaptchaModal.show();
const captchaToken = await RegisterCaptchaModal.promise;
if (captchaToken === undefined || captchaToken === "") {
Notifications.add("Please complete the captcha", -1);

View file

@ -55,9 +55,7 @@ export function verify(result: Result<Mode>): string | null {
for (const requirementType in TestState.activeChallenge.requirements) {
if (!requirementsMet) return null;
const requirementValue =
TestState.activeChallenge.requirements[
requirementType as keyof typeof TestState.activeChallenge.requirements
];
TestState.activeChallenge.requirements[requirementType];
if (requirementValue === undefined) {
throw new Error("Requirement value is undefined");

View file

@ -746,7 +746,7 @@ function handleChar(
document.querySelectorAll<HTMLElement>("#words .word")[
TestUI.currentWordElementIndex - 1
]?.offsetTop ?? 0
) as number;
);
if (!Config.showAllLines) TestUI.lineJump(currentTop);
} else {
TestInput.input.current = TestInput.input.current.slice(0, -1);

View file

@ -35,12 +35,14 @@ export async function change(
console.debug(
`change page to ${pageName} stopped, page transition is true`
);
return resolve(false);
resolve(false);
return;
}
if (!options.force && ActivePage.get() === pageName) {
console.debug(`change page ${pageName} stoped, page already active`);
return resolve(false);
resolve(false);
return;
} else {
console.log(`changing page ${pageName}`);
}

View file

@ -990,6 +990,7 @@ function verifyResultFiltersStructure(filterIn: ResultFilters): ResultFilters {
const key = entry[0] as keyof ResultFilters;
const value = entry[1];
if (filter[key] === undefined) {
// @ts-expect-error key and value is based on default filter so this is safe to ignore
filter[key] = value;
}
});

View file

@ -62,7 +62,7 @@ function hide(): void {
for (const r of rewardsClaimed) {
if (r.type === "xp") {
totalXpClaimed += r.item as number;
totalXpClaimed += r.item;
} else if (r.type === "badge") {
const badge = BadgeController.getById(r.item.id);
if (badge) {

View file

@ -133,11 +133,11 @@ export async function refresh(
lts = layouts["qwerty"];
layoutString = "default";
} else {
lts = layouts[Config.layout as keyof typeof layouts];
lts = layouts[Config.layout];
layoutString = Config.layout;
}
} else {
lts = layouts[Config.keymapLayout as keyof typeof layouts];
lts = layouts[Config.keymapLayout];
layoutString = Config.keymapLayout;
}

View file

@ -198,9 +198,7 @@ function updateFooter(lb: LbKey): void {
let toppercent = "";
if (currentTimeRange === "allTime" && lbRank !== undefined && lbRank?.rank) {
const num = Numbers.roundTo2(
(lbRank.rank / (currentRank[lb].count as number)) * 100
);
const num = Numbers.roundTo2((lbRank.rank / currentRank[lb].count) * 100);
if (currentRank[lb].rank === 1) {
toppercent = "GOAT";
} else {
@ -482,10 +480,11 @@ async function update(): Promise<void> {
if (failedResponses.length > 0) {
hideLoader("15");
hideLoader("60");
return Notifications.add(
Notifications.add(
"Failed to load leaderboards: " + failedResponses[0]?.message,
-1
);
return;
}
const [lb15Data, lb60Data] = responses.map((response) => response.data);

View file

@ -90,8 +90,8 @@ function updateParticle(particle: Particle): void {
particle.prev.x = particle.x;
particle.prev.y = particle.y;
// Update pos
particle.x += particle.vel.x * (ctx.deltaTime as number);
particle.y += particle.vel.y * (ctx.deltaTime as number);
particle.x += particle.vel.x * ctx.deltaTime;
particle.y += particle.vel.y * ctx.deltaTime;
if (particle.x > ctx.canvas.width) {
particle.vel.x *= -particleBounceMod;

View file

@ -67,7 +67,7 @@ export default class SettingsGroup<T extends ConfigValue> {
let typed = value as T;
if (typed === "true") typed = true as T;
if (typed === "false") typed = false as T;
this.setValue(typed as T);
this.setValue(typed);
}
);
} else if (this.mode === "range") {
@ -109,9 +109,9 @@ export default class SettingsGroup<T extends ConfigValue> {
`.pageSettings .section[data-config-name='${this.configName}'] button`
).removeClass("active");
if (this.mode === "select") {
const select = document.querySelector(
const select = document.querySelector<HTMLSelectElement>(
`.pageSettings .section[data-config-name='${this.configName}'] select`
) as HTMLSelectElement | null;
);
if (select === null) {
return;
@ -131,12 +131,12 @@ export default class SettingsGroup<T extends ConfigValue> {
`.pageSettings .section[data-config-name='${this.configName}'] button[data-config-value='${this.configValue}']`
).addClass("active");
} else if (this.mode === "range") {
const range = document.querySelector(
const range = document.querySelector<HTMLInputElement>(
`.pageSettings .section[data-config-name='${this.configName}'] input[type=range]`
) as HTMLInputElement | null;
);
const rangeValue = document.querySelector(
`.pageSettings .section[data-config-name='${this.configName}'] .value`
) as HTMLSpanElement | null;
);
if (range === null || rangeValue === null) {
return;

View file

@ -20,7 +20,7 @@ function updateActiveButton(): void {
Config.randomTheme !== "custom" &&
ThemeController.randomTheme !== null
) {
activeThemeName = ThemeController.randomTheme as string;
activeThemeName = ThemeController.randomTheme;
}
document
@ -169,7 +169,7 @@ export async function refreshButtons(): Promise<void> {
Config.randomTheme !== "custom" &&
ThemeController.randomTheme !== null
) {
activeThemeName = ThemeController.randomTheme as string;
activeThemeName = ThemeController.randomTheme;
}
let themes;

View file

@ -53,7 +53,7 @@ function addToGlobal(items: Record<string, unknown>): void {
void loadFromLocalStorage();
void VersionButton.update();
void Focus.set(true, true);
Focus.set(true, true);
addToGlobal({
snapshot: DB.getSnapshot,

View file

@ -47,7 +47,7 @@ function refreshList(): void {
}
</button>
</td>
<td onClick=${console.log(key)}>${key.name}</td>
<td>${key.name}</td>
<td>${format(new Date(key.createdOn), "dd MMM yyyy HH:mm")}</td>
<td>${format(new Date(key.modifiedOn), "dd MMM yyyy HH:mm")}</td>
<td>${
@ -116,7 +116,8 @@ async function toggleActiveKey(keyId: string): Promise<void> {
const response = await Ape.apeKeys.update(keyId, { enabled: !key.enabled });
Loader.hide();
if (response.status !== 200) {
return Notifications.add("Failed to update key: " + response.message, -1);
Notifications.add("Failed to update key: " + response.message, -1);
return;
}
key.enabled = !key.enabled;
refreshList();

View file

@ -109,17 +109,17 @@ const modal = new AnimatedModal({
}
});
modalEl.querySelector(".acceptSelected")?.addEventListener("click", () => {
const analytics = (
const analyticsChecked = (
modalEl.querySelector(".cookie.analytics input") as HTMLInputElement
).checked;
const accepted = {
security: true,
analytics,
analytics: analyticsChecked,
};
setAcceptedObject(accepted);
void hide();
if (analytics === true) {
if (analyticsChecked) {
activateAnalytics();
}
});

View file

@ -8,7 +8,7 @@ function parseInput(input: string): number {
const re = /((-\s*)?\d+(\.\d+)?\s*[hms]?)/g;
const seconds = [...input.toLowerCase().matchAll(re)]
.map((match) => {
const part = match[0] as string;
const part = match[0];
const duration = parseFloat(part.replace(/\s+/g, ""));
if (part.includes("h")) {

View file

@ -121,10 +121,8 @@ async function save(): Promise<void> {
state.tags = state.tags.filter((el) => el !== undefined);
if (response.status !== 200) {
return Notifications.add(
"Failed to update result tags: " + response.message,
-1
);
Notifications.add("Failed to update result tags: " + response.message, -1);
return;
}
//can do this because the response will not be null if the status is 200

View file

@ -59,15 +59,17 @@ async function hide(): Promise<void> {
async function apply(): Promise<void> {
if (!signedInUser) {
return Notifications.add(
Notifications.add(
"Missing user credential. Please close the popup and try again.",
-1
);
return;
}
const captcha = CaptchaController.getResponse("googleSignUpModal");
if (!captcha) {
return Notifications.add("Please complete the captcha", 0);
Notifications.add("Please complete the captcha", 0);
return;
}
disableInput();
@ -190,10 +192,11 @@ const checkNameDebounced = debounce(1000, async () => {
if (response.status !== 200) {
nameIndicator.show("unavailable");
return Notifications.add(
Notifications.add(
"Failed to check name availability: " + response.message,
-1
);
return;
}
});
@ -206,7 +209,8 @@ async function setup(modalEl: HTMLElement): Promise<void> {
disableButton();
const val = $("#googleSignUpModal input").val() as string;
if (val === "") {
return nameIndicator.hide();
nameIndicator.hide();
return;
} else {
nameIndicator.show("checking");
void checkNameDebounced();

View file

@ -96,10 +96,8 @@ async function getQuotes(): Promise<void> {
Loader.hide();
if (response.status !== 200) {
return Notifications.add(
"Failed to get new quotes: " + response.message,
-1
);
Notifications.add("Failed to get new quotes: " + response.message, -1);
return;
}
quotes = response.data ?? [];
@ -158,10 +156,8 @@ async function approveQuote(index: number, dbid: string): Promise<void> {
if (response.status !== 200) {
resetButtons(index);
quote.find("textarea, input").prop("disabled", false);
return Notifications.add(
"Failed to approve quote: " + response.message,
-1
);
Notifications.add("Failed to approve quote: " + response.message, -1);
return;
}
Notifications.add(`Quote approved. ${response.message ?? ""}`, 1);
@ -182,7 +178,8 @@ async function refuseQuote(index: number, dbid: string): Promise<void> {
if (response.status !== 200) {
resetButtons(index);
quote.find("textarea, input").prop("disabled", false);
return Notifications.add("Failed to refuse quote: " + response.message, -1);
Notifications.add("Failed to refuse quote: " + response.message, -1);
return;
}
Notifications.add("Quote refused.", 1);
@ -213,10 +210,8 @@ async function editQuote(index: number, dbid: string): Promise<void> {
if (response.status !== 200) {
resetButtons(index);
quote.find("textarea, input").prop("disabled", false);
return Notifications.add(
"Failed to approve quote: " + response.message,
-1
);
Notifications.add("Failed to approve quote: " + response.message, -1);
return;
}
Notifications.add(`Quote edited and approved. ${response.message ?? ""}`, 1);

View file

@ -131,7 +131,8 @@ function hide(clearChain = false): void {
async function submit(): Promise<void> {
if (rating === 0) {
return Notifications.add("Please select a rating");
Notifications.add("Please select a rating");
return;
}
if (!currentQuote) {
return;
@ -143,10 +144,8 @@ async function submit(): Promise<void> {
Loader.hide();
if (response.status !== 200) {
return Notifications.add(
"Failed to submit quote rating: " + response.message,
-1
);
Notifications.add("Failed to submit quote rating: " + response.message, -1);
return;
}
const snapshot = DB.getSnapshot();

View file

@ -52,10 +52,7 @@ export async function show(
},
});
new CharacterCounter(
$("#quoteReportModal .comment") as JQuery<HTMLTextAreaElement>,
250
);
new CharacterCounter($("#quoteReportModal .comment"), 250);
},
});
}
@ -74,32 +71,37 @@ async function hide(clearChain = false): Promise<void> {
async function submitReport(): Promise<void> {
const captchaResponse = CaptchaController.getResponse("quoteReportModal");
if (!captchaResponse) {
return Notifications.add("Please complete the captcha");
Notifications.add("Please complete the captcha");
return;
}
const quoteId = state.quoteToReport?.id.toString();
const quoteLanguage = removeLanguageSize(Config.language);
const reason = $("#quoteReportModal .reason").val() as string;
const comment = $("#quoteReportModal .comment").val() as string;
const captcha = captchaResponse as string;
const captcha = captchaResponse;
if (quoteId === undefined || quoteId === "") {
return Notifications.add("Please select a quote");
Notifications.add("Please select a quote");
return;
}
if (!reason) {
return Notifications.add("Please select a valid report reason");
Notifications.add("Please select a valid report reason");
return;
}
if (!comment) {
return Notifications.add("Please provide a comment");
Notifications.add("Please provide a comment");
return;
}
const characterDifference = comment.length - 250;
if (characterDifference > 0) {
return Notifications.add(
Notifications.add(
`Report comment is ${characterDifference} character(s) too long`
);
return;
}
Loader.show();
@ -113,7 +115,8 @@ async function submitReport(): Promise<void> {
Loader.hide();
if (response.status !== 200) {
return Notifications.add("Failed to report quote: " + response.message, -1);
Notifications.add("Failed to report quote: " + response.message, -1);
return;
}
Notifications.add("Report submitted. Thank you!", 1);

View file

@ -209,7 +209,7 @@ async function updateResults(searchText: string): Promise<void> {
const searchResults = modal
.getModal()
.querySelectorAll(".searchResult") as NodeListOf<HTMLElement>;
.querySelectorAll<HTMLElement>(".searchResult");
for (const searchResult of searchResults) {
const quoteId = parseInt(searchResult.dataset["quoteId"] as string);
searchResult
@ -325,7 +325,7 @@ function hide(clearChain = false): void {
function apply(val: number): void {
if (isNaN(val)) {
val = parseInt(
(document.getElementById("searchBox") as HTMLInputElement).value as string
(document.getElementById("searchBox") as HTMLInputElement).value
);
}
if (val !== null && !isNaN(val) && val >= 0) {

View file

@ -32,7 +32,8 @@ async function submitQuote(): Promise<void> {
const captcha = CaptchaController.getResponse("submitQuote");
if (!text || !source || !language) {
return Notifications.add("Please fill in all fields", 0);
Notifications.add("Please fill in all fields", 0);
return;
}
Loader.show();
@ -40,7 +41,8 @@ async function submitQuote(): Promise<void> {
Loader.hide();
if (response.status !== 200) {
return Notifications.add("Failed to submit quote: " + response.message, -1);
Notifications.add("Failed to submit quote: " + response.message, -1);
return;
}
Notifications.add("Quote submitted.", 1);
@ -71,10 +73,7 @@ export async function show(showOptions: ShowOptions): Promise<void> {
$("#quoteSubmitModal .newQuoteLanguage").trigger("change");
$("#quoteSubmitModal input").val("");
new CharacterCounter(
$("#quoteSubmitModal .newQuoteText") as JQuery<HTMLTextAreaElement>,
250
);
new CharacterCounter($("#quoteSubmitModal .newQuoteText"), 250);
},
});
}

View file

@ -111,7 +111,6 @@ type PopupKey =
| "resetAccount"
| "clearTagPb"
| "optOutOfLeaderboards"
| "clearTagPb"
| "applyCustomFont"
| "resetPersonalBests"
| "resetSettings"
@ -287,7 +286,7 @@ class SimpleModal {
attributes["value"] = input.initVal?.toString() ?? "";
attributes["type"] = input.type;
}
if (!input.hidden && !input.optional === true) {
if (!input.hidden && !input.optional) {
attributes["required"] = true;
}
if (input.disabled) {
@ -1887,7 +1886,7 @@ export function showPopup(
Notifications.add("Failed to show popup - popup is not defined", -1);
return;
}
if (popup.onlineOnly === true && !ConnectionState.get()) {
if (popup.onlineOnly && !ConnectionState.get()) {
Notifications.add("You are offline", 0, { duration: 2 });
return;
}

View file

@ -28,7 +28,8 @@ let select: SlimSelect | undefined = undefined;
export async function show(options: ShowOptions): Promise<void> {
if (!isAuthenticated()) {
return Notifications.add("You must be logged in to submit a report", 0);
Notifications.add("You must be logged in to submit a report", 0);
return;
}
void modal.show({
@ -59,10 +60,7 @@ export async function show(options: ShowOptions): Promise<void> {
},
});
new CharacterCounter(
$("#userReportModal .comment") as JQuery<HTMLTextAreaElement>,
250
);
new CharacterCounter($("#userReportModal .comment"), 250);
}
async function hide(): Promise<void> {
@ -78,36 +76,41 @@ async function hide(): Promise<void> {
async function submitReport(): Promise<void> {
const captchaResponse = CaptchaController.getResponse("userReportModal");
if (!captchaResponse) {
return Notifications.add("Please complete the captcha");
Notifications.add("Please complete the captcha");
return;
}
const reason = $("#userReportModal .reason").val() as string;
const comment = $("#userReportModal .comment").val() as string;
const captcha = captchaResponse as string;
const captcha = captchaResponse;
if (!reason) {
return Notifications.add("Please select a valid report reason");
Notifications.add("Please select a valid report reason");
return;
}
if (!comment) {
return Notifications.add("Please provide a comment");
Notifications.add("Please provide a comment");
return;
}
if (reason === "Suspected cheating" && state.lbOptOut) {
return Notifications.add(
Notifications.add(
"You cannot report this user for suspected cheating as they have opted out of the leaderboards.",
0,
{
duration: 10,
}
);
return;
}
const characterDifference = comment.length - 250;
if (characterDifference > 0) {
return Notifications.add(
Notifications.add(
`Report comment is ${characterDifference} character(s) too long`
);
return;
}
Loader.show();
@ -120,7 +123,8 @@ async function submitReport(): Promise<void> {
Loader.hide();
if (response.status !== 200) {
return Notifications.add("Failed to report user: " + response.message, -1);
Notifications.add("Failed to report user: " + response.message, -1);
return;
}
Notifications.add("Report submitted. Thank you!", 1);

View file

@ -220,8 +220,8 @@ async function fillContent(): Promise<void> {
PbTables.update(snapshot.personalBests);
void Profile.update("account", snapshot);
void TestActivity.init(snapshot.testActivity, new Date(snapshot.addedAt));
void void ResultBatches.update();
TestActivity.init(snapshot.testActivity, new Date(snapshot.addedAt));
void ResultBatches.update();
chartData = [];
accChartData = [];

View file

@ -260,7 +260,8 @@ $(".page.pageLogin .register.side .usernameInput").on("input", () => {
".page.pageLogin .register.side .usernameInput"
).val() as string;
if (val === "") {
return nameIndicator.hide();
nameIndicator.hide();
return;
} else {
nameIndicator.show("checking");
void checkNameDebounced();

View file

@ -186,10 +186,8 @@ async function update(options: UpdateOptions): Promise<void> {
$(".page.pageProfile .error .message").text(message);
} else if (response.status !== 200) {
// $(".page.pageProfile .failedToLoad").removeClass("hidden");
return Notifications.add(
"Failed to load profile: " + response.message,
-1
);
Notifications.add("Failed to load profile: " + response.message, -1);
return;
} else {
window.history.replaceState(null, "", `/profile/${response.data.name}`);
await Profile.update("profile", response.data);

View file

@ -169,11 +169,15 @@ export function setCustomTextLongProgress(
}
function getLocalStorage(): CustomTextObject {
return JSON.parse(window.localStorage.getItem("customText") ?? "{}");
return JSON.parse(
window.localStorage.getItem("customText") ?? "{}"
) as CustomTextObject;
}
function getLocalStorageLong(): CustomTextLongObject {
return JSON.parse(window.localStorage.getItem("customTextLong") ?? "{}");
return JSON.parse(
window.localStorage.getItem("customTextLong") ?? "{}"
) as CustomTextLongObject;
}
function setLocalStorage(data: CustomTextObject): void {

View file

@ -139,7 +139,7 @@ export function canSetConfigWithCurrentFunboxes(
Notifications.add(
`You can't set ${Strings.camelCaseToWords(
key
)} to ${value} with currently active funboxes.`,
)} to ${value.toString()} with currently active funboxes.`,
0,
{
duration: 5,
@ -185,7 +185,7 @@ export function canSetFunboxWithConfig(
errorStrings.push(
`${Strings.capitalizeFirstLetter(
Strings.camelCaseToWords(error.key)
)} cannot be set to ${error.value}.`
)} cannot be set to ${error.value.toString()}.`
);
}
Notifications.add(

View file

@ -893,7 +893,7 @@ export async function finish(difficultyFailed = false): Promise<void> {
dontSave = true;
}
const completedEvent = JSON.parse(JSON.stringify(ce));
const completedEvent = JSON.parse(JSON.stringify(ce)) as CompletedEvent;
///////// completed event ready
@ -1008,7 +1008,7 @@ export async function finish(difficultyFailed = false): Promise<void> {
) {
// They bailed out
const historyLength = TestInput.input.getHistory()?.length as number;
const historyLength = TestInput.input.getHistory()?.length;
const newProgress =
CustomText.getCustomTextLongProgress(customTextName) + historyLength;
CustomText.setCustomTextLongProgress(customTextName, newProgress);
@ -1049,7 +1049,9 @@ export async function finish(difficultyFailed = false): Promise<void> {
$("#result .stats .dailyLeaderboard").addClass("hidden");
TestStats.setLastResult(JSON.parse(JSON.stringify(completedEvent)));
TestStats.setLastResult(
JSON.parse(JSON.stringify(completedEvent)) as CompletedEvent
);
if (!ConnectionState.get()) {
ConnectionState.showOfflineBanner();
@ -1067,6 +1069,7 @@ export async function finish(difficultyFailed = false): Promise<void> {
);
if (completedEvent.chartData !== "toolong") {
// @ts-expect-error TODO: check if this is needed
delete completedEvent.chartData.unsmoothedRaw;
}
@ -1089,7 +1092,7 @@ export async function finish(difficultyFailed = false): Promise<void> {
Result.updateRateQuote(TestWords.currentQuote);
AccountButton.loading(true);
if (completedEvent.bailedOut !== true) {
if (!completedEvent.bailedOut) {
completedEvent.challenge = ChallengeContoller.verify(completedEvent);
}
@ -1151,7 +1154,8 @@ async function saveResult(
response.message =
"Looks like your result data is using an incorrect schema. Please refresh the page to download the new update. If the problem persists, please contact support.";
}
return Notifications.add("Failed to save result: " + response.message, -1);
Notifications.add("Failed to save result: " + response.message, -1);
return;
}
$("#result .stats .tags .editTagsButton").attr(

View file

@ -69,13 +69,13 @@ export function getStats(): unknown {
try {
// @ts-expect-error
ret.keypressTimings.spacing.average =
(TestInput.keypressTimings.spacing.array as number[]).reduce(
TestInput.keypressTimings.spacing.array.reduce(
(previous, current) => (current += previous)
) / TestInput.keypressTimings.spacing.array.length;
// @ts-expect-error
ret.keypressTimings.spacing.sd = Numbers.stdDev(
TestInput.keypressTimings.spacing.array as number[]
TestInput.keypressTimings.spacing.array
);
} catch (e) {
//
@ -83,13 +83,13 @@ export function getStats(): unknown {
try {
// @ts-expect-error
ret.keypressTimings.duration.average =
(TestInput.keypressTimings.duration.array as number[]).reduce(
TestInput.keypressTimings.duration.array.reduce(
(previous, current) => (current += previous)
) / TestInput.keypressTimings.duration.array.length;
// @ts-expect-error
ret.keypressTimings.duration.sd = Numbers.stdDev(
TestInput.keypressTimings.duration.array as number[]
TestInput.keypressTimings.duration.array
);
} catch (e) {
//
@ -295,7 +295,7 @@ function countChars(): CharCount {
correctChars += targetWord.length;
if (
i < inputWords.length - 1 &&
Strings.getLastChar(inputWord as string) !== "\n"
Strings.getLastChar(inputWord) !== "\n"
) {
correctspaces++;
}

View file

@ -390,7 +390,7 @@ export function showWords(): void {
let wordsHTML = "";
if (Config.mode !== "zen") {
for (let i = 0; i < TestWords.words.length; i++) {
wordsHTML += getWordHTML(TestWords.words.get(i) as string);
wordsHTML += getWordHTML(TestWords.words.get(i));
}
} else {
wordsHTML =
@ -421,9 +421,7 @@ export async function updateWordsInputPosition(initial = false): Promise<void> {
const isLanguageRTL = currentLanguage.rightToLeft;
const el = document.querySelector("#wordsInput") as HTMLElement;
const activeWord = document.querySelector(
"#words .active"
) as HTMLElement | null;
const activeWord = document.querySelector<HTMLElement>("#words .active");
if (!activeWord) {
el.style.top = "0px";
@ -1385,7 +1383,7 @@ export async function applyBurstHeatmap(): Promise<void> {
if (wordBurstAttr === undefined) {
$(word).css("color", unreachedColor);
} else {
let wordBurstVal = parseInt(wordBurstAttr as string);
let wordBurstVal = parseInt(wordBurstAttr);
wordBurstVal = Math.round(
getTypingSpeedUnit(Config.typingSpeedUnit).fromWpm(wordBurstVal)
);
@ -1503,7 +1501,8 @@ $(".pageTest #copyWordsListButton").on("click", async () => {
if (Config.mode === "zen") {
words = TestInput.input.history.join(" ");
} else {
words = (TestWords.words.get() as string[])
words = TestWords.words
.get()
.slice(0, TestInput.input.history.length)
.join(" ");
}

View file

@ -672,18 +672,18 @@ export async function generateWords(
}
ret.hasTab =
ret.words.some((w) => /\t/.test(w)) ||
currentWordset.words.some((w) => /\t/.test(w)) ||
ret.words.some((w) => w.includes("\t")) ||
currentWordset.words.some((w) => w.includes("\t")) ||
(Config.mode === "quote" &&
(quote as MonkeyTypes.QuoteWithTextSplit).textSplit.some((w) =>
/\t/.test(w)
w.includes("\t")
));
ret.hasNewline =
ret.words.some((w) => /\n/.test(w)) ||
currentWordset.words.some((w) => /\n/.test(w)) ||
ret.words.some((w) => w.includes("\n")) ||
currentWordset.words.some((w) => w.includes("\n")) ||
(Config.mode === "quote" &&
(quote as MonkeyTypes.QuoteWithTextSplit).textSplit.some((w) =>
/\n/.test(w)
w.includes("\n")
));
sectionHistory = []; //free up a bit of memory? is that even a thing?

View file

@ -195,7 +195,10 @@ export default class AnimatedModal<
async show(options?: ShowOptions<IncomingModalChainData>): Promise<void> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
if (this.open) return resolve();
if (this.open) {
resolve();
return;
}
Skeleton.append(this.dialogId, this.skeletonAppendParent);
if (!this.setupRan) {
@ -203,7 +206,10 @@ export default class AnimatedModal<
this.setupRan = true;
}
if (isPopupVisible(this.dialogId)) return resolve();
if (isPopupVisible(this.dialogId)) {
resolve();
return;
}
const modalAnimationDuration =
(options?.customAnimation?.modal?.durationMs ??
@ -316,7 +322,10 @@ export default class AnimatedModal<
async hide(options?: HideOptions<OutgoingModalChainData>): Promise<void> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
if (!isPopupVisible(this.dialogId)) return resolve();
if (!isPopupVisible(this.dialogId)) {
resolve();
return;
}
if (options?.clearModalChain) {
this.previousModalInChain = undefined;

View file

@ -30,17 +30,13 @@ export async function linkDiscord(hashOverride: string): Promise<void> {
Loader.hide();
if (response.status !== 200) {
return Notifications.add(
"Failed to link Discord: " + response.message,
-1
);
Notifications.add("Failed to link Discord: " + response.message, -1);
return;
}
if (response.data === null) {
return Notifications.add(
"Failed to link Discord: data returned was null",
-1
);
Notifications.add("Failed to link Discord: data returned was null", -1);
return;
}
Notifications.add(response.message, 1);
@ -71,7 +67,8 @@ export function loadCustomThemeFromUrl(getOverride?: string): void {
try {
decoded = JSON.parse(atob(getValue));
} catch (e) {
return Notifications.add("Invalid custom theme ", 0);
Notifications.add("Invalid custom theme ", 0);
return;
}
let colorArray = [];
@ -87,7 +84,8 @@ export function loadCustomThemeFromUrl(getOverride?: string): void {
}
if (colorArray.length === 0) {
return Notifications.add("Invalid custom theme ", 0);
Notifications.add("Invalid custom theme ", 0);
return;
}
const oldCustomTheme = Config.customTheme;
@ -213,7 +211,7 @@ export function loadChallengeFromUrl(getOverride?: string): void {
Notifications.add("Loading challenge", 0);
ChallengeController.setup(getValue)
.then((result) => {
if (result === true) {
if (result) {
Notifications.add("Challenge loaded", 1);
restartTest({
nosave: true,

View file

@ -54,9 +54,39 @@ module.exports = {
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
// "plugin:@typescript-eslint/recommended-requiring-type-checking"
"plugin:@typescript-eslint/strict-type-checked",
],
rules: {
//strict type checked
"@typescript-eslint/require-await": "off",
"@typescript-eslint/unbound-method": "off",
"@typescript-eslint/await-thenable": "off",
"@typescript-eslint/no-useless-template-literals": "off",
"@typescript-eslint/prefer-promise-reject-errors": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-unnecessary-type-arguments": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-redundant-type-constituents": "off",
"@typescript-eslint/restrict-plus-operands": "off",
// TODO: enable at some point
"@typescript-eslint/no-unsafe-return": "off", //~12
"@typescript-eslint/no-unsafe-assignment": "off", //~63
"@typescript-eslint/no-unsafe-argument": "off", //~37
"@typescript-eslint/no-unsafe-call": "off", //~76
"@typescript-eslint/no-unsafe-member-access": "off", //~105
//
"@typescript-eslint/no-unnecessary-type-assertion": [
"error",
{
typesToIgnore: ["HTMLElement", "Element"],
},
],
"@typescript-eslint/no-confusing-void-expression": [
"error",
{ ignoreArrowShorthand: true },
],
"@typescript-eslint/explicit-function-return-type": ["error"],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "warn",

View file

@ -184,7 +184,6 @@ export type DBResult<T extends Mode> = Omit<
| "customText"
| "quoteLength"
| "isPb"
| "customText"
> & {
correctChars?: number; // --------------
incorrectChars?: number; // legacy results
@ -241,7 +240,7 @@ export type CustomTextDataWithTextLen = Omit<CustomTextData, "text"> & {
textLen: number;
};
export interface ResultFilters {
export type ResultFilters = {
_id: string;
name: string;
pb: {
@ -300,7 +299,7 @@ export interface ResultFilters {
funbox: {
none?: boolean;
} & Record<string, boolean>;
}
};
export type PSA = {
_id: string;