103 lines
3.1 KiB
Dart
103 lines
3.1 KiB
Dart
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,
|
|
|
|
/// KOReader sync document hash (partial MD5 of the EPUB file)
|
|
String? syncHash,
|
|
}) = _ReadingProgress;
|
|
|
|
const ReadingProgress._();
|
|
|
|
/// Create ReadingProgress from JSON
|
|
factory ReadingProgress.fromJson(Map<String, dynamic> json) =>
|
|
_$ReadingProgressFromJson(json);
|
|
|
|
/// Create ReadingProgress from database row
|
|
factory ReadingProgress.fromDatabase(Map<String, dynamic> 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?,
|
|
syncHash: row['sync_hash'] as String?,
|
|
);
|
|
}
|
|
|
|
/// Calculate reading progress percentage
|
|
double get progressPercentage {
|
|
if (totalPages == 0) return 0.0;
|
|
// EPUB progress stores currentPage as a 0-100 percentage value directly,
|
|
// while OPDS-PS stores 0-indexed page numbers.
|
|
if (epubLocation != null) {
|
|
return currentPage / totalPages;
|
|
}
|
|
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 {
|
|
if (epubLocation != null) {
|
|
return currentPage >= totalPages;
|
|
}
|
|
return currentPage >= totalPages - 1;
|
|
}
|
|
}
|
|
|
|
extension ReadingProgressX on ReadingProgress {
|
|
/// Convert ReadingProgress to database row
|
|
Map<String, dynamic> 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,
|
|
'sync_hash': syncHash,
|
|
};
|
|
}
|
|
}
|