Skip to content

Commit

Permalink
feat: w3console cleanup and updates to packages to support it (storac…
Browse files Browse the repository at this point in the history
…ha#507)

Integrate some learnings from creating a new app from the ground up with
some tasks I've been meaning to do for a little while to clean up
w3console. This is mostly either:
a) extracting components to their own files OR
b) lifting up types to eliminate 2-level-deep dependencies (ie, allowing
w3console to only depend on the `@w3ui/react-*` packages rather than
pulling in `@w3ui/*-core` and `multiformats`)
  • Loading branch information
travis authored Apr 27, 2023
1 parent 40bc621 commit 78aee2a
Show file tree
Hide file tree
Showing 24 changed files with 221 additions and 220 deletions.
6 changes: 3 additions & 3 deletions docs/react-keyring.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ Must be used inside of a [`KeyringProvider`](#keyringprovider).

#### `Authenticator.Form`

A `form` element designed to work with the [`Authenticator`](#authenticator) component. Any passed props will be passed along to the `form` element.
A `form` element designed to be used inside of an [`Authenticator`](#authenticator) component. Any passed props will be passed along to the `form` element.

#### `Authenticator.Email`

An email `input` element designed to work with [`Authenticator.Form`](#authenticatorform). Any passed props will be passed along to the `input` component.
An email `input` element designed to be used inside of an [`Authenticator.Form`](#authenticatorform). Any passed props will be passed along to the `input` component.

#### `Authenticator.CancelButton`

A `button` element that will cancel space registration, designed to work with [`Authenticator.Form`](#authenticatorform). Any passed props will be passed along to the `button` element.
A `button` element that will cancel space registration, designed to be used inside of an [`Authenticator.Form`](#authenticatorform). Any passed props will be passed along to the `button` element.

### `KeyringProvider`

Expand Down
6 changes: 4 additions & 2 deletions docs/react-uploader.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ Top-level component of the headless Uploader.

Designed to be used with [`Uploader.Form`](#uploaderform) and [`Uploader.Input`](#uploaderinput) to easily create a custom component for uploading files to web3.storage.

Must be used inside of an [`UploaderProvider`](#uploaderprovider).

#### `Uploader.Form`

Form component for the headless Uploader.

Renders a `form` element designed to work with [`Uploader`](#uploader) that will send files from an [`Uploader.Input`](#uploaderinput) component to the web3.storage service when the form is submitted.
Renders a `form` element designed to be used inside of an [`Uploader`](#uploader) that will send files from an [`Uploader.Input`](#uploaderinput) component to the web3.storage service when the form is submitted.

Any passed props will be passed along to the `input` element.

#### `Uploader.Input`

Input component for the headless Uploader.

Renders a file `input` element designed to work with [`Uploader`](#uploader). When used inside of an [`Uploader.Form`](#uploaderform), files added to the input will be uploaded when the form is submitted.
Renders a file `input` element designed to be used inside of an [`Uploader`](#uploader). When used inside of an [`Uploader.Form`](#uploaderform), files added to the input will be uploaded when the form is submitted.

### `UploaderProvider`

Expand Down
6 changes: 4 additions & 2 deletions docs/react-uploads-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ Top-level component of the headless UploadsList.

Designed to be used with [`UploadsList.NextButton`](#uploadslistnextbutton), [`UploadsList.ReloadButton`](#uploadslistreloadbutton), etc to easily create a custom component for listing uploads in a web3.storage space.

Must be used inside of an [`UploadsListProvider`](#uploadslistprovider).

#### `UploadsList.NextButton`

Button that loads the next page of results in an UploadsList.

Renders a `button` element designed to work with [`UploadsList`](#uploadslist). When pressed, will request the next page of uploads from the service.
Renders a `button` element designed to be used inside of an [`UploadsList`](#uploadslist). When pressed, will request the next page of uploads from the service.

Any passed props will be passed along to the `button` element.

#### `UploadsList.ReloadButton`

Button that reloads an UploadsList.

Renders a `button` element designed to work with [`UploadsList`](#uploadslist). When pressed, will reload list data from the service.
Renders a `button` element designed to be used inside of an [`UploadsList`](#uploadslist). When pressed, will reload list data from the service.

Any passed props will be passed along to the `button` element.

Expand Down
2 changes: 0 additions & 2 deletions examples/react/w3console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@
"@heroicons/react": "^2.0.17",
"@ipld/car": "^5.1.0",
"@ipld/dag-ucan": "^3.2.0",
"@w3ui/keyring-core": "workspace:^",
"@w3ui/react-keyring": "workspace:^",
"@w3ui/react-uploader": "workspace:^",
"@w3ui/react-uploads-list": "workspace:^",
"@w3ui/uploader-core": "workspace:^",
"blueimp-md5": "^2.19.0",
"preact": "^10.11.3",
"react-router-dom": "^6.9.0"
Expand Down
12 changes: 8 additions & 4 deletions examples/react/w3console/src/components/Authenticator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import {
useAuthenticator
} from '@w3ui/react-keyring'
import { serviceName, tosUrl, Logo } from '../brand'
import Loader from './Loader'

export function AuthenticationForm (): JSX.Element {
const [{ submitted }] = useAuthenticator()
return (
<div className='authenticator'>
<AuthCore.Form className='text-white/80 bg-gradient-to-br from-blue-500 to-cyan-500 rounded-xl shadow-md px-10 pt-8 pb-8'>
<div className='flex flex-row gap-4 mb-8 flex justify-center gap-4'>
<Logo className='w-36'/>
<Logo className='w-36' />
</div>
<div>
<label className='block mb-2 uppercase text-xs font-semibold tracking-wider m-1 font-mono' htmlFor='authenticator-email'>Email</label>
Expand Down Expand Up @@ -43,7 +44,7 @@ export function AuthenticationSubmitted (): JSX.Element {
<div className='authenticator'>
<div className='text-white bg-gradient-to-br from-blue-500 to-cyan-500 rounded-xl shadow-md px-10 pt-8 pb-8'>
<div className='flex flex-row gap-4 mb-8 flex justify-center gap-4'>
<Logo className='w-36'/>
<Logo className='w-36' />
</div>
<h1 className='text-xl font-semibold'>Verify your email address!</h1>
<p className='pt-2 pb-4'>
Expand All @@ -62,15 +63,18 @@ export function AuthenticationEnsurer ({
}: {
children: JSX.Element | JSX.Element[]
}): JSX.Element {
const [{ submitted, account }] = useAuthenticator()
const [{ submitted, account, agent }] = useAuthenticator()
const authenticated = !!account
if (authenticated) {
return <>{children}</>
}
if (submitted) {
return <AuthenticationSubmitted />
}
return <AuthenticationForm />
if (agent) {
return <AuthenticationForm />
}
return <Loader className='w-12 h-12' />
}


2 changes: 1 addition & 1 deletion examples/react/w3console/src/components/Loader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ArrowPathIcon } from '@heroicons/react/20/solid'

export default ({ className = '' }: { className?: 'string' }) => (
export default ({ className = '' }: { className?: string }) => (
<ArrowPathIcon className={`animate-spin ${className}`} />
)
27 changes: 27 additions & 0 deletions examples/react/w3console/src/components/SpaceEnsurer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useKeyring } from '@w3ui/react-keyring';
import { SpaceCreatorForm } from './SpaceCreator';

export function SpaceEnsurer ({
children
}: {
children: JSX.Element | JSX.Element[];
}): JSX.Element | JSX.Element[]{
const [{ spaces, account }] = useKeyring();
if (spaces && spaces.length > 0) {
return children;
}
return (
<div className="flex flex-col justify-center items-center h-screen">
<div className="text-gray-200 text-center">
<h1 className="my-4 text-lg">Welcome {account}!</h1>
<p>
To get started with w3up you'll need to create a space.
</p>
<p>
Give it a name and hit create!
</p>
<SpaceCreatorForm className='mt-4' />
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion examples/react/w3console/src/components/SpaceFinder.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Space } from '@w3ui/keyring-core'
import type { Space } from '@w3ui/react-keyring'

import React, { Fragment, useState } from 'react'
import { Combobox, Transition } from '@headlessui/react'
Expand Down
41 changes: 41 additions & 0 deletions examples/react/w3console/src/components/SpaceRegistrar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useState } from 'react';
import { useKeyring } from '@w3ui/react-keyring';
import Loader from './Loader';

export function SpaceRegistrar (): JSX.Element {
const [{ account }, { registerSpace }] = useKeyring();
const [submitted, setSubmitted] = useState(false);
async function onSubmit (e: React.MouseEvent<HTMLButtonElement>): Promise<void> {
e.preventDefault();
if (account) {
setSubmitted(true);
try {
await registerSpace(account, { provider: import.meta.env.VITE_W3UP_PROVIDER });
} catch (err) {
console.log(err);
throw new Error('failed to register', { cause: err });
} finally {
setSubmitted(false);
}
} else {
throw new Error('cannot register space, no account found, have you authorized your email?');
}
}
return (
<div className='flex flex-col items-center space-y-12 pt-12'>
<div className='flex flex-col items-center space-y-4'>
<h3 className='text-lg'>This space is not yet registered.</h3>
<p>
Click the button below to register this space and start uploading.
</p>
</div>
<div className='flex flex-col items-center space-y-4'>
{submitted ? (
<Loader className='w-6' />
) : (
<button className='w3ui-button' onClick={onSubmit}>Register Space</button>
)}
</div>
</div>
);
}
79 changes: 79 additions & 0 deletions examples/react/w3console/src/components/SpaceSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useEffect } from 'react';
import { useKeyring } from '@w3ui/react-keyring';
import { useUploadsList } from '@w3ui/react-uploads-list';
import { ShareIcon } from '@heroicons/react/20/solid';
import md5 from 'blueimp-md5';
import { SpaceShare } from '../share';
import { Uploader } from './Uploader';
import { UploadsList } from './UploadsList';
import { SpaceRegistrar } from './SpaceRegistrar';

interface SpaceSectionProps {
viewSpace: (did: string) => void;
setShare: (share: boolean) => void;
share: boolean;
}
export function SpaceSection (props: SpaceSectionProps): JSX.Element {
const { viewSpace, share, setShare } = props;
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>
<header className='py-3'>
{space !== undefined && (
<div className='flex flex-row items-start gap-2'>
<img
title={space.did()}
src={`https://www.gravatar.com/avatar/${md5(
space.did()
)}?d=identicon`}
className='w-10 hover:saturate-200 saturate-0 invert border-solid border-gray-500 border' />
<div className='grow overflow-hidden whitespace-nowrap text-ellipsis text-gray-500'>
<h1 className='text-xl font-semibold leading-5 text-white'>
{space.name() ?? 'Untitled'}
</h1>
<label className='font-mono text-xs'>
{space.did()}
</label>
</div>
<button
className='h-6 w-6 text-gray-500 hover:text-gray-100'
onClick={() => setShare(!share)}
>
<ShareIcon />
</button>
</div>
)}
</header>
<div className='container mx-auto'>
{share && <SpaceShare viewSpace={viewSpace} />}
{registered && !share && (
<>
<Uploader
onUploadComplete={() => {
void reload();
}} />
<div className='mt-8'>
<UploadsList />
</div>
</>
)}
{(space && !registered) && !share && <SpaceRegistrar />}
{!space && (
<div className="text-center">
<h1 className="text-xl">Select a space from the dropdown on the left to get started.</h1>
</div>
)}
</div>
</div>
);
}
10 changes: 6 additions & 4 deletions examples/react/w3console/src/components/Uploader.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { OnUploadComplete } from '@w3ui/react-uploader'
import type {
OnUploadComplete,
CARMetadata,
CID
} from '@w3ui/react-uploader'

import { Link, Version } from 'multiformats'
import { CloudArrowUpIcon } from '@heroicons/react/24/outline'
import { CARMetadata } from '@w3ui/uploader-core'
import {
Status,
Uploader as UploaderCore,
Expand Down Expand Up @@ -38,7 +40,7 @@ export const Errored = ({ error }: { error: any }): JSX.Element => (

interface DoneProps {
file?: File
dataCID?: Link<unknown, number, number, Version>
dataCID?: CID
storedDAGShards?: CARMetadata[]
}

Expand Down
6 changes: 1 addition & 5 deletions examples/react/w3console/src/components/W3API.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useMemo } from 'react'
import { ServiceConfig } from '@w3ui/uploader-core'
import { useMemo } from 'react'
import {
useUploader,
UploaderContextValue,
Expand All @@ -22,9 +21,6 @@ export interface W3APIContextValue {
uploader: UploaderContextValue
uploadsList: UploadsListContextValue
}
export interface UploaderProviderProps extends ServiceConfig {
children?: JSX.Element
}

export interface W3APIProviderProps {
children: JSX.Element | JSX.Element[]
Expand Down
19 changes: 19 additions & 0 deletions examples/react/w3console/src/pages/SpaceSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Space } from '@w3ui/react-keyring';
import { SpaceFinder } from '../components/SpaceFinder';

export function SpaceSelector (props: any): JSX.Element {
const { selected, setSelected, spaces } = props;
return (
<div>
<h3 className='text-xs tracking-wider uppercase font-bold my-2 text-gray-400 font-mono'>
Spaces
</h3>
<SpaceFinder
spaces={spaces}
selected={selected}
setSelected={(space: Space) => {
void setSelected(space.did());
}} />
</div>
);
}
Loading

0 comments on commit 78aee2a

Please sign in to comment.