import 'package:flutter/material.dart'; import 'package:timeago/timeago.dart' as timeago; import '../models/post.dart'; import '../theme/app_theme.dart'; import 'media/signed_media_image.dart'; class ReadingPostCard extends StatefulWidget { final Post post; final VoidCallback? onTap; final VoidCallback? onAppreciate; final VoidCallback? onSave; final bool isAppreciated; final bool isSaved; final bool showDivider; const ReadingPostCard({ super.key, required this.post, this.onTap, this.onAppreciate, this.onSave, this.isAppreciated = false, this.isSaved = false, this.showDivider = true, }); @override State createState() => _ReadingPostCardState(); } class _ReadingPostCardState extends State { bool _isPressed = false; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildPostCard(), if (widget.showDivider) _buildDivider(), ], ); } Widget _buildPostCard() { return Container( constraints: const BoxConstraints(maxWidth: 680), margin: _getMargin(), decoration: BoxDecoration( color: AppTheme.white, borderRadius: BorderRadius.circular(AppTheme.radiusSm), border: Border( left: BorderSide( color: _isPressed ? AppTheme.brightNavy : AppTheme.egyptianBlue, width: AppTheme.flowLineWidth, ), right: BorderSide(color: AppTheme.egyptianBlue, width: 1), top: BorderSide(color: AppTheme.egyptianBlue, width: 1), bottom: BorderSide(color: AppTheme.egyptianBlue, width: 1), ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.03), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onTap, onTapDown: (_) => setState(() => _isPressed = true), onTapUp: (_) => setState(() => _isPressed = false), onTapCancel: () => setState(() => _isPressed = false), borderRadius: BorderRadius.circular(AppTheme.radiusSm), splashColor: AppTheme.queenPink.withValues(alpha: 0.3), highlightColor: Colors.transparent, child: Padding( padding: const EdgeInsets.fromLTRB( AppTheme.spacingMd, AppTheme.spacingSm, AppTheme.spacingLg, AppTheme.spacingMd, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildAuthorRow(), const SizedBox(height: AppTheme.spacingMd), _buildBodyText(), const SizedBox(height: AppTheme.spacingLg), _buildActionRow(), ], ), ), ), ), ); } Widget _buildDivider() { return Padding( padding: const EdgeInsets.symmetric(horizontal: AppTheme.spacingMd), child: Container( height: AppTheme.dividerThickness, decoration: BoxDecoration(color: AppTheme.egyptianBlue), ), ); } EdgeInsets _getMargin() { final charCount = widget.post.body.length; if (charCount < 100) { return EdgeInsets.only( left: AppTheme.spacingMd, right: AppTheme.spacingMd, top: AppTheme.spacingPostShort); } else if (charCount < 300) { return EdgeInsets.only( left: AppTheme.spacingMd, right: AppTheme.spacingMd, top: AppTheme.spacingPostMedium); } return EdgeInsets.only( left: AppTheme.spacingMd, right: AppTheme.spacingMd, top: AppTheme.spacingPostLong); } Widget _buildAuthorRow() { final avatarUrl = widget.post.author?.avatarUrl; final handle = widget.post.author?.handle ?? ''; final fallbackColor = _getAvatarColor(handle); return Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: fallbackColor, borderRadius: BorderRadius.circular(10), ), child: avatarUrl != null && avatarUrl.isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(9), child: SignedMediaImage( url: avatarUrl, width: 36, height: 36, fit: BoxFit.cover, ), ) : Center( child: Text( handle.isNotEmpty ? handle[0].toUpperCase() : '?', style: AppTheme.textTheme.labelMedium?.copyWith( color: Colors.white, fontWeight: FontWeight.w600, ), ), ), ), const SizedBox(width: AppTheme.spacingSm), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Flexible( child: Text( widget.post.author?.displayName ?? 'Unknown', style: AppTheme.textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w700, color: AppTheme.navyBlue, ), overflow: TextOverflow.ellipsis, ), ), if (widget.post.author?.isOfficial == true) ...[ const SizedBox(width: AppTheme.spacingXs), _buildOfficialBadge(), ], if (widget.post.author?.isOfficial != true && widget.post.author?.trustState != null) ...[ const SizedBox(width: AppTheme.spacingXs), _buildTrustBadge(), ], ], ), const SizedBox(height: 2), Text( timeago.format(widget.post.createdAt), style: AppTheme.textTheme.labelSmall ?.copyWith(color: AppTheme.egyptianBlue), ), ], ), ), ], ); } Widget _buildBodyText() { final charCount = widget.post.body.length; final style = charCount >= 10 ? AppTheme.postBodyLong : AppTheme.postBody; return Text(widget.post.body, style: style); } Widget _buildActionRow() { return Row( children: [ if (widget.post.contentIntegrityScore < 1.0) ...[ Container( padding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingSm, vertical: 2), decoration: BoxDecoration( color: _getCISColor(widget.post.contentIntegrityScore) .withValues(alpha: 0.08), borderRadius: BorderRadius.circular(AppTheme.radiusXs), ), child: Text( 'CIS ${(widget.post.contentIntegrityScore * 100).toInt()}%', style: AppTheme.textTheme.labelSmall?.copyWith( color: _getCISColor(widget.post.contentIntegrityScore), fontSize: 10, fontWeight: FontWeight.w600, ), ), ), const SizedBox(width: AppTheme.spacingMd), ], const Spacer(), _buildActionButton( icon: widget.isAppreciated ? Icons.favorite : Icons.favorite_border, isActive: widget.isAppreciated, onPressed: widget.onAppreciate, color: AppTheme.brightNavy, ), const SizedBox(width: AppTheme.spacingMd), _buildActionButton( icon: widget.isSaved ? Icons.bookmark : Icons.bookmark_border, isActive: widget.isSaved, onPressed: widget.onSave, color: AppTheme.brightNavy, ), ], ); } Widget _buildActionButton({ required IconData icon, required bool isActive, VoidCallback? onPressed, required Color color, }) { return InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(AppTheme.radiusSm), child: Padding( padding: const EdgeInsets.symmetric( horizontal: AppTheme.spacingSm, vertical: AppTheme.spacingXs), child: Icon(icon, size: 18, color: isActive ? color : AppTheme.royalPurple), ), ); } Widget _buildOfficialBadge() { return Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), decoration: BoxDecoration( color: AppTheme.info.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(AppTheme.radiusXs), ), child: Text('sojorn', style: AppTheme.textTheme.labelSmall?.copyWith( fontSize: 8, fontWeight: FontWeight.w600, color: AppTheme.info, letterSpacing: 0.4, )), ); } Widget _buildTrustBadge() { final tier = widget.post.author!.trustState!.tier; return Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), decoration: BoxDecoration( color: _getTierColor(tier.value).withValues(alpha: 0.12), borderRadius: BorderRadius.circular(AppTheme.radiusXs), ), child: Text(tier.displayName.toUpperCase(), style: AppTheme.textTheme.labelSmall?.copyWith( fontSize: 8, fontWeight: FontWeight.w600, color: _getTierColor(tier.value), letterSpacing: 0.4, )), ); } Color _getAvatarColor(String handle) { final hash = handle.hashCode; final hue = (hash % 360).toDouble(); return HSLColor.fromAHSL(1.0, hue, 0.45, 0.55).toColor(); } Color _getTierColor(String tier) { switch (tier) { case 'established': return AppTheme.tierEstablished; case 'trusted': return AppTheme.tierTrusted; default: return AppTheme.tierNew; } } Color _getCISColor(double cis) { if (cis >= 0.8) return AppTheme.success; if (cis >= 0.6) return AppTheme.warning; return AppTheme.error; } }