fixes for image gen
This commit is contained in:
@@ -169,8 +169,41 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
images_failed = 0
|
||||
errors = []
|
||||
|
||||
# Initialize image queue in meta for frontend
|
||||
image_queue = []
|
||||
for idx, img in enumerate(images, 1):
|
||||
content_obj = img.content
|
||||
if not content_obj:
|
||||
if img.task:
|
||||
content_title = img.task.title
|
||||
else:
|
||||
content_title = "Content"
|
||||
else:
|
||||
content_title = content_obj.title or content_obj.meta_title or "Content"
|
||||
|
||||
image_queue.append({
|
||||
'image_id': img.id,
|
||||
'index': idx,
|
||||
'label': f"{img.image_type.replace('_', ' ').title()} Image",
|
||||
'content_title': content_title,
|
||||
'status': 'pending',
|
||||
'progress': 0,
|
||||
'image_url': None,
|
||||
'error': None
|
||||
})
|
||||
|
||||
# Send initial queue to frontend
|
||||
if progress_tracker:
|
||||
initial_meta = step_tracker.get_meta() if step_tracker else {}
|
||||
initial_meta['image_queue'] = image_queue
|
||||
progress_tracker.update("PREP", 10, f"Preparing to generate {total_images} image{'s' if total_images != 1 else ''}", meta=initial_meta)
|
||||
|
||||
# Process each image sequentially
|
||||
for index, image in enumerate(images, 1):
|
||||
queue_item = image_queue[index - 1]
|
||||
queue_item['status'] = 'processing'
|
||||
queue_item['progress'] = 0
|
||||
|
||||
try:
|
||||
# Get content title
|
||||
content = image.content
|
||||
@@ -202,16 +235,32 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
if progress_tracker and step_tracker:
|
||||
prep_msg = f"Generating image {index} of {total_images}: {image.image_type}"
|
||||
step_tracker.add_request_step("PREP", "success", prep_msg)
|
||||
queue_item['progress'] = 10
|
||||
# Update queue in meta
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_pct = 10 + int((index - 1) / total_images * 15) # 10-25% for PREP
|
||||
progress_tracker.update("PREP", progress_pct, prep_msg, meta=step_tracker.get_meta())
|
||||
progress_tracker.update("PREP", progress_pct, prep_msg, meta=meta)
|
||||
|
||||
# Generate image
|
||||
# Generate image - update progress incrementally
|
||||
if progress_tracker and step_tracker:
|
||||
ai_msg = f"Generating {image.image_type} image {index} of {total_images} with AI"
|
||||
step_tracker.add_response_step("AI_CALL", "success", ai_msg)
|
||||
queue_item['progress'] = 25
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_pct = 25 + int((index - 1) / total_images * 45) # 25-70% for AI_CALL
|
||||
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=step_tracker.get_meta())
|
||||
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta)
|
||||
|
||||
# Update progress to 50% (simulating API call start)
|
||||
queue_item['progress'] = 50
|
||||
if progress_tracker and step_tracker:
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta)
|
||||
|
||||
# Generate image (this is the actual API call)
|
||||
# Frontend will simulate smooth progress from 50% to 95% while waiting
|
||||
result = ai_core.generate_image(
|
||||
prompt=formatted_prompt,
|
||||
provider=provider,
|
||||
@@ -221,8 +270,18 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
function_name='generate_images_from_prompts'
|
||||
)
|
||||
|
||||
# Update progress to 90% (API call completed, processing response)
|
||||
queue_item['progress'] = 90
|
||||
if progress_tracker and step_tracker:
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_tracker.update("AI_CALL", progress_pct, ai_msg, meta=meta)
|
||||
|
||||
if result.get('error'):
|
||||
# Mark as failed
|
||||
queue_item['status'] = 'failed'
|
||||
queue_item['progress'] = 100
|
||||
queue_item['error'] = result['error']
|
||||
with transaction.atomic():
|
||||
image.status = 'failed'
|
||||
image.save(update_fields=['status', 'updated_at'])
|
||||
@@ -235,14 +294,19 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
if progress_tracker and step_tracker:
|
||||
parse_msg = f"Image {index} failed: {result['error']}"
|
||||
step_tracker.add_response_step("PARSE", "error", parse_msg)
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_pct = 70 + int((index - 1) / total_images * 15) # 70-85% for PARSE
|
||||
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=step_tracker.get_meta())
|
||||
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta)
|
||||
|
||||
continue
|
||||
|
||||
image_url = result.get('url')
|
||||
if not image_url:
|
||||
# Mark as failed
|
||||
queue_item['status'] = 'failed'
|
||||
queue_item['progress'] = 100
|
||||
queue_item['error'] = 'No URL returned'
|
||||
with transaction.atomic():
|
||||
image.status = 'failed'
|
||||
image.save(update_fields=['status', 'updated_at'])
|
||||
@@ -255,17 +319,22 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
if progress_tracker and step_tracker:
|
||||
parse_msg = f"Image {index} failed: No URL returned"
|
||||
step_tracker.add_response_step("PARSE", "error", parse_msg)
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_pct = 70 + int((index - 1) / total_images * 15)
|
||||
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=step_tracker.get_meta())
|
||||
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta)
|
||||
|
||||
continue
|
||||
|
||||
# Update progress: PARSE phase
|
||||
# Update progress: PARSE phase (90%)
|
||||
queue_item['progress'] = 90
|
||||
if progress_tracker and step_tracker:
|
||||
parse_msg = f"Image {index} of {total_images} generated successfully"
|
||||
step_tracker.add_response_step("PARSE", "success", parse_msg)
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_pct = 70 + int((index - 1) / total_images * 15) # 70-85% for PARSE
|
||||
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=step_tracker.get_meta())
|
||||
progress_tracker.update("PARSE", progress_pct, parse_msg, meta=meta)
|
||||
|
||||
# Update image record
|
||||
with transaction.atomic():
|
||||
@@ -273,6 +342,10 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
image.status = 'generated'
|
||||
image.save(update_fields=['image_url', 'status', 'updated_at'])
|
||||
|
||||
# Mark queue item as completed
|
||||
queue_item['status'] = 'completed'
|
||||
queue_item['progress'] = 100
|
||||
queue_item['image_url'] = image_url
|
||||
images_generated += 1
|
||||
logger.info(f"Image {image.id} ({image.image_type}) generated successfully: {image_url}")
|
||||
|
||||
@@ -280,8 +353,10 @@ class GenerateImagesFromPromptsFunction(BaseAIFunction):
|
||||
if progress_tracker and step_tracker:
|
||||
save_msg = f"Saved image {index} of {total_images}"
|
||||
step_tracker.add_request_step("SAVE", "success", save_msg)
|
||||
meta = step_tracker.get_meta()
|
||||
meta['image_queue'] = image_queue
|
||||
progress_pct = 85 + int((index - 1) / total_images * 13) # 85-98% for SAVE
|
||||
progress_tracker.update("SAVE", progress_pct, save_msg, meta=step_tracker.get_meta())
|
||||
progress_tracker.update("SAVE", progress_pct, save_msg, meta=meta)
|
||||
|
||||
except Exception as e:
|
||||
# Mark as failed
|
||||
|
||||
281
frontend/src/components/common/ImageQueueModal.tsx
Normal file
281
frontend/src/components/common/ImageQueueModal.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Image Queue Modal - Shows per-image progress for image generation
|
||||
* Similar to WordPress plugin's image queue processor
|
||||
*/
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Modal } from '../ui/modal';
|
||||
|
||||
export interface ImageQueueItem {
|
||||
image_id: number;
|
||||
index: number;
|
||||
label: string;
|
||||
content_title: string;
|
||||
status: 'pending' | 'processing' | 'completed' | 'failed';
|
||||
progress: number;
|
||||
image_url: string | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
interface ImageQueueModalProps {
|
||||
isOpen: boolean;
|
||||
queue: ImageQueueItem[];
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function ImageQueueModal({
|
||||
isOpen,
|
||||
queue,
|
||||
onClose,
|
||||
}: ImageQueueModalProps) {
|
||||
const [localQueue, setLocalQueue] = useState<ImageQueueItem[]>(queue);
|
||||
const progressIntervalsRef = useRef<Map<number, NodeJS.Timeout>>(new Map());
|
||||
|
||||
// Update local queue when prop changes
|
||||
useEffect(() => {
|
||||
setLocalQueue(queue);
|
||||
}, [queue]);
|
||||
|
||||
// Simulate smooth progress for processing images (like WP plugin)
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
||||
localQueue.forEach((item) => {
|
||||
if (item.status === 'processing' && item.progress < 95) {
|
||||
// Clear existing interval for this item
|
||||
const existing = progressIntervalsRef.current.get(item.image_id);
|
||||
if (existing) {
|
||||
clearInterval(existing);
|
||||
}
|
||||
|
||||
// Progressive loading: 50% in 7s, 75% in next 5s, then 5% every second until 95%
|
||||
let currentProgress = item.progress;
|
||||
let phase = 1;
|
||||
let phaseStartTime = Date.now();
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const elapsed = Date.now() - phaseStartTime;
|
||||
|
||||
if (phase === 1 && currentProgress < 50) {
|
||||
// Phase 1: 0% to 50% in 7 seconds (7.14% per second)
|
||||
currentProgress += 0.714;
|
||||
if (currentProgress >= 50 || elapsed >= 7000) {
|
||||
currentProgress = 50;
|
||||
phase = 2;
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
} else if (phase === 2 && currentProgress < 75) {
|
||||
// Phase 2: 50% to 75% in 5 seconds (5% per second)
|
||||
currentProgress += 0.5;
|
||||
if (currentProgress >= 75 || elapsed >= 5000) {
|
||||
currentProgress = 75;
|
||||
phase = 3;
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
} else if (phase === 3 && currentProgress < 95) {
|
||||
// Phase 3: 75% to 95% - 5% every second
|
||||
if (elapsed >= 1000) {
|
||||
currentProgress = Math.min(95, currentProgress + 5);
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
// Update local state
|
||||
setLocalQueue((prev) =>
|
||||
prev.map((q) =>
|
||||
q.image_id === item.image_id
|
||||
? { ...q, progress: Math.min(95, currentProgress) }
|
||||
: q
|
||||
)
|
||||
);
|
||||
}, 100);
|
||||
|
||||
progressIntervalsRef.current.set(item.image_id, interval);
|
||||
} else {
|
||||
// Clear interval if not processing
|
||||
const existing = progressIntervalsRef.current.get(item.image_id);
|
||||
if (existing) {
|
||||
clearInterval(existing);
|
||||
progressIntervalsRef.current.delete(item.image_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
// Cleanup intervals on unmount
|
||||
progressIntervalsRef.current.forEach((interval) => clearInterval(interval));
|
||||
progressIntervalsRef.current.clear();
|
||||
};
|
||||
}, [isOpen, localQueue]);
|
||||
|
||||
// Check if all images are completed
|
||||
const allCompleted = localQueue.every(
|
||||
(item) => item.status === 'completed' || item.status === 'failed'
|
||||
);
|
||||
const completedCount = localQueue.filter(
|
||||
(item) => item.status === 'completed'
|
||||
).length;
|
||||
const failedCount = localQueue.filter((item) => item.status === 'failed').length;
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return 'bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800';
|
||||
case 'failed':
|
||||
return 'bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800';
|
||||
case 'processing':
|
||||
return 'bg-blue-50 border-blue-200 dark:bg-blue-900/20 dark:border-blue-800';
|
||||
default:
|
||||
return 'bg-gray-50 border-gray-200 dark:bg-gray-800 dark:border-gray-700';
|
||||
}
|
||||
};
|
||||
|
||||
const getProgressColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return 'bg-green-500';
|
||||
case 'failed':
|
||||
return 'bg-red-500';
|
||||
case 'processing':
|
||||
return 'bg-blue-500';
|
||||
default:
|
||||
return 'bg-gray-300';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return '✅';
|
||||
case 'failed':
|
||||
return '❌';
|
||||
case 'processing':
|
||||
return '⏳';
|
||||
default:
|
||||
return '⏸️';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return 'Complete';
|
||||
case 'failed':
|
||||
return 'Failed';
|
||||
case 'processing':
|
||||
return 'Generating...';
|
||||
default:
|
||||
return 'Pending';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
className="max-w-4xl"
|
||||
showCloseButton={!allCompleted}
|
||||
>
|
||||
<div className="p-6">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-1 text-center">
|
||||
🎨 Generating Images
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 text-center">
|
||||
Total: {localQueue.length} image{localQueue.length !== 1 ? 's' : ''} in queue
|
||||
{allCompleted && (
|
||||
<span className="ml-2">
|
||||
({completedCount} completed{failedCount > 0 ? `, ${failedCount} failed` : ''})
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Image Queue */}
|
||||
<div className="space-y-3 max-h-[60vh] overflow-y-auto">
|
||||
{localQueue.map((item) => (
|
||||
<div
|
||||
key={item.image_id}
|
||||
className={`p-4 rounded-lg border-2 transition-colors ${getStatusColor(
|
||||
item.status
|
||||
)}`}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Left side: Info and progress */}
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold flex items-center justify-center flex-shrink-0">
|
||||
{item.index}
|
||||
</div>
|
||||
<span className="font-semibold text-sm text-gray-900 dark:text-white">
|
||||
{item.label}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 flex-1 truncate">
|
||||
{item.content_title}
|
||||
</span>
|
||||
<span className="text-xs font-semibold text-gray-700 dark:text-gray-300">
|
||||
{getStatusIcon(item.status)} {getStatusText(item.status)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Progress bar */}
|
||||
<div className="relative h-5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className={`h-full ${getProgressColor(
|
||||
item.status
|
||||
)} transition-all duration-300 ease-out`}
|
||||
style={{ width: `${item.progress}%` }}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-xs font-bold text-gray-900 dark:text-white">
|
||||
{item.status === 'processing'
|
||||
? `${Math.round(item.progress)}%`
|
||||
: item.status === 'completed'
|
||||
? '100%'
|
||||
: item.status === 'failed'
|
||||
? 'Failed'
|
||||
: '0%'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error message */}
|
||||
{item.status === 'failed' && item.error && (
|
||||
<div className="mt-2 p-2 bg-red-100 dark:bg-red-900/30 border-l-3 border-red-500 rounded text-xs text-red-700 dark:text-red-300">
|
||||
{item.error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right side: Thumbnail */}
|
||||
<div className="w-20 h-20 bg-gray-100 dark:bg-gray-800 rounded-lg flex items-center justify-center overflow-hidden flex-shrink-0">
|
||||
{item.image_url ? (
|
||||
<img
|
||||
src={item.image_url}
|
||||
alt={item.label}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xs text-gray-400">No image</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Success message when all done */}
|
||||
{allCompleted && (
|
||||
<div className="mt-6 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
|
||||
<p className="text-sm text-green-800 dark:text-green-200 text-center font-medium">
|
||||
Image generation complete! {completedCount} image
|
||||
{completedCount !== 1 ? 's' : ''} generated successfully
|
||||
{failedCount > 0 && `, ${failedCount} failed`}.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,17 @@ export interface ProgressState {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ImageQueueItem {
|
||||
image_id: number;
|
||||
index: number;
|
||||
label: string;
|
||||
content_title: string;
|
||||
status: 'pending' | 'processing' | 'completed' | 'failed';
|
||||
progress: number;
|
||||
image_url: string | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface UseProgressModalReturn {
|
||||
progress: ProgressState;
|
||||
isOpen: boolean;
|
||||
@@ -32,6 +43,7 @@ export interface UseProgressModalReturn {
|
||||
message: string;
|
||||
timestamp?: number;
|
||||
}>; // Step logs for debugging
|
||||
imageQueue?: ImageQueueItem[]; // Image queue for image generation
|
||||
}
|
||||
|
||||
export function useProgressModal(): UseProgressModalReturn {
|
||||
@@ -54,6 +66,9 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
timestamp?: number;
|
||||
}>>([]);
|
||||
|
||||
// Image queue state for image generation
|
||||
const [imageQueue, setImageQueue] = useState<ImageQueueItem[] | undefined>(undefined);
|
||||
|
||||
// Track displayed percentage and current step for step-based progress
|
||||
const displayedPercentageRef = useRef(0);
|
||||
const currentStepRef = useRef<string | null>(null);
|
||||
@@ -458,6 +473,11 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
}
|
||||
}
|
||||
|
||||
// Extract image queue if available (for image generation)
|
||||
if (meta.image_queue && Array.isArray(meta.image_queue)) {
|
||||
setImageQueue(meta.image_queue as ImageQueueItem[]);
|
||||
}
|
||||
|
||||
// Update step logs if available
|
||||
if (meta.request_steps || meta.response_steps) {
|
||||
// Collect all steps for display in modal
|
||||
@@ -736,6 +756,7 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
displayedPercentageRef.current = 0;
|
||||
currentStepRef.current = null;
|
||||
setStepLogs([]); // Clear step logs when closing modal
|
||||
setImageQueue(undefined); // Clear image queue when closing modal
|
||||
setIsOpen(false);
|
||||
// Clear taskId to stop polling when modal closes
|
||||
setTaskId(null);
|
||||
@@ -768,6 +789,8 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
message: 'Getting started...',
|
||||
status: 'pending',
|
||||
});
|
||||
setStepLogs([]);
|
||||
setImageQueue(undefined);
|
||||
setTaskId(null);
|
||||
setTitle('');
|
||||
setIsOpen(false);
|
||||
@@ -785,6 +808,7 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
taskId, // Expose taskId for use in ProgressModal
|
||||
functionId, // Expose functionId for use in ProgressModal
|
||||
stepLogs, // Expose step logs for debugging
|
||||
imageQueue, // Expose image queue for image generation
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||
import { FileIcon, DownloadIcon, BoltIcon } from '../../icons';
|
||||
import { createImagesPageConfig } from '../../config/pages/images.config';
|
||||
import ProgressModal from '../../components/common/ProgressModal';
|
||||
import ImageQueueModal from '../../components/common/ImageQueueModal';
|
||||
import { useProgressModal } from '../../hooks/useProgressModal';
|
||||
|
||||
export default function Images() {
|
||||
@@ -284,29 +285,49 @@ export default function Images() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Progress Modal for AI Functions */}
|
||||
<ProgressModal
|
||||
isOpen={progressModal.isOpen}
|
||||
title={progressModal.title}
|
||||
percentage={progressModal.progress.percentage}
|
||||
status={progressModal.progress.status}
|
||||
message={progressModal.progress.message}
|
||||
details={progressModal.progress.details}
|
||||
taskId={progressModal.taskId || undefined}
|
||||
functionId={progressModal.functionId}
|
||||
onClose={() => {
|
||||
const wasCompleted = progressModal.progress.status === 'completed';
|
||||
progressModal.closeModal();
|
||||
// Reload data after modal closes (if completed)
|
||||
if (wasCompleted && !hasReloadedRef.current) {
|
||||
hasReloadedRef.current = true;
|
||||
loadImages();
|
||||
setTimeout(() => {
|
||||
hasReloadedRef.current = false;
|
||||
}, 1000);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* Image Queue Modal for Image Generation */}
|
||||
{progressModal.imageQueue && progressModal.imageQueue.length > 0 ? (
|
||||
<ImageQueueModal
|
||||
isOpen={progressModal.isOpen}
|
||||
queue={progressModal.imageQueue}
|
||||
onClose={() => {
|
||||
const wasCompleted = progressModal.progress.status === 'completed';
|
||||
progressModal.closeModal();
|
||||
// Reload data after modal closes (if completed)
|
||||
if (wasCompleted && !hasReloadedRef.current) {
|
||||
hasReloadedRef.current = true;
|
||||
loadImages();
|
||||
setTimeout(() => {
|
||||
hasReloadedRef.current = false;
|
||||
}, 1000);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
/* Progress Modal for other AI Functions */
|
||||
<ProgressModal
|
||||
isOpen={progressModal.isOpen}
|
||||
title={progressModal.title}
|
||||
percentage={progressModal.progress.percentage}
|
||||
status={progressModal.progress.status}
|
||||
message={progressModal.progress.message}
|
||||
details={progressModal.progress.details}
|
||||
taskId={progressModal.taskId || undefined}
|
||||
functionId={progressModal.functionId}
|
||||
onClose={() => {
|
||||
const wasCompleted = progressModal.progress.status === 'completed';
|
||||
progressModal.closeModal();
|
||||
// Reload data after modal closes (if completed)
|
||||
if (wasCompleted && !hasReloadedRef.current) {
|
||||
hasReloadedRef.current = true;
|
||||
loadImages();
|
||||
setTimeout(() => {
|
||||
hasReloadedRef.current = false;
|
||||
}, 1000);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user