Rename all OPDS-prefixed models to generic names (Server, Feed, Entry, Link, StreamLink), expand the ServerSoftware interface to cover all server interactions, and implement a full Kavita REST API client that replaces the OPDS delegation. - Rename OPDS* models to generic names across ~60 files - Add database migrations 13 (opds_id → entry_id) and 14 (unify credentials) - Create OPDSServerSoftware wrapping existing OPDS services - Create KavitaApiClient for direct Kavita REST API calls - Create KavitaFeedMapper to convert Kavita JSON to Feed/Entry models - Rewrite KavitaServerSoftware to use native API (no OPDS delegation) - Unify server credentials (remove softwareUsername/softwarePassword) - Simplify add/edit server UI to single auth section - Add test connection button to server add/edit screens - Add progress indicator to PublicationCard using local and server data - Eliminate softwareType branching in reader screens - Add EntryProgress and fetchEntryProgress to ServerSoftware interface - Fix Entry.acquisitionLink crash on empty links - Add 59 new tests covering models, services, and providers
80 lines
1.9 KiB
Dart
80 lines
1.9 KiB
Dart
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<Link> links,
|
|
|
|
/// All entries in the feed
|
|
@Default([]) List<Entry> 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<String, dynamic> 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<Link?>().firstWhere(
|
|
(link) => link?.rel == 'start',
|
|
orElse: () => null,
|
|
);
|
|
|
|
/// Get the self link
|
|
Link? get selfLink => links.cast<Link?>().firstWhere(
|
|
(link) => link?.rel == 'self',
|
|
orElse: () => null,
|
|
);
|
|
|
|
/// Get the next page link
|
|
Link? get nextLink => links.cast<Link?>().firstWhere(
|
|
(link) => link?.rel == 'next',
|
|
orElse: () => null,
|
|
);
|
|
|
|
/// Get the previous page link
|
|
Link? get previousLink => links.cast<Link?>().firstWhere(
|
|
(link) => link?.rel == 'previous' || link?.rel == 'prev',
|
|
orElse: () => null,
|
|
);
|
|
}
|