11"use client" ;
22
33import { useCallback , useEffect , useState } from "react" ;
4- import { useDropzone } from "react-dropzone" ;
54import toast from "react-hot-toast" ;
65import { Button } from "@/components/ui/button" ;
76import axios , { AxiosError } from "axios" ;
@@ -37,7 +36,7 @@ export default function Page() {
3736 { id : string ; file : File ; preview : string } [ ]
3837 > ( [ ] ) ;
3938 const [ isUploading , setIsUploading ] = useState ( false ) ;
40- const [ isDragging , setIsDragging ] = useState ( false ) ;
39+ const [ setIsDragging ] = useState ( false ) ;
4140 const [ isGlobalDragging , setIsGlobalDragging ] = useState ( false ) ;
4241 const [ zoomIndex , setZoomIndex ] = useState < number | null > ( null ) ;
4342
@@ -57,18 +56,18 @@ export default function Page() {
5756 } ;
5857 } , [ ] ) ;
5958
60-
59+ // Cleanup URLs when component unmounts
6160 useEffect ( ( ) => {
6261 return ( ) => {
6362 previews . forEach ( ( item ) => {
6463 try {
6564 URL . revokeObjectURL ( item . preview ) ;
6665 } catch ( e ) {
67-
66+ // Ignore errors
6867 }
6968 } ) ;
7069 } ;
71- } , [ previews ] ) ;
70+ } , [ ] ) ; // Only run on unmount
7271
7372 const fileCheckAndSelect = useCallback (
7473 ( acceptedFiles : File [ ] ) => {
@@ -90,11 +89,23 @@ export default function Page() {
9089 ( file ) => file . type === "application/pdf" ,
9190 ) ;
9291
92+ const hasExistingImages = files . some ( ( file ) =>
93+ file . type . startsWith ( "image/" ) ,
94+ ) ;
95+
96+ const hasExistingPdf = files . some (
97+ ( file ) => file . type === "application/pdf" ,
98+ ) ;
99+
93100 if (
94101 ( isNewPdf && acceptedFiles . length > 1 ) ||
95- ( isNewPdf && files . length > 0 )
102+ ( isNewPdf && hasExistingImages ) ||
103+ ( isNewPdf && hasExistingPdf ) ||
104+ ( ! isNewPdf && hasExistingPdf )
96105 ) {
97- toast . error ( "PDFs must be uploaded separately" , { id : toastId } ) ;
106+ toast . error ( "PDFs must be uploaded separately from images" , {
107+ id : toastId ,
108+ } ) ;
98109 return ;
99110 }
100111
@@ -117,9 +128,8 @@ export default function Page() {
117128 return ;
118129 }
119130
120-
121131 const newPreviews = acceptedFiles . map ( ( file , idx ) => ( {
122- id : `${ file . name } -${ file . lastModified } -${ Date . now ( ) } -${ files . length + idx } -add ` ,
132+ id : `${ file . name } -${ file . lastModified } -${ Date . now ( ) } -${ files . length + idx } ` ,
123133 file,
124134 preview : URL . createObjectURL ( file ) ,
125135 } ) ) ;
@@ -131,25 +141,13 @@ export default function Page() {
131141 } ,
132142 [ files ] ,
133143 ) ;
134-
135144 const onDrop = useCallback (
136145 ( acceptedFiles : File [ ] ) => {
137146 fileCheckAndSelect ( acceptedFiles ) ;
138147 } ,
139148 [ fileCheckAndSelect ] ,
140149 ) ;
141150
142- const { getRootProps, getInputProps } = useDropzone ( {
143- onDrop,
144- multiple : true ,
145- noClick : false ,
146- noKeyboard : false ,
147- accept : {
148- "application/pdf" : [ ".pdf" ] ,
149- "image/*" : [ ".jpeg" , ".jpg" , ".png" , ".gif" ] ,
150- } ,
151- } ) ;
152-
153151 const sensors = useSensors (
154152 useSensor ( PointerSensor , { activationConstraint : { distance : 5 } } ) ,
155153 ) ;
@@ -169,18 +167,18 @@ export default function Page() {
169167 transition,
170168 isDragging,
171169 } = useSortable ( { id } ) ;
172-
170+
173171 const style = {
174172 transform : CSS . Transform . toString ( transform ) ,
175173 transition,
176174 zIndex : isDragging ? 1000 : 1 ,
177175 } ;
178-
176+
179177 return (
180- < div
181- ref = { setNodeRef }
182- style = { style }
183- { ...attributes }
178+ < div
179+ ref = { setNodeRef }
180+ style = { style }
181+ { ...attributes }
184182 { ...listeners }
185183 className = { isDragging ? "cursor-grabbing" : "cursor-grab" }
186184 >
@@ -195,20 +193,8 @@ export default function Page() {
195193 const oldIndex = previews . findIndex ( ( item ) => item . id === active . id ) ;
196194 const newIndex = previews . findIndex ( ( item ) => item . id === over . id ) ;
197195
198- const newFiles = arrayMove (
199- previews . map ( ( p ) => p . file ) ,
200- oldIndex ,
201- newIndex ,
202- ) ;
203-
204- previews . forEach ( ( item ) => URL . revokeObjectURL ( item . preview ) ) ;
205-
206-
207- const newPreviews = newFiles . map ( ( file , idx ) => ( {
208- id : `${ file . name } -${ file . lastModified } -${ Date . now ( ) } -${ idx } -reorder` ,
209- file,
210- preview : URL . createObjectURL ( file ) ,
211- } ) ) ;
196+ const newPreviews = arrayMove ( previews , oldIndex , newIndex ) ;
197+ const newFiles = arrayMove ( files , oldIndex , newIndex ) ;
212198
213199 setFiles ( newFiles ) ;
214200 setPreviews ( newPreviews ) ;
@@ -220,28 +206,26 @@ export default function Page() {
220206 if ( deletedPreview ) {
221207 URL . revokeObjectURL ( deletedPreview . preview ) ;
222208 }
223-
224-
209+
225210 const remainingFiles = files . filter ( ( _ , i ) => i !== index ) ;
226-
227-
228- previews . forEach ( ( item , i ) => {
229- if ( i !== index ) {
230- URL . revokeObjectURL ( item . preview ) ;
231- }
232- } ) ;
233-
234-
235- const newPreviews = remainingFiles . map ( ( file , idx ) => ( {
236- id : `${ file . name } -${ file . lastModified } -${ Date . now ( ) } -${ idx } -delete` ,
237- file,
238- preview : URL . createObjectURL ( file ) ,
239- } ) ) ;
240-
211+ const remainingPreviews = previews . filter ( ( _ , i ) => i !== index ) ;
212+
241213 setFiles ( remainingFiles ) ;
242- setPreviews ( newPreviews ) ;
214+ setPreviews ( remainingPreviews ) ;
243215 } ;
244216
217+ const clearAllFiles = useCallback ( ( ) => {
218+ // Clean up all URLs
219+ previews . forEach ( ( item ) => {
220+ try {
221+ URL . revokeObjectURL ( item . preview ) ;
222+ } catch ( e ) { }
223+ } ) ;
224+
225+ setFiles ( [ ] ) ;
226+ setPreviews ( [ ] ) ;
227+ } , [ previews ] ) ;
228+
245229 const handlePrint = async ( ) => {
246230 const isPdf = files . length === 1 && files [ 0 ] ?. type === "application/pdf" ;
247231
@@ -280,26 +264,17 @@ export default function Page() {
280264 } ,
281265 ) ;
282266
283- setFiles ( [ ] ) ;
284- setPreviews ( [ ] ) ;
285-
286-
287- previews . forEach ( ( item ) => {
288- try {
289- URL . revokeObjectURL ( item . preview ) ;
290- } catch ( e ) {
291-
292- }
293- } ) ;
267+ clearAllFiles ( ) ;
294268 } catch ( error ) {
269+
295270 } finally {
296271 setIsUploading ( false ) ;
297272 }
298273 } ;
299274
300275 return (
301276 < main className = "mx-auto max-w-3xl px-4 py-8" >
302- < div className = "flex h-[calc(100vh-85px )] flex-col justify-center px-6 font-play" >
277+ < div className = "flex h-[calc(100vh-90px )] flex-col justify-center px-6 font-play" >
303278 < div className = "2xl:my-15 flex flex-col items-center" >
304279 { previews . length === 0 && (
305280 < fieldset className = "mb-4 w-full max-w-md rounded-lg border-2 border-gray-300 p-4 pr-8" >
@@ -328,8 +303,6 @@ export default function Page() {
328303 ? "border-solid border-[#6D28D9] bg-purple-50 dark:bg-[#130E1F]"
329304 : "border-dashed border-gray-300"
330305 } p-8 text-center transition-all duration-200`}
331- onDragEnter = { ( ) => setIsDragging ( true ) }
332- onDragLeave = { ( ) => setIsDragging ( false ) }
333306 >
334307 < input { ...getInputProps ( ) } />
335308 { isDragActive || isGlobalDragging ? (
@@ -340,11 +313,11 @@ export default function Page() {
340313 < Upload className = "mt-2 h-10 w-10 animate-bounce text-[#6D28D9]" />
341314 </ div >
342315 ) : (
343- < p >
316+ < div >
344317 Drag 'n' drop some files here, or{ " " }
345318 < span className = "text-[#6D28D9]" > click</ span > to
346319 select files
347- </ p >
320+ </ div >
348321 ) }
349322 < div
350323 className = { `mt-2 text-xs ${
@@ -355,6 +328,12 @@ export default function Page() {
355328 >
356329 { files . length } files selected
357330 </ div >
331+ < div className = "mt-4 text-sm text-gray-500" >
332+ Note: Uploaded papers are first reviewed by our team
333+ before appearing on the website. If your paper doesn't
334+ show up immediately, please be patient, it's likely
335+ still under review.
336+ </ div >
358337 </ section >
359338 ) }
360339 </ Dropzone >
@@ -372,7 +351,7 @@ export default function Page() {
372351 < div className = "flex w-max gap-4" >
373352 < div
374353 className = "scrollbar-hide flex aspect-[2/1] w-full max-w-4xl flex-col justify-between overflow-x-auto overflow-y-hidden rounded-[40px] border-[6px] border-indigo-900 bg-indigo-900/10 p-8"
375- style = { { minHeight : 320 } }
354+ style = { { minHeight : 340 , maxHeight : 340 } }
376355 >
377356 < DndContext
378357 sensors = { sensors }
@@ -387,16 +366,13 @@ export default function Page() {
387366 { previews . map ( ( item , index ) => (
388367 < SortablePreview key = { item . id } id = { item . id } >
389368 < div className = "group relative flex-shrink-0" >
390-
391369 < div className = "relative h-60 w-48 overflow-hidden rounded-2xl outline outline-2 outline-white" >
392-
393370 < div className = "absolute left-0 top-0 z-20 flex h-10 w-10 items-center justify-center rounded-br-2xl rounded-tl-2xl bg-slate-600" >
394371 < span className = "text-xl font-bold text-white" >
395372 { index + 1 }
396373 </ span >
397374 </ div >
398375
399-
400376 < Button
401377 onClick = { ( e ) => {
402378 e . preventDefault ( ) ;
@@ -411,7 +387,6 @@ export default function Page() {
411387 < FiTrash className = "h-5 w-5" />
412388 </ Button >
413389
414- { /* File preview content */ }
415390 < div className = "absolute inset-0 z-10" >
416391 { item . file . type . startsWith ( "image/" ) ? (
417392 < Image
@@ -435,7 +410,6 @@ export default function Page() {
435410 </ SortablePreview >
436411 ) ) }
437412
438- { /* Add more button */ }
439413 < Dropzone
440414 onDrop = { onDrop }
441415 accept = { {
@@ -453,7 +427,7 @@ export default function Page() {
453427 >
454428 { ( { getRootProps, getInputProps } ) => (
455429 < div
456- className = "relative h-20 w-20 cursor-pointer flex-shrink-0"
430+ className = "relative h-20 w-20 flex-shrink-0 cursor-pointer "
457431 { ...getRootProps ( ) }
458432 >
459433 < input { ...getInputProps ( ) } />
@@ -469,14 +443,21 @@ export default function Page() {
469443 </ div >
470444 ) }
471445 </ Dropzone >
472- </ div >
446+ </ div > { " " }
447+ { previews . length > 2 && (
448+ < div className = "text-l mt-4 text-right text-white/50" >
449+ scroll to view more >>
450+ </ div >
451+ ) }
473452 </ SortableContext >
474453 </ DndContext >
475- < p className = "mt-4 text-center text-xl text-white/50" >
476- Drag to re-order.
477- </ p >
478454 </ div >
479455 </ div >
456+ { previews . length > 1 && (
457+ < div className = "mt-4 text-right text-xl text-white/50" >
458+ Drag to reorder
459+ </ div >
460+ ) }
480461 </ section >
481462 ) }
482463
@@ -529,4 +510,4 @@ export default function Page() {
529510 ) }
530511 </ main >
531512 ) ;
532- }
513+ }
0 commit comments