Skip to content

Commit 850b981

Browse files
committed
Fix: Add image generation UI hint & date storage
1 parent cf88b55 commit 850b981

File tree

4 files changed

+98
-34
lines changed

4 files changed

+98
-34
lines changed

src/components/image/ImageGenerateHistoryItem.tsx

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React from 'react';
22
import { ImageGenerationResult } from '../../types/image';
33
import { MessageContent } from '../../types/chat';
4+
import { Loader } from 'lucide-react';
5+
import { useTranslation } from 'react-i18next';
46

57
interface ImageGenerateHistoryItemProps {
68
imageResult: ImageGenerationResult;
@@ -9,10 +11,13 @@ interface ImageGenerateHistoryItemProps {
911
const ImageGenerateHistoryItem: React.FC<ImageGenerateHistoryItemProps> = ({
1012
imageResult,
1113
}) => {
14+
const { t } = useTranslation();
15+
const isGenerating = imageResult.status === 'generating';
16+
const isFailed = imageResult.status === 'failed';
1217

1318
// Function to render image grid based on number of images and aspect ratio
1419
const renderImageGrid = (images: MessageContent[]) => {
15-
if (images.length === 0) return null;
20+
if (images.length === 0 && !isGenerating) return null;
1621

1722
// Convert aspect ratio string (e.g. "16:9") to calculate best layout
1823
const [widthRatio, heightRatio] = imageResult.aspectRatio.split(':').map(Number);
@@ -29,6 +34,29 @@ const ImageGenerateHistoryItem: React.FC<ImageGenerateHistoryItemProps> = ({
2934
gridClass = "grid-cols-2 grid-rows-2";
3035
}
3136

37+
// If still generating, show a loading indicator
38+
if (isGenerating) {
39+
return (
40+
<div className="flex items-center justify-center h-64 bg-gray-50" style={{ aspectRatio: imageResult.aspectRatio.replace(':', '/') }}>
41+
<div className="flex flex-col items-center">
42+
<Loader size={40} className="text-primary-500 animate-spin" />
43+
<p className="mt-4 text-sm text-gray-600">{t('imageGeneration.creatingImage')}</p>
44+
</div>
45+
</div>
46+
);
47+
}
48+
49+
// Show error message if generation failed
50+
if (isFailed) {
51+
return (
52+
<div className="flex items-center justify-center h-64 bg-red-50" style={{ aspectRatio: imageResult.aspectRatio.replace(':', '/') }}>
53+
<div className="flex flex-col items-center">
54+
<p className="text-red-600">{t('imageGeneration.generationFailed')}</p>
55+
</div>
56+
</div>
57+
);
58+
}
59+
3260
return (
3361
<div className={`grid gap-2 ${gridClass}`}>
3462
{images.map((image, index) => (
@@ -51,7 +79,7 @@ const ImageGenerateHistoryItem: React.FC<ImageGenerateHistoryItemProps> = ({
5179
};
5280

5381
return (
54-
<div className="mb-6 overflow-hidden border rounded-lg shadow-sm image-result-item">
82+
<div className={`mb-6 overflow-hidden border rounded-lg shadow-sm image-result-item ${isGenerating ? 'border-primary-300' : ''}`}>
5583
<div className="image-result-container">
5684
{renderImageGrid(imageResult.images)}
5785
</div>
@@ -63,18 +91,23 @@ const ImageGenerateHistoryItem: React.FC<ImageGenerateHistoryItemProps> = ({
6391
<p className="mb-1 text-sm font-medium text-gray-900 truncate">{imageResult.prompt}</p>
6492

6593
<div className="flex flex-wrap gap-2 mt-2">
66-
<span className="px-2 py-1 text-xs bg-gray-100 rounded-full">
94+
<span className={`px-2 py-1 text-xs rounded-full ${isGenerating ? 'bg-primary-100 text-primary-800' : 'bg-gray-100'}`}>
6795
{imageResult.model}
6896
</span>
69-
<span className="px-2 py-1 text-xs bg-gray-100 rounded-full">
97+
<span className={`px-2 py-1 text-xs rounded-full ${isGenerating ? 'bg-primary-100 text-primary-800' : 'bg-gray-100'}`}>
7098
{imageResult.provider}
7199
</span>
72-
<span className="px-2 py-1 text-xs bg-gray-100 rounded-full">
100+
<span className={`px-2 py-1 text-xs rounded-full ${isGenerating ? 'bg-primary-100 text-primary-800' : 'bg-gray-100'}`}>
73101
{imageResult.aspectRatio}
74102
</span>
75-
<span className="px-2 py-1 text-xs bg-gray-100 rounded-full">
103+
<span className={`px-2 py-1 text-xs rounded-full ${isGenerating ? 'bg-primary-100 text-primary-800' : 'bg-gray-100'}`}>
76104
Seed: {imageResult.seed}
77105
</span>
106+
{isGenerating && (
107+
<span className="px-2 py-1 text-xs text-white rounded-full bg-primary-500 animate-pulse">
108+
{t('imageGeneration.generating')}
109+
</span>
110+
)}
78111
</div>
79112
</div>
80113
</div>

src/components/pages/ImageGenerationPage.tsx

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,17 @@ export const ImageGenerationPage = () => {
4343
const dbService = DatabaseIntegrationService.getInstance();
4444
const results = await dbService.getImageGenerationResults();
4545
if (results && results.length > 0) {
46-
// Sort by most recent first
47-
const sortedResults = results.sort((a, b) =>
48-
b.imageResultId.localeCompare(a.imageResultId)
49-
);
46+
// Sort by most recent first using updatedAt timestamp
47+
const sortedResults = results.sort((a, b) => {
48+
// Convert string dates to Date objects if necessary
49+
const dateA = a.updatedAt instanceof Date ? a.updatedAt : new Date(a.updatedAt);
50+
const dateB = b.updatedAt instanceof Date ? b.updatedAt : new Date(b.updatedAt);
51+
// Sort newest first
52+
return dateB.getTime() - dateA.getTime();
53+
});
5054
setHistoryResults(sortedResults);
55+
} else {
56+
setHistoryResults([]);
5157
}
5258
} catch (error) {
5359
console.error('Error refreshing image history:', error);
@@ -142,6 +148,32 @@ export const ImageGenerationPage = () => {
142148
};
143149
}, []);
144150

151+
// Process and update the result after generation
152+
const processGenerationResult = async (handler: ImageGenerationHandler, images: string[]) => {
153+
// Convert image data to full URLs if needed
154+
const processedImages = images.map(img => {
155+
const base64Data = img as string;
156+
if (base64Data.startsWith('data:image')) {
157+
return base64Data;
158+
} else {
159+
return `data:image/png;base64,${base64Data}`;
160+
}
161+
});
162+
163+
// Update the handler with successful results
164+
await handler.setSuccess(processedImages);
165+
166+
// Refresh history to include the new generation
167+
await refreshImageHistory();
168+
169+
// Remove the handler to prevent duplication with database results
170+
const imageManager = ImageGenerationManager.getInstance();
171+
imageManager.removeHandler(handler.getId());
172+
173+
// Generate new seed for next generation
174+
generateNewSeed();
175+
};
176+
145177
// Handle generating an image using selected provider
146178
const handleGenerateImage = async () => {
147179
if (!prompt.trim()) return;
@@ -195,24 +227,7 @@ export const ImageGenerationPage = () => {
195227

196228
// Process the result
197229
if (images && images.length > 0) {
198-
// Convert image data to full URLs if needed
199-
const processedImages = images.map(img => {
200-
const base64Data = img as string;
201-
if (base64Data.startsWith('data:image')) {
202-
return base64Data;
203-
} else {
204-
return `data:image/png;base64,${base64Data}`;
205-
}
206-
});
207-
208-
// Update the handler with successful results
209-
handler.setSuccess(processedImages);
210-
211-
// Refresh history to include the new generation
212-
refreshImageHistory();
213-
214-
// Generate new seed for next generation
215-
generateNewSeed();
230+
await processGenerationResult(handler, images as string[]);
216231
} else {
217232
throw new Error("No images generated");
218233
}
@@ -239,11 +254,13 @@ export const ImageGenerationPage = () => {
239254
if (b.status === ImageGenerationStatus.GENERATING && a.status !== ImageGenerationStatus.GENERATING) {
240255
return 1;
241256
}
242-
// Then sort by most recent first (assuming imageResultId is a UUID with timestamp components)
243-
return b.imageResultId.localeCompare(a.imageResultId);
257+
// Then sort by most recent first using updatedAt timestamp
258+
const dateA = a.updatedAt instanceof Date ? a.updatedAt : new Date(a.updatedAt);
259+
const dateB = b.updatedAt instanceof Date ? b.updatedAt : new Date(b.updatedAt);
260+
return dateB.getTime() - dateA.getTime();
244261
});
245262

246-
// Combine with historical results
263+
// Combine with historical results and ensure newest is first
247264
return [...handlerResults, ...historyResults];
248265
};
249266

src/services/image-generation-handler.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class ImageGenerationHandler {
2828
private errorMessage: string | null = null;
2929
private onStatusChangeCallback: (handler: ImageGenerationHandler) => void;
3030
private dbService: DatabaseIntegrationService;
31+
private updatedAt: Date;
3132

3233
constructor(
3334
options: ImageGenerationOptions,
@@ -38,6 +39,7 @@ export class ImageGenerationHandler {
3839
this.status = ImageGenerationStatus.PENDING;
3940
this.onStatusChangeCallback = onStatusChangeCallback;
4041
this.dbService = DatabaseIntegrationService.getInstance();
42+
this.updatedAt = new Date();
4143
}
4244

4345
public getId(): string {
@@ -71,30 +73,34 @@ export class ImageGenerationHandler {
7173
aspectRatio: this.options.aspectRatio,
7274
provider: this.options.provider,
7375
model: this.options.model,
74-
images: this.images
76+
images: this.images,
77+
updatedAt: this.updatedAt
7578
};
7679
}
7780

7881
public setGenerating(): void {
7982
this.status = ImageGenerationStatus.GENERATING;
83+
this.updatedAt = new Date();
8084
this.notifyStatusChange();
8185
}
8286

83-
public setSuccess(imageUrls: string[]): void {
87+
public async setSuccess(imageUrls: string[]): Promise<void> {
8488
this.status = ImageGenerationStatus.SUCCESS;
89+
this.updatedAt = new Date();
8590
this.images = imageUrls.map(url => ({
8691
type: MessageContentType.Image,
8792
content: url,
8893
dataJson: ''
8994
}));
9095

9196
// Save to database
92-
this.saveToDatabase();
97+
await this.saveToDatabase();
9398
this.notifyStatusChange();
9499
}
95100

96101
public setFailed(error: Error): void {
97102
this.status = ImageGenerationStatus.FAILED;
103+
this.updatedAt = new Date();
98104
this.errorMessage = error.message;
99105
this.notifyStatusChange();
100106
}
@@ -146,6 +152,13 @@ export class ImageGenerationManager {
146152
return this.handlers.get(id);
147153
}
148154

155+
public removeHandler(id: string): void {
156+
if (this.handlers.has(id)) {
157+
this.handlers.delete(id);
158+
this.notifyUpdate();
159+
}
160+
}
161+
149162
public getAllHandlers(): Map<string, ImageGenerationHandler> {
150163
return this.handlers;
151164
}

src/types/image.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ export interface ImageGenerationResult {
1111
provider: string;
1212
model: string;
1313
images: MessageContent[];
14+
updatedAt: Date;
1415
}

0 commit comments

Comments
 (0)