Skip to content

Commit

Permalink
feat(oauth-client-browser): simplify login ux & allow popup mode
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieusieben committed Apr 24, 2024
1 parent cd6d324 commit 3def7cb
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 31 deletions.
6 changes: 1 addition & 5 deletions packages/oauth-client-browser-example/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ function App() {
<button onClick={signOut}>Logout</button>
</div>
) : (
<LoginForm
error={error}
loading={loading}
onLogin={(input) => void signIn(input)}
/>
<LoginForm error={error} loading={loading} onLogin={signIn} />
)
}

Expand Down
78 changes: 52 additions & 26 deletions packages/oauth-client-browser-example/src/login-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FormEvent, useState } from 'react'
import { FormEvent, useEffect, useState } from 'react'

/**
* @returns Nice tailwind css form asking to enter either a handle or the host
Expand All @@ -7,61 +7,85 @@ import { FormEvent, useState } from 'react'
export default function LoginForm({
onLogin,
loading,
error,
error = null,
...props
}: {
loading?: boolean
error?: null | string
onLogin: (input: string) => void
onLogin: (input: string, options?: { display?: 'popup' | 'page' }) => void
} & React.HTMLAttributes<HTMLFormElement>) {
const [value, setValue] = useState('')
const [loginType, setLoginType] = useState<'handle' | 'host'>('handle')
const [display, setDisplay] = useState<'popup' | 'page'>('popup')
const [localError, setLocalError] = useState<string | null>(error)

useEffect(() => {
setLocalError(null)
}, [value])

useEffect(() => {
setLocalError(error)
}, [error])

const onSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (loading) return

onLogin(
loginType === 'host' && !/^https?:\/\//.test(value)
? `https://${value}`
: value,
)
if (value.startsWith('did:')) {
if (value.length > 5) onLogin(value, { display })
else setLocalError('DID must be at least 6 characters')
return
}

if (value.startsWith('https://')) {
try {
const url = new URL(value)
if (value !== url.origin) throw new Error('PDS URL must be a origin')
onLogin(value, { display })
} catch (err) {
setLocalError((err as any)?.message || String(err))
}
return
}

if (value.startsWith('http://')) {
setLocalError('PDS URL must be a secure origin')
return
}

if (value.includes('.') && value.length > 3) {
const handle = value.startsWith('@') ? value.slice(1) : value
if (handle.length > 3) onLogin(handle, { display })
else setLocalError('Handle must be at least 4 characters')
return
}

setLocalError('Please provide a valid handle, DID or PDS URL')
}

return (
<form {...props} className="max-w-lg w-full m-4" onSubmit={onSubmit}>
<fieldset className="rounded-md border border-solid border-slate-200 dark:border-slate-700 text-neutral-700 dark:text-neutral-100">
<div className="relative p-1 flex flex-wrap items-center justify-stretch">
<select
value={loginType}
value={display}
className="border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 focus:ring-inset"
onChange={(e) => setLoginType(e.target.value as 'handle' | 'host')}
onChange={(e) => setDisplay(e.target.value as 'popup' | 'page')}
>
<option value="handle">Login using you handle or DID</option>
<option value="host">Login using your PDS host</option>
<option value="popup">Login in pop-up window</option>
<option value="page">Login using redirects</option>
</select>
</div>

{/* <hr className="border-slate-200 dark:border-slate-700" /> */}

<div className="relative p-1 flex flex-wrap items-center justify-stretch">
<label
htmlFor="value"
className="w-8 text-center text-base leading-[1.6]"
>
@
</label>
<input
id="value"
name="value"
type="text"
className="relative m-0 block w-[1px] min-w-0 flex-auto px-3 py-[0.25rem] leading-[1.6] bg-transparent bg-clip-padding text-base text-inherit outline-none dark:placeholder:text-neutral-100"
placeholder={
loginType === 'host' ? 'PDS host name' : 'Handle or DID'
}
aria-label={
loginType === 'host' ? 'PDS host name' : 'Handle or DID'
}
placeholder="@handle, DID or PDS url"
aria-label="@handle, DID or PDS url"
required
value={value}
onChange={(e) => setValue(e.target.value)}
Expand All @@ -76,7 +100,9 @@ export default function LoginForm({
</div>
</fieldset>

{error ? <div className="alert alert-error">{error}</div> : null}
{localError ? (
<div className="alert alert-error">{localError}</div>
) : null}
</form>
)
}

0 comments on commit 3def7cb

Please sign in to comment.