mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-09-20 07:16:17 +08:00
refactor: enable @typescript-eslint/strict
(#5028)
* enable rule * eslint --fix * fixes * --fix * fixes * fixes * fixes * extract into const
This commit is contained in:
parent
dbc5898e49
commit
084d42c62e
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -83,7 +83,7 @@ export async function sendVerificationEmail(
|
|||
);
|
||||
})
|
||||
).emailVerified;
|
||||
if (isVerified === true) {
|
||||
if (isVerified) {
|
||||
throw new MonkeyError(400, "Email already verified");
|
||||
}
|
||||
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -16,9 +16,9 @@ function getPresetKeyFilter(
|
|||
};
|
||||
}
|
||||
|
||||
interface PresetCreationResult {
|
||||
type PresetCreationResult = {
|
||||
presetId: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const getPresetsCollection = (): Collection<WithId<DBConfigPreset>> =>
|
||||
db.collection<DBConfigPreset>("presets");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[],
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -76,7 +76,7 @@ async function errorHandlingMiddleware(
|
|||
}
|
||||
|
||||
if (monkeyResponse.status < 500) {
|
||||
delete monkeyResponse.data["errorId"];
|
||||
delete monkeyResponse.data.errorId;
|
||||
}
|
||||
|
||||
return handleMonkeyResponse(monkeyResponse, res);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
86
backend/src/types/types.d.ts
vendored
86
backend/src/types/types.d.ts
vendored
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ?? [],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
12
frontend/src/ts/ape/types/ape.d.ts
vendored
12
frontend/src/ts/ape/types/ape.d.ts
vendored
|
@ -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;
|
||||
}
|
||||
|
|
8
frontend/src/ts/ape/types/leaderboards.d.ts
vendored
8
frontend/src/ts/ape/types/leaderboards.d.ts
vendored
|
@ -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[];
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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: [],
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,6 @@ extendedGlobal.egVideoListener = egVideoListener;
|
|||
extendedGlobal.toggleDebugLogs = Logger.toggleDebugLogs;
|
||||
|
||||
if (isDevEnvironment()) {
|
||||
//@ts-ignore
|
||||
//@ts-expect-error
|
||||
extendedGlobal.$ = $;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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[] = [];
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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", "");
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> = {};
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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(" "));
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]]))
|
||||
);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue