import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:worldhopper/models/opds_entry.dart'; /// Card widget displaying a publication or navigation entry class PublicationCard extends StatelessWidget { final OPDSEntry entry; final String serverId; final VoidCallback onTap; const PublicationCard({ super.key, required this.entry, required this.serverId, required this.onTap, }); @override Widget build(BuildContext context) { return Card( clipBehavior: Clip.antiAlias, child: InkWell( onTap: onTap, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Cover image Expanded( child: _buildCover(context), ), // Title and metadata Padding( padding: const EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( entry.title, style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.bold, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), if (entry.authors.isNotEmpty) ...[ const SizedBox(height: 4), Text( entry.authors.join(', '), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withOpacity(0.6), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], if (entry.hasStreamLink) ...[ const SizedBox(height: 4), Row( children: [ Icon( Icons.auto_stories, size: 14, color: Theme.of(context).colorScheme.primary, ), const SizedBox(width: 4), Text( '${entry.streamLink!.pageCount} pages', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.primary, ), ), ], ), ], if (entry.isNavigation) ...[ const SizedBox(height: 4), Row( children: [ Icon( Icons.folder, size: 14, color: Theme.of(context).colorScheme.secondary, ), const SizedBox(width: 4), Text( 'Collection', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.secondary, ), ), ], ), ], ], ), ), ], ), ), ); } Widget _buildCover(BuildContext context) { final imageUrl = entry.thumbnailUrl ?? entry.coverUrl; if (imageUrl == null) { return _buildPlaceholder(context); } return CachedNetworkImage( imageUrl: imageUrl, fit: BoxFit.cover, placeholder: (context, url) => _buildPlaceholder(context), errorWidget: (context, url, error) => _buildPlaceholder(context), ); } Widget _buildPlaceholder(BuildContext context) { return Container( color: Theme.of(context).colorScheme.surfaceContainerHighest, child: Center( child: Icon( entry.isNavigation ? Icons.folder : Icons.book, size: 48, color: Theme.of(context).colorScheme.onSurface.withOpacity(0.3), ), ), ); } }