import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:worldhopper/models/opds_entry.dart'; import 'package:worldhopper/models/opds_link.dart'; part 'opds_feed.freezed.dart'; part 'opds_feed.g.dart'; /// Represents an OPDS feed (navigation or acquisition) @freezed class OPDSFeed with _$OPDSFeed { const factory OPDSFeed({ /// Feed ID required String id, /// Feed title required String title, /// Feed subtitle String? subtitle, /// Feed icon URL String? icon, /// Last update timestamp DateTime? updated, /// Feed author String? author, /// All links in the feed @Default([]) List links, /// All entries in the feed @Default([]) List entries, /// Total results (for paginated feeds) int? totalResults, /// Items per page (for paginated feeds) int? itemsPerPage, /// Start index (for paginated feeds) int? startIndex, }) = _OPDSFeed; const OPDSFeed._(); /// Create OPDSFeed from JSON factory OPDSFeed.fromJson(Map json) => _$OPDSFeedFromJson(json); /// Check if this is a navigation feed bool get isNavigationFeed => entries.any((entry) => entry.isNavigation); /// Check if this is an acquisition feed (contains publications) bool get isAcquisitionFeed => !isNavigationFeed; /// Get the start (root) link OPDSLink? get startLink => links.cast().firstWhere( (link) => link?.rel == 'start', orElse: () => null, ); /// Get the self link OPDSLink? get selfLink => links.cast().firstWhere( (link) => link?.rel == 'self', orElse: () => null, ); /// Get the next page link OPDSLink? get nextLink => links.cast().firstWhere( (link) => link?.rel == 'next', orElse: () => null, ); /// Get the previous page link OPDSLink? get previousLink => links.cast().firstWhere( (link) => link?.rel == 'previous' || link?.rel == 'prev', orElse: () => null, ); }