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

feat: w3console cleanup and updates to packages to support it #507

Merged
merged 8 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
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 {
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