import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:worldhopper/models/entry.dart'; import 'package:worldhopper/models/link.dart'; part 'feed.freezed.dart'; part 'feed.g.dart'; /// Represents an OPDS feed (navigation or acquisition) @freezed class Feed with _$Feed { const factory Feed({ /// 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, }) = _Feed; const Feed._(); /// Create Feed from JSON factory Feed.fromJson(Map json) => _$FeedFromJson(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 Link? get startLink => links.cast().firstWhere( (link) => link?.rel == 'start', orElse: () => null, ); /// Get the self link Link? get selfLink => links.cast().firstWhere( (link) => link?.rel == 'self', orElse: () => null, ); /// Get the next page link Link? get nextLink => links.cast().firstWhere( (link) => link?.rel == 'next', orElse: () => null, ); /// Get the previous page link Link? get previousLink => links.cast().firstWhere( (link) => link?.rel == 'previous' || link?.rel == 'prev', orElse: () => null, ); }