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/// 17/// The [maxDepth] parameter controls how deeply nested comments can be 18/// before they're rendered at the same level to prevent UI overflow. 19class CommentThread extends StatelessWidget { 20 const CommentThread({ 21 required this.thread, 22 this.depth = 0, 23 this.maxDepth = 5, 24 this.currentTime, 25 this.onLoadMoreReplies, 26 super.key, 27 }); 28 29 final ThreadViewComment thread; 30 final int depth; 31 final int maxDepth; 32 final DateTime? currentTime; 33 final VoidCallback? onLoadMoreReplies; 34 35 @override 36 Widget build(BuildContext context) { 37 // Calculate effective depth (flatten after maxDepth) 38 final effectiveDepth = depth > maxDepth ? maxDepth : depth; 39 40 return Column( 41 crossAxisAlignment: CrossAxisAlignment.start, 42 children: [ 43 // Render the comment 44 CommentCard( 45 comment: thread.comment, 46 depth: effectiveDepth, 47 currentTime: currentTime, 48 ), 49 50 // Render replies recursively 51 if (thread.replies != null && thread.replies!.isNotEmpty) 52 ...thread.replies!.map( 53 (reply) => CommentThread( 54 thread: reply, 55 depth: depth + 1, 56 maxDepth: maxDepth, 57 currentTime: currentTime, 58 onLoadMoreReplies: onLoadMoreReplies, 59 ), 60 ), 61 62 // Show "Load more replies" button if there are more 63 if (thread.hasMore) _buildLoadMoreButton(context), 64 ], 65 ); 66 } 67 68 /// Builds the "Load more replies" button 69 Widget _buildLoadMoreButton(BuildContext context) { 70 // Calculate left padding based on depth (align with replies) 71 final effectiveDepth = depth > maxDepth ? maxDepth : depth; 72 final leftPadding = 16.0 + ((effectiveDepth + 1) * 12.0); 73 74 return Container( 75 padding: EdgeInsets.fromLTRB(leftPadding, 8, 16, 8), 76 decoration: const BoxDecoration( 77 border: Border(bottom: BorderSide(color: AppColors.border)), 78 ), 79 child: InkWell( 80 onTap: () { 81 if (onLoadMoreReplies != null) { 82 onLoadMoreReplies!(); 83 } else { 84 if (kDebugMode) { 85 debugPrint('Load more replies tapped (no handler provided)'); 86 } 87 } 88 }, 89 child: Padding( 90 padding: const EdgeInsets.symmetric(vertical: 4), 91 child: Row( 92 children: [ 93 Icon( 94 Icons.add_circle_outline, 95 size: 16, 96 color: AppColors.primary.withValues(alpha: 0.8), 97 ), 98 const SizedBox(width: 6), 99 Text( 100 'Load more replies', 101 style: TextStyle( 102 color: AppColors.primary.withValues(alpha: 0.8), 103 fontSize: 13, 104 fontWeight: FontWeight.w500, 105 ), 106 ), 107 ], 108 ), 109 ), 110 ), 111 ); 112 } 113}