Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: w3console polish #284

Merged
merged 4 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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": {
"@heroicons/react": "^2.0.13",
"@w3ui/keyring-core": "workspace:^2.0.1",
"@w3ui/react": "workspace:^",
"@w3ui/react-keyring": "workspace:^",
Expand Down
122 changes: 73 additions & 49 deletions examples/react/w3console/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { ChangeEvent, useEffect, useState } from 'react'
import type { ChangeEvent } from 'react'
import type { Space } from '@w3ui/keyring-core'

import { useEffect, useState } from 'react'
import { Authenticator, Uploader, UploadsList, W3APIProvider, SpaceFinder } from '@w3ui/react'
import { useKeyring } from '@w3ui/react-keyring'
import { useUploadsList } from '@w3ui/react-uploads-list'
import { ArrowPathIcon } from '@heroicons/react/20/solid'
import md5 from 'blueimp-md5'
import '@w3ui/react/src/styles/uploader.css'

function SpaceRegistrar (): JSX.Element {
const [, { registerSpace }] = useKeyring()
const [email, setEmail] = useState('')
const [submitted, setSubmitted] = useState(false)
function resetForm (): void {
setEmail('')
}
async function onSubmit (e: React.FormEvent<HTMLFormElement>): Promise<void> {
e.preventDefault()
setSubmitted(true)
Expand All @@ -20,35 +25,40 @@ function SpaceRegistrar (): JSX.Element {
console.log(err)
throw new Error('failed to register', { cause: err })
} finally {
resetForm()
setSubmitted(false)
}
}
return (
<div>
{submitted
? (
<div className='flex flex-col items-center space-y-24 pt-12'>
<div className='flex flex-col items-center space-y-2'>
<h3 className='text-lg'>Verify your email address!</h3>
<p>Click the link in the email we sent to start uploading to this space.</p>
</div>
<div className='flex flex-col items-center space-y-4'>
<h5>
Need a new verification email?
</h5>
<form
className='flex flex-col items-center space-y-2'
onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}
>
<input
className='text-black px-2 py-1 rounded'
type='email' placeholder='Email'
value={email}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setEmail(e.target.value) }}
/>
<input
type='submit' className='w3ui-button' value='Re-send Verification Email'
disabled={email === ''}
/>
</form>
{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>
</>
)}
Verification re-sent, please check your email for a verification email.
</p>}
</div>
</div>
)
}
Expand Down Expand Up @@ -102,48 +112,62 @@ function SpaceCreator (props: any): JSX.Element {
const [email, setEmail] = useState('')
const [name, setName] = useState('')

function resetForm (): void {
setEmail('')
setName('')
}

async function onSubmit (e: React.FormEvent<HTMLFormElement>): Promise<void> {
e.preventDefault()
setSubmitted(true)
try {
await createSpace(name)
await registerSpace(email)
// ignore this because the UI knows how to help the user recover
// from space registration failure
void registerSpace(email)
} catch (err) {
console.log(err)
throw new Error('failed to register', { cause: err })
} finally {
resetForm()
setSubmitted(false)
}
}
return (
<div {...props}>
{(creating)
? (
<form onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}>
<input
className='text-black'
type='email' placeholder='Email' autofocus
value={email}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setEmail(e.target.value) }}
/>
<input
className='text-black'
placeholder='Name'
value={name}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setName(e.target.value) }}
/>
<input type='submit' className='w3ui-button' value='Create' />
</form>
)
: submitted
? submitted
? (
<div>creating space...</div>
<div className='flex flex-col items-center space-y-4'>
<h5>Creating Space...</h5>
<ArrowPathIcon className='animate-spin w-6' />
</div>
)
: (
<button className='w3ui-button py-2' onClick={() => setCreating(true)}>
Add Space
</button>
)}
<form
className='flex flex-col space-y-2'
onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}
>
<input
className='text-black py-1 px-2 rounded'
type='email' placeholder='Email' autofocus
value={email}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setEmail(e.target.value) }}
/>
<input
className='text-black py-1 px-2 rounded'
placeholder='Name'
value={name}
onChange={(e: ChangeEvent<HTMLInputElement>) => { setName(e.target.value) }}
/>
<input type='submit' className='w3ui-button' value='Create' />
</form>
)
: (
<button className='w3ui-button py-2' onClick={() => setCreating(true)}>
Add Space
</button>
)}
</div>
)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/react/w3console/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
}

.w3ui-uploader__file {
@apply rounded-md w-full grid grid-cols-2 p-4 bg-gray-200 dark:bg-gray-900;
@apply rounded-md w-full grid grid-cols-2 p-4 bg-gray-200 text-gray-500 dark:bg-gray-900 dark:text-white;
}

.w3ui-uploader__file .name {
Expand All @@ -54,7 +54,7 @@
}

.w3ui-uploader-console {
@apply rounded-md w-full bg-gray-200 dark:bg-gray-900 p-4 truncate;
@apply rounded-md w-full bg-gray-200 text-gray-500 dark:bg-gray-900 dark:text-white p-4 truncate;
}

.w3ui-simple-authenticator-verify-email {
Expand Down
9 changes: 7 additions & 2 deletions packages/react-uploader/src/Uploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export type UploaderComponentContextActions = UploaderContextActions & {
* Set a file to be uploaded to web3.storage. The file will be uploaded
* when `handleUploadSubmit` is called.
*/
setFile: React.Dispatch<React.SetStateAction<File | undefined>>
setFile: (file?: File) => void
}

export type UploaderComponentContextValue = [
Expand Down Expand Up @@ -93,6 +93,11 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent((props
const [status, setStatus] = useState(Status.Idle)
const [error, setError] = useState()

const setFileAndReset = (file?: File): void => {
setFile(file)
setStatus(Status.Idle)
}

const handleUploadSubmit = async (e: Event): Promise<void> => {
e.preventDefault()
if (file != null) {
Expand All @@ -114,7 +119,7 @@ export const UploaderRoot: Component<UploaderRootProps> = createComponent((props

const uploaderComponentContextValue = useMemo<UploaderComponentContextValue>(() => [
{ ...uploaderState, file, dataCID, status, error, handleUploadSubmit },
{ ...uploaderActions, setFile }
{ ...uploaderActions, setFile: setFileAndReset }
], [uploaderState, file, dataCID, status, error, handleUploadSubmit, uploaderActions, setFile])

return (
Expand Down
82 changes: 45 additions & 37 deletions packages/react/src/Uploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,45 +29,68 @@ interface DoneProps {
storedDAGShards?: CARMetadata[]
}

export const Done = ({ file, dataCID, storedDAGShards }: DoneProps): JSX.Element => {
export const Done = ({ dataCID }: DoneProps): JSX.Element => {
const [, { setFile }] = useUploaderComponent()
const cid: string = dataCID?.toString() ?? ''
return (
<div className='done'>
<h1 className='title'>Done!</h1>
<p className='cid'>{cid}</p>
<p className='view'><a href={`https://${cid}.ipfs.w3s.link/`}>View {file?.name} on IPFS Gateway.</a></p>
<h5 className='chunks'>Shards ({storedDAGShards?.length}):</h5>
{storedDAGShards?.map(({ cid, size }) => (
<p className='shard' key={cid.toString()}>
{cid.toString()} ({size} bytes)
</p>
))}
<p className='cid'>
Uploaded to <a href={`https://${cid}.ipfs.w3s.link/`}>{cid}</a>
</p>
<button
className='w3ui-button'
onClick={() => { setFile(undefined) }}
>
Add More
</button>
</div>
)
}

const UploaderForm = (): JSX.Element => {
const [{ file }] = useUploaderComponent()
const [{ status, file }] = useUploaderComponent()
const hasFile = (file !== undefined)
return (
<UploaderCore.Form>
<div className='w3ui-uploader'>
<div className={`w3ui-uploader ${status} ${hasFile ? 'has-file' : 'no-file'}`}>
<label className='w3ui-uploader__label'>File:</label>
<UploaderCore.Input className='w3ui-uploader__input' />
<UploaderContents />
</div>
{(file !== undefined) && (
<div className='w3ui-uploader__file'>
<span className='name'>{file.name}</span>
<span className='type'>{file.type}</span>
<span className='size'>{file.size}</span>
</div>
)}
<button type='submit' className='w3ui-button' disabled={file === undefined}>
Upload
</button>
</UploaderCore.Form>
)
}

const UploaderContents = (): JSX.Element => {
const [{ status, file }] = useUploaderComponent()
const hasFile = (file !== undefined)
if (status === Status.Idle) {
if (hasFile) {
return (
<>
<div className='w3ui-uploader__file'>
<span className='name'>{file.name}</span>
<span className='type'>{file.type}</span>
<span className='size'>{file.size}</span>
</div>
<button type='submit' className='w3ui-button' disabled={file === undefined}>
Upload
</button>
</>
)
} else {
return <></>
}
} else {
return (
<div className='w3ui-uploader-console'>
<UploaderConsole />
</div>
)
}
}

const UploaderConsole = (): JSX.Element => {
const [{ status, file, error, dataCID, storedDAGShards }] = useUploaderComponent()
switch (status) {
Expand All @@ -84,29 +107,14 @@ const UploaderConsole = (): JSX.Element => {
}
}

const UploaderBody = (): JSX.Element => {
const [{ status }] = useUploaderComponent()

return (
<>
<UploaderForm />
{(status !== Status.Idle) && (
<div className='w3ui-uploader-console'>
<UploaderConsole />
</div>
)}
</>
)
}

export interface SimpleUploaderProps {
onUploadComplete?: OnUploadComplete
}

export const Uploader = ({ onUploadComplete }: SimpleUploaderProps): JSX.Element => {
return (
<UploaderCore as='div' className='w3ui-uploader-wrapper' onUploadComplete={onUploadComplete}>
<UploaderBody />
<UploaderForm />
</UploaderCore>
)
}
6 changes: 5 additions & 1 deletion packages/react/src/UploadsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ function Uploads ({ uploads }: { uploads?: UploadListResult[] }): JSX.Element {
<tbody>
{uploads.map(({ root }) => (
<tr key={root.toString()}>
<td>{root.toString()}</td>
<td>
<a href={`https://${root.toString()}.ipfs.w3s.link/`}>
{root.toString()}
</a>
</td>
</tr>
))}
</tbody>
Expand Down
Loading