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