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':
|