1
1
'use client'
2
2
3
+ import { MouseEventHandler , useState } from 'react'
4
+ import { Dialog } from '@headlessui/react'
5
+ import { TrashIcon , ExclamationTriangleIcon } from '@heroicons/react/24/outline'
3
6
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'
6
9
import { UnknownLink , parse as parseLink } from 'multiformats/link'
7
10
import DefaultLoader from '@/components/Loader'
8
- import * as Claims from '@web3-storage/content-claims/client'
9
- import { Piece , PieceLink } from '@web3-storage/data-segment'
10
11
import Link from 'next/link'
11
12
import CopyIcon from '@/components/CopyIcon'
12
- import BackIcon from '@/components/BackIcon'
13
13
import { Breadcrumbs } from '@/components/Breadcrumbs'
14
+ import { useRouter } from 'next/navigation'
15
+ import { createUploadsListKey } from '@/cache'
14
16
15
17
interface PageProps {
16
18
params : {
@@ -39,9 +41,23 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
39
41
onError : err => console . error ( err . message , err . cause )
40
42
} )
41
43
44
+ const [ isRemoveConfirmModalOpen , setRemoveConfirmModalOpen ] = useState ( false )
45
+ const router = useRouter ( )
46
+ const { mutate } = useSWRConfig ( )
47
+
42
48
if ( ! space ) {
43
49
return < h1 > Space not found</ h1 >
44
50
}
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
+
45
61
const url = `https://${ root } .ipfs.w3s.link`
46
62
return (
47
63
< div >
@@ -62,6 +78,17 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
62
78
? < DefaultLoader className = 'w-5 h-5 inline-block' />
63
79
: upload . data ?. shards ?. map ( link => < Shard space = { space . did ( ) } root = { root } shard = { link } key = { link . toString ( ) } /> ) }
64
80
</ 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
+ />
65
92
</ div >
66
93
)
67
94
}
@@ -74,3 +101,52 @@ function Shard ({ space, root, shard }: { space: SpaceDID, root: UnknownLink, sh
74
101
</ div >
75
102
)
76
103
}
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
+ }
0 commit comments