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'; 6 7import '../constants/app_colors.dart'; 8import '../models/post.dart'; 9import '../providers/auth_provider.dart'; 10import '../providers/vote_provider.dart'; 11import '../utils/date_time_utils.dart'; 12import 'icons/animated_heart_icon.dart'; 13import 'icons/reply_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: () { 71 // TODO: Handle share interaction with backend 72 if (kDebugMode) { 73 debugPrint('Share button tapped for post'); 74 } 75 }, 76 child: Padding( 77 padding: const EdgeInsets.symmetric( 78 horizontal: 8, 79 vertical: 10, 80 ), 81 child: ShareIcon( 82 color: AppColors.textPrimary.withValues(alpha: 0.6), 83 ), 84 ), 85 ), 86 ), 87 ], 88 ), 89 90 // Right side: Comment and heart 91 Row( 92 mainAxisSize: MainAxisSize.min, 93 children: [ 94 // Comment button (hidden in detail view) 95 if (showCommentButton) ...[ 96 Semantics( 97 button: true, 98 label: 99 'View ${post.post.stats.commentCount} ${post.post.stats.commentCount == 1 ? "comment" : "comments"}', 100 child: InkWell( 101 onTap: () { 102 // Navigate to post detail screen (works for ALL post types) 103 final encodedUri = Uri.encodeComponent(post.post.uri); 104 context.push('/post/$encodedUri', extra: post); 105 }, 106 child: Padding( 107 padding: const EdgeInsets.symmetric( 108 horizontal: 12, 109 vertical: 10, 110 ), 111 child: Row( 112 mainAxisSize: MainAxisSize.min, 113 children: [ 114 ReplyIcon( 115 color: AppColors.textPrimary.withValues(alpha: 0.6), 116 ), 117 const SizedBox(width: 5), 118 Text( 119 DateTimeUtils.formatCount(post.post.stats.commentCount), 120 style: TextStyle( 121 color: AppColors.textPrimary.withValues(alpha: 0.6), 122 fontSize: 13, 123 ), 124 ), 125 ], 126 ), 127 ), 128 ), 129 ), 130 const SizedBox(width: 8), 131 ], 132 133 // Heart button 134 Consumer<VoteProvider>( 135 builder: (context, voteProvider, child) { 136 final isLiked = voteProvider.isLiked(post.post.uri); 137 final adjustedScore = voteProvider.getAdjustedScore( 138 post.post.uri, 139 post.post.stats.score, 140 ); 141 142 return Semantics( 143 button: true, 144 label: 145 isLiked 146 ? 'Unlike post, $adjustedScore ${adjustedScore == 1 ? "like" : "likes"}' 147 : 'Like post, $adjustedScore ${adjustedScore == 1 ? "like" : "likes"}', 148 child: InkWell( 149 onTap: () async { 150 // Check authentication 151 final authProvider = context.read<AuthProvider>(); 152 if (!authProvider.isAuthenticated) { 153 // Show sign-in dialog 154 final shouldSignIn = await SignInDialog.show( 155 context, 156 message: 'You need to sign in to like posts.', 157 ); 158 159 if ((shouldSignIn ?? false) && context.mounted) { 160 // TODO: Navigate to sign-in screen 161 if (kDebugMode) { 162 debugPrint('Navigate to sign-in screen'); 163 } 164 } 165 return; 166 } 167 168 // Light haptic feedback on both like and unlike 169 await HapticFeedback.lightImpact(); 170 171 // Toggle vote with optimistic update 172 try { 173 await voteProvider.toggleVote( 174 postUri: post.post.uri, 175 postCid: post.post.cid, 176 ); 177 } on Exception catch (e) { 178 if (kDebugMode) { 179 debugPrint('Failed to toggle vote: $e'); 180 } 181 // TODO: Show error snackbar 182 } 183 }, 184 child: Padding( 185 padding: const EdgeInsets.symmetric( 186 horizontal: 12, 187 vertical: 10, 188 ), 189 child: Row( 190 mainAxisSize: MainAxisSize.min, 191 children: [ 192 AnimatedHeartIcon( 193 isLiked: isLiked, 194 color: AppColors.textPrimary.withValues(alpha: 0.6), 195 likedColor: const Color(0xFFFF0033), 196 ), 197 const SizedBox(width: 5), 198 Text( 199 DateTimeUtils.formatCount(adjustedScore), 200 style: TextStyle( 201 color: AppColors.textPrimary.withValues( 202 alpha: 0.6, 203 ), 204 fontSize: 13, 205 ), 206 ), 207 ], 208 ), 209 ), 210 ), 211 ); 212 }, 213 ), 214 ], 215 ), 216 ], 217 ); 218 } 219}