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