Commit 73495fd9 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: compress avatar images client-side before upload

Resize any image to 512x512 center-cropped JPEG at 82% quality before
uploading. Handles huge photos from phone cameras that would fail or
timeout on upload. Removes the 5MB size gate since compression brings
all images to ~50-150KB.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 3d0e89de
...@@ -79,17 +79,14 @@ export async function mountView(el) { ...@@ -79,17 +79,14 @@ export async function mountView(el) {
avatarInput.addEventListener('change', async (e) => { avatarInput.addEventListener('change', async (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file || isUploading) return; if (!file || isUploading) return;
if (file.size > 5 * 1024 * 1024) {
bus.emit('toast', { text: 'الصورة كبيرة جداً (الحد 5MB)', type: 'error' });
return;
}
isUploading = true; isUploading = true;
avatarWrap.style.opacity = '0.5'; avatarWrap.style.opacity = '0.5';
const uploading = el.querySelector('#avatar-uploading'); const uploading = el.querySelector('#avatar-uploading');
uploading.style.display = 'block'; uploading.style.display = 'block';
try { try {
const compressed = await compressAvatar(file);
const formData = new FormData(); const formData = new FormData();
formData.append('avatar', file); formData.append('avatar', compressed, 'avatar.jpg');
const token = store.get('auth.token'); const token = store.get('auth.token');
const res = await fetch('/api/avatar.php', { const res = await fetch('/api/avatar.php', {
method: 'POST', method: 'POST',
...@@ -124,6 +121,33 @@ export async function mountView(el) { ...@@ -124,6 +121,33 @@ export async function mountView(el) {
}); });
} }
function compressAvatar(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const size = 512;
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
const min = Math.min(img.width, img.height);
const sx = (img.width - min) / 2;
const sy = (img.height - min) / 2;
ctx.drawImage(img, sx, sy, min, min, 0, 0, size, size);
canvas.toBlob(
(blob) => blob ? resolve(blob) : reject(new Error('Compression failed')),
'image/jpeg',
0.82
);
URL.revokeObjectURL(img.src);
};
img.onerror = () => { URL.revokeObjectURL(img.src); reject(new Error('Invalid image')); };
img.src = URL.createObjectURL(file);
});
}
function renderRating(label, rating) { function renderRating(label, rating) {
return ` return `
<div style="display:flex;justify-content:space-between;align-items:center;"> <div style="display:flex;justify-content:space-between;align-items:center;">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment