1import 'package:flutter/foundation.dart'; 2import 'package:flutter/material.dart'; 3import 'package:flutter/services.dart'; 4import 'package:go_router/go_router.dart'; 5import 'package:provider/provider.dart'; 6import 'package:share_plus/share_plus.dart'; 7 8import '../constants/app_colors.dart'; 9import '../models/post.dart'; 10import '../providers/auth_provider.dart'; 11import '../providers/vote_provider.dart'; 12import '../utils/date_time_utils.dart'; 13import 'icons/animated_heart_icon.dart'; 14import 'icons/share_icon.dart'; 15import 'sign_in_dialog.dart'; 16 17/// Action buttons row for post cards 18/// 19/// Displays menu, share, comment, and like buttons with proper 20/// authentication handling and optimistic updates. 21class PostCardActions extends StatelessWidget { 22 const PostCardActions({ 23 required this.post, 24 this.showCommentButton = true, 25 super.key, 26 }); 27 28 final FeedViewPost post; 29 final bool showCommentButton; 30 31 @override 32 Widget build(BuildContext context) { 33 return Row( 34 mainAxisAlignment: MainAxisAlignment.spaceBetween, 35 children: [ 36 // Left side: Three dots menu and share 37 Row( 38 mainAxisSize: MainAxisSize.min, 39 children: [ 40 // Three dots menu button 41 Semantics( 42 button: true, 43 label: 'Post options menu', 44 child: InkWell( 45 onTap: () { 46 // TODO: Show post options menu 47 if (kDebugMode) { 48 debugPrint('Menu button tapped for post'); 49 } 50 }, 51 child: Padding( 52 padding: const EdgeInsets.symmetric( 53 horizontal: 8, 54 vertical: 10, 55 ), 56 child: Icon( 57 Icons.more_horiz, 58 size: 20, 59 color: AppColors.textPrimary.withValues(alpha: 0.6), 60 ), 61 ), 62 ), 63 ), 64 65 // Share button 66 Semantics( 67 button: true, 68 label: 'Share post', 69 child: InkWell( 70 onTap: () async { 71 // Add haptic feedback 72 await HapticFeedback.lightImpact(); 73 74 // Share post title and URI 75 final postUri = post.post.uri; 76 final title = post.post.title ?? 'Check out this post'; 77 await Share.share('$title\n\n$postUri', subject: title); 78 }, 79 child: Padding( 80 padding: const EdgeInsets.symmetric( 81 horizontal: 8, 82 vertical: 10, 83 ), 84 child: ShareIcon( 85 color: AppColors.textPrimary.withValues(alpha: 0.6), 86 ), 87 ), 88 ), 89 ), 90 ], 91 ), 92 93 // Right side: Comment and heart 94 Row( 95 mainAxisSize: MainAxisSize.min, 96 children: [ 97 // Comment button (hidden in detail view) 98 if (showCommentButton) ...[ 99 Builder( 100 builder: (context) { 101 final count = post.post.stats.commentCount; 102 final commentText = count == 1 ? 'comment' : 'comments'; 103 return Semantics( 104 button: true, 105 label: 'View $count $commentText', 106 child: InkWell( 107 onTap: () { 108 // Navigate to post detail screen (ALL post types) 109 final encodedUri = Uri.encodeComponent(post.post.uri); 110 context.push('/post/$encodedUri', extra: post); 111 }, 112 child: Padding( 113 padding: const EdgeInsets.symmetric( 114 horizontal: 12, 115 vertical: 10, 116 ), 117 child: Row( 118 mainAxisSize: MainAxisSize.min, 119 children: [ 120 Icon( 121 Icons.chat_bubble_outline, 122 size: 20, 123 color: AppColors.textPrimary.withValues( 124 alpha: 0.6, 125 ), 126 ), 127 const SizedBox(width: 5), 128 Text( 129 DateTimeUtils.formatCount(count), 130 style: TextStyle( 131 color: AppColors.textPrimary.withValues( 132 alpha: 0.6, 133 ), 134 fontSize: 13, 135 ), 136 ), 137 ], 138 ), 139 ), 140 ), 141 ); 142 }, 143 ), 144 const SizedBox(width: 8), 145 ], 146 147 // Heart button 148 Consumer<VoteProvider>( 149 builder: (context, voteProvider, child) { 150 final isLiked = voteProvider.isLiked(post.post.uri); 151 final adjustedScore = voteProvider.getAdjustedScore( 152 post.post.uri, 153 post.post.stats.score, 154 ); 155 156 return Semantics( 157 button: true, 158 label: 159 isLiked 160 ? 'Unlike post, $adjustedScore ' 161 '${adjustedScore == 1 ? "like" : "likes"}' 162 : 'Like post, $adjustedScore ' 163 '${adjustedScore == 1 ? "like" : "likes"}', 164 child: InkWell( 165 onTap: () async { 166 // Check authentication 167 final authProvider = context.read<AuthProvider>(); 168 if (!authProvider.isAuthenticated) { 169 // Show sign-in dialog 170 final shouldSignIn = await SignInDialog.show( 171 context, 172 message: 'You need to sign in to like posts.', 173 ); 174 175 if ((shouldSignIn ?? false) && context.mounted) { 176 // TODO: Navigate to sign-in screen 177 if (kDebugMode) { 178 debugPrint('Navigate to sign-in screen'); 179 } 180 } 181 return; 182 } 183 184 // Light haptic feedback on both like and unlike 185 await HapticFeedback.lightImpact(); 186 187 // Toggle vote with optimistic update 188 try { 189 await voteProvider.toggleVote( 190 postUri: post.post.uri, 191 postCid: post.post.cid, 192 ); 193 } on Exception catch (e) { 194 if (kDebugMode) { 195 debugPrint('Failed to toggle vote: $e'); 196 } 197 // TODO: Show error snackbar 198 } 199 }, 200 child: Padding( 201 padding: const EdgeInsets.symmetric( 202 horizontal: 12, 203 vertical: 10, 204 ), 205 child: Row( 206 mainAxisSize: MainAxisSize.min, 207 children: [ 208 AnimatedHeartIcon( 209 isLiked: isLiked, 210 color: AppColors.textPrimary.withValues(alpha: 0.6), 211 likedColor: const Color(0xFFFF0033), 212 ), 213 const SizedBox(width: 5), 214 Text( 215 DateTimeUtils.formatCount(adjustedScore), 216 style: TextStyle( 217 color: AppColors.textPrimary.withValues( 218 alpha: 0.6, 219 ), 220 fontSize: 13, 221 ), 222 ), 223 ], 224 ), 225 ), 226 ), 227 ); 228 }, 229 ), 230 ], 231 ), 232 ], 233 ); 234 } 235}