mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-26 09:20:24 -05:00
149 lines
3.5 KiB
TypeScript
149 lines
3.5 KiB
TypeScript
import { countries } from "countries-list";
|
|
import { z } from "zod";
|
|
import { USER } from "~/constants";
|
|
import "~/styles/u-edit.css";
|
|
import { BADGE } from "~/features/badges/badges-contants";
|
|
import { isCustomUrl } from "~/utils/urls";
|
|
import {
|
|
actualNumber,
|
|
checkboxValueToDbBoolean,
|
|
customCssVarObject,
|
|
dbBoolean,
|
|
emptyArrayToNull,
|
|
falsyToNull,
|
|
id,
|
|
processMany,
|
|
safeJSONParse,
|
|
safeNullableStringSchema,
|
|
undefinedToNull,
|
|
weaponSplId,
|
|
} from "~/utils/zod";
|
|
import { allSeasons } from "../mmr/season";
|
|
import {
|
|
HIGHLIGHT_CHECKBOX_NAME,
|
|
HIGHLIGHT_TOURNAMENT_CHECKBOX_NAME,
|
|
} from "./components/UserResultsTable";
|
|
|
|
export const userParamsSchema = z.object({ identifier: z.string() });
|
|
|
|
export const seasonsSearchParamsSchema = z.object({
|
|
page: z.coerce.number().optional(),
|
|
info: z.enum(["weapons", "stages", "mates", "enemies"]).optional(),
|
|
season: z.coerce
|
|
.number()
|
|
.optional()
|
|
.refine((nth) => !nth || allSeasons(new Date()).includes(nth)),
|
|
});
|
|
|
|
export const userEditActionSchema = z
|
|
.object({
|
|
country: z.preprocess(
|
|
falsyToNull,
|
|
z
|
|
.string()
|
|
.refine(
|
|
(val) => !val || Object.keys(countries).some((code) => val === code),
|
|
)
|
|
.nullable(),
|
|
),
|
|
bio: z.preprocess(
|
|
falsyToNull,
|
|
z.string().max(USER.BIO_MAX_LENGTH).nullable(),
|
|
),
|
|
customUrl: z.preprocess(
|
|
falsyToNull,
|
|
z
|
|
.string()
|
|
.max(USER.CUSTOM_URL_MAX_LENGTH)
|
|
.refine((val) => val === null || isCustomUrl(val), {
|
|
message: "forms.errors.invalidCustomUrl.numbers",
|
|
})
|
|
.refine((val) => val === null || /^[a-zA-Z0-9-_]+$/.test(val), {
|
|
message: "forms.errors.invalidCustomUrl.strangeCharacter",
|
|
})
|
|
.transform((val) => val?.toLowerCase())
|
|
.nullable(),
|
|
),
|
|
customName: safeNullableStringSchema({ max: USER.CUSTOM_NAME_MAX_LENGTH }),
|
|
battlefy: z.preprocess(
|
|
falsyToNull,
|
|
z.string().max(USER.BATTLEFY_MAX_LENGTH).nullable(),
|
|
),
|
|
stickSens: z.preprocess(
|
|
processMany(actualNumber, undefinedToNull),
|
|
z
|
|
.number()
|
|
.min(-50)
|
|
.max(50)
|
|
.refine((val) => val % 5 === 0)
|
|
.nullable(),
|
|
),
|
|
motionSens: z.preprocess(
|
|
processMany(actualNumber, undefinedToNull),
|
|
z
|
|
.number()
|
|
.min(-50)
|
|
.max(50)
|
|
.refine((val) => val % 5 === 0)
|
|
.nullable(),
|
|
),
|
|
inGameNameText: z.preprocess(
|
|
falsyToNull,
|
|
z.string().max(USER.IN_GAME_NAME_TEXT_MAX_LENGTH).nullable(),
|
|
),
|
|
inGameNameDiscriminator: z.preprocess(
|
|
falsyToNull,
|
|
z
|
|
.string()
|
|
.refine((val) => /^[0-9a-z]{4,5}$/.test(val))
|
|
.nullable(),
|
|
),
|
|
css: customCssVarObject,
|
|
weapons: z.preprocess(
|
|
safeJSONParse,
|
|
z
|
|
.array(
|
|
z.object({
|
|
weaponSplId,
|
|
isFavorite: dbBoolean,
|
|
}),
|
|
)
|
|
.max(USER.WEAPON_POOL_MAX_SIZE),
|
|
),
|
|
favoriteBadgeIds: z.preprocess(
|
|
processMany(safeJSONParse, emptyArrayToNull),
|
|
z
|
|
.array(id)
|
|
.min(1)
|
|
.max(BADGE.SMALL_BADGES_PER_DISPLAY_PAGE + 1)
|
|
.nullable(),
|
|
),
|
|
showDiscordUniqueName: z.preprocess(checkboxValueToDbBoolean, dbBoolean),
|
|
commissionsOpen: z.preprocess(checkboxValueToDbBoolean, dbBoolean),
|
|
commissionText: z.preprocess(
|
|
falsyToNull,
|
|
z.string().max(USER.COMMISSION_TEXT_MAX_LENGTH).nullable(),
|
|
),
|
|
})
|
|
.refine(
|
|
(val) => {
|
|
if (val.motionSens !== null && val.stickSens === null) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
{
|
|
message: "forms.errors.invalidSens",
|
|
},
|
|
);
|
|
|
|
export const editHighlightsActionSchema = z.object({
|
|
[HIGHLIGHT_CHECKBOX_NAME]: z.optional(
|
|
z.union([z.array(z.string()), z.string()]),
|
|
),
|
|
[HIGHLIGHT_TOURNAMENT_CHECKBOX_NAME]: z.optional(
|
|
z.union([z.array(z.string()), z.string()]),
|
|
),
|
|
});
|