1import 'package:flutter/foundation.dart'; 2import 'package:flutter/material.dart'; 3 4import '../constants/app_colors.dart'; 5import '../models/comment.dart'; 6import 'comment_card.dart'; 7 8/// Comment thread widget for displaying comments and their nested replies 9/// 10/// Recursively displays a ThreadViewComment and its replies: 11/// - Renders the comment using CommentCard with optimistic voting 12/// via VoteProvider 13/// - Indents nested replies visually 14/// - Limits nesting depth to prevent excessive indentation 15/// - Shows "Load more replies" button when hasMore is true 16/// - Supports tap-to-reply via [onCommentTap] callback 17/// 18/// The [maxDepth] parameter controls how deeply nested comments can be 19/// before they're rendered at the same level to prevent UI overflow. 20class CommentThread extends StatelessWidget { 21 const CommentThread({ 22 required this.thread, 23 this.depth = 0, 24 this.maxDepth = 5, 25 this.currentTime, 26 this.onLoadMoreReplies, 27 this.onCommentTap, 28 super.key, 29 }); 30 31 final ThreadViewComment thread; 32 final int depth; 33 final int maxDepth; 34 final DateTime? currentTime; 35 final VoidCallback? onLoadMoreReplies; 36 37 /// Callback when a comment is tapped (for reply functionality) 38 final void Function(ThreadViewComment)? onCommentTap; 39 40 @override 41 Widget build(BuildContext context) { 42 // Calculate effective depth (flatten after maxDepth) 43 final effectiveDepth = depth > maxDepth ? maxDepth : depth; 44 45 return Column( 46 crossAxisAlignment: CrossAxisAlignment.start, 47 children: [ 48 // Render the comment with tap handler 49 CommentCard( 50 comment: thread.comment, 51 depth: effectiveDepth, 52 currentTime: currentTime, 53 onTap: onCommentTap != null ? () => onCommentTap!(thread) : null, 54 ), 55 56 // Render replies recursively 57 if (thread.replies != null && thread.replies!.isNotEmpty) 58 ...thread.replies!.map( 59 (reply) => CommentThread( 60 thread: reply, 61 depth: depth + 1, 62 maxDepth: maxDepth, 63 currentTime: currentTime, 64 onLoadMoreReplies: onLoadMoreReplies, 65 onCommentTap: onCommentTap, 66 ), 67 ), 68 69 // Show "Load more replies" button if there are more 70 if (thread.hasMore) _buildLoadMoreButton(context), 71 ], 72 ); 73 } 74 75 /// Builds the "Load more replies" button 76 Widget _buildLoadMoreButton(BuildContext context) { 77 // Calculate left padding based on depth (align with replies) 78 final effectiveDepth = depth > maxDepth ? maxDepth : depth; 79 final leftPadding = 16.0 + ((effectiveDepth + 1) * 12.0); 80 81 return Container( 82 padding: EdgeInsets.fromLTRB(leftPadding, 8, 16, 8), 83 decoration: const BoxDecoration( 84 border: Border(bottom: BorderSide(color: AppColors.border)), 85 ), 86 child: InkWell( 87 onTap: () { 88 if (onLoadMoreReplies != null) { 89 onLoadMoreReplies!(); 90 } else { 91 if (kDebugMode) { 92 debugPrint('Load more replies tapped (no handler provided)'); 93 } 94 } 95 }, 96 child: Padding( 97 padding: const EdgeInsets.symmetric(vertical: 4), 98 child: Row( 99 children: [ 100 Icon( 101 Icons.add_circle_outline, 102 size: 16, 103 color: AppColors.primary.withValues(alpha: 0.8), 104 ), 105 const SizedBox(width: 6), 106 Text( 107 'Load more replies', 108 style: TextStyle( 109 color: AppColors.primary.withValues(alpha: 0.8), 110 fontSize: 13, 111 fontWeight: FontWeight.w500, 112 ), 113 ), 114 ], 115 ), 116 ), 117 ), 118 ); 119 } 120}