Skip to content

Commit

Permalink
feat: fix SpaceFinder and improve nav (#16)
Browse files Browse the repository at this point in the history
- make navigating around console make sense by moving the logo back up
top, and making it return you to the root of the app
- make the space finder work again
- fix small screen and desktop layouts


![space-finder-strikes-back](https://github.com/web3-storage/console/assets/58871/a78f7938-1c60-4584-ac56-e5a9cc90dbd9)

License: MIT

Signed-off-by: Oli Evans <[email protected]>
  • Loading branch information
olizilla authored Nov 6, 2023
1 parent 9755f89 commit 94bcfe4
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 83 deletions.
4 changes: 2 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export default function RootLayout ({
<html lang="en">
<body className='bg-gray-dark min-h-screen'>
<SidebarLayout>
<H2 explain='a decentralised bucket identified by a DID'>
{/* <H2 explain='a decentralised bucket identified by a DID'>
<Link href='/'>Space</Link>
</H2>
</H2> */}
{children}
</SidebarLayout>
</body>
Expand Down
2 changes: 1 addition & 1 deletion src/app/space/[did]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function Layout ({children, params}: LayoutProps): JSX.Element {
</div>
</div>
</header>
<Nav className='mb-8'>
<Nav className='mb-8 mt-7'>
<NavLink href={`/space/${space.did()}`}>List</NavLink>
<NavLink href={`/space/${space.did()}/share`}>Share</NavLink>
<NavLink href={`/space/${space.did()}/upload`}>Upload</NavLink>
Expand Down
2 changes: 1 addition & 1 deletion src/app/space/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function CreateSpacePage (): JSX.Element {
return (
<>
<SpacesNav />
<div className='max-w-xl pt-4'>
<div className='max-w-xl'>
<H2>Create a new Space</H2>
<SpaceCreatorForm />
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/app/space/import/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import { ImportSpace } from '@/share'
import { SpacesNav } from '../layout'
import { H2 } from '@/components/Text'

export default function ImportPage (): JSX.Element {
return (
<>
<SpacesNav />
<H2>Import a Space</H2>
<ImportSpace />
</>
)
Expand Down
12 changes: 4 additions & 8 deletions src/brand.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import Image from "next/image"
import Link from "next/link"

export const serviceName = 'w3up.web3.storage'
export const tosUrl = 'https://web3.storage/terms'
export const Web3StorageLogoIcon = () => (
<svg
width='30'
viewBox='0 0 27.2 27.18'
xmlns='http://www.w3.org/2000/svg'
>
<svg width='30' viewBox='0 0 27.2 27.18' xmlns='http://www.w3.org/2000/svg'>
<path
d='M13.6 27.18A13.59 13.59 0 1127.2 13.6a13.61 13.61 0 01-13.6 13.58zM13.6 2a11.59 11.59 0 1011.6 11.6A11.62 11.62 0 0013.6 2z'
fill='currentColor'
Expand All @@ -20,10 +16,10 @@ export const Web3StorageLogoIcon = () => (
)

export const Web3StorageLogo = ({ className = '' }) => (
<div className={`${className} font-bold flex flex-row justify-center items-center gap-2`}>
<Link href='/' className={`${className} font-bold flex flex-row justify-center items-center gap-2`}>
<Web3StorageLogoIcon />
<span>console</span>
</div>
</Link>
)

export const Logo = Web3StorageLogo
21 changes: 18 additions & 3 deletions src/components/SidebarLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import { Logo } from '../brand'
import { Fragment, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
import { Authenticator } from '@w3ui/react-keyring'
import { Authenticator, useKeyring, Space } from '@w3ui/react-keyring'
import { AuthenticationEnsurer } from '../components/Authenticator'
import { SpaceEnsurer } from '../components/SpaceEnsurer'
import { W3APIProvider } from '@/components/W3API'
import { SpaceFinder } from './SpaceFinder'
import { usePathname, useRouter } from 'next/navigation'
import { H2 } from './Text'

const navLinks = [
{ name: 'Terms', href: '/terms' },
Expand All @@ -20,12 +23,24 @@ interface SidebarComponentProps {
}

function Sidebar ({ sidebar = <div></div> }: SidebarComponentProps): JSX.Element {
const [{space, spaces}, { setCurrentSpace }] = useKeyring()
const router = useRouter()
const pathname = usePathname()
const goToSpace = (s: Space) => {
router.push(`/space/${s.did()}`)
}
return (
<nav className='flex-none w-64 bg-gray-900 text-white px-4 pb-4 border-r border-gray-800 h-screen'>
<div className='flex flex-col justify-between h-full'>
<div>
<header className='opacity-0 lg:opacity-100'>
<Logo className='py-8' />
</header>
<H2>Spaces</H2>
<SpaceFinder spaces={spaces} selected={space} setSelected={goToSpace} />
</div>
{sidebar}
<div className='flex flex-col items-center'>
<a href='/'><Logo className='w-36 mb-2' /></a>
<div className='flex flex-row space-x-2'>
{navLinks.map((link, i) => (
<a key={i} className='text-xs block text-center mt-2' href={link.href}>{link.name}</a>
Expand Down Expand Up @@ -89,7 +104,7 @@ export default function SidebarLayout ({ children }: LayoutComponentProps): JSX.
{/* top nav bar for narrow browsers, mainly to have a place to put the hamburger */}
<div className='lg:hidden flex justify-between pt-4 px-4'>
<Bars3Icon className='w-6 h-6' onClick={() => setSidebarOpen(true)} />
<a href='/'><Logo className='w-36 mb-2' /></a>
<Logo className='w-full' />
</div>
<main className='grow bg-gray-dark text-white p-4'>
{children}
Expand Down
91 changes: 61 additions & 30 deletions src/components/SpaceCreator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import React, { useState } from 'react'
import { useKeyring } from '@w3ui/react-keyring'
import { ArrowPathIcon } from '@heroicons/react/20/solid'
import Loader from '../components/Loader'
import { DID } from '@ucanto/interface'
import { DID, DIDKey } from '@ucanto/interface'
import { DidIcon } from './DidIcon'
import Link from 'next/link'

export function SpaceCreatorCreating (): JSX.Element {
return (
Expand All @@ -22,8 +24,9 @@ interface SpaceCreatorFormProps {
export function SpaceCreatorForm ({
className = ''
}: SpaceCreatorFormProps): JSX.Element {
const [{ account }, { createSpace, registerSpace }] = useKeyring()
const [{ account, space }, { createSpace, registerSpace }] = useKeyring()
const [submitted, setSubmitted] = useState(false)
const [created, setCreated] = useState(false)
const [name, setName] = useState('')

function resetForm (): void {
Expand All @@ -35,47 +38,50 @@ export function SpaceCreatorForm ({
if (account) {
setSubmitted(true)
try {
await createSpace(name)
const did = await createSpace(name)
await registerSpace(account, { provider: (process.env.NEXT_PUBLIC_W3UP_PROVIDER || 'did:web:web3.storage') as DID<'web'> })
setCreated(true)
resetForm()
} catch (error) {
/* eslint-disable no-console */
console.error(error)
/* eslint-enable no-console */
throw new Error('failed to register', { cause: error })
} finally {
resetForm()
setSubmitted(false)
}
} else {
throw new Error('cannot create space, no account found, have you authorized your email?')
}
}

if (created) {
return (
<div className={className}>
<SpacePreview did={space.did()} name={space.name()} />
</div>
)
}

if (submitted) {
return (
<div className={className}>
<SpaceCreatorCreating />
</div>
)
}

return (
<div className={className}>
{
submitted
? (
<SpaceCreatorCreating />
)
: (
<form
className=''
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
void onSubmit(e)
}}
>
<input
className='text-black py-1 px-2 rounded block w-full mb-4'
placeholder='Name'
value={name}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setName(e.target.value)
}}
/>
<button type='submit' className='w3ui-button'>Create</button>
</form>
)
}
<form className='' onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}>
<input
className='text-black py-1 px-2 rounded block w-full mb-4'
placeholder='Name'
value={name}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setName(e.target.value)
}}
/>
<button type='submit' className='w3ui-button'>Create</button>
</form>
</div>
)
}
Expand Down Expand Up @@ -107,3 +113,28 @@ export function SpaceCreator ({
)
/* eslint-enable no-nested-ternary */
}

export function SpacePreview ({ did, name }: { did: DIDKey, name?: string }) {
return (
<figure className='p-4 flex flex-row items-start gap-2'>
<Link href={`/space/${did}`} className='block'>
<DidIcon did={did} />
</Link>
<figcaption className='grow'>
<Link href={`/space/${did}`} className='block'>
<span className='block text-lg font-semibold leading-5 mb-1'>
{ name ?? 'Untitled'}
</span>
<span className='block font-mono text-xs text-gray-500 truncate'>
{did}
</span>
</Link>
</figcaption>
<div>
<Link href={`/space/${did}`} className='text-sm font-semibold align-[-8px] hover:text-blue-400'>
View
</Link>
</div>
</figure>
)
}
4 changes: 2 additions & 2 deletions src/components/SpaceFinder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export function SpaceFinder ({
by={(a, b) => a?.sameAs(b)}
>
<div className='relative mt-1'>
<div className='relative w-full overflow-hidden rounded-lg bg-white text-left shadow-md'>
<div className='relative w-full overflow-hidden rounded-md bg-white text-left shadow-md'>
<Combobox.Input
className='w-full border-none py-2 pl-3 pr-10 text-sm text-gray-900'
displayValue={(space: Space) => space.name() ?? space.did()}
onChange={(event) => { setQuery(event.target.value) }}
/>
<Combobox.Button className='absolute inset-y-0 right-0 flex items-center pl-2'>
<Combobox.Button className='absolute inset-y-0 right-0 flex items-center pl-1 pr-2'>
<ChevronUpDownIcon
className='h-5 w-5 text-gray-400'
aria-hidden='true'
Expand Down
42 changes: 6 additions & 36 deletions src/share.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { CarReader } from '@ipld/car/reader'
import { importDAG } from '@ucanto/core/delegation'
import type { PropsWithChildren } from 'react'
import type { Delegation, DIDKey } from '@ucanto/interface'
import { DidIcon } from './components/DidIcon'
import { H2 } from './components/Text'
import { SpacePreview } from './components/SpaceCreator'

function Header(props: PropsWithChildren): JSX.Element {
return (
Expand Down Expand Up @@ -45,7 +44,7 @@ export async function toDelegation(car: Blob): Promise<Delegation> {
}

export function ShareSpace (): JSX.Element {
const [{ createDelegation }] = useKeyring()
const [, { createDelegation }] = useKeyring()
const [value, setValue] = useState('')
const [downloadUrl, setDownloadUrl] = useState('')

Expand All @@ -66,7 +65,7 @@ export function ShareSpace (): JSX.Element {
const url = URL.createObjectURL(blob)
setDownloadUrl(url)
} catch (err) {
throw new Error('failed to register', { cause: err })
throw new Error(err.message ?? err, { cause: err })
}
}

Expand Down Expand Up @@ -122,11 +121,7 @@ export function ShareSpace (): JSX.Element {
)
}

export function ImportSpace ({
viewSpace,
}: {
viewSpace: (did: DIDKey) => void
}) {
export function ImportSpace () {
const [{ agent }, { addSpace }] = useKeyring()
const [proof, setProof] = useState<Delegation>()

Expand All @@ -150,7 +145,7 @@ export function ImportSpace ({

return (
<>
<p className='mb-2'>Send your DID to your friend, and click import to use the UCAN they send you.</p>
<p className='mt-4 mb-8'>Send your DID to your friend, and click import to use the UCAN they send you.</p>
<div className='bg-opacity-10 bg-white font-mono text-sm py-2 px-3 rounded break-words max-w-4xl'>
{agent?.did()}
</div>
Expand All @@ -172,32 +167,7 @@ export function ImportSpace ({
<Header>Added</Header>
<div className='max-w-3xl border border-gray-700 shadow-xl'>
{proof.capabilities.map((cap, i) => (
<figure className='p-4 flex flex-row items-start gap-2' key={i}>
<DidIcon did={cap.with} />
<figcaption className='grow'>
<a
href='#'
onClick={() => viewSpace(cap.with)}
className='block'
>
<span className='block text-xl font-semibold leading-5 mb-1'>
{proof.facts.at(i)?.space.name ?? 'Untitled Space'}
</span>
<span className='block font-mono text-xs text-gray-500 truncate'>
{cap.with}
</span>
</a>
</figcaption>
<div>
<a
href='#'
className='font-sm font-semibold align-[-8px]'
onClick={() => viewSpace(cap.with)}
>
View
</a>
</div>
</figure>
<SpacePreview did={cap.with} name={proof.facts.at(i)?.space.name} key={cap.with} />
))}
</div>
</div>
Expand Down

0 comments on commit 94bcfe4

Please sign in to comment.