${this.validationError
? html`
${this.validationError}
`
: null}
`;
}
private handleInput(e: Event) {
const input = e.target as HTMLInputElement;
const sanitized = sanitizeClanTag(input.value);
if (input.value.toUpperCase() !== sanitized) {
window.dispatchEvent(
new CustomEvent("show-message", {
detail: {
message: translateText("username.tag_invalid_chars"),
color: "red",
duration: 2000,
},
}),
);
}
input.value = sanitized;
this.clanTag = sanitized;
this.validate();
}
private validate() {
const tag = this.clanTag;
const result = validateClanTag(tag);
this.formatError = result.isValid ? "" : (result.error ?? "");
// Cancel any pending/in-flight ownership check. checkCounter++ marks
// any in-flight async work obsolete (stillCurrent() in checkOwnership
// returns false). Resolve the prior debounce so awaitValidation()
// callers don't hang on the cancelled chain.
if (this.checkTimer !== null) clearTimeout(this.checkTimer);
this.checkTimer = null;
this.checkCounter++;
if (this.resolveDebounce) this.resolveDebounce();
this.resolveDebounce = null;
if (!result.isValid || tag.length === 0) {
// Nothing to ask the server about — clear any old ownership error
// and wipe the stored tag so a reload doesn't restore a stale value
// that no longer matches the current (invalid/empty) input.
this.ownershipError = "";
localStorage.setItem(clanTagKey, "");
this.currentCheck = Promise.resolve();
} else {
const debounce = new Promise