import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:veloe_kemono_party_flutter/models/attachment.dart'; import 'smart_image_container.dart'; import 'package:share_plus/share_plus.dart'; import '../../models/post.dart'; class PostContainer extends StatelessWidget { final Post post; const PostContainer({super.key, required this.post}); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.all(8), child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ _PostHeader(post: post), _PostContentSection(post: post), _PostActionsFooter(post: post), ], ), ), ); } } class _PostHeader extends StatelessWidget { final Post post; const _PostHeader({required this.post}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: Row( children: [ /*CircleAvatar( backgroundImage: NetworkImage(post.author), radius: 20, ),*/ const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( post.title, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, ), ), ], ), ), // Time and menu Text( post.published, style: Theme.of(context).textTheme.bodySmall, ), /*IconButton( icon: const Icon(Icons.more_vert), onPressed: () => _showPostMenu(context), ),*/ ], ), ); } } // New footer component class _PostActionsFooter extends StatelessWidget { final Post post; const _PostActionsFooter({required this.post}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(top: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Interactive buttons Row( children: [/* IconButton( icon: Icon( post.isLiked ? Icons.favorite : Icons.favorite_border, color: post.isLiked ? Colors.red : null, ), onPressed: () => _toggleLike(), ), Text(_formatCount(post.likeCount)), const SizedBox(width: 16), IconButton( icon: const Icon(Icons.comment_outlined), onPressed: () => _showComments(), ), Text(_formatCount(post.commentCount)), */], ), // Share button IconButton( icon: const Icon(Icons.share_outlined), onPressed: () => _sharePost(post), ), ], ), ); } } void _sharePost(Post post) { var link = 'https://kemono.su/${post.service}/user/${post.user}/post/${post.id}'; Share.share(link); } class _PostContentSection extends StatelessWidget { final Post post; const _PostContentSection({required this.post}); @override Widget build(BuildContext context) { final imageAttachments = post.attachments .where((a) => _isImageFile(a.link)) .toList(); return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // Text Content HtmlWidget( post.content, textStyle: Theme.of(context).textTheme.bodyMedium, onTapUrl: (url) => _handleUrlLaunch(context, url), ), // Attachments Grid if (imageAttachments.isNotEmpty) LayoutBuilder( builder: (context, constraints) { return Column( children: [ const SizedBox(height: 12), GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: _calculateColumnCount(constraints.maxWidth), ), itemCount: imageAttachments.length, itemBuilder: (context, index) => Padding( padding: const EdgeInsets.all(4), child: SmartImageContainer( imageUrl: imageAttachments[index].link, //onTap: () => _handleAttachmentTap(index), ), ), ), // Attachment links list ...post.attachments.map( (attachment) => _buildAttachmentLink(context,attachment), ), ], ); }, ), ], ); } Widget _buildAttachmentLink(BuildContext context,Attachment attachment) { return Padding( padding: const EdgeInsets.only(top: 8), child: InkWell( onTap: () => _handleUrlLaunch(context, attachment.link), child: Row( children: [ const Icon(Icons.link, size: 16), const SizedBox(width: 8), Expanded( child: Text( attachment.name ?? _parseFileName(attachment.link), style: const TextStyle( color: Colors.blue, decoration: TextDecoration.underline, ), overflow: TextOverflow.ellipsis, ), ), ], ), ), ); } int _calculateColumnCount(double availableWidth) { return (availableWidth / 400).floor().clamp(1, 3); } String _parseFileName(String url) { try { return Uri.parse(url).pathSegments.last; } catch (e) { return 'Download'; } } bool _isImageFile(String url) { try { final uri = Uri.parse(url); final path = uri.path.toLowerCase(); final cleanPath = path.split('?').first.split('#').first; const imageExtensions = { 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tiff', 'heic', 'heif' }; return imageExtensions.any((ext) => cleanPath.endsWith('.$ext')); } catch (e) { return false; // Invalid URL format } } } Future _handleUrlLaunch(BuildContext context, String url) async { try { await launchUrl( Uri.parse(url), mode: LaunchMode.externalApplication, ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Could not open link: ${e.toString()}')), ); } return true; }