image processing during saving

This commit is contained in:
Stefan Hardegger
2026-01-16 13:40:59 +01:00
parent ccfc07ac2a
commit 28fa346b63
2 changed files with 57 additions and 9 deletions

View File

@@ -23,6 +23,7 @@ export default function EditStoryPage() {
const [story, setStory] = useState<Story | null>(null); const [story, setStory] = useState<Story | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [processingImages, setProcessingImages] = useState(false);
const [resetingPosition, setResetingPosition] = useState(false); const [resetingPosition, setResetingPosition] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({}); const [errors, setErrors] = useState<Record<string, string>>({});
@@ -41,6 +42,25 @@ export default function EditStoryPage() {
const [coverImage, setCoverImage] = useState<File | null>(null); const [coverImage, setCoverImage] = useState<File | null>(null);
// Helper function to detect external images in HTML content
const hasExternalImages = (htmlContent: string): boolean => {
if (!htmlContent) return false;
// Create a temporary DOM element to parse HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlContent;
const images = tempDiv.querySelectorAll('img');
for (let i = 0; i < images.length; i++) {
const img = images[i];
const src = img.getAttribute('src');
if (src && (src.startsWith('http://') || src.startsWith('https://'))) {
return true;
}
}
return false;
};
useEffect(() => { useEffect(() => {
const loadStory = async () => { const loadStory = async () => {
try { try {
@@ -185,7 +205,34 @@ export default function EditStoryPage() {
tagNames: formData.tags, tagNames: formData.tags,
}; };
const updatedStory = await storyApi.updateStory(storyId, updateData); await storyApi.updateStory(storyId, updateData);
// Process external images if present (download and replace URLs)
if (hasExternalImages(formData.contentHtml)) {
try {
setProcessingImages(true);
const imageResult = await storyApi.processContentImages(storyId, formData.contentHtml);
// If images were processed and content was updated, save the updated content
if (imageResult.processedContent !== formData.contentHtml) {
await storyApi.updateStory(storyId, {
title: formData.title,
summary: formData.summary || undefined,
contentHtml: imageResult.processedContent,
sourceUrl: formData.sourceUrl || undefined,
volume: formData.seriesName && formData.volume ? parseInt(formData.volume) : undefined,
...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName }),
...(formData.authorId ? { authorId: formData.authorId } : { authorName: formData.authorName }),
tagNames: formData.tags,
});
}
} catch (imageError) {
console.warn('Failed to process images, continuing with original content:', imageError);
// Don't fail the entire save if image processing fails
} finally {
setProcessingImages(false);
}
}
// If there's a new cover image, upload it separately // If there's a new cover image, upload it separately
if (coverImage) { if (coverImage) {
@@ -199,6 +246,7 @@ export default function EditStoryPage() {
setErrors({ submit: errorMessage }); setErrors({ submit: errorMessage });
} finally { } finally {
setSaving(false); setSaving(false);
setProcessingImages(false);
} }
}; };
@@ -448,7 +496,7 @@ export default function EditStoryPage() {
type="button" type="button"
variant="ghost" variant="ghost"
onClick={handleDelete} onClick={handleDelete}
disabled={saving} disabled={saving || processingImages}
className="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" className="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
> >
Delete Story Delete Story
@@ -459,17 +507,17 @@ export default function EditStoryPage() {
type="button" type="button"
variant="ghost" variant="ghost"
onClick={() => router.push(`/stories/${storyId}`)} onClick={() => router.push(`/stories/${storyId}`)}
disabled={saving} disabled={saving || processingImages}
> >
Cancel Cancel
</Button> </Button>
<Button <Button
type="submit" type="submit"
loading={saving} loading={saving || processingImages}
disabled={!formData.title || !formData.authorName || !formData.contentHtml} disabled={!formData.title || !formData.authorName || !formData.contentHtml}
> >
Save Changes {processingImages ? 'Processing images...' : 'Save Changes'}
</Button> </Button>
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long