GreenPlusbyGXS / web /src /utils /imageHandler.js
gaialive's picture
Upload 106 files
759768a verified
// Image handling utilities for community posts and uploads
class ImageHandler {
constructor() {
this.maxFileSize = 5 * 1024 * 1024; // 5MB
this.allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
this.storageKey = 'ecospire_images';
}
// Validate image file
validateImage(file) {
const errors = [];
if (!file) {
errors.push('No file selected');
return { valid: false, errors };
}
if (file.size > this.maxFileSize) {
errors.push(`File size must be less than ${this.maxFileSize / 1024 / 1024}MB`);
}
if (!this.allowedTypes.includes(file.type)) {
errors.push('File type not supported. Please use JPEG, PNG, GIF, or WebP');
}
return {
valid: errors.length === 0,
errors
};
}
// Convert file to base64 for storage
async fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// Compress image if needed
async compressImage(file, maxWidth = 800, quality = 0.8) {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = () => {
// Calculate new dimensions
let { width, height } = img;
if (width > maxWidth) {
height = (height * maxWidth) / width;
width = maxWidth;
}
canvas.width = width;
canvas.height = height;
// Draw and compress
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(resolve, file.type, quality);
};
img.src = URL.createObjectURL(file);
});
}
// Process image for upload
async processImage(file, options = {}) {
const validation = this.validateImage(file);
if (!validation.valid) {
throw new Error(validation.errors.join(', '));
}
const {
compress = true,
maxWidth = 800,
quality = 0.8
} = options;
let processedFile = file;
// Compress if requested and file is large
if (compress && file.size > 500 * 1024) { // 500KB threshold
processedFile = await this.compressImage(file, maxWidth, quality);
}
const base64 = await this.fileToBase64(processedFile);
return {
original: {
name: file.name,
size: file.size,
type: file.type
},
processed: {
name: processedFile.name || file.name,
size: processedFile.size,
type: processedFile.type,
data: base64
},
preview: base64
};
}
// Store image in localStorage (for demo purposes)
storeImage(imageData, imageId) {
try {
const stored = this.getStoredImages();
stored[imageId] = {
...imageData,
timestamp: Date.now(),
id: imageId
};
localStorage.setItem(this.storageKey, JSON.stringify(stored));
return true;
} catch (error) {
console.error('Failed to store image:', error);
return false;
}
}
// Get stored images
getStoredImages() {
try {
const stored = localStorage.getItem(this.storageKey);
return stored ? JSON.parse(stored) : {};
} catch (error) {
console.error('Failed to retrieve stored images:', error);
return {};
}
}
// Get specific image
getImage(imageId) {
const stored = this.getStoredImages();
return stored[imageId] || null;
}
// Delete image
deleteImage(imageId) {
try {
const stored = this.getStoredImages();
delete stored[imageId];
localStorage.setItem(this.storageKey, JSON.stringify(stored));
return true;
} catch (error) {
console.error('Failed to delete image:', error);
return false;
}
}
// Clean up old images (older than 30 days)
cleanupOldImages() {
try {
const stored = this.getStoredImages();
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
let cleaned = 0;
Object.keys(stored).forEach(imageId => {
if (stored[imageId].timestamp < thirtyDaysAgo) {
delete stored[imageId];
cleaned++;
}
});
if (cleaned > 0) {
localStorage.setItem(this.storageKey, JSON.stringify(stored));
console.log(`Cleaned up ${cleaned} old images`);
}
return cleaned;
} catch (error) {
console.error('Failed to cleanup old images:', error);
return 0;
}
}
// Get storage usage info
getStorageInfo() {
try {
const stored = this.getStoredImages();
const imageCount = Object.keys(stored).length;
let totalSize = 0;
Object.values(stored).forEach(image => {
totalSize += image.processed?.size || 0;
});
const storageUsed = new Blob([localStorage.getItem(this.storageKey) || '']).size;
return {
imageCount,
totalSize,
storageUsed,
formattedSize: this.formatFileSize(totalSize),
formattedStorageUsed: this.formatFileSize(storageUsed)
};
} catch (error) {
console.error('Failed to get storage info:', error);
return {
imageCount: 0,
totalSize: 0,
storageUsed: 0,
formattedSize: '0 B',
formattedStorageUsed: '0 B'
};
}
}
// Format file size for display
formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Generate placeholder images for demo
generatePlaceholderImage(type = 'environmental', width = 400, height = 300) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Background gradient
const gradient = ctx.createLinearGradient(0, 0, 0, height);
switch (type) {
case 'water':
gradient.addColorStop(0, '#87CEEB');
gradient.addColorStop(1, '#4682B4');
break;
case 'nature':
gradient.addColorStop(0, '#98FB98');
gradient.addColorStop(1, '#228B22');
break;
case 'air':
gradient.addColorStop(0, '#87CEEB');
gradient.addColorStop(1, '#B0E0E6');
break;
default:
gradient.addColorStop(0, '#90EE90');
gradient.addColorStop(1, '#32CD32');
}
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Add text
ctx.fillStyle = 'white';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.fillText('🌿 EcoSpire', width / 2, height / 2);
ctx.font = '16px Arial';
ctx.fillText('Environmental Image', width / 2, height / 2 + 30);
return canvas.toDataURL('image/png');
}
}
export const imageHandler = new ImageHandler();
export default imageHandler;