diff --git a/frontend/src/components/common/ImageQueueModal.tsx b/frontend/src/components/common/ImageQueueModal.tsx index e004cb16..28ddf9c9 100644 --- a/frontend/src/components/common/ImageQueueModal.tsx +++ b/frontend/src/components/common/ImageQueueModal.tsx @@ -83,9 +83,9 @@ export default function ImageQueueModal({ setSmoothProgress(prev => { const current = prev[index] ?? currentPercent; - // Only increment if still below 80% and status is processing or pending + // Only increment if still below 80% and item is actively processing const item = localQueue.find(item => item.index === index); - if (current < 80 && (item?.status === 'processing' || item?.status === 'pending')) { + if (current < 80 && item?.status === 'processing') { const newPercentage = Math.min(current + 2, 80); // 2% every 200ms currentPercent = newPercentage; @@ -111,118 +111,101 @@ export default function ImageQueueModal({ progressIntervalsRef.current[index] = interval; }; + const indicesToClear: number[] = []; + localQueue.forEach((item) => { - // Apply progress logic to all items that are processing OR pending (waiting in queue) - // This ensures all images start progress animation, not just the currently processing one - if (item.status === 'processing' || item.status === 'pending') { - // Initialize smoothProgress if not set (start at 1% for new items) + if (item.status === 'processing') { const currentProgress = smoothProgress[item.index] ?? (item.progress > 0 ? item.progress : 1); - - // Phase 1: 1% → 50% at 100ms per 1% (regardless of events) - // Only start if we're below 50% and no interval is already running + if (currentProgress < 50 && !progressIntervalsRef.current[item.index]) { - let currentPercent = Math.max(currentProgress, 1); // Start from 1% or current if higher - + let currentPercent = Math.max(currentProgress, 1); + const interval = setInterval(() => { setSmoothProgress(prev => { const current = prev[item.index] ?? currentPercent; - - // Only increment if still below 50% and status is processing or pending - if (current < 50 && (item.status === 'processing' || item.status === 'pending')) { - const newPercentage = Math.min(current + 1, 50); // 1% every 100ms + + if (current < 50 && item.status === 'processing') { + const newPercentage = Math.min(current + 1, 50); currentPercent = newPercentage; - - // Stop if we've reached 50% + if (newPercentage >= 50) { clearInterval(interval); delete progressIntervalsRef.current[item.index]; - - // Start Phase 2: 2% every 200ms until 80% + if (newPercentage < 80) { startPhase2Animation(item.index, newPercentage); } } - + return { ...prev, [item.index]: newPercentage }; } else { - // Stop if status changed or reached 50% clearInterval(interval); delete progressIntervalsRef.current[item.index]; return prev; } }); - }, 200); // 1% every 100ms - + }, 200); + progressIntervalsRef.current[item.index] = interval; - } - // Phase 2: 2% every 200ms until 80% - else if (currentProgress >= 50 && currentProgress < 80 && !progressIntervalsRef.current[item.index]) { + } else if (currentProgress >= 50 && currentProgress < 80 && !progressIntervalsRef.current[item.index]) { startPhase2Animation(item.index, currentProgress); - } - // Phase 3: Event-based smooth completion to 100% (after image is downloaded and shown) - // Check if image is ready (has imageUrl) even if still processing - else if (item.imageUrl && currentProgress < 100 && !progressIntervalsRef.current[item.index]) { - // Stop any existing animation + } else if (item.imageUrl && currentProgress < 100 && !progressIntervalsRef.current[item.index]) { if (progressIntervalsRef.current[item.index]) { clearInterval(progressIntervalsRef.current[item.index]); delete progressIntervalsRef.current[item.index]; } - - // Smoothly complete to 100% - if (!progressIntervalsRef.current[item.index]) { - let animatingProgress = Math.max(currentProgress, 80); // Start from 80% or current if higher - const startProgress = animatingProgress; - const endProgress = 100; - const duration = 800; // 500ms - const startTime = Date.now(); - - const interval = setInterval(() => { - const elapsed = Date.now() - startTime; - const progress = Math.min(1, elapsed / duration); - - // Ease-out quadratic - const eased = 1 - Math.pow(1 - progress, 2); - animatingProgress = startProgress + ((endProgress - startProgress) * eased); - - if (animatingProgress >= 100 || elapsed >= duration) { - animatingProgress = 100; - clearInterval(interval); - delete progressIntervalsRef.current[item.index]; - } - - setSmoothProgress(prev => ({ - ...prev, - [item.index]: Math.round(animatingProgress) - })); - }, 16); - - progressIntervalsRef.current[item.index] = interval; - } + + let animatingProgress = Math.max(currentProgress, 80); + const startProgress = animatingProgress; + const endProgress = 100; + const duration = 800; + const startTime = Date.now(); + + const interval = setInterval(() => { + const elapsed = Date.now() - startTime; + const progress = Math.min(1, elapsed / duration); + + const eased = 1 - Math.pow(1 - progress, 2); + animatingProgress = startProgress + ((endProgress - startProgress) * eased); + + if (animatingProgress >= 100 || elapsed >= duration) { + animatingProgress = 100; + clearInterval(interval); + delete progressIntervalsRef.current[item.index]; + } + + setSmoothProgress(prev => ({ + ...prev, + [item.index]: Math.round(animatingProgress) + })); + }, 16); + + progressIntervalsRef.current[item.index] = interval; } - } - }); - - // Handle non-processing items - localQueue.forEach((item) => { - if (item.status !== 'processing') { - // Stop animation for completed/failed items + } else { if (progressIntervalsRef.current[item.index]) { clearInterval(progressIntervalsRef.current[item.index]); delete progressIntervalsRef.current[item.index]; } - // Clear smooth progress for completed/failed items - if (item.status === 'completed' || item.status === 'failed') { - setSmoothProgress(prev => { - const next = { ...prev }; - delete next[item.index]; - return next; - }); + + if (smoothProgress[item.index] !== undefined) { + indicesToClear.push(item.index); } } }); + + if (indicesToClear.length > 0) { + setSmoothProgress(prev => { + const next = { ...prev }; + indicesToClear.forEach(idx => { + delete next[idx]; + }); + return next; + }); + } // Cleanup on unmount return () => { diff --git a/frontend/src/pages/Billing/Usage.tsx b/frontend/src/pages/Billing/Usage.tsx index b505f35f..4e35dba7 100644 --- a/frontend/src/pages/Billing/Usage.tsx +++ b/frontend/src/pages/Billing/Usage.tsx @@ -71,7 +71,7 @@ export default function Usage() {
Monitor your plan limits and usage statistics