2026-03-19 01:12:17 +08:00
|
|
|
import type { Chapter, ChapterViewModel, ImageAsset } from './types';
|
|
|
|
|
|
|
|
|
|
function countByStatus(images: ImageAsset[], status: string): number {
|
|
|
|
|
return images.filter((img) => img.status === status).length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function toChapterViewModel(chapter: Chapter): ChapterViewModel {
|
|
|
|
|
const images = chapter.images ?? [];
|
2026-03-20 10:30:07 +08:00
|
|
|
const cover = chapter.cover_image ?? chapter.cover_asset ?? null;
|
|
|
|
|
const imagesForStatus = cover ? [cover, ...images] : images;
|
|
|
|
|
const completedCount = countByStatus(imagesForStatus, 'completed');
|
|
|
|
|
const hasContent =
|
|
|
|
|
!!(chapter.canonical_markdown ?? '').trim() ||
|
|
|
|
|
!!(chapter.content ?? '').trim() ||
|
|
|
|
|
!!(chapter.summary ?? '').trim();
|
|
|
|
|
const wordCountFromSections = (chapter.sections ?? []).reduce(
|
|
|
|
|
(sum, s) => sum + (s.content?.length ?? 0),
|
|
|
|
|
0,
|
|
|
|
|
);
|
|
|
|
|
const wordCount =
|
|
|
|
|
typeof chapter.word_count === 'number' && chapter.word_count >= 0
|
|
|
|
|
? chapter.word_count
|
|
|
|
|
: wordCountFromSections;
|
2026-03-19 01:12:17 +08:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
id: chapter.id,
|
|
|
|
|
title: chapter.title,
|
|
|
|
|
category: chapter.category,
|
|
|
|
|
orderIndex: chapter.order_index,
|
2026-03-20 10:30:07 +08:00
|
|
|
isEmpty: chapter.status === 'empty' || !hasContent,
|
2026-03-19 01:12:17 +08:00
|
|
|
isNew: chapter.is_new,
|
2026-03-20 10:30:07 +08:00
|
|
|
hasImages: imagesForStatus.length > 0,
|
|
|
|
|
allImagesReady:
|
|
|
|
|
imagesForStatus.length > 0 && completedCount === imagesForStatus.length,
|
2026-03-19 01:12:17 +08:00
|
|
|
pendingImageCount:
|
2026-03-20 10:30:07 +08:00
|
|
|
countByStatus(imagesForStatus, 'pending') +
|
|
|
|
|
countByStatus(imagesForStatus, 'processing'),
|
|
|
|
|
failedImageCount: countByStatus(imagesForStatus, 'failed'),
|
2026-03-19 01:12:17 +08:00
|
|
|
sections: chapter.sections ?? [],
|
2026-03-20 10:30:07 +08:00
|
|
|
coverImageUrl: cover?.url ?? null,
|
2026-03-19 01:12:17 +08:00
|
|
|
updatedAt: chapter.updated_at,
|
2026-03-20 10:30:07 +08:00
|
|
|
wordCount,
|
2026-03-19 01:12:17 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function toChapterViewModels(chapters: Chapter[]): ChapterViewModel[] {
|
|
|
|
|
return chapters.map(toChapterViewModel);
|
|
|
|
|
}
|