feat(frontend): validate username and member name client-side too

This commit is contained in:
Sam 2023-03-27 01:23:04 +02:00
parent 6532393578
commit 5be0b168c5
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
3 changed files with 37 additions and 13 deletions

View file

@ -75,7 +75,7 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error
if !db.MemberNameValid(cmr.Name) {
return server.APIError{
Code: server.ErrBadRequest,
Details: "Member name cannot contain any of the following: @, \\, ?, !, #, /, \\, [, ], \", ', $, %, &, (, ), +, <, =, >, ^, |, ~, `, ,",
Details: "Member name cannot contain any of the following: @, ?, !, #, /, \\, [, ], \", ', $, %, &, (, ), +, <, =, >, ^, |, ~, `, ,",
}
}

View file

@ -58,10 +58,13 @@
memberPage = memberPage + 1;
};
const memberNameRegex = /^[^@\\?!#\/\\\\[\]\"'$%&()+<=>^|~`,]{1,100}$/;
let modalOpen = false;
let toggleModal = () => (modalOpen = !modalOpen);
let newMemberName = "";
let newMemberError: APIError | null = null;
let memberNameValid = true;
$: memberNameValid = memberNameRegex.test(newMemberName);
const createMember = async () => {
try {
@ -210,10 +213,8 @@
<p>
You don't have any members yet.
<br />
Members are sub-profiles that can have their own avatar,
names, pronouns, and preferred terms. <span class="text-muted"
>(only you can see this)</span
>
Members are sub-profiles that can have their own avatar, names, pronouns, and preferred terms.
<span class="text-muted">(only you can see this)</span>
</p>
</div>
{/if}
@ -221,12 +222,20 @@
<Modal header="Create member" isOpen={modalOpen} toggle={toggleModal}>
<ModalBody>
<Input bind:value={newMemberName} />
<p class="text-muted my-2">
<Icon name="info-circle-fill" aria-label="Info" /> Your members must have distinct names. Member
names must be 100 characters long at most, and cannot contain the following characters: @ ?
! # / \ [ ] " ' $ % & ( ) + &lt; = &gt; ^ | ~ ` and ,
</p>
{#if newMemberError}
<ErrorAlert error={newMemberError} />
{/if}
</ModalBody>
<ModalFooter>
<Button color="primary" on:click={createMember} disabled={newMemberName.length === 0}
{#if !memberNameValid && newMemberName.length > 0}
<span class="text-danger-emphasis mb-2">That member name is not valid.</span>
{/if}
<Button color="primary" on:click={createMember} disabled={!memberNameValid}
>Create member</Button
>
<Button color="secondary" on:click={toggleModal}>Cancel</Button>

View file

@ -43,7 +43,11 @@
let deleteCancelled: boolean;
let deleteError: APIError | null;
const usernameRegex = /^[\w-.]{2,40}$/;
let username: string;
let usernameValid = true;
$: usernameValid = usernameRegex.test(username);
let inviteCode: string;
let forceDeleteName = "";
let forceDeleteModalOpen = false;
@ -88,10 +92,10 @@
</script>
<svelte:head>
<title>Log in with the {authType} - pronouns.cc</title>
<title>Log in with {authType} - pronouns.cc</title>
</svelte:head>
<h1>Log in with the {authType}</h1>
<h1>Log in with {authType}</h1>
{#if error}
<ErrorAlert {error} />
@ -122,23 +126,34 @@
<FormGroup floating label="Username">
<Input id="username" name="username" bind:value={username} />
</FormGroup>
<div id="username-help" class="text-muted">
<Icon name="info-circle-fill" aria-label="Info" /> Your username must be unique, be at most 40
characters long, and only contain letters from the basic English alphabet, dashes, underscores,
and periods. Your username is used as part of your profile link, you can set a separate display
name.
</div>
</div>
{#if requireInvite}
<div>
<FormGroup floating label="Invite code">
<Input id="invite" name="invite" aria-describedby="invite-help" bind:value={inviteCode} />
</FormGroup>
<div id="invite-help" class="form-text">
<Icon name="info-circle-fill" /> You currently need an invite code to sign up. You can get
one from an existing user.
<div id="invite-help" class="text-muted">
<Icon name="info-circle-fill" aria-label="Info" /> You currently need an invite code to sign
up. You can get one from an existing user.
</div>
</div>
{/if}
<div class="form-text mb-1">
<div class="text-muted my-1">
By signing up, you agree to the <a href="/page/terms">terms of service</a> and the
<a href="/page/privacy">privacy policy</a>.
</div>
<Button type="submit" color="primary">Sign up</Button>
<p>
<Button type="submit" color="primary" disabled={!usernameValid}>Sign up</Button>
{#if !usernameValid && username.length > 0}
<span class="text-danger-emphasis mb-2">That username is not valid.</span>
{/if}
</p>
</form>
{:else if isDeleted && token && selfDelete && deletedAt}
<p>