From 1b78b9ae8aaf0ae9e8a7b318f02607b1a31e287a Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Wed, 13 May 2026 14:24:17 +0200 Subject: [PATCH 1/2] fix: don't check for disallowed words when dealing with existing user names (@fehmer) --- frontend/src/ts/commandline/lists/navigation.ts | 4 ++-- .../ts/components/pages/profile/ProfileSearchPage.tsx | 4 ++-- frontend/src/ts/pages/friends.ts | 4 ++-- frontend/src/ts/utils/misc.ts | 7 +------ packages/schemas/src/users.ts | 11 ++++++++++- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/frontend/src/ts/commandline/lists/navigation.ts b/frontend/src/ts/commandline/lists/navigation.ts index 5f61c557a756..30fea09f3901 100644 --- a/frontend/src/ts/commandline/lists/navigation.ts +++ b/frontend/src/ts/commandline/lists/navigation.ts @@ -3,7 +3,7 @@ import { isAuthenticated } from "../../states/core"; import { toggleFullscreen } from "../../utils/misc"; import { Command, withValidation } from "../types"; import { remoteValidation } from "../../utils/remote-validation"; -import { UserNameSchema } from "@monkeytype/schemas/users"; +import { UserNameWithoutFilterSchema } from "@monkeytype/schemas/users"; import Ape from "../../ape"; const commands: Command[] = [ @@ -60,7 +60,7 @@ const commands: Command[] = [ icon: "fa-search", input: true, validation: { - schema: UserNameSchema, + schema: UserNameWithoutFilterSchema, debounceDelay: 1000, isValid: remoteValidation( async (name) => Ape.users.getProfile({ params: { uidOrName: name } }), diff --git a/frontend/src/ts/components/pages/profile/ProfileSearchPage.tsx b/frontend/src/ts/components/pages/profile/ProfileSearchPage.tsx index d3989caae5e5..7fe44eae60d7 100644 --- a/frontend/src/ts/components/pages/profile/ProfileSearchPage.tsx +++ b/frontend/src/ts/components/pages/profile/ProfileSearchPage.tsx @@ -1,4 +1,4 @@ -import { UserNameSchema } from "@monkeytype/schemas/users"; +import { UserNameWithoutFilterSchema } from "@monkeytype/schemas/users"; import { createForm } from "@tanstack/solid-form"; import { createEffect, createSignal, JSXElement, Show } from "solid-js"; @@ -70,7 +70,7 @@ export function ProfileSearchPage(): JSXElement { { try { diff --git a/frontend/src/ts/pages/friends.ts b/frontend/src/ts/pages/friends.ts index c485ea43890f..4da6ef1f5185 100644 --- a/frontend/src/ts/pages/friends.ts +++ b/frontend/src/ts/pages/friends.ts @@ -31,7 +31,7 @@ import { getAuthenticatedUser } from "../firebase"; import * as ServerConfiguration from "../ape/server-configuration"; import { authEvent } from "../events/auth"; import { Connection } from "@monkeytype/schemas/connections"; -import { Friend, UserNameSchema } from "@monkeytype/schemas/users"; +import { UserNameWithoutFilterSchema, Friend } from "@monkeytype/schemas/users"; import { showLoaderBar, hideLoaderBar } from "../states/loader-bar"; import { LocalStorageWithSchema } from "../utils/local-storage-with-schema"; @@ -52,7 +52,7 @@ const addFriendModal = new SimpleModal({ type: "text", initVal: "", validation: { - schema: UserNameSchema, + schema: UserNameWithoutFilterSchema, isValid: remoteValidation( async (name) => Ape.users.getNameAvailability({ params: { name } }), { check: (data) => !data.available || "Unknown user" }, diff --git a/frontend/src/ts/utils/misc.ts b/frontend/src/ts/utils/misc.ts index cd8b132620d3..85dea5657d0f 100644 --- a/frontend/src/ts/utils/misc.ts +++ b/frontend/src/ts/utils/misc.ts @@ -2,7 +2,7 @@ import { lastElementFromArray } from "./arrays"; import { Config } from "@monkeytype/schemas/configs"; import { Mode, Mode2, PersonalBests } from "@monkeytype/schemas/shared"; import { Result } from "@monkeytype/schemas/results"; -import { RankAndCount, UserNameSchema } from "@monkeytype/schemas/users"; +import { RankAndCount } from "@monkeytype/schemas/users"; import { roundTo2 } from "@monkeytype/util/numbers"; import { animate, AnimationParams } from "animejs"; import { ElementWithUtils } from "./dom"; @@ -145,11 +145,6 @@ export function escapeHTML(str: T): T { return str.replace(/[&<>"'/`]/g, (char) => escapeMap[char] as string) as T; } -export function isUsernameValid(name: string): boolean { - if (name === null || name === undefined || name === "") return false; - return UserNameSchema.safeParse(name).success; -} - export function clearTimeouts(timeouts: (number | NodeJS.Timeout)[]): void { timeouts.forEach((to) => { if (typeof to === "number") clearTimeout(to); diff --git a/packages/schemas/src/users.ts b/packages/schemas/src/users.ts index e17cd691d631..c58e8c1ef1a5 100644 --- a/packages/schemas/src/users.ts +++ b/packages/schemas/src/users.ts @@ -235,9 +235,18 @@ export const FavoriteQuotesSchema = z.record( export type FavoriteQuotes = z.infer; export const UserEmailSchema = z.string().email(); + +/** + * username schema without profanity check + */ +export const UserNameWithoutFilterSchema = slug().min(1).max(15); + +/** + * username schema with profanity check + */ export const UserNameSchema = doesNotContainDisallowedWords( "substring", - slug().min(1).max(16), + UserNameWithoutFilterSchema, ); export const UserSchema = z.object({ From 013b881ef9b30706d32caaac5e53b54e5c527b3e Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Wed, 13 May 2026 14:29:50 +0200 Subject: [PATCH 2/2] derp --- packages/schemas/src/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/schemas/src/users.ts b/packages/schemas/src/users.ts index c58e8c1ef1a5..a02ef4783f0d 100644 --- a/packages/schemas/src/users.ts +++ b/packages/schemas/src/users.ts @@ -239,7 +239,7 @@ export const UserEmailSchema = z.string().email(); /** * username schema without profanity check */ -export const UserNameWithoutFilterSchema = slug().min(1).max(15); +export const UserNameWithoutFilterSchema = slug().min(1).max(16); /** * username schema with profanity check