refactor: enable @typescript-eslint/strict (#5028)

* enable rule

* eslint --fix

* fixes

* --fix

* fixes

* fixes

* fixes

* extract into const
This commit is contained in:
Jack 2024-02-12 16:46:56 +01:00 committed by GitHub
parent dbc5898e49
commit 084d42c62e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
121 changed files with 927 additions and 990 deletions

View file

@ -27,9 +27,9 @@
"plugin:import/typescript",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
"prettier",
// "plugin:@typescript-eslint/recommended-requiring-type-checking"
// "plugin:@typescript-eslint/strict"
"plugin:@typescript-eslint/strict"
],
"plugins": ["json", "require-path-exists", "@typescript-eslint"],
"rules": {
@ -82,8 +82,12 @@
"@typescript-eslint/strict-boolean-expressions": [
"error",
{ "allowNullableBoolean": true, "allowNullableNumber": true }
]
// "@typescript-eslint/no-unnecessary-condition": "error" TODO ADD THIS
],
//
"@typescript-eslint/non-nullable-type-assertion-style": "off",
"@typescript-eslint/no-unnecessary-condition": "off",
"@typescript-eslint/consistent-type-definitions": ["warn", "type"],
"@typescript-eslint/no-invalid-void-type": "off"
},
"parserOptions": {
"ecmaVersion": 12,

View file

@ -46,7 +46,7 @@ import { canFunboxGetPb } from "../../utils/pb";
import { buildDbResult } from "../../utils/result";
try {
if (anticheatImplemented() === false) throw new Error("undefined");
if (!anticheatImplemented()) throw new Error("undefined");
Logger.success("Anticheat module loaded");
} catch (e) {
if (isDevEnvironment()) {
@ -86,7 +86,7 @@ export async function getResults(
//check if premium features are disabled and current call exceeds the limit for regular users
if (
userHasPremium &&
premiumFeaturesEnabled === false &&
!premiumFeaturesEnabled &&
limit + offset > req.ctx.configuration.results.limits.regularUser
) {
throw new MonkeyError(503, "Premium feature disabled.");
@ -169,18 +169,6 @@ export async function updateTags(
});
}
interface AddResultData {
isPb: boolean;
tagPbs: string[];
insertedId: ObjectId;
dailyLeaderboardRank?: number;
weeklyXpLeaderboardRank?: number;
xp: number;
dailyXpBonus: boolean;
xpBreakdown: Record<string, number>;
streak: number;
}
export async function addResult(
req: MonkeyTypes.Request
): Promise<MonkeyResponse> {
@ -523,17 +511,18 @@ export async function addResult(
}
const streak = await UserDAL.updateStreak(uid, completedEvent.timestamp);
const badgeWaitingInInbox = (
user.inbox
?.map((i) =>
(i.rewards ?? []).map((r) => (r.type === "badge" ? r.item.id : null))
)
.flat() ?? []
).includes(14);
const shouldGetBadge =
streak >= 365 &&
user.inventory?.badges?.find((b) => b.id === 14) === undefined &&
(
user.inbox
?.map((i) =>
(i.rewards ?? []).map((r) => (r.type === "badge" ? r.item.id : null))
)
.flat() ?? []
).includes(14) === false;
!badgeWaitingInInbox;
if (shouldGetBadge) {
const mail = buildMonkeyMail({
@ -646,11 +635,11 @@ export async function addResult(
return new MonkeyResponse("Result saved", data);
}
interface XpResult {
type XpResult = {
xp: number;
dailyBonus?: boolean;
breakdown?: Record<string, number>;
}
};
async function calculateXp(
result: SharedTypes.CompletedEvent,

View file

@ -83,7 +83,7 @@ export async function sendVerificationEmail(
);
})
).emailVerified;
if (isVerified === true) {
if (isVerified) {
throw new MonkeyError(400, "Email already verified");
}

View file

@ -99,33 +99,33 @@ export const BASE_CONFIGURATION: SharedTypes.Configuration = {
},
};
interface BaseSchema {
type BaseSchema = {
type: string;
label?: string;
hint?: string;
}
};
interface NumberSchema extends BaseSchema {
type NumberSchema = {
type: "number";
min?: number;
}
} & BaseSchema;
interface BooleanSchema extends BaseSchema {
type BooleanSchema = {
type: "boolean";
}
} & BaseSchema;
interface StringSchema extends BaseSchema {
type StringSchema = {
type: "string";
}
interface ArraySchema<T extends any[]> extends BaseSchema {
} & BaseSchema;
type ArraySchema<T extends any[]> = {
type: "array";
items: Schema<T>[number];
}
} & BaseSchema;
interface ObjectSchema<T> extends BaseSchema {
type ObjectSchema<T> = {
type: "object";
fields: Schema<T>;
}
} & BaseSchema;
type Schema<T> = {
[P in keyof T]: T[P] extends any[]

View file

@ -1,11 +1,11 @@
import _ from "lodash";
interface Status {
type Status = {
code: number;
message: string;
}
};
interface Statuses {
type Statuses = {
TEST_TOO_SHORT: Status;
RESULT_HASH_INVALID: Status;
RESULT_DATA_INVALID: Status;
@ -18,7 +18,7 @@ interface Statuses {
APE_KEY_INACTIVE: Status;
APE_KEY_MALFORMED: Status;
APE_KEY_RATE_LIMIT_EXCEEDED: Status;
}
};
const statuses: Statuses = {
TEST_TOO_SHORT: {

View file

@ -4,7 +4,7 @@ import { performance } from "perf_hooks";
import { setLeaderboard } from "../utils/prometheus";
import { isDevEnvironment } from "../utils/misc";
const leaderboardUpdating: { [key: string]: boolean } = {};
const leaderboardUpdating: Record<string, boolean> = {};
export async function get(
mode: string,
@ -28,11 +28,11 @@ export async function get(
return preset;
}
interface GetRankResponse {
type GetRankResponse = {
count: number;
rank: number | null;
entry: SharedTypes.LeaderboardEntry | null;
}
};
export async function getRank(
mode: string,

View file

@ -16,18 +16,18 @@ try {
git = undefined;
}
interface AddQuoteReturn {
type AddQuoteReturn = {
languageError?: number;
duplicateId?: number;
similarityScore?: number;
}
};
export async function add(
text: string,
source: string,
language: string,
uid: string
): Promise<AddQuoteReturn | void> {
): Promise<AddQuoteReturn | undefined> {
if (git === undefined) throw new MonkeyError(500, "Git not available.");
const quote = {
_id: new ObjectId(),
@ -79,6 +79,7 @@ export async function add(
return { duplicateId, similarityScore };
}
await db.collection("new-quotes").insertOne(quote);
return undefined;
}
export async function get(language: string): Promise<MonkeyTypes.NewQuote[]> {
@ -105,18 +106,18 @@ export async function get(language: string): Promise<MonkeyTypes.NewQuote[]> {
.toArray();
}
interface Quote {
type Quote = {
id?: number;
text: string;
source: string;
length: number;
approvedBy: string;
}
};
interface ApproveReturn {
type ApproveReturn = {
quote: Quote;
message: string;
}
};
export async function approve(
quoteId: string,

View file

@ -16,9 +16,9 @@ function getPresetKeyFilter(
};
}
interface PresetCreationResult {
type PresetCreationResult = {
presetId: string;
}
};
export const getPresetsCollection = (): Collection<WithId<DBConfigPreset>> =>
db.collection<DBConfigPreset>("presets");

View file

@ -81,11 +81,11 @@ export async function getResultByTimestamp(
return await db.collection<DBResult>("results").findOne({ uid, timestamp });
}
interface GetResultsOpts {
type GetResultsOpts = {
onOrAfterTimestamp?: number;
limit?: number;
offset?: number;
}
};
export async function getResults(
uid: string,

View file

@ -693,7 +693,7 @@ export async function getPersonalBests(
export async function getStats(
uid: string
): Promise<{ [key: string]: number | undefined }> {
): Promise<Record<string, number | undefined>> {
const user = await getUser(uid, "get stats");
return {
@ -720,10 +720,7 @@ export async function addFavoriteQuote(
const user = await getUser(uid, "add favorite quote");
if (user.favoriteQuotes) {
if (
user.favoriteQuotes[language] &&
user.favoriteQuotes[language]?.includes(quoteId)
) {
if (user.favoriteQuotes[language]?.includes(quoteId)) {
return;
}
@ -758,11 +755,7 @@ export async function removeFavoriteQuote(
): Promise<void> {
const user = await getUser(uid, "remove favorite quote");
if (
!user.favoriteQuotes ||
!user.favoriteQuotes[language] ||
!user.favoriteQuotes[language]?.includes(quoteId)
) {
if (!user.favoriteQuotes?.[language]?.includes(quoteId)) {
return;
}
@ -849,10 +842,10 @@ export async function getInbox(
return user.inbox ?? [];
}
interface AddToInboxBulkEntry {
type AddToInboxBulkEntry = {
uid: string;
mail: MonkeyTypes.MonkeyMail[];
}
};
export async function addToInboxBulk(
entries: AddToInboxBulkEntry[],

View file

@ -8,10 +8,10 @@ import { recordEmail } from "../utils/prometheus";
import { EmailTaskContexts, EmailType } from "../queues/email-queue";
import { isDevEnvironment } from "../utils/misc";
interface EmailMetadata {
type EmailMetadata = {
subject: string;
templateName: string;
}
};
const templates: Record<EmailType, EmailMetadata> = {
verify: {
@ -63,7 +63,7 @@ export async function init(): Promise<void> {
Logger.info("Verifying email client configuration...");
const result = await transporter.verify();
if (result !== true) {
if (!result) {
throw new Error(
`Could not verify email client configuration: ` + JSON.stringify(result)
);
@ -77,10 +77,10 @@ export async function init(): Promise<void> {
}
}
interface MailResult {
type MailResult = {
success: boolean;
message: string;
}
};
export async function sendEmail<M extends EmailType>(
templateName: EmailType,

View file

@ -7,10 +7,10 @@ import { getUser } from "../dal/user";
import { isAdmin } from "../dal/admin-uids";
import { isDevEnvironment } from "../utils/misc";
interface ValidationOptions<T> {
type ValidationOptions<T> = {
criteria: (data: T) => boolean;
invalidMessage?: string;
}
};
const emptyMiddleware = (
_req: MonkeyTypes.Request,
@ -125,20 +125,20 @@ function asyncHandler(handler: AsyncHandler): RequestHandler {
};
}
interface ValidationSchema {
type ValidationSchema = {
body?: object;
query?: object;
params?: object;
headers?: object;
}
};
interface ValidationSchemaOption {
type ValidationSchemaOption = {
allowUnknown?: boolean;
}
};
interface ValidationHandlingOptions {
type ValidationHandlingOptions = {
validationErrorMessage?: string;
}
};
type ValidationSchemaOptions = {
[schema in keyof ValidationSchema]?: ValidationSchemaOption;

View file

@ -14,12 +14,12 @@ import {
import crypto from "crypto";
import { performance } from "perf_hooks";
interface RequestAuthenticationOptions {
type RequestAuthenticationOptions = {
isPublic?: boolean;
acceptApeKeys?: boolean;
requireFreshToken?: boolean;
noCache?: boolean;
}
};
const DEFAULT_OPTIONS: RequestAuthenticationOptions = {
isPublic: false,
@ -159,7 +159,7 @@ async function authenticateWithBearerToken(
try {
const decodedToken = await verifyIdToken(
token,
options.requireFreshToken || options.noCache
(options.requireFreshToken ?? false) || (options.noCache ?? false)
);
if (options.requireFreshToken) {

View file

@ -76,7 +76,7 @@ async function errorHandlingMiddleware(
}
if (monkeyResponse.status < 500) {
delete monkeyResponse.data["errorId"];
delete monkeyResponse.data.errorId;
}
return handleMonkeyResponse(monkeyResponse, res);

View file

@ -4,11 +4,11 @@ const QUEUE_NAME = "email-tasks";
export type EmailType = "verify" | "resetPassword";
export interface EmailTask<M extends EmailType> {
export type EmailTask<M extends EmailType> = {
type: M;
email: string;
ctx: EmailTaskContexts[M];
}
};
export type EmailTaskContexts = {
verify: {

View file

@ -2,10 +2,10 @@ import { MonkeyQueue } from "./monkey-queue";
const QUEUE_NAME = "george-tasks";
interface GeorgeTask {
type GeorgeTask = {
name: string;
args: any[];
}
};
function buildGeorgeTask(taskName: string, taskArgs: any[]): GeorgeTask {
return {

View file

@ -9,10 +9,10 @@ export type LaterTaskType =
| "daily-leaderboard-results"
| "weekly-xp-leaderboard-results";
export interface LaterTask<T extends LaterTaskType> {
export type LaterTask<T extends LaterTaskType> = {
taskName: LaterTaskType;
ctx: LaterTaskContexts[T];
}
};
export type LaterTaskContexts = {
"daily-leaderboard-results": {

View file

@ -42,9 +42,7 @@ export class MonkeyQueue<T> {
await this.jobQueue.add(taskName, task, jobOpts);
}
async getJobCounts(): Promise<{
[index: string]: number;
}> {
async getJobCounts(): Promise<Record<string, number>> {
if (this.jobQueue === undefined) {
return {};
}

View file

@ -2,27 +2,27 @@ import * as RedisClient from "../init/redis";
import LaterQueue from "../queues/later-queue";
import { getCurrentWeekTimestamp } from "../utils/misc";
interface InternalWeeklyXpLeaderboardEntry {
type InternalWeeklyXpLeaderboardEntry = {
uid: string;
name: string;
discordAvatar?: string;
discordId?: string;
badgeId?: number;
lastActivityTimestamp: number;
}
};
interface WeeklyXpLeaderboardEntry extends InternalWeeklyXpLeaderboardEntry {
type WeeklyXpLeaderboardEntry = {
totalXp: number;
rank: number;
count?: number;
timeTypedSeconds: number;
}
} & InternalWeeklyXpLeaderboardEntry;
interface AddResultOpts {
type AddResultOpts = {
entry: InternalWeeklyXpLeaderboardEntry;
xpGained: number;
timeTypedSeconds: number;
}
};
const weeklyXpLeaderboardLeaderboardNamespace =
"monkeytype:weekly-xp-leaderboard";
@ -98,8 +98,8 @@ export class WeeklyXpLeaderboard {
const totalTimeTypedSeconds =
timeTypedSeconds + (currentEntryTimeTypedSeconds ?? 0);
const [rank]: [number, void] = await Promise.all([
// @ts-ignore
const [rank] = await Promise.all([
// @ts-expect-error
connection.addResultIncrement(
2,
weeklyXpLeaderboardScoresKey,
@ -131,7 +131,7 @@ export class WeeklyXpLeaderboard {
const { weeklyXpLeaderboardScoresKey, weeklyXpLeaderboardResultsKey } =
this.getThisWeeksXpLeaderboardKeys();
// @ts-ignore
// @ts-expect-error
const [results, scores]: string[][] = await connection.getResults(
2, // How many of the arguments are redis keys (https://redis.io/docs/manual/programmability/lua-api/)
weeklyXpLeaderboardScoresKey,
@ -178,7 +178,7 @@ export class WeeklyXpLeaderboard {
connection.set;
// @ts-ignore
// @ts-expect-error
const [[, rank], [, totalXp], [, count], [, result]] = await connection
.multi()
.zrevrank(weeklyXpLeaderboardScoresKey, uid)

View file

@ -3,24 +3,24 @@ type ObjectId = import("mongodb").ObjectId;
type ExpressRequest = import("express").Request;
declare namespace MonkeyTypes {
interface DecodedToken {
type DecodedToken = {
type: "Bearer" | "ApeKey" | "None";
uid: string;
email: string;
}
};
interface Context {
type Context = {
configuration: SharedTypes.Configuration;
decodedToken: DecodedToken;
}
};
interface Request extends ExpressRequest {
type Request = {
ctx: Readonly<Context>;
}
} & ExpressRequest;
// Data Model
interface UserProfileDetails {
type UserProfileDetails = {
bio?: string;
keyboard?: string;
socialProfiles: {
@ -28,37 +28,37 @@ declare namespace MonkeyTypes {
github?: string;
website?: string;
};
}
};
interface Reward<T> {
type Reward<T> = {
type: string;
item: T;
}
};
interface XpReward extends Reward<number> {
type XpReward = {
type: "xp";
item: number;
}
} & Reward<number>;
interface BadgeReward extends Reward<Badge> {
type BadgeReward = {
type: "badge";
item: Badge;
}
} & Reward<Badge>;
type AllRewards = XpReward | BadgeReward;
interface MonkeyMail {
type MonkeyMail = {
id: string;
subject: string;
body: string;
timestamp: number;
read: boolean;
rewards: AllRewards[];
}
};
type UserIpHistory = string[];
interface User {
type User = {
autoBanTimestamps?: number[];
addedAt: number;
verified?: boolean;
@ -96,33 +96,29 @@ declare namespace MonkeyTypes {
lbOptOut?: boolean;
premium?: PremiumInfo;
ips?: UserIpHistory;
}
};
interface UserStreak {
type UserStreak = {
lastResultTimestamp: number;
length: number;
maxLength: number;
hourOffset?: number;
}
};
interface UserInventory {
type UserInventory = {
badges: Badge[];
}
};
interface Badge {
type Badge = {
id: number;
selected?: boolean;
}
};
type UserQuoteRatings = Record<string, Record<string, number>>;
interface LbPersonalBests {
time: {
[key: number]: {
[key: string]: SharedTypes.PersonalBest;
};
};
}
type LbPersonalBests = {
time: Record<number, Record<string, SharedTypes.PersonalBest>>;
};
type WithObjectId<T extends { _id: string }> = Omit<T, "_id"> & {
_id: ObjectId;
@ -133,17 +129,17 @@ declare namespace MonkeyTypes {
_id: ObjectId;
}[];
interface UserTag {
type UserTag = {
_id: ObjectId;
name: string;
personalBests: SharedTypes.PersonalBests;
}
};
interface CustomTheme {
type CustomTheme = {
_id: ObjectId;
name: string;
colors: string[];
}
};
type ApeKeyDB = SharedTypes.ApeKey & {
_id: ObjectId;
@ -152,7 +148,7 @@ declare namespace MonkeyTypes {
useCount: number;
};
interface NewQuote {
type NewQuote = {
_id: ObjectId;
text: string;
source: string;
@ -160,11 +156,11 @@ declare namespace MonkeyTypes {
submittedBy: string;
timestamp: number;
approved: boolean;
}
};
type ReportTypes = "quote" | "user";
interface Report {
type Report = {
_id: ObjectId;
id: string;
type: ReportTypes;
@ -173,28 +169,28 @@ declare namespace MonkeyTypes {
contentId: string;
reason: string;
comment: string;
}
};
interface QuoteRating {
type QuoteRating = {
_id: string;
average: number;
language: string;
quoteId: number;
ratings: number;
totalRating: number;
}
};
interface FunboxMetadata {
type FunboxMetadata = {
name: string;
canGetPb: boolean;
difficultyLevel: number;
properties?: string[];
frontendForcedConfig?: Record<string, string[] | boolean[]>;
frontendFunctions?: string[];
}
};
interface PremiumInfo {
type PremiumInfo = {
startTimestamp: number;
expirationTimestamp: number;
}
};
}

View file

@ -1,12 +1,12 @@
import fetch from "node-fetch";
import { isDevEnvironment } from "./misc";
interface CaptchaData {
type CaptchaData = {
success: boolean;
challenge_ts?: number;
hostname: string;
"error-codes"?: string[];
}
};
export async function verify(captcha: string): Promise<boolean> {
if (isDevEnvironment()) {

View file

@ -3,7 +3,7 @@ import * as RedisClient from "../init/redis";
import LaterQueue from "../queues/later-queue";
import { getCurrentDayTimestamp, matchesAPattern, kogascore } from "./misc";
interface DailyLeaderboardEntry {
type DailyLeaderboardEntry = {
uid: string;
name: string;
wpm: number;
@ -14,18 +14,18 @@ interface DailyLeaderboardEntry {
discordAvatar?: string;
discordId?: string;
badgeId?: number;
}
};
interface GetRankResponse {
type GetRankResponse = {
minWpm: number;
count: number;
rank: number | null;
entry: DailyLeaderboardEntry | null;
}
};
interface LbEntryWithRank extends DailyLeaderboardEntry {
type LbEntryWithRank = {
rank: number;
}
} & DailyLeaderboardEntry;
const dailyLeaderboardNamespace = "monkeytype:dailyleaderboard";
const scoresNamespace = `${dailyLeaderboardNamespace}:scores`;
@ -88,7 +88,7 @@ export class DailyLeaderboard {
const resultScore = kogascore(entry.wpm, entry.acc, entry.timestamp);
// @ts-ignore
// @ts-expect-error
const rank = await connection.addResult(
2,
leaderboardScoresKey,
@ -133,7 +133,7 @@ export class DailyLeaderboard {
const { leaderboardScoresKey, leaderboardResultsKey } =
this.getTodaysLeaderboardKeys();
// @ts-ignore
// @ts-expect-error
const [results]: string[][] = await connection.getResults(
2,
leaderboardScoresKey,
@ -171,7 +171,7 @@ export class DailyLeaderboard {
const { leaderboardScoresKey, leaderboardResultsKey } =
this.getTodaysLeaderboardKeys();
// @ts-ignore
// @ts-expect-error
const [[, rank], [, count], [, result], [, minScore]] = await connection
.multi()
.zrevrank(leaderboardScoresKey, uid)
@ -211,7 +211,7 @@ export async function purgeUserFromDailyLeaderboards(
return;
}
// @ts-ignore
// @ts-expect-error
await connection.purgeResults(0, uid, dailyLeaderboardNamespace);
}

View file

@ -3,7 +3,7 @@ import { isDevEnvironment } from "./misc";
const BASE_URL = "https://discord.com/api";
interface DiscordUser {
type DiscordUser = {
id: string;
username: string;
discriminator: string;
@ -19,7 +19,7 @@ interface DiscordUser {
flags?: number;
premium_type?: number;
public_flags?: number;
}
};
export async function getDiscordUser(
tokenType: string,

View file

@ -12,13 +12,13 @@ const infoColor = chalk.white;
const logFolderPath = process.env["LOG_FOLDER_PATH"] ?? "./logs";
const maxLogSize = parseInt(process.env["LOG_FILE_MAX_SIZE"] ?? "10485760");
interface Log {
type Log = {
type?: string;
timestamp: number;
uid: string;
event: string;
message: string;
}
};
const customLevels = {
error: 0,

View file

@ -45,11 +45,11 @@ export function base64UrlDecode(data: string): string {
return Buffer.from(data, "base64url").toString();
}
interface AgentLog {
type AgentLog = {
ip: string;
agent: string;
device?: string;
}
};
export function buildAgentLog(req: MonkeyTypes.Request): AgentLog {
const agent = uaparser(req.headers["user-agent"]);
@ -287,7 +287,7 @@ export function intersect<T>(a: T[], b: T[], removeDuplicates = false): T[] {
let t;
if (b.length > a.length) (t = b), (b = a), (a = t); // indexOf to loop over shorter
const filtered = a.filter(function (e) {
return b.indexOf(e) > -1;
return b.includes(e);
});
return removeDuplicates ? [...new Set(filtered)] : filtered;
}

View file

@ -9,8 +9,8 @@ export function buildMonkeyMail(
id: v4(),
subject: options.subject ?? "",
body: options.body ?? "",
timestamp: options.timestamp || Date.now(),
timestamp: options.timestamp ?? Date.now(),
read: false,
rewards: options.rewards || [],
rewards: options.rewards ?? [],
};
}

View file

@ -24,7 +24,7 @@ export function handleMonkeyResponse(
res.statusMessage = message;
}
//@ts-ignore ignored so that we can see message in swagger stats
//@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);

View file

@ -1,11 +1,11 @@
import _ from "lodash";
import FunboxList from "../constants/funbox-list";
interface CheckAndUpdatePbResult {
type CheckAndUpdatePbResult = {
isPb: boolean;
personalBests: SharedTypes.PersonalBests;
lbPersonalBests?: MonkeyTypes.LbPersonalBests;
}
};
type Result = Omit<
SharedTypes.DBResult<SharedTypes.Config.Mode>,

View file

@ -41,14 +41,14 @@ export function buildDbResult(
name: userName,
};
if (ce.bailedOut === false) delete res.bailedOut;
if (ce.blindMode === false) delete res.blindMode;
if (ce.lazyMode === false) delete res.lazyMode;
if (!ce.bailedOut) delete res.bailedOut;
if (!ce.blindMode) delete res.blindMode;
if (!ce.lazyMode) delete res.lazyMode;
if (ce.difficulty === "normal") delete res.difficulty;
if (ce.funbox === "none") delete res.funbox;
if (ce.language === "english") delete res.language;
if (ce.numbers === false) delete res.numbers;
if (ce.punctuation === false) delete res.punctuation;
if (!ce.numbers) delete res.numbers;
if (!ce.punctuation) delete res.punctuation;
if (ce.mode !== "custom") delete res.customText;
if (ce.mode !== "quote") delete res.quoteLength;
if (ce.restartCount === 0) delete res.restartCount;

View file

@ -116,8 +116,8 @@ export function areFunboxesCompatible(funboxesString: string): boolean {
const oneWordModifierMax =
funboxesToCheck.filter(
(f) =>
f.frontendFunctions?.includes("getWord") ||
f.frontendFunctions?.includes("pullSection") ||
f.frontendFunctions?.includes("getWord") ??
f.frontendFunctions?.includes("pullSection") ??
f.frontendFunctions?.includes("withWords")
).length <= 1;
const layoutUsability =
@ -177,7 +177,7 @@ export function areFunboxesCompatible(funboxesString: string): boolean {
const oneToPushOrPullSectionMax =
funboxesToCheck.filter(
(f) =>
f.properties?.some((fp) => fp.startsWith("toPush:")) ||
f.properties?.some((fp) => fp.startsWith("toPush:")) ??
f.frontendFunctions?.includes("pullSection")
).length <= 1;
const oneApplyCSSMax =

View file

@ -87,7 +87,7 @@ export async function load(): Promise<void> {
let reset = false;
for (const key of Object.keys(defaultResultFilters)) {
if (reset === true) break;
if (reset) break;
if (newFiltersObject[key] === undefined) {
reset = true;
break;
@ -116,9 +116,7 @@ export async function load(): Promise<void> {
}
}
const newTags: {
[tag: string]: boolean;
} = { none: false };
const newTags: Record<string, boolean> = { none: false };
Object.keys(defaultResultFilters.tags).forEach((tag) => {
if (filters.tags[tag] !== undefined) {
@ -337,10 +335,10 @@ export function updateActive(): void {
const filterValue = getFilter(group, filter);
if (filterValue === true) {
groupAboveChartDisplay["array"]?.push(filter);
groupAboveChartDisplay.array?.push(filter);
} else {
if (groupAboveChartDisplay["all"] !== undefined) {
groupAboveChartDisplay["all"] = false;
if (groupAboveChartDisplay.all !== undefined) {
groupAboveChartDisplay.all = false;
}
}
let buttonEl;
@ -504,7 +502,7 @@ $(
setAllFilters(group, true);
});
setAllFilters("date", false);
filters["date"]["all"] = true;
filters.date.all = true;
} else if ($(e.target).hasClass("noFilters")) {
Misc.typedKeys(getFilters()).forEach((group) => {
// id and name field do not correspond to any ui elements, no need to update
@ -545,7 +543,7 @@ $(".pageAccount .topFilters button.allFilters").on("click", () => {
setAllFilters(group, true);
});
setAllFilters("date", false);
filters["date"]["all"] = true;
filters.date.all = true;
updateActive();
save();
});
@ -563,24 +561,24 @@ $(".pageAccount .topFilters button.currentConfigFilter").on("click", () => {
setAllFilters(group, false);
});
filters["pb"]["no"] = true;
filters["pb"]["yes"] = true;
filters.pb.no = true;
filters.pb.yes = true;
filters["difficulty"][Config.difficulty] = true;
filters["mode"][Config.mode] = true;
filters.difficulty[Config.difficulty] = true;
filters.mode[Config.mode] = true;
if (Config.mode === "time") {
if ([15, 30, 60, 120].includes(Config.time)) {
const configTime = Config.time as MonkeyTypes.DefaultTimeModes;
filters["time"][configTime] = true;
filters.time[configTime] = true;
} else {
filters["time"]["custom"] = true;
filters.time.custom = true;
}
} else if (Config.mode === "words") {
if ([10, 25, 50, 100, 200].includes(Config.words)) {
const configWords = Config.words as MonkeyTypes.DefaultWordsModes;
filters["words"][configWords] = true;
filters.words[configWords] = true;
} else {
filters["words"]["custom"] = true;
filters.words.custom = true;
}
} else if (Config.mode === "quote") {
const filterName: MonkeyTypes.Filter<"quoteLength">[] = [
@ -593,26 +591,26 @@ $(".pageAccount .topFilters button.currentConfigFilter").on("click", () => {
if (
Config.quoteLength.includes(index as SharedTypes.Config.QuoteLength)
) {
filters["quoteLength"][ql] = true;
filters.quoteLength[ql] = true;
} else {
filters["quoteLength"][ql] = false;
filters.quoteLength[ql] = false;
}
});
}
if (Config.punctuation) {
filters["punctuation"]["on"] = true;
filters.punctuation.on = true;
} else {
filters["punctuation"]["off"] = true;
filters.punctuation.off = true;
}
if (Config.numbers) {
filters["numbers"]["on"] = true;
filters.numbers.on = true;
} else {
filters["numbers"]["off"] = true;
filters.numbers.off = true;
}
if (Config.mode === "quote" && /english.*/.test(Config.language)) {
filters["language"]["english"] = true;
filters.language["english"] = true;
} else {
filters["language"][Config.language] = true;
filters.language[Config.language] = true;
}
if (Config.funbox === "none") {
@ -623,16 +621,16 @@ $(".pageAccount .topFilters button.currentConfigFilter").on("click", () => {
}
}
filters["tags"]["none"] = true;
filters.tags["none"] = true;
DB.getSnapshot()?.tags?.forEach((tag) => {
if (tag.active === true) {
filters["tags"]["none"] = false;
filters["tags"][tag._id] = true;
filters.tags["none"] = false;
filters.tags[tag._id] = true;
}
});
filters["date"]["all"] = true;
filters.date.all = true;
updateActive();
save();
});

View file

@ -1,10 +1,10 @@
const BASE_PATH = "/public";
interface SpeedStatsQuery {
type SpeedStatsQuery = {
language: string;
mode: string;
mode2: string;
}
};
export default class Public {
constructor(private httpClient: Ape.HttpClient) {

View file

@ -1,15 +1,15 @@
declare namespace Ape {
interface RequestOptions {
type RequestOptions = {
headers?: Record<string, string>;
searchQuery?: Record<string, any>;
payload?: any;
}
};
interface HttpClientResponse<Data> {
type HttpClientResponse<Data> = {
status: number;
message: string;
data: Data | null;
}
};
type EndpointResponse<Data = any> = Promise<HttpClientResponse<Data>>;
@ -18,13 +18,13 @@ declare namespace Ape {
config?: RequestOptions
) => EndpointResponse<Data>;
interface HttpClient {
type HttpClient = {
get: HttpClientMethod;
post: HttpClientMethod;
put: HttpClientMethod;
patch: HttpClientMethod;
delete: HttpClientMethod;
}
};
type HttpMethodTypes = keyof HttpClient;
}

View file

@ -1,18 +1,18 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// for some reason when using the dot notaion, the types are not being recognized as used
declare namespace Ape.Leaderboards {
interface Query {
type Query = {
language: string;
mode: SharedTypes.Config.Mode;
mode2: string;
isDaily?: boolean;
daysBefore?: number;
}
};
interface QueryWithPagination extends Query {
type QueryWithPagination = {
skip?: number;
limit?: number;
}
} & Query;
type GetLeaderboard = SharedTypes.LeaderboardEntry[];

View file

@ -3,11 +3,11 @@ type ShouldRetryCallback<ResponseDataType> = (
response?: Ape.HttpClientResponse<ResponseDataType>
) => boolean;
interface RetryOptions<ResponseDataType = unknown> {
type RetryOptions<ResponseDataType = unknown> = {
shouldRetry?: ShouldRetryCallback<ResponseDataType>;
retryAttempts?: number;
retryDelayMs?: number;
}
};
const wait = async (delay: number): Promise<number> =>
new Promise((resolve) => window.setTimeout(resolve, delay));

View file

@ -79,7 +79,7 @@ function showFound(): void {
$.each(list.list, (_index, obj) => {
if (obj.found && (obj.available !== undefined ? obj.available() : true)) {
let icon = obj.icon ?? "fa-chevron-right";
const faIcon = /^fa-/g.test(icon);
const faIcon = icon.startsWith("fa-");
if (!faIcon) {
icon = `<div class="textIcon">${icon}</div>`;
} else {
@ -189,7 +189,7 @@ function updateSuggested(): void {
return;
}
//ignore the preceeding ">"s in the command line input
if (inputVal[0] !== undefined && inputVal[0][0] === ">") {
if (inputVal[0]?.startsWith(">")) {
inputVal[0] = inputVal[0].replace(/^>+/, "");
}
if (inputVal[0] === "" && inputVal.length === 1) {
@ -689,7 +689,7 @@ $(document).on("keydown", (e) => {
if (
Config.singleListCommandLine === "manual" &&
isSingleListCommandLineActive() &&
inputVal[0] !== ">"
!inputVal.startsWith(">")
) {
restoreOldCommandLine(false);
updateSuggested();

View file

@ -29,7 +29,7 @@ const commands: MonkeyTypes.Command[] = [
function update(): void {
const snapshot = DB.getSnapshot();
subgroup.list = [];
if (!snapshot || !snapshot.presets || snapshot.presets.length === 0) return;
if (!snapshot?.presets || snapshot.presets.length === 0) return;
snapshot.presets.forEach((preset: MonkeyTypes.SnapshotPreset) => {
const dis = preset.display;

View file

@ -91,7 +91,7 @@ export function isConfigValueValid(
default:
if (isArray(possibleType)) {
if (possibleType.includes(<never>val)) isValid = true;
if (possibleType.includes(val as never)) isValid = true;
}
break;
}

View file

@ -49,7 +49,7 @@ function saveToLocalStorage(
window.localStorage.setItem("config", localToSaveStringified);
if (!noDbCheck) {
(configToSend[key] as typeof config[typeof key]) = config[key];
void saveToDatabase();
saveToDatabase();
}
ConfigEvent.dispatch("saveToLocalStorage", localToSaveStringified);
}
@ -956,7 +956,7 @@ export function setTapeMode(
return false;
}
if (mode !== "off" && config.showAllLines === true) {
if (mode !== "off" && config.showAllLines) {
setShowAllLines(false, true);
}
@ -1362,7 +1362,7 @@ export function setTheme(name: string, nosave?: boolean): boolean {
if (!isConfigValueValid("theme", name, ["string"])) return false;
config.theme = name;
if (config.customTheme === true) setCustomTheme(false);
if (config.customTheme) setCustomTheme(false);
saveToLocalStorage("theme", nosave);
ConfigEvent.dispatch("theme", config.theme);
@ -1984,7 +1984,7 @@ function replaceLegacyValues(
): SharedTypes.Config | MonkeyTypes.ConfigChanges {
const configObj = configToApply as SharedTypes.Config;
//@ts-ignore
//@ts-expect-error
if (configObj.quickTab === true) {
configObj.quickRestart = "tab";
}
@ -1993,17 +1993,17 @@ function replaceLegacyValues(
configObj.smoothCaret = configObj.smoothCaret ? "medium" : "off";
}
//@ts-ignore
//@ts-expect-error
if (configObj.swapEscAndTab === true) {
configObj.quickRestart = "esc";
}
//@ts-ignore
//@ts-expect-error
if (configObj.alwaysShowCPM === true) {
configObj.typingSpeedUnit = "cpm";
}
//@ts-ignore
//@ts-expect-error
if (configObj.showAverage === "wpm") {
configObj.showAverage = "speed";
}

View file

@ -1,4 +1,4 @@
export default <SharedTypes.Config>{
export default {
theme: "serika_dark",
themeLight: "serika",
themeDark: "serika_dark",
@ -95,4 +95,4 @@ export default <SharedTypes.Config>{
lazyMode: false,
showAverage: "off",
tapeMode: "off",
};
} as SharedTypes.Config;

View file

@ -1,11 +1,11 @@
interface Config {
type Config = {
backendUrl: string;
isDevelopment: boolean;
}
};
// @ts-ignore
// @ts-expect-error
const backendUrl = BACKEND_URL;
// @ts-ignore
// @ts-expect-error
const isDevelopment = IS_DEVELOPMENT;
export const envConfig: Config = {

View file

@ -99,8 +99,9 @@ async function getDataAndInit(): Promise<boolean> {
}
);
}
const msg = e.message || e;
const msg = e.message || "Unknown error";
Notifications.add("Failed to get user data: " + msg, -1);
console.error(e);
$("header nav .account").css("opacity", 1);
return false;
@ -180,7 +181,7 @@ export async function loadUser(user: UserType): Promise<void> {
// User is signed in.
PageTransition.set(false);
AccountButton.loading(true);
if ((await getDataAndInit()) === false) {
if (!(await getDataAndInit())) {
signOut();
}
const { discordId, discordAvatar, xp, inboxUnreadSize } =
@ -557,7 +558,7 @@ async function signUp(): Promise<void> {
createdAuthUser.user.uid
);
if (signInResponse.status !== 200) {
throw signInResponse;
throw new Error(`Failed to sign in: ${signInResponse.message}`);
}
await updateProfile(createdAuthUser.user, { displayName: nname });

View file

@ -142,7 +142,7 @@ export async function checkAdblock(): Promise<void> {
return new Promise((resolve) => {
if (choice === "eg") {
if (adBlock === undefined) {
//@ts-ignore
//@ts-expect-error
if (window.egAdPack === undefined) {
adBlock = true;
} else {
@ -150,7 +150,7 @@ export async function checkAdblock(): Promise<void> {
}
}
} else if (choice === "pw") {
//@ts-ignore
//@ts-expect-error
if (window.ramp === undefined) {
adBlock = true;
}
@ -168,13 +168,13 @@ export async function checkCookieblocker(): Promise<void> {
return;
}
//@ts-ignore
//@ts-expect-error
if (window.__tcfapi === undefined) {
cookieBlocker = true;
resolve();
return;
}
//@ts-ignore
//@ts-expect-error
window.__tcfapi("getTCData", 2, (tcData, success) => {
if (success as boolean) {
if (tcData.eventStatus === "cmpuishown") {
@ -267,12 +267,12 @@ export function updateFooterAndVerticalAds(visible: boolean): void {
export function showConsentPopup(): void {
if (choice === "eg") {
//@ts-ignore
//@ts-expect-error
window.__tcfapi("displayConsentUi", 2, function () {
//
});
} else {
//@ts-ignore
//@ts-expect-error
ramp.showCmpModal();
}
}
@ -318,9 +318,9 @@ $(document).ready(() => {
});
window.onerror = function (error): void {
//@ts-ignore
//@ts-expect-error
if (choice === "eg") {
if (typeof error === "string" && error.substring(0, 6) === "EG APS") {
if (typeof error === "string" && error.startsWith("EG APS")) {
$("#ad-result-wrapper .iconAndText").addClass("withLeft");
}
}

View file

@ -11,7 +11,7 @@ let analytics: AnalyticsType;
export async function log(
eventName: string,
params?: { [key: string]: string }
params?: Record<string, string>
): Promise<void> {
try {
logEvent(analytics, eventName, params);
@ -32,7 +32,7 @@ if (lsString !== undefined && lsString !== null && lsString !== "") {
}
if (acceptedCookies !== null) {
if (acceptedCookies["analytics"] === true) {
if (acceptedCookies.analytics) {
activateAnalytics();
}
}

View file

@ -44,7 +44,7 @@ export function verify(
let requirementsMet = true;
const failReasons = [];
for (const requirementType in TestState.activeChallenge.requirements) {
if (requirementsMet === false) return null;
if (!requirementsMet) return null;
const requirementValue =
TestState.activeChallenge.requirements[
requirementType as keyof typeof TestState.activeChallenge.requirements
@ -282,7 +282,7 @@ export async function setup(challengeName: string): Promise<boolean> {
UpdateConfig.setTheme(challenge.parameters[1] as string);
}
if (challenge.parameters[2] !== null) {
void Funbox.activate(<string>challenge.parameters[2]);
void Funbox.activate(challenge.parameters[2] as string);
}
} else if (challenge.type === "accuracy") {
UpdateConfig.setTimeConfig(0, true);

View file

@ -71,6 +71,7 @@ class ChartWithUpdateColors<
TLabel = unknown,
DatasetIds = never
> extends Chart<TType, TData, TLabel> {
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor(
item: ChartItem,
config: ChartConfiguration<TType, TData, TLabel>
@ -83,190 +84,187 @@ class ChartWithUpdateColors<
}
getDataset(id: DatasetIds): ChartDataset<TType, TData> {
//@ts-ignore
//@ts-expect-error
return this.data.datasets?.find((x) => x.yAxisID === id);
}
getScale(
id: DatasetIds extends never ? never : "x" | DatasetIds
): DatasetIds extends never ? never : CartesianScaleOptions {
//@ts-ignore
//@ts-expect-error
return this.options.scales[id];
}
}
let prevTi: TooltipItem<"line" | "scatter"> | undefined;
export const result: ChartWithUpdateColors<
export const result = new ChartWithUpdateColors<
"line" | "scatter",
number[],
string,
"wpm" | "raw" | "error"
> = new ChartWithUpdateColors(
document.querySelector("#wpmChart") as HTMLCanvasElement,
{
type: "line",
data: {
labels: [],
datasets: [
{
//@ts-ignore the type is defined incorrectly, have to ingore the error
clip: false,
label: "wpm",
data: [],
borderColor: "rgba(125, 125, 125, 1)",
borderWidth: 3,
yAxisID: "wpm",
order: 2,
pointRadius: 1,
>(document.querySelector("#wpmChart") as HTMLCanvasElement, {
type: "line",
data: {
labels: [],
datasets: [
{
//@ts-expect-error the type is defined incorrectly, have to ingore the error
clip: false,
label: "wpm",
data: [],
borderColor: "rgba(125, 125, 125, 1)",
borderWidth: 3,
yAxisID: "wpm",
order: 2,
pointRadius: 1,
},
{
//@ts-expect-error the type is defined incorrectly, have to ingore the error
clip: false,
label: "raw",
data: [],
borderColor: "rgba(125, 125, 125, 1)",
borderWidth: 3,
yAxisID: "raw",
order: 3,
pointRadius: 1,
},
{
//@ts-expect-error the type is defined incorrectly, have to ingore the error
clip: false,
label: "errors",
data: [],
borderColor: "rgba(255, 125, 125, 1)",
pointBackgroundColor: "rgba(255, 125, 125, 1)",
borderWidth: 2,
order: 1,
yAxisID: "error",
type: "scatter",
pointStyle: "crossRot",
pointRadius: function (context): number {
const index = context.dataIndex;
const value = context.dataset.data[index] as number;
return (value ?? 0) <= 0 ? 0 : 3;
},
{
//@ts-ignore the type is defined incorrectly, have to ingore the error
clip: false,
label: "raw",
data: [],
borderColor: "rgba(125, 125, 125, 1)",
borderWidth: 3,
yAxisID: "raw",
order: 3,
pointRadius: 1,
pointHoverRadius: function (context): number {
const index = context.dataIndex;
const value = context.dataset.data[index] as number;
return (value ?? 0) <= 0 ? 0 : 5;
},
{
//@ts-ignore the type is defined incorrectly, have to ingore the error
clip: false,
label: "errors",
data: [],
borderColor: "rgba(255, 125, 125, 1)",
pointBackgroundColor: "rgba(255, 125, 125, 1)",
borderWidth: 2,
order: 1,
yAxisID: "error",
type: "scatter",
pointStyle: "crossRot",
pointRadius: function (context): number {
const index = context.dataIndex;
const value = context.dataset.data[index] as number;
return (value ?? 0) <= 0 ? 0 : 3;
},
pointHoverRadius: function (context): number {
const index = context.dataIndex;
const value = context.dataset.data[index] as number;
return (value ?? 0) <= 0 ? 0 : 5;
},
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
axis: "x",
ticks: {
autoSkip: true,
autoSkipPadding: 20,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
axis: "x",
ticks: {
autoSkip: true,
autoSkipPadding: 20,
},
display: true,
title: {
display: false,
text: "Seconds",
},
},
wpm: {
axis: "y",
display: true,
title: {
display: true,
text: "Words per Minute",
},
beginAtZero: true,
min: 0,
ticks: {
autoSkip: true,
autoSkipPadding: 20,
},
grid: {
display: true,
},
},
raw: {
axis: "y",
display: true,
title: {
display: false,
title: {
display: true,
text: "Raw Words per Minute",
},
beginAtZero: true,
min: 0,
ticks: {
autoSkip: true,
autoSkipPadding: 20,
},
grid: {
display: false,
},
},
error: {
axis: "y",
display: true,
position: "right",
title: {
display: true,
text: "Errors",
},
beginAtZero: true,
ticks: {
precision: 0,
autoSkip: true,
autoSkipPadding: 20,
},
grid: {
display: false,
},
text: "Seconds",
},
},
plugins: {
annotation: {
annotations: [],
wpm: {
axis: "y",
display: true,
title: {
display: true,
text: "Words per Minute",
},
tooltip: {
animation: { duration: 250 },
mode: "index",
intersect: false,
callbacks: {
afterLabel: function (ti): string {
if (prevTi === ti) return "";
prevTi = ti;
try {
const keypressIndex = Math.round(parseFloat(ti.label)) - 1;
const wordsToHighlight =
TestInput.errorHistory[keypressIndex]?.words;
beginAtZero: true,
min: 0,
ticks: {
autoSkip: true,
autoSkipPadding: 20,
},
grid: {
display: true,
},
},
raw: {
axis: "y",
display: false,
title: {
display: true,
text: "Raw Words per Minute",
},
beginAtZero: true,
min: 0,
ticks: {
autoSkip: true,
autoSkipPadding: 20,
},
grid: {
display: false,
},
},
error: {
axis: "y",
display: true,
position: "right",
title: {
display: true,
text: "Errors",
},
beginAtZero: true,
ticks: {
precision: 0,
autoSkip: true,
autoSkipPadding: 20,
},
grid: {
display: false,
},
},
},
plugins: {
annotation: {
annotations: [],
},
tooltip: {
animation: { duration: 250 },
mode: "index",
intersect: false,
callbacks: {
afterLabel: function (ti): string {
if (prevTi === ti) return "";
prevTi = ti;
try {
const keypressIndex = Math.round(parseFloat(ti.label)) - 1;
const wordsToHighlight =
TestInput.errorHistory[keypressIndex]?.words;
const unique = [...new Set(wordsToHighlight)];
const firstHighlightWordIndex = unique[0];
const lastHighlightWordIndex = unique[unique.length - 1];
if (
firstHighlightWordIndex === undefined ||
lastHighlightWordIndex === undefined
) {
return "";
}
void ResultWordHighlight.highlightWordsInRange(
firstHighlightWordIndex,
lastHighlightWordIndex
);
} catch {}
return "";
},
const unique = [...new Set(wordsToHighlight)];
const firstHighlightWordIndex = unique[0];
const lastHighlightWordIndex = unique[unique.length - 1];
if (
firstHighlightWordIndex === undefined ||
lastHighlightWordIndex === undefined
) {
return "";
}
void ResultWordHighlight.highlightWordsInRange(
firstHighlightWordIndex,
lastHighlightWordIndex
);
} catch {}
return "";
},
},
},
},
}
);
},
});
export let accountHistoryActiveIndex: number;
export const accountHistory: ChartWithUpdateColors<
export const accountHistory = new ChartWithUpdateColors<
"line",
| MonkeyTypes.HistoryChartData[]
| MonkeyTypes.AccChartData[]
@ -279,7 +277,7 @@ export const accountHistory: ChartWithUpdateColors<
| "accAvgTen"
| "wpmAvgHundred"
| "accAvgHundred"
> = new ChartWithUpdateColors(
>(
document.querySelector(
".pageAccount #accountHistoryChart"
) as HTMLCanvasElement,
@ -554,12 +552,12 @@ export const accountHistory: ChartWithUpdateColors<
}
);
export const accountActivity: ChartWithUpdateColors<
export const accountActivity = new ChartWithUpdateColors<
"bar" | "line",
MonkeyTypes.ActivityChartDataPoint[],
string,
"count" | "avgWpm"
> = new ChartWithUpdateColors(
>(
document.querySelector(
".pageAccount #accountActivityChart"
) as HTMLCanvasElement,
@ -699,12 +697,12 @@ export const accountActivity: ChartWithUpdateColors<
}
);
export const accountHistogram: ChartWithUpdateColors<
export const accountHistogram = new ChartWithUpdateColors<
"bar",
MonkeyTypes.ActivityChartDataPoint[],
string,
"count"
> = new ChartWithUpdateColors(
>(
document.querySelector(
".pageAccount #accountHistogramChart"
) as HTMLCanvasElement,
@ -802,12 +800,12 @@ export const accountHistogram: ChartWithUpdateColors<
}
);
export const globalSpeedHistogram: ChartWithUpdateColors<
export const globalSpeedHistogram = new ChartWithUpdateColors<
"bar",
MonkeyTypes.ActivityChartDataPoint[],
string,
"count"
> = new ChartWithUpdateColors(
>(
document.querySelector(
".pageAbout #publicStatsHistogramChart"
) as HTMLCanvasElement,
@ -871,12 +869,12 @@ export const globalSpeedHistogram: ChartWithUpdateColors<
}
);
export const miniResult: ChartWithUpdateColors<
export const miniResult = new ChartWithUpdateColors<
"line" | "scatter",
number[],
string,
"wpm" | "raw" | "error"
> = new ChartWithUpdateColors(
>(
document.querySelector(".pageAccount #miniResultChart") as HTMLCanvasElement,
{
type: "line",
@ -1117,9 +1115,9 @@ async function updateColors<
const gridcolor = Misc.blendTwoHexColors(bgcolor, subaltcolor, 0.75);
//@ts-ignore
//@ts-expect-error
chart.data.datasets[0].borderColor = (ctx): string => {
const isPb = ctx.raw?.["isPb"] as boolean;
const isPb = ctx.raw?.isPb as boolean;
const color = isPb ? textcolor : maincolor;
return color;
};
@ -1138,8 +1136,8 @@ async function updateColors<
if (chart?.data?.datasets[0]?.type === undefined) {
if (chart.config.type === "line") {
dataset0.pointBackgroundColor = (ctx): string => {
//@ts-ignore
const isPb = ctx.raw?.["isPb"] as boolean;
//@ts-expect-error
const isPb = ctx.raw?.isPb as boolean;
const color = isPb ? textcolor : maincolor;
return color;
};

View file

@ -14,7 +14,7 @@ export function init(): void {
export function renderResult(widerThanBreakpoint: boolean): void {
if (widerThanBreakpoint) {
//@ts-ignore
//@ts-expect-error
window.egAps.render([
"ad-result",
"ad-vertical-left",
@ -22,7 +22,7 @@ export function renderResult(widerThanBreakpoint: boolean): void {
"ad-footer",
]);
} else {
//@ts-ignore
//@ts-expect-error
window.egAps.render([
"ad-result-small",
"ad-vertical-left",
@ -34,7 +34,7 @@ export function renderResult(widerThanBreakpoint: boolean): void {
export function reinstate(): boolean {
try {
//@ts-ignore
//@ts-expect-error
window.egAps.reinstate();
return true;
} catch (e) {
@ -44,7 +44,7 @@ export function reinstate(): boolean {
}
export async function refreshVisible(): Promise<void> {
//@ts-ignore
////@ts-expect-error
// const adDivs = Object.keys(window.egAdPack.gptAdSlots);
// const visibleAdDivs = [];
// for (let i = 0; i < adDivs.length; i++) {

View file

@ -152,7 +152,7 @@ function backspaceToPrevious(): void {
if (Config.mode === "zen") {
TimerProgress.update();
const els: HTMLElement[] = (document.querySelector("#words")?.children ||
const els = (document.querySelector("#words")?.children ??
[]) as HTMLElement[];
for (let i = els.length - 1; i >= 0; i--) {
@ -661,7 +661,7 @@ function handleChar(
//update the active word top, but only once
if (testInputLength === 1 && TestWords.words.currentIndex === 0) {
TestUI.setActiveWordTop(
(<HTMLElement>document.querySelector("#words .active"))?.offsetTop
(document.querySelector("#words .active") as HTMLElement)?.offsetTop
);
}
@ -1298,7 +1298,7 @@ $("#wordsInput").on("input", (event) => {
if (containsChinese) {
if (
currTestInput.length - inputValue.length <= 2 &&
currTestInput.slice(0, currTestInput.length) === currTestInput
currTestInput.startsWith(currTestInput)
) {
TestInput.input.current = inputValue;
} else {

View file

@ -13,11 +13,11 @@ import * as PageTransition from "../states/page-transition";
import * as AdController from "../controllers/ad-controller";
import * as Focus from "../test/focus";
interface ChangeOptions {
type ChangeOptions = {
force?: boolean;
params?: { [key: string]: string };
params?: Record<string, string>;
data?: unknown;
}
};
export async function change(
pageName: MonkeyTypes.PageName,

View file

@ -6,26 +6,26 @@ import {
import { subscribe } from "../observables/config-event";
import * as DB from "../db";
interface JsonQuote {
type JsonQuote = {
text: string;
britishText?: string;
source: string;
length: number;
id: number;
}
};
interface QuoteData {
type QuoteData = {
language: string;
quotes: JsonQuote[];
groups: [number, number][];
}
};
interface QuoteCollection {
type QuoteCollection = {
quotes: MonkeyTypes.Quote[];
length: number;
language: string | null;
groups: MonkeyTypes.Quote[][];
}
};
const defaultQuoteCollection: QuoteCollection = {
quotes: [],

View file

@ -8,10 +8,10 @@ import { Auth } from "../firebase";
// https://www.youtube.com/watch?v=OstALBk-jTc
//this will be used in tribe
interface NavigateOptions {
type NavigateOptions = {
empty?: boolean;
data?: unknown;
}
};
function pathToRegex(path: string): RegExp {
return new RegExp(
@ -19,9 +19,10 @@ function pathToRegex(path: string): RegExp {
);
}
function getParams(match: { route: Route; result: RegExpMatchArray }): {
[key: string]: string;
} {
function getParams(match: {
route: Route;
result: RegExpMatchArray;
}): Record<string, string> {
const values = match.result.slice(1);
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(
(result) => result[1]
@ -30,13 +31,13 @@ function getParams(match: { route: Route; result: RegExpMatchArray }): {
return Object.fromEntries(keys.map((key, index) => [key, values[index]]));
}
interface Route {
type Route = {
path: string;
load: (
params: { [key: string]: string },
params: Record<string, string>,
navigateOptions: NavigateOptions
) => void;
}
};
const route404: Route = {
path: "404",

View file

@ -10,19 +10,21 @@ import { leftState, rightState } from "../test/shift-tracker";
import { capsState } from "../test/caps-warning";
import * as Notifications from "../elements/notifications";
interface ClickSounds {
[key: string]: {
type ClickSounds = Record<
string,
{
sounds: Howl[];
counter: number;
}[];
}
}[]
>;
interface ErrorSounds {
[key: string]: {
type ErrorSounds = Record<
string,
{
sounds: Howl[];
counter: number;
}[];
}
}[]
>;
let errorSounds: ErrorSounds | null = null;
let clickSounds: ClickSounds | null = null;
@ -509,11 +511,11 @@ const scales: Record<ValidScales, ValidNotes[]> = {
wholetone: ["C", "D", "E", "Gb", "Ab", "Bb"],
};
interface ScaleData {
type ScaleData = {
octave: number; // current octave of scale
direction: number; // whether scale is ascending or descending
position: number; // current position in scale
}
};
function createPreviewScale(scaleName: ValidScales): () => void {
// We use a JavaScript closure to create a preview function that can be called multiple times and progress through the scale
@ -529,11 +531,11 @@ function createPreviewScale(scaleName: ValidScales): () => void {
};
}
interface ScaleMeta {
type ScaleMeta = {
name: ValidScales;
preview: ReturnType<typeof createPreviewScale>;
meta: ScaleData;
}
};
const defaultScaleData: ScaleData = {
position: 0,

View file

@ -377,10 +377,7 @@ ConfigEvent.subscribe(async (eventKey, eventValue, nosave) => {
if (eventKey === "setThemes") {
await clearPreview(false);
if (Config.autoSwitchTheme) {
if (
window.matchMedia !== undefined &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
await set(Config.themeDark, true);
} else {
await set(Config.themeLight, true);
@ -398,10 +395,7 @@ ConfigEvent.subscribe(async (eventKey, eventValue, nosave) => {
if (eventKey === "customBackgroundSize") applyCustomBackgroundSize();
if (eventKey === "autoSwitchTheme") {
if (eventValue as boolean) {
if (
window.matchMedia !== undefined &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
await set(Config.themeDark, true);
} else {
await set(Config.themeLight, true);
@ -413,10 +407,7 @@ ConfigEvent.subscribe(async (eventKey, eventValue, nosave) => {
if (
eventKey === "themeLight" &&
Config.autoSwitchTheme &&
!(
window.matchMedia !== undefined &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) &&
!window.matchMedia?.("(prefers-color-scheme: dark)").matches &&
!nosave
) {
await set(Config.themeLight, true);

View file

@ -61,19 +61,23 @@ export async function initSnapshot(): Promise<
Ape.presets.get(),
]);
//these objects are explicitly handled so its ok to throw that way
if (userResponse.status !== 200) {
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw {
message: `${userResponse.message} (user)`,
responseCode: userResponse.status,
};
}
if (configResponse.status !== 200) {
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw {
message: `${configResponse.message} (config)`,
responseCode: configResponse.status,
};
}
if (presetsResponse.status !== 200) {
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw {
message: `${presetsResponse.message} (presets)`,
responseCode: presetsResponse.status,
@ -402,7 +406,7 @@ async function _getUserHighestWpm<M extends SharedTypes.Config.Mode>(
result.language === language &&
result.difficulty === difficulty &&
(result.lazyMode === lazyMode ||
(result.lazyMode === undefined && lazyMode === false))
(result.lazyMode === undefined && !lazyMode))
) {
if (result.wpm > topWpm) {
topWpm = result.wpm;
@ -452,7 +456,7 @@ export async function getUserAverage10<M extends SharedTypes.Config.Mode>(
result.language === language &&
result.difficulty === difficulty &&
(result.lazyMode === lazyMode ||
(result.lazyMode === undefined && lazyMode === false)) &&
(result.lazyMode === undefined && !lazyMode)) &&
(activeTagIds.length === 0 ||
activeTagIds.some((tagId) => result.tags.includes(tagId)))
) {
@ -530,7 +534,7 @@ export async function getUserDailyBest<M extends SharedTypes.Config.Mode>(
result.language === language &&
result.difficulty === difficulty &&
(result.lazyMode === lazyMode ||
(result.lazyMode === undefined && lazyMode === false)) &&
(result.lazyMode === undefined && !lazyMode)) &&
(activeTagIds.length === 0 ||
activeTagIds.some((tagId) => result.tags.includes(tagId)))
) {
@ -594,8 +598,7 @@ export async function getLocalPB<M extends SharedTypes.Config.Mode>(
pb.punctuation === punctuation &&
pb.difficulty === difficulty &&
pb.language === language &&
(pb.lazyMode === lazyMode ||
(pb.lazyMode === undefined && lazyMode === false))
(pb.lazyMode === lazyMode || (pb.lazyMode === undefined && !lazyMode))
) {
ret = pb.wpm;
}
@ -654,8 +657,7 @@ export async function saveLocalPB<M extends SharedTypes.Config.Mode>(
pb.punctuation === punctuation &&
pb.difficulty === difficulty &&
pb.language === language &&
(pb.lazyMode === lazyMode ||
(pb.lazyMode === undefined && lazyMode === false))
(pb.lazyMode === lazyMode || (pb.lazyMode === undefined && !lazyMode))
) {
found = true;
pb.wpm = wpm;
@ -733,8 +735,7 @@ export async function getLocalTagPB<M extends SharedTypes.Config.Mode>(
pb.punctuation === punctuation &&
pb.difficulty === difficulty &&
pb.language === language &&
(pb.lazyMode === lazyMode ||
(pb.lazyMode === undefined && lazyMode === false))
(pb.lazyMode === lazyMode || (pb.lazyMode === undefined && !lazyMode))
)?.wpm ?? 0;
return ret;
@ -792,8 +793,7 @@ export async function saveLocalTagPB<M extends SharedTypes.Config.Mode>(
pb.punctuation === punctuation &&
pb.difficulty === difficulty &&
pb.language === language &&
(pb.lazyMode === lazyMode ||
(pb.lazyMode === undefined && lazyMode === false))
(pb.lazyMode === lazyMode || (pb.lazyMode === undefined && !lazyMode))
) {
found = true;
pb.wpm = wpm;
@ -905,7 +905,7 @@ export function saveLocalResult(
const snapshot = getSnapshot();
if (!snapshot) return;
if (snapshot !== null && snapshot.results !== undefined) {
if (snapshot?.results !== undefined) {
snapshot.results.unshift(result);
setSnapshot(snapshot);
@ -918,7 +918,7 @@ export function updateLocalStats(started: number, time: number): void {
if (snapshot.typingStats === undefined) {
snapshot.typingStats = {} as MonkeyTypes.TypingStats;
}
if (snapshot !== null && snapshot.typingStats !== undefined) {
if (snapshot?.typingStats !== undefined) {
if (snapshot.typingStats.timeTyping === undefined) {
snapshot.typingStats.timeTyping = time;
} else {

View file

@ -17,10 +17,10 @@ let maxMail = 0;
let mailToMarkRead: string[] = [];
let mailToDelete: string[] = [];
interface State {
type State = {
notifications: { message: string; level: number; customTitle?: string }[];
psas: { message: string; level: number }[];
}
};
const state: State = {
notifications: [],
@ -226,7 +226,7 @@ async function getAccountAlerts(): Promise<void> {
let rewardsString = "";
if (ie.rewards.length > 0 && ie.read === false) {
if (ie.rewards.length > 0 && !ie.read) {
rewardsString = `<div class="rewards">
<i class="fas fa-fw fa-gift"></i>
<span>${ie.rewards.length}</span>
@ -246,13 +246,12 @@ async function getAccountAlerts(): Promise<void> {
</div>
<div class="buttons">
${
ie.rewards.length > 0 && ie.read === false
ie.rewards.length > 0 && !ie.read
? `<div class="markReadAlert textButton" aria-label="Claim" data-balloon-pos="left"><i class="fas fa-gift"></i></div>`
: ``
}
${
(ie.rewards.length > 0 && ie.read === true) ||
ie.rewards.length === 0
(ie.rewards.length > 0 && ie.read) || ie.rewards.length === 0
? `<div class="deleteAlert textButton" aria-label="Delete" data-balloon-pos="left"><i class="fas fa-trash"></i></div>`
: ``
}
@ -264,21 +263,21 @@ async function getAccountAlerts(): Promise<void> {
}
export function addPSA(message: string, level: number): void {
state["psas"].push({
state.psas.push({
message,
level,
});
}
function fillPSAs(): void {
if (state["psas"].length === 0) {
if (state.psas.length === 0) {
$("#alertsPopup .psas .list").html(
`<div class="nothing">Nothing to show</div>`
);
} else {
$("#alertsPopup .psas .list").empty();
for (const p of state["psas"]) {
for (const p of state.psas) {
const { message, level } = p;
let levelClass = "";
if (level === -1) {
@ -301,14 +300,14 @@ function fillPSAs(): void {
}
function fillNotifications(): void {
if (state["notifications"].length === 0) {
if (state.notifications.length === 0) {
$("#alertsPopup .notificationHistory .list").html(
`<div class="nothing">Nothing to show</div>`
);
} else {
$("#alertsPopup .notificationHistory .list").empty();
for (const n of state["notifications"]) {
for (const n of state.notifications) {
const { message, level, customTitle } = n;
let title = "Notice";
let levelClass = "sub";
@ -400,7 +399,7 @@ function updateClaimDeleteAllButton(): void {
if (accountAlerts.length > 0) {
let rewardsCount = 0;
for (const ie of accountAlerts) {
if (ie.read === false && !mailToMarkRead.includes(ie.id)) {
if (!ie.read && !mailToMarkRead.includes(ie.id)) {
rewardsCount += ie.rewards.length;
}
}
@ -418,7 +417,7 @@ function updateClaimDeleteAllButton(): void {
$("#alertsPopupWrapper .accountAlerts").on("click", ".claimAll", () => {
for (const ie of accountAlerts) {
if (ie.read === false && !mailToMarkRead.includes(ie.id)) {
if (!ie.read && !mailToMarkRead.includes(ie.id)) {
markReadAlert(ie.id);
}
}
@ -471,13 +470,13 @@ $(document).on("keydown", (e) => {
});
NotificationEvent.subscribe((message, level, customTitle) => {
state["notifications"].push({
state.notifications.push({
message,
level,
customTitle,
});
if (state["notifications"].length > 25) {
state["notifications"].shift();
if (state.notifications.length > 25) {
state.notifications.shift();
}
});

View file

@ -48,15 +48,15 @@ export function apply(): void {
}
function syncSliders(): void {
$(".section.customBackgroundFilter .blur input").val(filters["blur"].value);
$(".section.customBackgroundFilter .blur input").val(filters.blur.value);
$(".section.customBackgroundFilter .brightness input").val(
filters["brightness"].value
filters.brightness.value
);
$(".section.customBackgroundFilter .saturate input").val(
filters["saturate"].value
filters.saturate.value
);
$(".section.customBackgroundFilter .opacity input").val(
filters["opacity"].value
filters.opacity.value
);
}
@ -85,7 +85,7 @@ function loadConfig(config: SharedTypes.Config.CustomBackgroundFilter): void {
}
$(".section.customBackgroundFilter .blur input").on("input", () => {
filters["blur"].value = parseFloat(
filters.blur.value = parseFloat(
$(".section.customBackgroundFilter .blur input").val() as string
);
updateNumbers();
@ -93,7 +93,7 @@ $(".section.customBackgroundFilter .blur input").on("input", () => {
});
$(".section.customBackgroundFilter .brightness input").on("input", () => {
filters["brightness"].value = parseFloat(
filters.brightness.value = parseFloat(
$(".section.customBackgroundFilter .brightness input").val() as string
);
updateNumbers();
@ -101,7 +101,7 @@ $(".section.customBackgroundFilter .brightness input").on("input", () => {
});
$(".section.customBackgroundFilter .saturate input").on("input", () => {
filters["saturate"].value = parseFloat(
filters.saturate.value = parseFloat(
$(".section.customBackgroundFilter .saturate input").val() as string
);
updateNumbers();
@ -109,7 +109,7 @@ $(".section.customBackgroundFilter .saturate input").on("input", () => {
});
$(".section.customBackgroundFilter .opacity input").on("input", () => {
filters["opacity"].value = parseFloat(
filters.opacity.value = parseFloat(
$(".section.customBackgroundFilter .opacity input").val() as string
);
updateNumbers();

View file

@ -1,18 +1,18 @@
interface InputIndicatorOption {
type InputIndicatorOption = {
icon: string;
spinIcon?: true;
message?: string;
level: -1 | 0 | 1;
}
};
export class InputIndicator {
private inputElement: JQuery<HTMLElement>;
private parentElement: JQuery<HTMLElement>;
private inputElement: JQuery;
private parentElement: JQuery;
private options: Record<string, InputIndicatorOption>;
private currentStatus: keyof typeof this.options | null;
constructor(
inputElement: JQuery<HTMLElement>,
inputElement: JQuery,
options: Record<string, InputIndicatorOption>
) {
this.inputElement = inputElement;

View file

@ -195,7 +195,7 @@ function updateFooter(lb: LbKey): void {
const num = Misc.roundTo2(
(lbRank.rank / (currentRank[lb].count as number)) * 100
);
if (currentRank[lb]["rank"] === 1) {
if (currentRank[lb].rank === 1) {
toppercent = "GOAT";
} else {
toppercent = `Top ${num}%`;
@ -458,14 +458,15 @@ async function update(): Promise<void> {
const responses = await Promise.all(lbDataRequests);
const rankResponses = await Promise.all(lbRankRequests);
const atLeastOneFailed =
responses.find((response) => response.status !== 200) ||
rankResponses.find((response) => response.status !== 200);
if (atLeastOneFailed) {
const failedResponses = [
...(responses.filter((response) => response.status !== 200) ?? []),
...(rankResponses.filter((response) => response.status !== 200) ?? []),
];
if (failedResponses.length > 0) {
hideLoader("15");
hideLoader("60");
return Notifications.add(
"Failed to load leaderboards: " + atLeastOneFailed.message,
"Failed to load leaderboards: " + failedResponses[0]?.message,
-1
);
}

View file

@ -2,25 +2,25 @@ import * as ThemeColors from "./theme-colors";
import * as SlowTimer from "../states/slow-timer";
import Config from "../config";
interface Particle {
type Particle = {
x: number;
y: number;
color: string;
alpha: number;
prev: { x: number; y: number };
vel: { x: number; y: number };
}
};
interface CTX {
type CTX = {
particles: Particle[];
caret?: JQuery<HTMLElement>;
caret?: JQuery;
canvas?: HTMLCanvasElement;
context2d?: CanvasRenderingContext2D;
rendering: boolean;
lastFrame?: number;
deltaTime?: number;
resetTimeOut?: number;
}
};
/**
* @typedef {{ x: number, y: number }} vec2

View file

@ -41,7 +41,7 @@ class Notification {
this.type = type;
this.message = allowHTML ? message : Misc.escapeHTML(message);
this.level = level;
this.important = important || false;
this.important = important ?? false;
if (type === "banner") {
this.duration = duration as number;
} else {

View file

@ -10,10 +10,10 @@ import formatDistanceToNowStrict from "date-fns/formatDistanceToNowStrict";
type ProfileViewPaths = "profile" | "account";
export interface ProfileData extends MonkeyTypes.Snapshot {
export type ProfileData = {
allTimeLbs: MonkeyTypes.LeaderboardMemory;
uid: string;
}
} & MonkeyTypes.Snapshot;
export async function update(
where: ProfileViewPaths,

View file

@ -90,7 +90,7 @@ export async function show(): Promise<void> {
Alerts.addPSA(psa.message, psa.level ?? -1);
if (localmemory.includes(psa._id) && (psa.sticky ?? false) === false) {
if (localmemory.includes(psa._id) && !(psa.sticky ?? false)) {
return;
}

View file

@ -23,10 +23,10 @@ type Line = {
let lines: Line[] = [];
// JQuery collection of all word elements
let wordEls: JQuery<HTMLElement>;
let wordEls: JQuery;
// Dictionary mapping word indices to line indices
let wordIndexToLineIndexDict: { [wordIndex: number]: number } = {};
let wordIndexToLineIndexDict: Record<number, number> = {};
// Array of container elements for highlights
let highlightContainerEls: HTMLElement[] = [];
@ -142,8 +142,7 @@ export async function highlightWordsInRange(
// Function to clear all highlights
export function clear(): void {
for (let i = 0; i < highlightEls.length; i++) {
const highlightEl = highlightEls[i] as HTMLElement;
for (const highlightEl of highlightEls) {
highlightEl.classList.add("highlight-hidden");
}
isFirstHighlightSinceClear = true;
@ -155,8 +154,7 @@ export function destroy(): void {
if (!isInitialized) return;
// Remove highlight containers from DOM
for (let i = 0; i < highlightContainerEls.length; i++) {
const highlightContainerEl = highlightContainerEls[i] as HTMLElement;
for (const highlightContainerEl of highlightContainerEls) {
highlightContainerEl.remove();
}

View file

@ -74,6 +74,6 @@ extendedGlobal.egVideoListener = egVideoListener;
extendedGlobal.toggleDebugLogs = Logger.toggleDebugLogs;
if (isDevEnvironment()) {
//@ts-ignore
//@ts-expect-error
extendedGlobal.$ = $;
}

View file

@ -146,7 +146,7 @@ function loadMoreLines(lineIndex?: number): void {
}
let pb = "";
if (result.isPb === true) {
if (result.isPb) {
pb = '<i class="fas fa-fw fa-crown"></i>';
} else {
pb = "";
@ -258,13 +258,14 @@ async function fillContent(): Promise<void> {
let totalCons10 = 0;
let consCount = 0;
interface ActivityChartData {
[key: number]: {
type ActivityChartData = Record<
number,
{
amount: number;
time: number;
totalWpm: number;
};
}
}
>;
const activityChartData: ActivityChartData = {};
const histogramChartData: number[] = [];
@ -279,9 +280,7 @@ async function fillContent(): Promise<void> {
//apply filters
try {
if (
!ResultFilters.getFilter("pb", result.isPb === true ? "yes" : "no")
) {
if (!ResultFilters.getFilter("pb", result.isPb ? "yes" : "no")) {
if (filterDebug) {
console.log(`skipping result due to pb filter`, result);
}
@ -1055,7 +1054,7 @@ async function fillContent(): Promise<void> {
export async function downloadResults(offset?: number): Promise<void> {
const results = await DB.getUserResults(offset);
if (results === false && !ConnectionState.get()) {
if (!results && !ConnectionState.get()) {
Notifications.add("Could not get results - you are offline", -1, {
duration: 5,
});
@ -1103,7 +1102,7 @@ function sortAndRefreshHistory(
// This allows to reverse the sorting order when clicking multiple times on the table header
let descending = true;
if (forceDescending !== null) {
if (forceDescending === true) {
if (forceDescending) {
$(headerClass).append(
'<i class="fas fa-sort-down" aria-hidden="true"></i>'
);
@ -1303,7 +1302,7 @@ $(".pageAccount .profile").on("click", ".details .copyLink", () => {
});
$(".pageAccount button.loadMoreResults").on("click", async () => {
const offset = DB.getSnapshot()?.results?.length || 0;
const offset = DB.getSnapshot()?.results?.length ?? 0;
Loader.show();
ResultBatches.disableButton();

View file

@ -1,7 +1,7 @@
interface Options<T> {
type Options<T> = {
params?: Record<string, string>;
data?: T;
}
};
export default class Page<T> {
public name: MonkeyTypes.PageName;

View file

@ -149,10 +149,10 @@ function reset(): void {
</div><div class="lbOptOutReminder hidden"></div>`);
}
interface UpdateOptions {
type UpdateOptions = {
uidOrName?: string;
data?: undefined | Profile.ProfileData;
}
};
async function update(options: UpdateOptions): Promise<void> {
const getParamExists = checkIfGetParameterExists("isUid");
@ -161,17 +161,15 @@ async function update(options: UpdateOptions): Promise<void> {
await Profile.update("profile", options.data);
PbTables.update(options.data.personalBests, true);
} else if (options.uidOrName !== undefined && options.uidOrName !== "") {
const response =
getParamExists === true
? await Ape.users.getProfileByUid(options.uidOrName)
: await Ape.users.getProfileByName(options.uidOrName);
const response = getParamExists
? await Ape.users.getProfileByUid(options.uidOrName)
: await Ape.users.getProfileByName(options.uidOrName);
$(".page.pageProfile .preloader").addClass("hidden");
if (response.status === 404) {
const message =
getParamExists === true
? "User not found"
: `User ${options.uidOrName} not found`;
const message = getParamExists
? "User not found"
: `User ${options.uidOrName} not found`;
$(".page.pageProfile .preloader").addClass("hidden");
$(".page.pageProfile .error").removeClass("hidden");
$(".page.pageProfile .error .message").text(message);

View file

@ -21,9 +21,10 @@ import { get as getTypingSpeedUnit } from "../utils/typing-speed-units";
import * as Skeleton from "../popups/skeleton";
interface SettingsGroups<T extends SharedTypes.ConfigValue> {
[key: string]: SettingsGroup<T>;
}
type SettingsGroups<T extends SharedTypes.ConfigValue> = Record<
string,
SettingsGroup<T>
>;
export const groups: SettingsGroups<SharedTypes.ConfigValue> = {};
@ -1023,7 +1024,7 @@ $(".pageSettings .section.minBurst").on("click", ".button.save", () => {
//funbox
$(".pageSettings .section.funbox").on("click", ".button", (e) => {
const funbox = <string>$(e.currentTarget).attr("funbox");
const funbox = $(e.currentTarget).attr("funbox") as string;
toggleFunbox(funbox);
setActiveFunboxButton();
});
@ -1123,7 +1124,7 @@ $(".pageSettings .section.fontSize .inputAndButton input").on(
).val() as string
)
);
if (didConfigSave === true) {
if (didConfigSave) {
Notifications.add("Saved", 1, {
duration: 1,
});

View file

@ -9,10 +9,10 @@ const wrapperId = "cookiePopupWrapper";
let visible = false;
interface Accepted {
type Accepted = {
security: boolean;
analytics: boolean;
}
};
function getAcceptedObject(): Accepted | null {
const acceptedCookies = localStorage.getItem("acceptedCookies") ?? "";
@ -44,7 +44,7 @@ export function show(): void {
visible = true;
if (
$("#cookiePopupWrapper")[0] === undefined ||
$("#cookiePopupWrapper").is(":visible") === false ||
!$("#cookiePopupWrapper").is(":visible") ||
$("#cookiePopupWrapper").outerHeight(true) === 0
) {
//removed by cookie popup blocking extension
@ -60,7 +60,7 @@ export function show(): void {
.removeClass("hidden")
.animate({ opacity: 1 }, 0, () => {
if (
$("#cookiePopupWrapper").is(":visible") === false ||
!$("#cookiePopupWrapper").is(":visible") ||
$("#cookiePopupWrapper").outerHeight(true) === 0
) {
visible = false;
@ -100,7 +100,7 @@ function verifyVisible(): void {
if (!visible) return;
if (
$("#cookiePopupWrapper")[0] === undefined ||
$("#cookiePopupWrapper").is(":visible") === false ||
!$("#cookiePopupWrapper").is(":visible") ||
$("#cookiePopupWrapper").outerHeight(true) === 0
) {
//removed by cookie popup blocking extension

View file

@ -122,10 +122,10 @@ $(`${popup} .delimiterCheck input`).on("change", () => {
CustomText.setDelimiter(delimiter);
});
interface HideOptions {
type HideOptions = {
noAnim?: boolean | undefined;
resetState?: boolean | undefined;
}
};
function hide(options = {} as HideOptions): void {
if (options.noAnim === undefined) options.noAnim = false;
@ -143,7 +143,7 @@ function hide(options = {} as HideOptions): void {
() => {
if (options.resetState) {
const newText = CustomText.text.map((word) => {
if (word[word.length - 1] === "|") {
if (word.endsWith("|")) {
word = word.slice(0, -1);
}
return word;

View file

@ -36,7 +36,7 @@ export function show(callbackOnHide: () => void): void {
function hide(): void {
if (isPopupVisible(wrapperId)) {
callbackFuncOnHide && callbackFuncOnHide();
callbackFuncOnHide?.();
$("#editProfilePopupWrapper")
.stop(true, true)
.css("opacity", 1)

View file

@ -100,7 +100,7 @@ async function apply(): Promise<void> {
if (name.length === 0) throw new Error("Name cannot be empty");
const response = await Ape.users.create(name, captcha);
if (response.status !== 200) {
throw response;
throw new Error(`Failed to create user: ${response.message}`);
}
if (response.status === 200) {

View file

@ -40,7 +40,7 @@ async function hide(): Promise<void> {
} catch (e) {
Notifications.add("Failed to import settings: " + e, -1);
}
void UpdateConfig.saveFullConfigToLocalStorage();
UpdateConfig.saveFullConfigToLocalStorage();
}
$("#settingsImportWrapper")
.stop(true, true)

View file

@ -3,9 +3,9 @@ import format from "date-fns/format";
import * as Skeleton from "./skeleton";
import { getLanguageDisplayString, isPopupVisible } from "../utils/misc";
interface PersonalBest extends SharedTypes.PersonalBest {
type PersonalBest = {
mode2: SharedTypes.Config.Mode2<SharedTypes.Config.Mode>;
}
} & SharedTypes.PersonalBest;
const wrapperId = "pbTablesPopupWrapper";

View file

@ -10,13 +10,13 @@ const wrapperId = "quoteRatePopupWrapper";
let rating = 0;
interface QuoteStats {
type QuoteStats = {
average?: number;
ratings?: number;
totalRating?: number;
quoteId?: number;
language?: string;
}
};
let quoteStats: QuoteStats | null | Record<string, never> = null;
let currentQuote: MonkeyTypes.Quote | null = null;

View file

@ -10,16 +10,16 @@ import { isPopupVisible, removeLanguageSize } from "../utils/misc";
const wrapperId = "quoteReportPopupWrapper";
interface State {
type State = {
previousPopupShowCallback?: () => void;
quoteToReport?: MonkeyTypes.Quote;
}
};
interface Options {
type Options = {
quoteId: number;
previousPopupShowCallback?: () => void;
noAnim: boolean;
}
};
const state: State = {
previousPopupShowCallback: undefined,

View file

@ -303,7 +303,7 @@ function hide(noAnim = false, focusWords = true): void {
export function apply(val: number): boolean {
if (isNaN(val)) {
val = parseInt(
(<HTMLInputElement>document.getElementById("searchBox")).value as string
(document.getElementById("searchBox") as HTMLInputElement).value as string
);
}
let ret;
@ -321,7 +321,7 @@ export function apply(val: number): boolean {
}
const searchForQuotes = debounce(250, (): void => {
const searchText = (<HTMLInputElement>document.getElementById("searchBox"))
const searchText = (document.getElementById("searchBox") as HTMLInputElement)
.value;
currentPageNumber = 1;
void updateResults(searchText);
@ -337,7 +337,7 @@ $("#quoteSearchPopupWrapper .quoteLengthFilter").on("change", searchForQuotes);
$(
"#quoteSearchPageNavigator .nextPage, #quoteSearchPageNavigator .prevPage"
).on("click", function () {
const searchText = (<HTMLInputElement>document.getElementById("searchBox"))
const searchText = (document.getElementById("searchBox") as HTMLInputElement)
.value;
if ($(this).hasClass("nextPage")) {

View file

@ -6,7 +6,7 @@ const wrapperId = "registerCaptchaPopupWrapper";
let resolvePromise: (token?: string) => void;
export let promise: Promise<string | undefined> = new Promise((resolve) => {
export let promise = new Promise<string | undefined>((resolve) => {
resolvePromise = resolve;
});

View file

@ -89,7 +89,7 @@ function save(): boolean {
}
$("#popups").on("click", `#saveCustomTextPopupWrapper .button.save`, () => {
if (save() === true) hide(true);
if (save()) hide(true);
});
$("#saveCustomTextPopupWrapper").on("mousedown", (e) => {

View file

@ -35,24 +35,24 @@ import * as ThemeController from "../controllers/theme-controller";
const wrapperId = "simplePopupWrapper";
interface Input {
type Input = {
placeholder?: string;
type?: string;
initVal: string;
hidden?: boolean;
disabled?: boolean;
label?: string;
}
};
let activePopup: SimplePopup | null = null;
interface ExecReturn {
type ExecReturn = {
status: 1 | 0 | -1;
message: string;
showNotification?: false;
notificationOptions?: MonkeyTypes.AddNotificationOptions;
afterHide?: () => void;
}
};
type PopupKey =
| "updateEmail"
@ -403,16 +403,16 @@ $("#popups").on("submit", "#simplePopupWrapper form", (e) => {
type ReauthMethod = "passwordOnly" | "passwordFirst";
interface ReauthSuccess {
type ReauthSuccess = {
status: 1;
message: string;
user: User;
}
};
interface ReauthFailed {
type ReauthFailed = {
status: -1 | 0;
message: string;
}
};
async function reauthenticate(
method: ReauthMethod,
@ -548,7 +548,7 @@ list.updateEmail = new SimplePopup(
}
);
list["removeGoogleAuth"] = new SimplePopup(
list.removeGoogleAuth = new SimplePopup(
"removeGoogleAuth",
"text",
"Remove Google Authentication",
@ -602,7 +602,7 @@ list["removeGoogleAuth"] = new SimplePopup(
}
);
list["updateName"] = new SimplePopup(
list.updateName = new SimplePopup(
"updateName",
"text",
"Update Name",
@ -683,7 +683,7 @@ list["updateName"] = new SimplePopup(
}
);
list["updatePassword"] = new SimplePopup(
list.updatePassword = new SimplePopup(
"updatePassword",
"text",
"Update Password",
@ -768,7 +768,7 @@ list["updatePassword"] = new SimplePopup(
}
);
list["addPasswordAuth"] = new SimplePopup(
list.addPasswordAuth = new SimplePopup(
"addPasswordAuth",
"text",
"Add Password Authentication",
@ -860,7 +860,7 @@ list["addPasswordAuth"] = new SimplePopup(
}
);
list["deleteAccount"] = new SimplePopup(
list.deleteAccount = new SimplePopup(
"deleteAccount",
"text",
"Delete Account",
@ -933,7 +933,7 @@ list["deleteAccount"] = new SimplePopup(
}
);
list["resetAccount"] = new SimplePopup(
list.resetAccount = new SimplePopup(
"resetAccount",
"text",
"Reset Account",
@ -987,7 +987,7 @@ list["resetAccount"] = new SimplePopup(
}
);
list["optOutOfLeaderboards"] = new SimplePopup(
list.optOutOfLeaderboards = new SimplePopup(
"optOutOfLeaderboards",
"text",
"Opt out of leaderboards",
@ -1037,7 +1037,7 @@ list["optOutOfLeaderboards"] = new SimplePopup(
}
);
list["clearTagPb"] = new SimplePopup(
list.clearTagPb = new SimplePopup(
"clearTagPb",
"text",
"Clear Tag PB",
@ -1092,7 +1092,7 @@ list["clearTagPb"] = new SimplePopup(
}
);
list["applyCustomFont"] = new SimplePopup(
list.applyCustomFont = new SimplePopup(
"applyCustomFont",
"text",
"Custom font",
@ -1115,7 +1115,7 @@ list["applyCustomFont"] = new SimplePopup(
}
);
list["resetPersonalBests"] = new SimplePopup(
list.resetPersonalBests = new SimplePopup(
"resetPersonalBests",
"text",
"Reset Personal Bests",
@ -1179,7 +1179,7 @@ list["resetPersonalBests"] = new SimplePopup(
}
);
list["resetSettings"] = new SimplePopup(
list.resetSettings = new SimplePopup(
"resetSettings",
"text",
"Reset Settings",
@ -1201,7 +1201,7 @@ list["resetSettings"] = new SimplePopup(
}
);
list["revokeAllTokens"] = new SimplePopup(
list.revokeAllTokens = new SimplePopup(
"revokeAllTokens",
"text",
"Revoke All Tokens",
@ -1252,7 +1252,7 @@ list["revokeAllTokens"] = new SimplePopup(
}
);
list["unlinkDiscord"] = new SimplePopup(
list.unlinkDiscord = new SimplePopup(
"unlinkDiscord",
"text",
"Unlink Discord",
@ -1295,7 +1295,7 @@ list["unlinkDiscord"] = new SimplePopup(
}
);
list["generateApeKey"] = new SimplePopup(
list.generateApeKey = new SimplePopup(
"generateApeKey",
"text",
"Generate new key",
@ -1335,7 +1335,7 @@ list["generateApeKey"] = new SimplePopup(
}
);
list["viewApeKey"] = new SimplePopup(
list.viewApeKey = new SimplePopup(
"viewApeKey",
"text",
"Ape Key",
@ -1371,7 +1371,7 @@ list["viewApeKey"] = new SimplePopup(
}
);
list["deleteApeKey"] = new SimplePopup(
list.deleteApeKey = new SimplePopup(
"deleteApeKey",
"text",
"Delete Ape Key",
@ -1402,7 +1402,7 @@ list["deleteApeKey"] = new SimplePopup(
}
);
list["editApeKey"] = new SimplePopup(
list.editApeKey = new SimplePopup(
"editApeKey",
"text",
"Edit Ape Key",
@ -1440,7 +1440,7 @@ list["editApeKey"] = new SimplePopup(
}
);
list["deleteCustomText"] = new SimplePopup(
list.deleteCustomText = new SimplePopup(
"deleteCustomText",
"text",
"Delete custom text",
@ -1465,7 +1465,7 @@ list["deleteCustomText"] = new SimplePopup(
}
);
list["deleteCustomTextLong"] = new SimplePopup(
list.deleteCustomTextLong = new SimplePopup(
"deleteCustomTextLong",
"text",
"Delete custom text",
@ -1490,7 +1490,7 @@ list["deleteCustomTextLong"] = new SimplePopup(
}
);
list["resetProgressCustomTextLong"] = new SimplePopup(
list.resetProgressCustomTextLong = new SimplePopup(
"resetProgressCustomTextLong",
"text",
"Reset progress for custom text",
@ -1518,7 +1518,7 @@ list["resetProgressCustomTextLong"] = new SimplePopup(
}
);
list["updateCustomTheme"] = new SimplePopup(
list.updateCustomTheme = new SimplePopup(
"updateCustomTheme",
"text",
"Update custom theme",
@ -1602,7 +1602,7 @@ list["updateCustomTheme"] = new SimplePopup(
}
);
list["deleteCustomTheme"] = new SimplePopup(
list.deleteCustomTheme = new SimplePopup(
"deleteCustomTheme",
"text",
"Delete Custom Theme",
@ -1626,7 +1626,7 @@ list["deleteCustomTheme"] = new SimplePopup(
}
);
list["forgotPassword"] = new SimplePopup(
list.forgotPassword = new SimplePopup(
"forgotPassword",
"text",
"Forgot Password",

View file

@ -22,7 +22,7 @@ type ParentOverride = "main";
export function append(id: string, parentOverride?: ParentOverride): void {
const popup = skeletons.get(id) as HTMLElement;
if (parentOverride) {
(<HTMLElement>document.querySelector(parentOverride)).append(popup);
(document.querySelector(parentOverride) as HTMLElement).append(popup);
} else {
parent.append(popup);
}

View file

@ -7,21 +7,21 @@ import { isPopupVisible } from "../utils/misc";
const wrapperId = "userReportPopupWrapper";
interface State {
type State = {
userUid?: string;
lbOptOut?: boolean;
}
};
const state: State = {
userUid: undefined,
lbOptOut: undefined,
};
interface ShowOptions {
type ShowOptions = {
uid: string;
name: string;
lbOptOut: boolean;
}
};
export async function show(options: ShowOptions): Promise<void> {
Skeleton.append(wrapperId);

View file

@ -37,7 +37,7 @@ export async function show(): Promise<void> {
.css("opacity", 0)
.removeClass("hidden")
.animate({ opacity: 1 }, 125, () => {
//@ts-ignore
//@ts-expect-error
window.dataLayer.push({ event: "EG_Video" });
});
}

View file

@ -7,11 +7,11 @@ const wrapperId = "wordFilterPopupWrapper";
let initialised = false;
interface FilterPreset {
type FilterPreset = {
display: string;
getIncludeString: (layout: MonkeyTypes.Layout) => string[];
getExcludeString: (layout: MonkeyTypes.Layout) => string[];
}
};
const presets: Record<string, FilterPreset> = {
homeKeys: {

View file

@ -10,7 +10,7 @@ import * as PSA from "./elements/psa";
import * as ConnectionState from "./states/connection";
import { Workbox } from "workbox-window";
import * as FunboxList from "./test/funbox/funbox-list";
//@ts-ignore
//@ts-expect-error
import Konami from "konami";
import { log } from "./controllers/analytics-controller";
import { envConfig } from "./constants/env-config";

View file

@ -77,6 +77,8 @@ export default class SettingsGroup<T extends SharedTypes.ConfigValue> {
.trigger("change.select2");
} else if (this.mode === "button") {
$(
// this cant be an object?
// eslint-disable-next-line @typescript-eslint/no-base-to-string
`.pageSettings .section.${this.configName} .button[${this.configName}='${this.configValue}']`
).addClass("active");
}

View file

@ -34,7 +34,7 @@ function updateActiveButton(): void {
}
function updateColors(
colorPicker: JQuery<HTMLElement>,
colorPicker: JQuery,
color: string,
onlyStyle = false,
noThemeUpdate = false
@ -196,16 +196,16 @@ export async function refreshButtons(): Promise<void> {
<div class="favButton active"><i class="fas fa-star"></i></div>
<div class="text">${theme.name.replace(/_/g, " ")}</div>
<div class="themeBubbles" style="background: ${
theme["bgColor"]
};outline: 0.25rem solid ${theme["bgColor"]};">
theme.bgColor
};outline: 0.25rem solid ${theme.bgColor};">
<div class="themeBubble" style="background: ${
theme["mainColor"]
theme.mainColor
}"></div>
<div class="themeBubble" style="background: ${
theme["subColor"]
theme.subColor
}"></div>
<div class="themeBubble" style="background: ${
theme["textColor"]
theme.textColor
}"></div>
</div>
</div>
@ -232,17 +232,11 @@ export async function refreshButtons(): Promise<void> {
<div class="favButton"><i class="far fa-star"></i></div>
<div class="text">${theme.name.replace(/_/g, " ")}</div>
<div class="themeBubbles" style="background: ${
theme["bgColor"]
};outline: 0.25rem solid ${theme["bgColor"]};">
<div class="themeBubble" style="background: ${
theme["mainColor"]
}"></div>
<div class="themeBubble" style="background: ${
theme["subColor"]
}"></div>
<div class="themeBubble" style="background: ${
theme["textColor"]
}"></div>
theme.bgColor
};outline: 0.25rem solid ${theme.bgColor};">
<div class="themeBubble" style="background: ${theme.mainColor}"></div>
<div class="themeBubble" style="background: ${theme.subColor}"></div>
<div class="themeBubble" style="background: ${theme.textColor}"></div>
</div>
</div>
`;
@ -270,11 +264,11 @@ function toggleFavourite(themeName: string): void {
UpdateConfig.setFavThemes(Config.favThemes.filter((t) => t !== themeName));
} else {
// add to favourites
const newList: Array<string> = Config.favThemes;
const newList: string[] = Config.favThemes;
newList.push(themeName);
UpdateConfig.setFavThemes(newList);
}
void UpdateConfig.saveFullConfigToLocalStorage();
UpdateConfig.saveFullConfigToLocalStorage();
}
function saveCustomThemeColors(): void {

View file

@ -3,8 +3,5 @@ export function get(): boolean {
}
export function set(value: boolean): void {
localStorage.setItem(
"prefersArabicLazyMode",
value === true ? "true" : "false"
);
localStorage.setItem("prefersArabicLazyMode", value ? "true" : "false");
}

View file

@ -2,11 +2,11 @@ import Config from "../config";
import { capitalizeFirstLetterOfEachWord } from "../utils/misc";
import * as CustomText from "../test/custom-text";
interface BritishEnglishReplacement {
type BritishEnglishReplacement = {
0: string;
1: string;
2?: string[];
}
};
let list: BritishEnglishReplacement[] = [];

View file

@ -25,7 +25,7 @@ function update(event: JQuery.KeyDownEvent | JQuery.KeyUpEvent): void {
if (event?.originalEvent?.key === "CapsLock" && capsState !== null) {
capsState = !capsState;
} else {
capsState = event?.originalEvent?.getModifierState("CapsLock") || false;
capsState = event?.originalEvent?.getModifierState("CapsLock") ?? false;
}
try {

View file

@ -8,7 +8,7 @@ export let caretAnimating = true;
const caret = document.querySelector("#caret") as HTMLElement;
export function stopAnimation(): void {
if (caretAnimating === true) {
if (caretAnimating) {
caret.style.animationName = "none";
caret.style.opacity = "1";
caretAnimating = false;
@ -16,7 +16,7 @@ export function stopAnimation(): void {
}
export function startAnimation(): void {
if (caretAnimating === false) {
if (!caretAnimating) {
if (Config.smoothCaret !== "off" && !SlowTimer.get()) {
caret.style.animationName = "caretFlashSmooth";
} else {
@ -82,7 +82,7 @@ export async function updatePosition(): Promise<void> {
let newLeft = letterPosLeft - (fullWidthCaret ? 0 : caretWidth / 2);
const wordsWrapperWidth =
$(<HTMLElement>document.querySelector("#wordsWrapper")).width() ?? 0;
$(document.querySelector("#wordsWrapper") as HTMLElement).width() ?? 0;
if (Config.tapeMode === "letter") {
newLeft = wordsWrapperWidth / 2 - (fullWidthCaret ? 0 : caretWidth / 2);
@ -121,7 +121,7 @@ export async function updatePosition(): Promise<void> {
};
if (newWidth !== "") {
animation["width"] = newWidth;
animation.width = newWidth;
} else {
jqcaret.css("width", "");
}

View file

@ -122,6 +122,7 @@ export function setCustomText(
export function deleteCustomText(name: string, long: boolean): void {
const customText = long ? getLocalStorageLong() : getLocalStorage();
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete customText[name];
if (long) {

View file

@ -49,7 +49,7 @@ export function set(foc: boolean, withCursor = false): void {
$(document).on("mousemove", function (event) {
if (!state) return;
if (ActivePage.get() === "loading") return;
if (ActivePage.get() === "account" && state === true) return;
if (ActivePage.get() === "account" && state) return;
if (
event.originalEvent &&
// To avoid mouse/desk vibration from creating a flashy effect, we'll unfocus @ >5px instead of >0px

View file

@ -5,9 +5,7 @@ type ValueAndSetFunction<T> = {
setFunction: SetFunction<T>;
};
type SettingsMemory<T> = {
[key: string]: ValueAndSetFunction<T>;
};
type SettingsMemory<T> = Record<string, ValueAndSetFunction<T>>;
let settingsMemory: SettingsMemory<SharedTypes.ConfigValue> = {};

View file

@ -8,7 +8,7 @@ export function checkFunboxForcedConfigs(
funbox: string
): {
result: boolean;
forcedConfigs?: Array<SharedTypes.ConfigValue>;
forcedConfigs?: SharedTypes.ConfigValue[];
} {
if (FunboxList.get(funbox).length === 0) return { result: true };
@ -62,7 +62,7 @@ export function checkFunboxForcedConfigs(
}
return {
result: (forcedConfigs[key] ?? []).includes(
<SharedTypes.ConfigValue>value
value as SharedTypes.ConfigValue
),
forcedConfigs: forcedConfigs[key],
};
@ -93,16 +93,16 @@ export function canSetConfigWithCurrentFunboxes(
fb = fb.concat(
FunboxList.get(funbox).filter(
(f) =>
f.functions?.getWord ||
f.functions?.pullSection ||
f.functions?.alterText ||
f.functions?.withWords ||
f.properties?.includes("changesCapitalisation") ||
f.properties?.includes("nospace") ||
(f.properties?.find((fp) => fp.startsWith("toPush:")) ?? "") ||
f.properties?.includes("changesWordsVisibility") ||
f.properties?.includes("speaks") ||
f.properties?.includes("changesLayout") ||
f.functions?.getWord ??
f.functions?.pullSection ??
f.functions?.alterText ??
f.functions?.withWords ??
f.properties?.includes("changesCapitalisation") ??
f.properties?.includes("nospace") ??
f.properties?.find((fp) => fp.startsWith("toPush:")) ??
f.properties?.includes("changesWordsVisibility") ??
f.properties?.includes("speaks") ??
f.properties?.includes("changesLayout") ??
f.properties?.includes("changesWordsFrequency")
)
);
@ -111,9 +111,9 @@ export function canSetConfigWithCurrentFunboxes(
fb = fb.concat(
FunboxList.get(funbox).filter(
(f) =>
f.functions?.getWord ||
f.functions?.pullSection ||
f.functions?.withWords ||
f.functions?.getWord ??
f.functions?.pullSection ??
f.functions?.withWords ??
f.properties?.includes("changesWordsFrequency")
)
);
@ -225,8 +225,8 @@ export function areFunboxesCompatible(
const oneWordModifierMax =
funboxesToCheck.filter(
(f) =>
f.functions?.getWord ||
f.functions?.pullSection ||
f.functions?.getWord ??
f.functions?.pullSection ??
f.functions?.withWords
).length <= 1;
const layoutUsability =

View file

@ -24,7 +24,7 @@ import * as LayoutfluidFunboxTimer from "./layoutfluid-funbox-timer";
const prefixSize = 2;
class CharDistribution {
public chars: { [char: string]: number };
public chars: Record<string, number>;
public count: number;
constructor() {
this.chars = {};
@ -55,7 +55,7 @@ class CharDistribution {
}
class PseudolangWordGenerator extends Wordset {
public ngrams: { [prefix: string]: CharDistribution } = {};
public ngrams: Record<string, CharDistribution> = {};
constructor(words: string[]) {
super(words);
// Can generate an unbounded number of words in theory.
@ -635,8 +635,8 @@ export async function activate(funbox?: string): Promise<boolean | undefined> {
configValue,
Config.funbox
);
if (check.result === true) continue;
if (check.result === false) {
if (check.result) continue;
if (!check.result) {
if (check.forcedConfigs && check.forcedConfigs.length > 0) {
if (configKey === "mode") {
UpdateConfig.setMode(
@ -750,7 +750,7 @@ async function setFunboxBodyClasses(): Promise<boolean> {
$body
?.attr("class")
?.split(/\s+/)
?.filter((it) => !it.startsWith("fb-")) || [];
?.filter((it) => !it.startsWith("fb-")) ?? [];
$body.attr("class", [...currentClasses, ...activeFbClasses].join(" "));

View file

@ -204,11 +204,11 @@ export async function getCharFromEvent(
const layoutKeys = layout.keys;
const layoutMap = layoutKeys["row1"]
.concat(layoutKeys["row2"])
.concat(layoutKeys["row3"])
.concat(layoutKeys["row4"])
.concat(layoutKeys["row5"]);
const layoutMap = layoutKeys.row1
.concat(layoutKeys.row2)
.concat(layoutKeys.row3)
.concat(layoutKeys.row4)
.concat(layoutKeys.row5);
const mapIndex = keyEventCodes.indexOf(event.code);
if (mapIndex === -1) {

View file

@ -42,7 +42,7 @@ const accents: [string, string][] = [
["þ", "th"],
];
const accentsMap: Map<string, string> = new Map(
const accentsMap = new Map<string, string>(
accents.flatMap((rule) => [...rule[0]].map((accent) => [accent, rule[1]]))
);

View file

@ -7,16 +7,16 @@ import * as Misc from "../utils/misc";
import * as TestState from "./test-state";
import * as ConfigEvent from "../observables/config-event";
interface Settings {
type Settings = {
wpm: number;
cps: number;
spc: number;
correction: number;
currentWordIndex: number;
currentLetterIndex: number;
wordsStatus: { [key: number]: true | undefined };
wordsStatus: Record<number, true | undefined>;
timeout: NodeJS.Timeout | null;
}
};
export let settings: Settings | null = null;
@ -39,9 +39,9 @@ function resetCaretPosition(): void {
if (Config.mode === "zen") return;
const caret = $("#paceCaret");
const firstLetter = <HTMLElement>(
document?.querySelector("#words .word")?.querySelector("letter")
);
const firstLetter = document
?.querySelector("#words .word")
?.querySelector("letter") as HTMLElement;
const firstLetterHeight = $(firstLetter).height();
@ -96,7 +96,7 @@ export async function init(): Promise<void> {
wpm = Math.round(wpm);
} else if (Config.paceCaret === "custom") {
wpm = Config.paceCaretCustomSpeed;
} else if (Config.paceCaret === "last" || TestState.isPaceRepeat === true) {
} else if (Config.paceCaret === "last" || TestState.isPaceRepeat) {
wpm = lastTestWpm;
}
if (wpm === undefined || wpm < 1 || Number.isNaN(wpm)) {
@ -186,11 +186,11 @@ export function update(expectedStepEnd: number): void {
newIndex
] as HTMLElement;
if (settings.currentLetterIndex === -1) {
currentLetter = <HTMLElement>word.querySelectorAll("letter")[0];
currentLetter = word.querySelectorAll("letter")[0] as HTMLElement;
} else {
currentLetter = <HTMLElement>(
word.querySelectorAll("letter")[settings.currentLetterIndex]
);
currentLetter = word.querySelectorAll("letter")[
settings.currentLetterIndex
] as HTMLElement;
}
const currentLetterHeight = $(currentLetter).height(),
@ -202,7 +202,9 @@ export function update(expectedStepEnd: number): void {
currentLetterWidth === undefined ||
caretWidth === undefined
) {
throw ``;
throw new Error(
"Undefined current letter height, width or caret width."
);
}
newTop =
@ -262,7 +264,7 @@ export function update(expectedStepEnd: number): void {
}
export function reset(): void {
if (settings !== null && settings.timeout !== null) {
if (settings?.timeout != null) {
clearTimeout(settings.timeout);
}
settings = null;

View file

@ -19,11 +19,9 @@ export class Poem extends Section {
let count = 0;
const scrubbedWords = [];
for (let i = 0; i < this.words.length; i++) {
const word = this.words[i] as string;
for (const word of this.words) {
let scrubbed = "";
for (let j = 0; j < word.length; j++) {
const char = word[j] as string;
for (const char of word) {
if (!bannedChars.includes(char)) {
scrubbed += char;
}
@ -41,11 +39,11 @@ export class Poem extends Section {
}
}
interface PoemObject {
type PoemObject = {
lines: string[];
title: string;
author: string;
}
};
export async function getPoem(): Promise<Section | false> {
console.log("Getting poem");

Some files were not shown because too many files have changed in this diff Show more