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: 124 AppColors.textPrimary.withValues( 125 alpha: 0.6, 126 ), 127 ), 128 const SizedBox(width: 5), 129 Text( 130 DateTimeUtils.formatCount(count), 131 style: TextStyle( 132 color: 133 AppColors.textPrimary.withValues( 134 alpha: 0.6, 135 ), 136 fontSize: 13, 137 ), 138 ), 139 ], 140 ), 141 ), 142 ), 143 ); 144 }, 145 ), 146 const SizedBox(width: 8), 147 ], 148 149 // Heart button 150 Consumer<VoteProvider>( 151 builder: (context, voteProvider, child) { 152 final isLiked = voteProvider.isLiked(post.post.uri); 153 final adjustedScore = voteProvider.getAdjustedScore( 154 post.post.uri, 155 post.post.stats.score, 156 ); 157 158 return Semantics( 159 button: true, 160 label: 161 isLiked 162 ? 'Unlike post, $adjustedScore ' 163 '${adjustedScore == 1 ? "like" : "likes"}' 164 : 'Like post, $adjustedScore ' 165 '${adjustedScore == 1 ? "like" : "likes"}', 166 child: InkWell( 167 onTap: () async { 168 // Check authentication 169 final authProvider = context.read<AuthProvider>(); 170 if (!authProvider.isAuthenticated) { 171 // Show sign-in dialog 172 final shouldSignIn = await SignInDialog.show( 173 context, 174 message: 'You need to sign in to like posts.', 175 ); 176 177 if ((shouldSignIn ?? false) && context.mounted) { 178 // TODO: Navigate to sign-in screen 179 if (kDebugMode) { 180 debugPrint('Navigate to sign-in screen'); 181 } 182 } 183 return; 184 } 185 186 // Light haptic feedback on both like and unlike 187 await HapticFeedback.lightImpact(); 188 189 // Toggle vote with optimistic update 190 try { 191 await voteProvider.toggleVote( 192 postUri: post.post.uri, 193 postCid: post.post.cid, 194 ); 195 } on Exception catch (e) { 196 if (kDebugMode) { 197 debugPrint('Failed to toggle vote: $e'); 198 } 199 // TODO: Show error snackbar 200 } 201 }, 202 child: Padding( 203 padding: const EdgeInsets.symmetric( 204 horizontal: 12, 205 vertical: 10, 206 ), 207 child: Row( 208 mainAxisSize: MainAxisSize.min, 209 children: [ 210 AnimatedHeartIcon( 211 isLiked: isLiked, 212 color: AppColors.textPrimary.withValues(alpha: 0.6), 213 likedColor: const Color(0xFFFF0033), 214 ), 215 const SizedBox(width: 5), 216 Text( 217 DateTimeUtils.formatCount(adjustedScore), 218 style: TextStyle( 219 color: AppColors.textPrimary.withValues( 220 alpha: 0.6, 221 ), 222 fontSize: 13, 223 ), 224 ), 225 ], 226 ), 227 ), 228 ), 229 ); 230 }, 231 ), 232 ], 233 ), 234 ], 235 ); 236 } 237}