Spaces:
Sleeping
Sleeping
refactor App component to manage temporary URLs and prevent memory leaks
Browse files- src/App.jsx +39 -12
src/App.jsx
CHANGED
@@ -25,14 +25,6 @@ import axios from 'axios';
|
|
25 |
import JSZip from 'jszip';
|
26 |
import { saveAs } from 'file-saver';
|
27 |
|
28 |
-
// Function to convert file to base64
|
29 |
-
const toBase64 = file => new Promise((resolve, reject) => {
|
30 |
-
const reader = new FileReader();
|
31 |
-
reader.readAsDataURL(file);
|
32 |
-
reader.onload = () => resolve(reader.result.split(',')[1]);
|
33 |
-
reader.onerror = error => reject(error);
|
34 |
-
});
|
35 |
-
|
36 |
// Function to get file extension
|
37 |
const getFileExtension = filename => {
|
38 |
return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
|
@@ -51,6 +43,7 @@ const App = () => {
|
|
51 |
const [progress, setProgress] = useState(0);
|
52 |
const fileInputRef = useRef(null);
|
53 |
const toast = useToast();
|
|
|
54 |
|
55 |
const handleFileChange = async (e) => {
|
56 |
const files = Array.from(e.target.files);
|
@@ -97,6 +90,10 @@ const App = () => {
|
|
97 |
setProcessing(true);
|
98 |
const pendingImages = uploadedImages.filter(img => img.status === 'pending');
|
99 |
|
|
|
|
|
|
|
|
|
100 |
for (let i = 0; i < pendingImages.length; i++) {
|
101 |
const image = pendingImages[i];
|
102 |
|
@@ -108,12 +105,13 @@ const App = () => {
|
|
108 |
));
|
109 |
|
110 |
try {
|
111 |
-
//
|
112 |
-
const
|
|
|
113 |
|
114 |
-
// Call the GLIF API
|
115 |
const response = await axios.post('https://simple-api.glif.app/cm7yya7850000la0ckalxpix2', {
|
116 |
-
image:
|
117 |
}, {
|
118 |
headers: {
|
119 |
'Authorization': `Bearer ${apiKey}`,
|
@@ -144,6 +142,10 @@ const App = () => {
|
|
144 |
}
|
145 |
}
|
146 |
|
|
|
|
|
|
|
|
|
147 |
setProgress(100);
|
148 |
setProcessing(false);
|
149 |
|
@@ -210,14 +212,39 @@ const App = () => {
|
|
210 |
};
|
211 |
|
212 |
const removeImage = (id) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
setUploadedImages(prev => prev.filter(img => img.id !== id));
|
214 |
};
|
215 |
|
216 |
const clearAll = () => {
|
|
|
|
|
|
|
|
|
|
|
217 |
setUploadedImages([]);
|
218 |
if (fileInputRef.current) fileInputRef.current.value = '';
|
219 |
};
|
220 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
const getStatusBadge = (status) => {
|
222 |
switch (status) {
|
223 |
case 'pending':
|
|
|
25 |
import JSZip from 'jszip';
|
26 |
import { saveAs } from 'file-saver';
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
// Function to get file extension
|
29 |
const getFileExtension = filename => {
|
30 |
return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
|
|
|
43 |
const [progress, setProgress] = useState(0);
|
44 |
const fileInputRef = useRef(null);
|
45 |
const toast = useToast();
|
46 |
+
const temporaryUrlsRef = useRef([]);
|
47 |
|
48 |
const handleFileChange = async (e) => {
|
49 |
const files = Array.from(e.target.files);
|
|
|
90 |
setProcessing(true);
|
91 |
const pendingImages = uploadedImages.filter(img => img.status === 'pending');
|
92 |
|
93 |
+
// Clean up any previous temporary URLs
|
94 |
+
temporaryUrlsRef.current.forEach(url => URL.revokeObjectURL(url));
|
95 |
+
temporaryUrlsRef.current = [];
|
96 |
+
|
97 |
for (let i = 0; i < pendingImages.length; i++) {
|
98 |
const image = pendingImages[i];
|
99 |
|
|
|
105 |
));
|
106 |
|
107 |
try {
|
108 |
+
// Create a URL for the image file
|
109 |
+
const imageUrl = URL.createObjectURL(image.file);
|
110 |
+
temporaryUrlsRef.current.push(imageUrl);
|
111 |
|
112 |
+
// Call the GLIF API with the image URL
|
113 |
const response = await axios.post('https://simple-api.glif.app/cm7yya7850000la0ckalxpix2', {
|
114 |
+
image: imageUrl
|
115 |
}, {
|
116 |
headers: {
|
117 |
'Authorization': `Bearer ${apiKey}`,
|
|
|
142 |
}
|
143 |
}
|
144 |
|
145 |
+
// Clean up temporary URLs after processing is done
|
146 |
+
temporaryUrlsRef.current.forEach(url => URL.revokeObjectURL(url));
|
147 |
+
temporaryUrlsRef.current = [];
|
148 |
+
|
149 |
setProgress(100);
|
150 |
setProcessing(false);
|
151 |
|
|
|
212 |
};
|
213 |
|
214 |
const removeImage = (id) => {
|
215 |
+
// Find the image to remove
|
216 |
+
const imageToRemove = uploadedImages.find(img => img.id === id);
|
217 |
+
if (imageToRemove && imageToRemove.preview) {
|
218 |
+
// Revoke the object URL to prevent memory leaks
|
219 |
+
URL.revokeObjectURL(imageToRemove.preview);
|
220 |
+
}
|
221 |
+
|
222 |
setUploadedImages(prev => prev.filter(img => img.id !== id));
|
223 |
};
|
224 |
|
225 |
const clearAll = () => {
|
226 |
+
// Revoke all preview URLs to prevent memory leaks
|
227 |
+
uploadedImages.forEach(img => {
|
228 |
+
if (img.preview) URL.revokeObjectURL(img.preview);
|
229 |
+
});
|
230 |
+
|
231 |
setUploadedImages([]);
|
232 |
if (fileInputRef.current) fileInputRef.current.value = '';
|
233 |
};
|
234 |
|
235 |
+
// Clean up URLs when component unmounts
|
236 |
+
React.useEffect(() => {
|
237 |
+
return () => {
|
238 |
+
// Clean up all preview URLs
|
239 |
+
uploadedImages.forEach(img => {
|
240 |
+
if (img.preview) URL.revokeObjectURL(img.preview);
|
241 |
+
});
|
242 |
+
|
243 |
+
// Clean up temporary URLs
|
244 |
+
temporaryUrlsRef.current.forEach(url => URL.revokeObjectURL(url));
|
245 |
+
};
|
246 |
+
}, []);
|
247 |
+
|
248 |
const getStatusBadge = (status) => {
|
249 |
switch (status) {
|
250 |
case 'pending':
|