fix: CRITICAL - remove double rotation (frontend + backend)

Root cause: Image was being rotated TWICE:
1. Frontend rotated it in the modal and sent processed blob
2. Backend rotated it AGAIN based on rotation_degrees

This double rotation made the image appear unrotated or distorted.

Solution: Remove frontend image processing entirely.
- Modal now sends ONLY the rotation value
- No imageBlob from modal (uses original)
- Backend receives original image + rotation value
- Backend applies rotation ONCE

Now image is rotated correctly by backend without duplication.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 14:23:20 +03:00
parent 743f377357
commit 285c3f17d4
3 changed files with 18 additions and 51 deletions

View File

@@ -96,9 +96,12 @@ export default function AIOnboarding({ onCancel, onComplete, categories, invento
// CRITICAL: Update state AND processed blob BEFORE calling hook // CRITICAL: Update state AND processed blob BEFORE calling hook
setExtractedItems(updatedItems); setExtractedItems(updatedItems);
// DON'T replace image blob - modal no longer processes image
// Backend will apply rotation to original image
if (adjustments.imageBlob) { if (adjustments.imageBlob) {
console.log('[Update] Setting extractedImageBlob to processed blob:', adjustments.imageBlob.size, 'bytes'); console.log('[Update] Processed blob received but NOT using (backend will process)');
setExtractedImageBlob(adjustments.imageBlob); } else {
console.log('[Update] No imageBlob - using original, backend will apply rotation');
} }
// CRITICAL: Call hook with UPDATED item, not old one // CRITICAL: Call hook with UPDATED item, not old one

View File

@@ -130,57 +130,21 @@ export function ImageAdjustmentModal({ imageUrl, onConfirm, onCancel }: ImageAdj
console.log('[User] Rotation:', rotation, '°'); console.log('[User] Rotation:', rotation, '°');
console.log('[User] Use image flag:', useImage); console.log('[User] Use image flag:', useImage);
// Process image: apply rotation ONLY (no crop) // DON'T process image in frontend - let backend handle rotation
const processCanvas = document.createElement('canvas'); // Just send the rotation value, backend will apply it once
const ctx = processCanvas.getContext('2d');
if (!ctx) {
console.log('[ERROR] Could not get canvas context');
return;
}
const { width, height } = imageDimensions; const { width, height } = imageDimensions;
// Calculate canvas size needed to fit rotated image (prevent clipping) console.log('[Decision] Sending rotation to backend for processing');
const radians = (rotation * Math.PI) / 180; console.log('[Decision] NOT processing image in frontend (avoid double rotation)');
const cos = Math.abs(Math.cos(radians)); console.log('=== END DEBUG ===');
const sin = Math.abs(Math.sin(radians));
const newWidth = Math.ceil(width * cos + height * sin);
const newHeight = Math.ceil(width * sin + height * cos);
processCanvas.width = newWidth; // Return rotation value ONLY - no imageBlob
processCanvas.height = newHeight; // Backend will apply rotation to original image
onConfirm({
console.log('[Processing] Original dimensions:', width, 'x', height); rotation,
console.log('[Processing] Canvas size for rotation:', newWidth, 'x', newHeight); cropBounds: { x: 0, y: 0, width, height }
console.log('[Processing] Applying rotation:', rotation, '°'); // NO imageBlob - use original from extractedImageBlob
});
// Apply rotation around center of canvas
ctx.save();
ctx.translate(newWidth / 2, newHeight / 2);
ctx.rotate(radians);
ctx.drawImage(imageRef.current, -width / 2, -height / 2);
ctx.restore();
console.log('[Processing] Canvas draw complete');
// Convert to blob
processCanvas.toBlob((blob) => {
if (blob) {
console.log('[Result] Processed image blob size:', blob.size, 'bytes');
console.log('[Result] Blob type:', blob.type);
console.log('[Result] Final canvas size:', newWidth, 'x', newHeight);
console.log('=== END DEBUG ===');
// Return with new canvas bounds and rotation
onConfirm({
rotation,
cropBounds: { x: 0, y: 0, width: newWidth, height: newHeight },
imageBlob: blob
});
} else {
console.log('[ERROR] Failed to convert canvas to blob');
onConfirm({ rotation, cropBounds: { x: 0, y: 0, width: newWidth, height: newHeight } });
}
}, 'image/jpeg', 0.95);
}; };
const handleWheel = (e: React.WheelEvent<HTMLCanvasElement>) => { const handleWheel = (e: React.WheelEvent<HTMLCanvasElement>) => {

File diff suppressed because one or more lines are too long