1
1
"use client" ;
2
2
3
- import { useState , useEffect } from "react" ;
3
+ import { useState } from "react" ;
4
+ import { useQuery , useMutation , useQueryClient } from "@tanstack/react-query" ;
4
5
import { toast } from "sonner" ;
5
6
import { cn } from "@/lib/utils" ;
6
7
@@ -19,10 +20,9 @@ export default function ObjectList({
19
20
className = "" ,
20
21
type = "server" , // default to "server"
21
22
} : {
22
- className ?: "" ;
23
+ className ?: string ;
23
24
type ?: "server" | "storage" ;
24
25
} ) {
25
- const [ objects , setObjects ] = useState < S3Object [ ] > ( [ ] ) ;
26
26
const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
27
27
const [ selectedFolder , setSelectedFolder ] = useState < string | null > ( null ) ;
28
28
const [ sortOrder , setSortOrder ] = useState < {
@@ -34,27 +34,51 @@ export default function ObjectList({
34
34
const itemsPerPage = 100 ;
35
35
const s3Region = process . env . AWS_S3_REGION ;
36
36
37
- useEffect ( ( ) => {
38
- const fetchObjects = async ( ) => {
39
- try {
40
- const res = await fetch ( `/api/objects?type=${ type } ` ) ;
41
- if ( ! res . ok ) {
42
- const errorMessage = `Error: ${ res . status } ${ res . statusText } ` ;
43
- throw new Error ( errorMessage ) ;
44
- }
45
- const data = ( await res . json ( ) ) as S3ObjectData ;
37
+ const queryClient = useQueryClient ( ) ;
46
38
47
- setObjects ( data . objects ) ;
48
- } catch ( err ) {
49
- const message = ( err as Error ) . message ;
50
- toast . error ( message ) ;
39
+ // Fetch objects using TanStack Query
40
+ const { data, error, isLoading } = useQuery < S3ObjectData , Error > ( {
41
+ queryKey : [ "objects" , type ] ,
42
+ queryFn : async ( ) => {
43
+ const res = await fetch ( `/api/objects?type=${ type } ` ) ;
44
+ if ( ! res . ok ) {
45
+ const errorMessage = `Error: ${ res . status } ${ res . statusText } ` ;
46
+ throw new Error ( errorMessage ) ;
51
47
}
52
- } ;
48
+ return res . json ( ) as Promise < S3ObjectData > ;
49
+ } ,
50
+ } ) ;
51
+
52
+ const deleteMutation = useMutation ( {
53
+ mutationFn : async ( keys : string [ ] ) => {
54
+ const res = await fetch ( "/api/delete-objects" , {
55
+ method : "DELETE" ,
56
+ headers : {
57
+ "Content-Type" : "application/json" ,
58
+ } ,
59
+ body : JSON . stringify ( { keys } ) ,
60
+ } ) ;
53
61
54
- fetchObjects ( ) . catch ( ( err ) => {
55
- toast . error ( `Unexpected error: ${ ( err as Error ) . message } ` ) ;
56
- } ) ;
57
- } , [ type ] ) ;
62
+ if ( ! res . ok ) {
63
+ const errorMessage = `Error deleting objects: ${ res . status } ${ res . statusText } ` ;
64
+ throw new Error ( errorMessage ) ;
65
+ }
66
+
67
+ return keys ;
68
+ } ,
69
+ onSuccess : ( deletedKeys ) => {
70
+ queryClient . setQueryData < S3ObjectData > ( [ "objects" , type ] , ( oldData ) => ( {
71
+ objects : oldData ?. objects . filter (
72
+ ( object ) => ! deletedKeys . includes ( object . key ) ,
73
+ ) ,
74
+ } ) ) ;
75
+ toast . success ( "Selected objects deleted successfully!" ) ;
76
+ setSelectedObjects ( [ ] ) ;
77
+ } ,
78
+ onError : ( error : Error ) => {
79
+ toast . error ( error . message ) ;
80
+ } ,
81
+ } ) ;
58
82
59
83
const handleSearch = ( e : React . ChangeEvent < HTMLInputElement > ) => {
60
84
setSearchQuery ( e . target . value ) ;
@@ -86,39 +110,20 @@ export default function ObjectList({
86
110
) ;
87
111
} ;
88
112
89
- const handleDeleteSelected = async ( ) => {
113
+ const handleDeleteSelected = ( ) => {
90
114
const confirmDelete = window . confirm (
91
115
"Are you sure you want to delete the selected objects? This action cannot be undone." ,
92
116
) ;
93
117
94
- if ( ! confirmDelete ) {
95
- return ;
118
+ if ( confirmDelete ) {
119
+ deleteMutation . mutate ( selectedObjects ) ;
96
120
}
121
+ } ;
97
122
98
- try {
99
- const res = await fetch ( "/api/delete-objects" , {
100
- method : "DELETE" ,
101
- headers : {
102
- "Content-Type" : "application/json" ,
103
- } ,
104
- body : JSON . stringify ( { keys : selectedObjects } ) ,
105
- } ) ;
123
+ if ( isLoading ) return < p > Loading...</ p > ;
124
+ if ( error ) return < p > Error: { error . message } </ p > ;
106
125
107
- if ( ! res . ok ) {
108
- const errorMessage = `Error deleting objects: ${ res . status } ${ res . statusText } ` ;
109
- throw new Error ( errorMessage ) ;
110
- }
111
-
112
- toast . success ( "Selected objects deleted successfully!" ) ;
113
- setObjects ( ( prevObjects ) =>
114
- prevObjects . filter ( ( object ) => ! selectedObjects . includes ( object . key ) ) ,
115
- ) ;
116
- setSelectedObjects ( [ ] ) ;
117
- } catch ( err ) {
118
- const message = ( err as Error ) . message ;
119
- toast . error ( message ) ;
120
- }
121
- } ;
126
+ const objects = data ?. objects ?? [ ] ;
122
127
123
128
const filteredObjects = objects . filter ( ( object ) => {
124
129
const matchesFolder = selectedFolder
0 commit comments