mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2024-09-20 07:16:17 +08:00
refactor: enable no-unsafe-argument rule (@miodec) (#5872)
This commit is contained in:
parent
4f75a00cb3
commit
93d6fff895
|
@ -116,7 +116,11 @@ export async function handleReports(
|
|||
});
|
||||
await UserDAL.addToInbox(report.uid, [mail], inboxConfig);
|
||||
} catch (e) {
|
||||
throw new MonkeyError(e.status, e.message);
|
||||
if (e instanceof MonkeyError) {
|
||||
throw new MonkeyError(e.status, e.message);
|
||||
} else {
|
||||
throw new MonkeyError(500, "Error handling reports: " + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1109,9 +1109,9 @@ export function generateCurrentTestActivity(
|
|||
//make sure lastYearData covers the full year
|
||||
if (lastYearData.length < Dates.getDaysInYear(lastYear)) {
|
||||
lastYearData.push(
|
||||
...new Array(Dates.getDaysInYear(lastYear) - lastYearData.length).fill(
|
||||
...(new Array(Dates.getDaysInYear(lastYear) - lastYearData.length).fill(
|
||||
undefined
|
||||
)
|
||||
) as (number | null)[])
|
||||
);
|
||||
}
|
||||
//use enough days of the last year to have 372 days in total to always fill the first week of the graph
|
||||
|
|
|
@ -73,9 +73,9 @@ export async function add(
|
|||
const quoteFile = await readFile(fileDir);
|
||||
const quoteFileJSON = JSON.parse(quoteFile.toString());
|
||||
quoteFileJSON.quotes.every((old) => {
|
||||
if (compareTwoStrings(old.text, quote.text) > 0.9) {
|
||||
if (compareTwoStrings(old.text as string, quote.text) > 0.9) {
|
||||
duplicateId = old.id;
|
||||
similarityScore = compareTwoStrings(old.text, quote.text);
|
||||
similarityScore = compareTwoStrings(old.text as string, quote.text);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -157,7 +157,7 @@ export async function approve(
|
|||
const quoteFile = await readFile(fileDir);
|
||||
const quoteObject = JSON.parse(quoteFile.toString());
|
||||
quoteObject.quotes.every((old) => {
|
||||
if (compareTwoStrings(old.text, quote.text) > 0.8) {
|
||||
if (compareTwoStrings(old.text as string, quote.text) > 0.8) {
|
||||
throw new MonkeyError(409, "Duplicate quote");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -756,7 +756,7 @@ export async function getStats(
|
|||
}
|
||||
|
||||
export async function getFavoriteQuotes(
|
||||
uid
|
||||
uid: string
|
||||
): Promise<NonNullable<MonkeyTypes.DBUser["favoriteQuotes"]>> {
|
||||
const user = await getPartialUser(uid, "get favorite quotes", [
|
||||
"favoriteQuotes",
|
||||
|
@ -1072,7 +1072,7 @@ export async function updateStreak(
|
|||
} else if (!isToday(streak.lastResultTimestamp, streak.hourOffset ?? 0)) {
|
||||
void addImportantLog(
|
||||
"streak_lost",
|
||||
JSON.parse(JSON.stringify(streak)),
|
||||
JSON.parse(JSON.stringify(streak)) as Record<string, unknown>,
|
||||
uid
|
||||
);
|
||||
streak.length = 1;
|
||||
|
|
|
@ -32,7 +32,7 @@ function mergeConfigurations(
|
|||
const isSourceValueObject = _.isPlainObject(sourceValue);
|
||||
|
||||
if (isBaseValueObject && isSourceValueObject) {
|
||||
merge(baseValue, sourceValue);
|
||||
merge(baseValue as object, sourceValue as object);
|
||||
} else if (identity(baseValue) === identity(sourceValue)) {
|
||||
base[key] = sourceValue;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ export async function connect(): Promise<void> {
|
|||
await mongoClient.connect();
|
||||
db = mongoClient.db(DB_NAME);
|
||||
} catch (error) {
|
||||
Logger.error(error.message);
|
||||
Logger.error(error.message as string);
|
||||
Logger.error(
|
||||
"Failed to connect to database. Exiting with exit status code 1."
|
||||
);
|
||||
|
|
|
@ -72,7 +72,7 @@ export async function init(): Promise<void> {
|
|||
Logger.success("Email client configuration verified");
|
||||
} catch (error) {
|
||||
transportInitialized = false;
|
||||
Logger.error(error.message);
|
||||
Logger.error(error.message as string);
|
||||
Logger.error("Failed to verify email client configuration.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export async function connect(): Promise<void> {
|
|||
|
||||
connected = true;
|
||||
} catch (error) {
|
||||
Logger.error(error.message);
|
||||
Logger.error(error.message as string);
|
||||
if (isDevEnvironment()) {
|
||||
await connection.quit();
|
||||
Logger.warning(
|
||||
|
|
|
@ -14,7 +14,10 @@ import crypto from "crypto";
|
|||
import { performance } from "perf_hooks";
|
||||
import { TsRestRequestHandler } from "@ts-rest/express";
|
||||
import { AppRoute, AppRouter } from "@ts-rest/core";
|
||||
import { RequestAuthenticationOptions } from "@monkeytype/contracts/schemas/api";
|
||||
import {
|
||||
EndpointMetadata,
|
||||
RequestAuthenticationOptions,
|
||||
} from "@monkeytype/contracts/schemas/api";
|
||||
import { Configuration } from "@monkeytype/contracts/schemas/configuration";
|
||||
|
||||
const DEFAULT_OPTIONS: RequestAuthenticationOptions = {
|
||||
|
@ -45,7 +48,8 @@ export function authenticateTsRestRequest<
|
|||
): Promise<void> => {
|
||||
const options = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...(req.tsRestRoute["metadata"]?.["authenticationOptions"] ?? {}),
|
||||
...((req.tsRestRoute["metadata"]?.["authenticationOptions"] ??
|
||||
{}) as EndpointMetadata),
|
||||
};
|
||||
return _authenticateRequestInternal(req, _res, next, options);
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ export function verifyRequiredConfiguration<
|
|||
next: NextFunction
|
||||
): Promise<void> => {
|
||||
const requiredConfigurations = getRequireConfigurations(
|
||||
req.tsRestRoute["metadata"]
|
||||
req.tsRestRoute["metadata"] as EndpointMetadata | undefined
|
||||
);
|
||||
|
||||
if (requiredConfigurations === undefined) {
|
||||
|
|
|
@ -68,7 +68,10 @@ async function errorHandlingMiddleware(
|
|||
) {
|
||||
recordServerErrorByVersion(version);
|
||||
|
||||
const { uid, errorId } = monkeyResponse.data;
|
||||
const { uid, errorId } = monkeyResponse.data as {
|
||||
uid: string;
|
||||
errorId: string;
|
||||
};
|
||||
|
||||
try {
|
||||
await addLog(
|
||||
|
@ -77,7 +80,7 @@ async function errorHandlingMiddleware(
|
|||
uid
|
||||
);
|
||||
await db.collection<DBError>("errors").insertOne({
|
||||
_id: errorId,
|
||||
_id: new ObjectId(errorId),
|
||||
timestamp: Date.now(),
|
||||
status: monkeyResponse.status,
|
||||
uid,
|
||||
|
@ -89,7 +92,8 @@ async function errorHandlingMiddleware(
|
|||
});
|
||||
} catch (e) {
|
||||
Logger.error("Logging to db failed.");
|
||||
Logger.error(e);
|
||||
Logger.error(e.message as string);
|
||||
console.error(e);
|
||||
}
|
||||
} else {
|
||||
Logger.error(`Error: ${error.message} Stack: ${error.stack}`);
|
||||
|
@ -103,7 +107,8 @@ async function errorHandlingMiddleware(
|
|||
return;
|
||||
} catch (e) {
|
||||
Logger.error("Error handling middleware failed.");
|
||||
Logger.error(e);
|
||||
Logger.error(e.message as string);
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
handleMonkeyResponse(
|
||||
|
|
|
@ -74,7 +74,7 @@ async function bootServer(port: number): Promise<Server> {
|
|||
recordServerVersion(version);
|
||||
} catch (error) {
|
||||
Logger.error("Failed to boot server");
|
||||
Logger.error(error.message);
|
||||
Logger.error(error.message as string);
|
||||
console.error(error);
|
||||
return process.exit(1);
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ export class WeeklyXpLeaderboard {
|
|||
}
|
||||
|
||||
//TODO parse with zod?
|
||||
const parsed = JSON.parse(result ?? "null") as Omit<
|
||||
const parsed = JSON.parse((result as string) ?? "null") as Omit<
|
||||
XpLeaderboardEntry,
|
||||
"rank" | "count" | "totalXp"
|
||||
>;
|
||||
|
@ -204,7 +204,7 @@ export class WeeklyXpLeaderboard {
|
|||
return {
|
||||
rank: rank + 1,
|
||||
count: count ?? 0,
|
||||
totalXp: parseInt(totalXp, 10),
|
||||
totalXp: parseInt(totalXp as string, 10),
|
||||
...parsed,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -167,17 +167,25 @@ export class DailyLeaderboard {
|
|||
const { leaderboardScoresKey, leaderboardResultsKey } =
|
||||
this.getTodaysLeaderboardKeys();
|
||||
|
||||
// @ts-expect-error
|
||||
const [[, rank], [, count], [, result], [, minScore]] = await connection
|
||||
const redisExecResult = (await connection
|
||||
.multi()
|
||||
.zrevrank(leaderboardScoresKey, uid)
|
||||
.zcard(leaderboardScoresKey)
|
||||
.hget(leaderboardResultsKey, uid)
|
||||
.zrange(leaderboardScoresKey, 0, 0, "WITHSCORES")
|
||||
.exec();
|
||||
.exec()) as [
|
||||
[null, number | null],
|
||||
[null, number | null],
|
||||
[null, string | null],
|
||||
[null, [string, string] | null]
|
||||
];
|
||||
|
||||
const [[, rank], [, count], [, result], [, minScore]] = redisExecResult;
|
||||
|
||||
const minWpm =
|
||||
minScore.length > 0 ? parseInt(minScore[1]?.slice(1, 6)) / 100 : 0;
|
||||
minScore !== null && minScore.length > 0
|
||||
? parseInt(minScore[1]?.slice(1, 6)) / 100
|
||||
: 0;
|
||||
if (rank === null) {
|
||||
return {
|
||||
minWpm,
|
||||
|
|
|
@ -35,7 +35,8 @@ export function handleMonkeyResponse(
|
|||
//@ts-expect-error ignored so that we can see message in swagger stats
|
||||
res.monkeyMessage = message;
|
||||
if ([301, 302].includes(status)) {
|
||||
res.redirect(data);
|
||||
// todo add stronger types here, maybe a MonkeyRedirectResponse
|
||||
res.redirect(data as string);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ import * as TestStats from "../test/test-stats";
|
|||
import * as QuoteSearchModal from "../modals/quote-search";
|
||||
import * as FPSCounter from "../elements/fps-counter";
|
||||
import { migrateConfig } from "../utils/config";
|
||||
import { PartialConfigSchema } from "@monkeytype/contracts/schemas/configs";
|
||||
|
||||
const layoutsPromise = JSONData.getLayoutsList();
|
||||
layoutsPromise
|
||||
|
@ -372,7 +373,10 @@ export const commands: MonkeyTypes.CommandsSubgroup = {
|
|||
exec: async ({ input }): Promise<void> => {
|
||||
if (input === undefined || input === "") return;
|
||||
try {
|
||||
await UpdateConfig.apply(migrateConfig(JSON.parse(input)));
|
||||
const parsedConfig = PartialConfigSchema.strip().parse(
|
||||
JSON.parse(input)
|
||||
);
|
||||
await UpdateConfig.apply(migrateConfig(parsedConfig));
|
||||
UpdateConfig.saveFullConfigToLocalStorage();
|
||||
void Settings.update();
|
||||
Notifications.add("Done", 1);
|
||||
|
|
|
@ -1138,7 +1138,11 @@ $(document).on("keydown", async (event) => {
|
|||
);
|
||||
if (funbox?.functions?.preventDefaultEvent) {
|
||||
if (
|
||||
await funbox.functions.preventDefaultEvent(event as JQuery.KeyDownEvent)
|
||||
await funbox.functions.preventDefaultEvent(
|
||||
//i cant figure this type out, but it works fine
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
event as JQuery.KeyDownEvent
|
||||
)
|
||||
) {
|
||||
event.preventDefault();
|
||||
handleChar(event.key, TestInput.input.current.length);
|
||||
|
|
|
@ -205,7 +205,7 @@ export function preview(
|
|||
debouncedPreview(themeIdentifier, customColorsOverride);
|
||||
}
|
||||
|
||||
const debouncedPreview = debounce(
|
||||
const debouncedPreview = debounce<(t: string, c?: string[]) => void>(
|
||||
250,
|
||||
(themeIdenfitier, customColorsOverride) => {
|
||||
isPreviewingTheme = true;
|
||||
|
|
|
@ -796,7 +796,9 @@ export async function appendButtons(
|
|||
): void | boolean => {
|
||||
return selectBeforeChangeFn(
|
||||
"language",
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
selectedOptions,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
oldSelectedOptions
|
||||
);
|
||||
},
|
||||
|
@ -855,7 +857,9 @@ export async function appendButtons(
|
|||
): void | boolean => {
|
||||
return selectBeforeChangeFn(
|
||||
"funbox",
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
selectedOptions,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
oldSelectedOptions
|
||||
);
|
||||
},
|
||||
|
@ -910,7 +914,9 @@ export async function appendButtons(
|
|||
): void | boolean => {
|
||||
return selectBeforeChangeFn(
|
||||
"tags",
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
selectedOptions,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
oldSelectedOptions
|
||||
);
|
||||
},
|
||||
|
@ -945,7 +951,7 @@ $(".group.presetFilterButtons .filterBtns").on(
|
|||
"click",
|
||||
".filterPresets .delete-filter-preset",
|
||||
(e) => {
|
||||
void deleteFilterPreset($(e.currentTarget).data("id"));
|
||||
void deleteFilterPreset($(e.currentTarget).data("id") as string);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ export default class SettingsGroup<T extends ConfigValue> {
|
|||
return;
|
||||
}
|
||||
|
||||
const debounced = debounce(250, (val) => {
|
||||
const debounced = debounce<(val: T) => void>(250, (val) => {
|
||||
this.setValue(val);
|
||||
});
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ $(accountPage).on("click", ".group.history .resultEditTagsButton", (e) => {
|
|||
const tags = $(e.target).attr("data-tags");
|
||||
EditResultTagsModal.show(
|
||||
resultid ?? "",
|
||||
JSON.parse(tags ?? "[]"),
|
||||
JSON.parse(tags ?? "[]") as string[],
|
||||
"accountPage"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -47,7 +47,7 @@ window.onerror = function (message, url, line, column, error): void {
|
|||
window.onunhandledrejection = function (e): void {
|
||||
if (Misc.isDevEnvironment()) {
|
||||
const message = e.reason.message ?? e.reason;
|
||||
Notifications.add(message, -1, {
|
||||
Notifications.add(`${message}`, -1, {
|
||||
customTitle: "DEV: Unhandled rejection",
|
||||
duration: 5,
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { PartialConfigSchema } from "@monkeytype/contracts/schemas/configs";
|
||||
import * as UpdateConfig from "../config";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
import AnimatedModal from "../utils/animated-modal";
|
||||
|
@ -46,10 +47,20 @@ const modal = new AnimatedModal({
|
|||
return;
|
||||
}
|
||||
try {
|
||||
await UpdateConfig.apply(migrateConfig(JSON.parse(state.value)));
|
||||
const parsedConfig = PartialConfigSchema.strip().parse(
|
||||
JSON.parse(state.value)
|
||||
);
|
||||
await UpdateConfig.apply(migrateConfig(parsedConfig));
|
||||
} catch (e) {
|
||||
Notifications.add("Failed to import settings: " + e, -1);
|
||||
Notifications.add(
|
||||
"Failed to import settings: incorrect data schema",
|
||||
0
|
||||
);
|
||||
console.error(e);
|
||||
void modal.hide();
|
||||
return;
|
||||
}
|
||||
Notifications.add("Settings imported", 1);
|
||||
UpdateConfig.saveFullConfigToLocalStorage();
|
||||
void modal.hide();
|
||||
});
|
||||
|
|
|
@ -34,7 +34,9 @@ function hide(): void {
|
|||
|
||||
function save(): boolean {
|
||||
const name = $("#saveCustomTextModal .textName").val() as string;
|
||||
const checkbox = $("#saveCustomTextModal .isLongText").prop("checked");
|
||||
const checkbox = $("#saveCustomTextModal .isLongText").prop(
|
||||
"checked"
|
||||
) as boolean;
|
||||
|
||||
if (!name) {
|
||||
Notifications.add("Custom text needs a name", 0);
|
||||
|
@ -54,7 +56,9 @@ function save(): boolean {
|
|||
|
||||
function updateIndicatorAndButton(): void {
|
||||
const val = $("#saveCustomTextModal .textName").val() as string;
|
||||
const checkbox = $("#saveCustomTextModal .isLongText").prop("checked");
|
||||
const checkbox = $("#saveCustomTextModal .isLongText").prop(
|
||||
"checked"
|
||||
) as boolean;
|
||||
|
||||
if (!val) {
|
||||
indicator?.hide();
|
||||
|
|
|
@ -6,6 +6,7 @@ import AnimatedModal, {
|
|||
ShowOptions,
|
||||
} from "../utils/animated-modal";
|
||||
import { showPopup } from "./simple-modals";
|
||||
import * as Notifications from "../elements/notifications";
|
||||
|
||||
async function fill(): Promise<void> {
|
||||
const names = CustomText.getCustomTextNames();
|
||||
|
@ -46,7 +47,13 @@ async function fill(): Promise<void> {
|
|||
longListEl.html(longList);
|
||||
|
||||
$("#savedTextsModal .list .savedText .button.delete").on("click", (e) => {
|
||||
const name = $(e.target).closest(".savedText").data("name");
|
||||
const name = $(e.target).closest(".savedText").data("name") as
|
||||
| string
|
||||
| undefined;
|
||||
if (name === undefined) {
|
||||
Notifications.add("Failed to show delete modal: no name found", -1);
|
||||
return;
|
||||
}
|
||||
showPopup("deleteCustomText", [name], {
|
||||
modalChain: modal as AnimatedModal<unknown, unknown>,
|
||||
});
|
||||
|
@ -55,7 +62,13 @@ async function fill(): Promise<void> {
|
|||
$("#savedTextsModal .listLong .savedLongText .button.delete").on(
|
||||
"click",
|
||||
(e) => {
|
||||
const name = $(e.target).closest(".savedLongText").data("name");
|
||||
const name = $(e.target).closest(".savedLongText").data("name") as
|
||||
| string
|
||||
| undefined;
|
||||
if (name === undefined) {
|
||||
Notifications.add("Failed to show delete modal: no name found", -1);
|
||||
return;
|
||||
}
|
||||
showPopup("deleteCustomTextLong", [name], {
|
||||
modalChain: modal as AnimatedModal<unknown, unknown>,
|
||||
});
|
||||
|
@ -65,7 +78,13 @@ async function fill(): Promise<void> {
|
|||
$("#savedTextsModal .listLong .savedLongText .button.resetProgress").on(
|
||||
"click",
|
||||
(e) => {
|
||||
const name = $(e.target).closest(".savedLongText").data("name");
|
||||
const name = $(e.target).closest(".savedLongText").data("name") as
|
||||
| string
|
||||
| undefined;
|
||||
if (name === undefined) {
|
||||
Notifications.add("Failed to show delete modal: no name found", -1);
|
||||
return;
|
||||
}
|
||||
showPopup("resetProgressCustomTextLong", [name], {
|
||||
modalChain: modal as AnimatedModal<unknown, unknown>,
|
||||
});
|
||||
|
|
|
@ -1217,7 +1217,7 @@ $(".pageAccount .group.presetFilterButtons").on(
|
|||
"click",
|
||||
".filterBtns .filterPresets .select-filter-preset",
|
||||
async (e) => {
|
||||
await ResultFilters.setFilterPreset($(e.target).data("id"));
|
||||
await ResultFilters.setFilterPreset($(e.target).data("id") as string);
|
||||
void update();
|
||||
}
|
||||
);
|
||||
|
|
|
@ -58,7 +58,7 @@ export async function replace(word: string): Promise<string> {
|
|||
(_, $1, $2, $3) =>
|
||||
$1 +
|
||||
($2.charAt(0) === $2.charAt(0).toUpperCase()
|
||||
? shouldWholeReplacementWordBeCapitalised($2)
|
||||
? shouldWholeReplacementWordBeCapitalised($2 as string)
|
||||
? randomReplacement.toUpperCase()
|
||||
: capitalizeFirstLetterOfEachWord(randomReplacement)
|
||||
: randomReplacement) +
|
||||
|
|
|
@ -325,9 +325,9 @@ $("#replayWords").on("click", "letter", (event) => {
|
|||
const replayWords = document.querySelector("#replayWords");
|
||||
|
||||
const words = [...(replayWords?.children ?? [])];
|
||||
targetWordPos = words.indexOf(event.target.parentNode);
|
||||
targetWordPos = words.indexOf(event.target.parentNode as HTMLElement);
|
||||
const letters = [...(words[targetWordPos] as HTMLElement).children];
|
||||
targetCurPos = letters.indexOf(event.target);
|
||||
targetCurPos = letters.indexOf(event.target as HTMLElement);
|
||||
|
||||
initializeReplayPrompt();
|
||||
loadOldReplay();
|
||||
|
|
|
@ -1054,7 +1054,9 @@ export async function finish(difficultyFailed = false): Promise<void> {
|
|||
|
||||
$("#result .stats .dailyLeaderboard").addClass("hidden");
|
||||
|
||||
TestStats.setLastResult(JSON.parse(JSON.stringify(completedEvent)));
|
||||
TestStats.setLastResult(
|
||||
JSON.parse(JSON.stringify(completedEvent)) as CompletedEvent
|
||||
);
|
||||
|
||||
if (!ConnectionState.get()) {
|
||||
ConnectionState.showOfflineBanner();
|
||||
|
|
|
@ -12,7 +12,16 @@ import * as AccountButton from "../elements/account-button";
|
|||
import { restart as restartTest } from "../test/test-logic";
|
||||
import * as ChallengeController from "../controllers/challenge-controller";
|
||||
import { Mode, Mode2 } from "@monkeytype/contracts/schemas/shared";
|
||||
import { Difficulty } from "@monkeytype/contracts/schemas/configs";
|
||||
import {
|
||||
CustomBackgroundFilter,
|
||||
CustomBackgroundFilterSchema,
|
||||
CustomBackgroundSize,
|
||||
CustomBackgroundSizeSchema,
|
||||
CustomThemeColors,
|
||||
CustomThemeColorsSchema,
|
||||
Difficulty,
|
||||
} from "@monkeytype/contracts/schemas/configs";
|
||||
import { z } from "zod";
|
||||
|
||||
export async function linkDiscord(hashOverride: string): Promise<void> {
|
||||
if (!hashOverride) return;
|
||||
|
@ -64,12 +73,33 @@ export function loadCustomThemeFromUrl(getOverride?: string): void {
|
|||
try {
|
||||
decoded = JSON.parse(atob(getValue));
|
||||
} catch (e) {
|
||||
Notifications.add("Invalid custom theme ", 0);
|
||||
console.log("Custom theme URL decoding failed", e);
|
||||
Notifications.add(
|
||||
"Failed to load theme from URL: could not decode theme",
|
||||
0
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let colorArray = [];
|
||||
let image, size, filter;
|
||||
const decodedSchema = z.object({
|
||||
c: CustomThemeColorsSchema,
|
||||
i: z.string().optional(),
|
||||
s: CustomBackgroundSizeSchema.optional(),
|
||||
f: CustomBackgroundFilterSchema.optional(),
|
||||
});
|
||||
|
||||
const parsed = decodedSchema.safeParse(decoded);
|
||||
if (!parsed.success) {
|
||||
Notifications.add("Failed to load theme from URL: invalid data schema", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
decoded = parsed.data;
|
||||
|
||||
let colorArray: CustomThemeColors | undefined;
|
||||
let image: string | undefined;
|
||||
let size: CustomBackgroundSize | undefined;
|
||||
let filter: CustomBackgroundFilter | undefined;
|
||||
if (Array.isArray(decoded.c) && decoded.c.length === 10) {
|
||||
colorArray = decoded.c;
|
||||
image = decoded.i;
|
||||
|
@ -77,11 +107,11 @@ export function loadCustomThemeFromUrl(getOverride?: string): void {
|
|||
filter = decoded.f;
|
||||
} else if (Array.isArray(decoded) && decoded.length === 10) {
|
||||
// This is for backward compatibility with old format
|
||||
colorArray = decoded;
|
||||
colorArray = decoded as unknown as CustomThemeColors;
|
||||
}
|
||||
|
||||
if (colorArray.length === 0) {
|
||||
Notifications.add("Invalid custom theme ", 0);
|
||||
if (colorArray === undefined || colorArray.length !== 10) {
|
||||
Notifications.add("Failed to load theme from URL: no colors found", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +121,7 @@ export function loadCustomThemeFromUrl(getOverride?: string): void {
|
|||
UpdateConfig.setCustomThemeColors(colorArray);
|
||||
Notifications.add("Custom theme applied", 1);
|
||||
|
||||
if (image !== undefined) {
|
||||
if (image !== undefined && size !== undefined && filter !== undefined) {
|
||||
UpdateConfig.setCustomBackground(image);
|
||||
UpdateConfig.setCustomBackgroundSize(size);
|
||||
UpdateConfig.setCustomBackgroundFilter(filter);
|
||||
|
|
|
@ -81,11 +81,11 @@ module.exports = {
|
|||
|
||||
// TODO: enable at some point
|
||||
"@typescript-eslint/no-unsafe-assignment": "off", //~63
|
||||
"@typescript-eslint/no-unsafe-argument": "off", //~37
|
||||
"@typescript-eslint/no-unsafe-call": "off", //~76
|
||||
"@typescript-eslint/no-unsafe-member-access": "off", //~105
|
||||
//
|
||||
|
||||
"@typescript-eslint/no-unsafe-argument": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/no-confusing-void-expression": [
|
||||
"error",
|
||||
|
|
Loading…
Reference in a new issue