Files
OpenFrontIO/src/client/UsernameInput.ts
T
Scott Anderson 0a40bcebf0 Enable the sort-imports eslint rule (#1849)
## Description:

Enable the `sort-imports` eslint rule.

Fixes #1784

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
2025-08-17 20:53:44 -04:00

119 lines
3.3 KiB
TypeScript

import { LitElement, html } from "lit";
import {
MAX_USERNAME_LENGTH,
validateUsername,
} from "../core/validations/username";
import { customElement, property, state } from "lit/decorators.js";
import { UserSettings } from "../core/game/UserSettings";
import { translateText } from "../client/Utils";
import { v4 as uuidv4 } from "uuid";
const usernameKey = "username";
@customElement("username-input")
export class UsernameInput extends LitElement {
@state() private username = "";
@property({ type: String }) validationError = "";
private _isValid = true;
private userSettings: UserSettings = new UserSettings();
// Remove static styles since we're using Tailwind
createRenderRoot() {
// Disable shadow DOM to allow Tailwind classes to work
return this;
}
public getCurrentUsername(): string {
return this.username;
}
connectedCallback() {
super.connectedCallback();
this.username = this.getStoredUsername();
this.dispatchUsernameEvent();
}
render() {
return html`
<input
type="text"
.value=${this.username}
@input=${this.handleChange}
@change=${this.handleChange}
placeholder="${translateText("username.enter_username")}"
maxlength="${MAX_USERNAME_LENGTH}"
class="w-full px-4 py-2 border border-gray-300 rounded-xl shadow-sm
text-2xl text-center focus:outline-none focus:ring-2
focus:ring-blue-500 focus:border-blue-500 dark:border-gray-300/60
dark:bg-gray-700 dark:text-white"
/>
${this.validationError
? html`<div
id="username-validation-error"
class="absolute z-10 w-full mt-2 px-3 py-1 text-lg border rounded
bg-white text-red-600 border-red-600 dark:bg-gray-700
dark:text-red-300 dark:border-red-300"
>
${this.validationError}
</div>`
: null}
`;
}
private handleChange(e: Event) {
const input = e.target as HTMLInputElement;
this.username = input.value.trim();
const result = validateUsername(this.username);
this._isValid = result.isValid;
if (result.isValid) {
this.storeUsername(this.username);
this.validationError = "";
} else {
this.validationError = result.error ?? "";
}
}
private getStoredUsername(): string {
const storedUsername = localStorage.getItem(usernameKey);
if (storedUsername) {
return storedUsername;
}
return this.generateNewUsername();
}
private storeUsername(username: string) {
if (username) {
localStorage.setItem(usernameKey, username);
}
}
private dispatchUsernameEvent() {
this.dispatchEvent(
new CustomEvent("username-change", {
detail: { username: this.username },
bubbles: true,
composed: true,
}),
);
}
private generateNewUsername(): string {
const newUsername = "Anon" + this.uuidToThreeDigits();
this.storeUsername(newUsername);
return newUsername;
}
private uuidToThreeDigits(): string {
const uuid = uuidv4();
const cleanUuid = uuid.replace(/-/g, "").toLowerCase();
const decimal = BigInt(`0x${cleanUuid}`);
const threeDigits = decimal % 1000n;
return threeDigits.toString().padStart(3, "0");
}
public isValid(): boolean {
return this._isValid;
}
}