import 'package:freezed_annotation/freezed_annotation.dart'; part 'reading_progress.freezed.dart'; part 'reading_progress.g.dart'; /// Represents a user's reading progress for a publication @freezed class ReadingProgress with _$ReadingProgress { const factory ReadingProgress({ /// Unique identifier for the progress record required String id, /// Publication identifier from OPDS feed required String publicationId, /// Server identifier required String serverId, /// Current page (0-indexed) required int currentPage, /// Total number of pages required int totalPages, /// EPUB location for EPUB files (CFI, chapter, or position) String? epubLocation, /// Last read timestamp required DateTime lastReadAt, /// Publication cache ID (references publications table) String? publicationCacheId, /// Feed URL this entry was opened from (for "next in series" navigation) String? seriesFeedUrl, }) = _ReadingProgress; const ReadingProgress._(); /// Create ReadingProgress from JSON factory ReadingProgress.fromJson(Map json) => _$ReadingProgressFromJson(json); /// Create ReadingProgress from database row factory ReadingProgress.fromDatabase(Map row) { return ReadingProgress( id: row['id'] as String, publicationId: row['publication_id'] as String, serverId: row['server_id'] as String, currentPage: row['current_page'] as int, totalPages: row['total_pages'] as int, epubLocation: row['epub_location'] as String?, lastReadAt: DateTime.fromMillisecondsSinceEpoch(row['last_read_at'] as int), publicationCacheId: row['publication_cache_id'] as String?, seriesFeedUrl: row['series_feed_url'] as String?, ); } /// Calculate reading progress percentage double get progressPercentage { if (totalPages == 0) return 0.0; return (currentPage + 1) / totalPages; } /// Check if the publication has been started bool get isStarted => currentPage > 0; /// Check if the publication is completed bool get isCompleted => currentPage >= totalPages - 1; } extension ReadingProgressX on ReadingProgress { /// Convert ReadingProgress to database row Map toDatabase() { return { 'id': id, 'publication_id': publicationId, 'server_id': serverId, 'current_page': currentPage, 'total_pages': totalPages, 'epub_location': epubLocation, 'last_read_at': lastReadAt.millisecondsSinceEpoch, 'publication_cache_id': publicationCacheId, 'series_feed_url': seriesFeedUrl, }; } }