Skip to content

Commit 3977281

Browse files
author
Travis Vachon
authored
Merge branch 'main' into feat/plan-update-error
2 parents 6213070 + a6d75ae commit 3977281

File tree

5 files changed

+98
-7
lines changed

5 files changed

+98
-7
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [1.5.0](https://github.com/web3-storage/console/compare/w3console-v1.4.1...w3console-v1.5.0) (2024-02-09)
4+
5+
6+
### Features
7+
8+
* remove upload ([#90](https://github.com/web3-storage/console/issues/90)) ([92aaabf](https://github.com/web3-storage/console/commit/92aaabfa331b3965c828145b267be940d81c5363))
9+
310
## [1.4.1](https://github.com/web3-storage/console/compare/w3console-v1.4.0...w3console-v1.4.1) (2024-02-07)
411

512

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "w3console",
3-
"version": "1.4.1",
3+
"version": "1.5.0",
44
"private": true,
55
"scripts": {
66
"dev": "next dev",

src/app/space/[did]/page.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { UploadsList } from '@/components/UploadsList'
44
import { useW3, UnknownLink, UploadListSuccess } from '@w3ui/react'
55
import useSWR from 'swr'
66
import { useRouter, usePathname } from 'next/navigation'
7+
import { createUploadsListKey } from '@/cache'
78

89
const pageSize = 20
910

@@ -22,7 +23,7 @@ export default function Page ({ params, searchParams }: PageProps): JSX.Element
2223
const spaceDID = decodeURIComponent(params.did)
2324
const space = spaces.find(s => s.did() === spaceDID)
2425

25-
const key = `/space/${spaceDID}/uploads?cursor=${searchParams.cursor ?? ''}&pre=${searchParams.pre ?? 'false'}`
26+
const key = space ? createUploadsListKey(space.did(), searchParams.cursor, searchParams.pre === 'true') : ''
2627
const { data: uploads, isLoading, isValidating, mutate } = useSWR<UploadListSuccess|undefined>(key, {
2728
fetcher: async () => {
2829
if (!client || !space) return

src/app/space/[did]/root/[cid]/page.tsx

+81-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
'use client'
22

3+
import { MouseEventHandler, useState } from 'react'
4+
import { Dialog } from '@headlessui/react'
5+
import { TrashIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'
36
import { H2 } from '@/components/Text'
4-
import { useW3, UploadGetSuccess, FilecoinInfoSuccess, SpaceDID, CARLink } from '@w3ui/react'
5-
import useSWR from 'swr'
7+
import { useW3, UploadGetSuccess, SpaceDID, CARLink } from '@w3ui/react'
8+
import useSWR, { useSWRConfig } from 'swr'
69
import { UnknownLink, parse as parseLink } from 'multiformats/link'
710
import DefaultLoader from '@/components/Loader'
8-
import * as Claims from '@web3-storage/content-claims/client'
9-
import { Piece, PieceLink } from '@web3-storage/data-segment'
1011
import Link from 'next/link'
1112
import CopyIcon from '@/components/CopyIcon'
12-
import BackIcon from '@/components/BackIcon'
1313
import { Breadcrumbs } from '@/components/Breadcrumbs'
14+
import { useRouter } from 'next/navigation'
15+
import { createUploadsListKey } from '@/cache'
1416

1517
interface PageProps {
1618
params: {
@@ -39,9 +41,23 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
3941
onError: err => console.error(err.message, err.cause)
4042
})
4143

44+
const [isRemoveConfirmModalOpen, setRemoveConfirmModalOpen] = useState(false)
45+
const router = useRouter()
46+
const { mutate } = useSWRConfig()
47+
4248
if (!space) {
4349
return <h1>Space not found</h1>
4450
}
51+
52+
const handleRemove = async () => {
53+
await client?.remove(root, { shards: true })
54+
setRemoveConfirmModalOpen(false)
55+
// ensure list data is fresh
56+
mutate(createUploadsListKey(space.did()))
57+
// navigate to list (this page no longer exists)
58+
router.replace(`/space/${spaceDID}`)
59+
}
60+
4561
const url = `https://${root}.ipfs.w3s.link`
4662
return (
4763
<div>
@@ -62,6 +78,17 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
6278
? <DefaultLoader className='w-5 h-5 inline-block' />
6379
: upload.data?.shards?.map(link => <Shard space={space.did()} root={root} shard={link} key={link.toString()} />)}
6480
</div>
81+
82+
<button onClick={e => { e.preventDefault(); setRemoveConfirmModalOpen(true) }} className={`inline-block bg-zinc-950 text-white font-bold text-sm pl-4 pr-6 py-2 rounded-full whitespace-nowrap hover:bg-red-700 hover:outline`}>
83+
<TrashIcon className='h-5 w-5 inline-block mr-1 align-middle' style={{marginTop: -4}} /> Remove
84+
</button>
85+
<RemoveConfirmModal
86+
isOpen={isRemoveConfirmModalOpen}
87+
root={root}
88+
shards={upload.data?.shards ?? []}
89+
onConfirm={handleRemove}
90+
onCancel={() => setRemoveConfirmModalOpen(false)}
91+
/>
6592
</div>
6693
)
6794
}
@@ -74,3 +101,52 @@ function Shard ({ space, root, shard }: { space: SpaceDID, root: UnknownLink, sh
74101
</div>
75102
)
76103
}
104+
105+
interface RemoveConfirmModalProps {
106+
isOpen: boolean
107+
root: UnknownLink
108+
shards: UnknownLink[]
109+
onConfirm: () => void
110+
onCancel: () => void
111+
}
112+
113+
function RemoveConfirmModal ({ isOpen, root, shards, onConfirm, onCancel }: RemoveConfirmModalProps) {
114+
const [confirmed, setConfirmed] = useState(false)
115+
const displayShards = shards.slice(0, 10)
116+
return (
117+
<Dialog open={isOpen} onClose={() => { setConfirmed(false); onCancel() }} className='relative z-50'>
118+
<div className='fixed inset-0 flex w-screen items-center justify-center bg-black/70' aria-hidden='true'>
119+
<Dialog.Panel className='bg-grad p-4 shadow-lg rounded-lg'>
120+
<Dialog.Title className='text-lg font-semibold leading-5 text-black text-center my-3'>
121+
<ExclamationTriangleIcon className='h-10 w-10 inline-block' /><br/>
122+
Confirm remove
123+
</Dialog.Title>
124+
<Dialog.Description className='py-2'>
125+
Are you sure you want to remove <span className='font-mono font-bold text-sm'>{root.toString()}</span>?
126+
</Dialog.Description>
127+
128+
<p className='py-2'>The following shards will be removed:</p>
129+
130+
<ul className='py-2 list-disc pl-6'>
131+
{displayShards.map(s => <li key={s.toString()} className='font-mono text-sm'>{s.toString()}</li>)}
132+
</ul>
133+
134+
{displayShards.length < shards.length ? <p className='py-2'>...and {shards.length - displayShards.length} more.</p> : null}
135+
136+
<p className='py-2'>
137+
Any uploads using the same shards as those listed above <em>will</em> be corrputed. This cannot be undone.
138+
</p>
139+
140+
<div className='py-2 text-center'>
141+
<button onClick={e => { e.preventDefault(); setConfirmed(true); onConfirm() }} className={`inline-block bg-red-700 text-white font-bold text-sm pl-4 pr-6 py-2 mr-3 rounded-full whitespace-nowrap ${confirmed ? 'opacity-50' : 'hover:outline'}`} disabled={confirmed}>
142+
<TrashIcon className='h-5 w-5 inline-block mr-1 align-middle' style={{marginTop: -4}} /> {confirmed ? 'Removing...' : 'Remove'}
143+
</button>
144+
<button onClick={e => { e.preventDefault(); setConfirmed(false); onCancel() }} className={`inline-block bg-zinc-950 text-white font-bold text-sm px-8 py-2 rounded-full whitespace-nowrap ${confirmed ? 'opacity-50' : 'hover:outline'}`} disabled={confirmed}>
145+
Cancel
146+
</button>
147+
</div>
148+
</Dialog.Panel>
149+
</div>
150+
</Dialog>
151+
)
152+
}

src/cache.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { DID } from '@ucanto/interface'
2+
3+
export const createUploadsListKey = (
4+
space: DID<'key'>,
5+
cursor?: string,
6+
pre?: boolean
7+
) => `/space/${space}/uploads?cursor=${cursor ?? ''}&pre=${pre ?? false}`

0 commit comments

Comments
 (0)