11"use client" ;
22
3- import { useState , useEffect } from "react" ;
3+ import { useState } from "react" ;
4+ import { useQuery , useMutation , useQueryClient } from "@tanstack/react-query" ;
45import { toast } from "sonner" ;
56import { cn } from "@/lib/utils" ;
67
@@ -19,10 +20,9 @@ export default function ObjectList({
1920 className = "" ,
2021 type = "server" , // default to "server"
2122} : {
22- className ?: "" ;
23+ className ?: string ;
2324 type ?: "server" | "storage" ;
2425} ) {
25- const [ objects , setObjects ] = useState < S3Object [ ] > ( [ ] ) ;
2626 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
2727 const [ selectedFolder , setSelectedFolder ] = useState < string | null > ( null ) ;
2828 const [ sortOrder , setSortOrder ] = useState < {
@@ -34,27 +34,51 @@ export default function ObjectList({
3434 const itemsPerPage = 100 ;
3535 const s3Region = process . env . AWS_S3_REGION ;
3636
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 ( ) ;
4638
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 ) ;
5147 }
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+ } ) ;
5361
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+ } ) ;
5882
5983 const handleSearch = ( e : React . ChangeEvent < HTMLInputElement > ) => {
6084 setSearchQuery ( e . target . value ) ;
@@ -86,39 +110,20 @@ export default function ObjectList({
86110 ) ;
87111 } ;
88112
89- const handleDeleteSelected = async ( ) => {
113+ const handleDeleteSelected = ( ) => {
90114 const confirmDelete = window . confirm (
91115 "Are you sure you want to delete the selected objects? This action cannot be undone." ,
92116 ) ;
93117
94- if ( ! confirmDelete ) {
95- return ;
118+ if ( confirmDelete ) {
119+ deleteMutation . mutate ( selectedObjects ) ;
96120 }
121+ } ;
97122
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 > ;
106125
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 ?? [ ] ;
122127
123128 const filteredObjects = objects . filter ( ( object ) => {
124129 const matchesFolder = selectedFolder
0 commit comments