Skip to content

Commit

Permalink
feat: let the user register spaces that didn't get registered
Browse files Browse the repository at this point in the history
Sometimes a space fails to register - before this patch if you
clicked on one of those spaces it would look like you logged
out.

With these changes we now give the user an opportunity to
register the space so that they can start uploading files to it.
  • Loading branch information
travis committed Jan 19, 2023
1 parent 8f1e7a2 commit e887d52
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 23 deletions.
1 change: 1 addition & 0 deletions examples/react/w3console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@w3ui/keyring-core": "workspace:^2.0.1",
"@w3ui/react": "workspace:^",
"@w3ui/react-keyring": "workspace:^",
"@w3ui/react-uploads-list": "workspace:^2.0.1",
Expand Down
153 changes: 136 additions & 17 deletions examples/react/w3console/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,159 @@
import { ChangeEvent, useEffect, useState } from 'react'
import type { Space } from '@w3ui/keyring-core'

import { Authenticator, Uploader, UploadsList, W3APIProvider } from '@w3ui/react'
import { useKeyring } from '@w3ui/react-keyring'
import { useUploadsList } from '@w3ui/react-uploads-list'
import md5 from 'blueimp-md5'
import '@w3ui/react/src/styles/uploader.css'

function Space (): JSX.Element {
function SpaceRegistrar (): JSX.Element {
const [, { registerSpace }] = useKeyring()
const [email, setEmail] = useState('')
const [submitted, setSubmitted] = useState(false)
async function onSubmit (e: React.FormEvent<HTMLFormElement>): Promise<void> {
e.preventDefault()
setSubmitted(true)
try {
await registerSpace(email)
} catch (err) {
console.log(err)
throw new Error('failed to register', { cause: err })
} finally {
setSubmitted(false)
}
}
return (
<div>
{submitted
? (
<p>
Please check your email for a verification email.
</p>
)
: (
<>
<p>
Before you upload files, you must register this space.
</p>
<form onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}>
<input
type='email' placeholder='Email' autofocus
value={email}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setEmail(e.target.value) }}
/>
<input
type='submit' className='w3ui-button' value='Register'
disabled={email === ''}
/>
</form>
</>
)}
</div>
)
}

function SpaceSection (): JSX.Element {
const [{ space }] = useKeyring()
const [, { reload }] = useUploadsList()
// reload the uploads list when the space changes
// TODO: this currently does a network request - we'd like to just reset
// to the latest state we have and revalidate in the background (SWR)
// but it's not clear how all that state should work yet - perhaps
// we need some sort of state management primitive in the uploads list?
useEffect(() => { void reload() }, [space])
const registered = Boolean(space?.registered())
return (
<div className='container mx-auto'>
<div className='flex flex-row space-x-4 mb-4 justify-between'>
<div className='shrink-0'>
{(space !== undefined) && (
<img src={`https://www.gravatar.com/avatar/${md5(space.did())}?d=identicon`} className='w-20' />
{registered
? (
<>
<div className='flex flex-row space-x-4 mb-4 justify-between'>
<div className='shrink-0'>
{(space !== undefined) && (
<img src={`https://www.gravatar.com/avatar/${md5(space.did())}?d=identicon`} className='w-20' />
)}
</div>
<Uploader onUploadComplete={() => { void reload() }} />
</div>
<UploadsList />
</>
)
: (
<SpaceRegistrar />
)}
</div>
<Uploader onUploadComplete={() => { void reload() }} />
</div>
<UploadsList />
</div>
)
}

function SpaceSelector (props: any) {
function SpaceCreator (props: any): JSX.Element {
const [, { createSpace, registerSpace }] = useKeyring()
const [creating, setCreating] = useState(false)
const [submitted, setSubmitted] = useState(false)
const [email, setEmail] = useState('')
const [name, setName] = useState('')

async function onSubmit (e: React.FormEvent<HTMLFormElement>): Promise<void> {
e.preventDefault()
setSubmitted(true)
try {
await createSpace(name)
await registerSpace(email)
} catch (err) {
console.log(err)
throw new Error('failed to register', { cause: err })
} finally {
setSubmitted(false)
}
}
return (
<div {...props}>
{(creating)
? (
<form onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}>
<input
type='email' placeholder='Email' autofocus
value={email}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setEmail(e.target.value) }}
/>
<input
placeholder='Name'
value={name}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setName(e.target.value) }}
/>
<input type='submit' className='w3ui-button' value='Create' />
</form>
)
: submitted
? (
<div>creating space...</div>
)
: (
<button className='w3ui-button py-2' onClick={() => setCreating(true)}>
Add Space
</button>
)}
</div>
)
}

function SpaceSelector (props: any): JSX.Element {
const [{ space: currentSpace, spaces }, { setCurrentSpace }] = useKeyring()
async function selectSpace (space: Space): Promise<void> {
await setCurrentSpace(space.did())
}
return (
<div {...props}>
Space selector
<ul>
<h3 className='text-lg uppercase font-bold my-4'>Spaces</h3>
<ul className='space-y-2'>
{spaces.map((space, i) => (
<li className={`${space.sameAs(currentSpace) ? 'font-bold' : ''} hover:font-bold`}>
<button onClick={() => setCurrentSpace(space.did())}>
{space.name() || `Space ${i + 1}`}
<li key={space.did()} className={`hover:font-bold ${space.sameAs(currentSpace) ? 'font-bold' : ''}`}>
<button onClick={() => { void selectSpace(space) }}>
{space.name() ?? `Space ${i + 1}`}
</button>
</li>
))}
<SpaceCreator className='mt-12' />
</ul>
</div>
)
Expand All @@ -53,11 +172,11 @@ export function App (): JSX.Element {
console
</h1>
</div>
<SpaceSelector className="flex-none grow"/>
<SpaceSelector className='flex-none grow' />
</div>
</nav>
<main className='grow bg-gray-100 dark:bg-dark-gray p-4'>
<Space />
<SpaceSection />
</main>
</div>
</Authenticator>
Expand Down
9 changes: 5 additions & 4 deletions packages/keyring-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ export interface KeyringContextActions {
*/
setCurrentSpace: (did: DID) => Promise<void>
/**
* Register the current space, verify the email address and store in secure
* storage. Use cancelRegisterSpace to abort. Automatically sets the
* newly registered space as the current space.
* Register a space (current space by default), verify the email
* address and store in secure storage. Use cancelRegisterSpace
* to abort. Automatically sets the newly registered space
* as the current space.
*/
registerSpace: (email: string) => Promise<void>
registerSpace: (email: string, did?: DID) => Promise<void>
/**
* Abort an ongoing account registration.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/Authenticator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export function AuthenticationSubmitted (): JSX.Element {
}

export function AuthenticationEnsurer ({ children }: { children: JSX.Element | JSX.Element[] }): JSX.Element {
const [{ space, submitted }] = useAuthenticator()
const registered = Boolean(space?.registered())
const [{ spaces, submitted }] = useAuthenticator()
const registered = Boolean(spaces.some(s => s.registered()))
if (registered) {
return <>{children}</>
} else if (submitted) {
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e887d52

Please sign in to comment.