Files
voidea/flutter/lib/widgets/comment_thread.dart

160 lines
4.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/collaboration.dart';
import '../core/theme/app_theme.dart';
class CommentThread extends StatelessWidget {
final List<Comment> comments;
final String currentUserId;
final Future<bool> Function(String body, {String? parentId}) onAddComment;
const CommentThread({
super.key,
required this.comments,
required this.currentUserId,
required this.onAddComment,
});
@override
Widget build(BuildContext context) {
final topLevel = comments.where((c) => c.parentId == null).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...topLevel.map((comment) => _CommentCard(
comment: comment,
currentUserId: currentUserId,
onReply: (parentId, body) => onAddComment(body, parentId: parentId),
)),
],
);
}
}
class _CommentCard extends StatelessWidget {
final Comment comment;
final String currentUserId;
final Future<bool> Function(String parentId, String body) onReply;
const _CommentCard({
required this.comment,
required this.currentUserId,
required this.onReply,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: VoIdeaColors.surface,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
radius: 14,
backgroundColor: VoIdeaColors.primary.withOpacity(0.1),
child: Text(
comment.userName.isNotEmpty
? comment.userName[0].toUpperCase()
: '?',
style: const TextStyle(
color: VoIdeaColors.primary,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(width: 8),
Text(
comment.userName,
style: const TextStyle(fontWeight: FontWeight.w500),
),
const Spacer(),
Text(
DateFormat('d MMM').format(comment.createdAt),
style: TextStyle(
color: VoIdeaColors.muted,
fontSize: 12,
),
),
],
),
const SizedBox(height: 8),
Text(comment.body),
if (comment.userId != currentUserId)
Padding(
padding: const EdgeInsets.only(top: 8),
child: GestureDetector(
onTap: () => _showReplyDialog(context),
child: Text(
'Ответить',
style: TextStyle(
color: VoIdeaColors.primary,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
if (comment.replies.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 24, top: 8),
child: Column(
children: comment.replies.map((reply) => _CommentCard(
comment: reply,
currentUserId: currentUserId,
onReply: onReply,
)).toList(),
),
),
],
),
);
}
void _showReplyDialog(BuildContext context) {
final controller = TextEditingController();
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Ответить'),
content: TextField(
controller: controller,
maxLines: 3,
decoration: const InputDecoration(
hintText: 'Напишите ответ...',
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Отмена'),
),
ElevatedButton(
onPressed: () {
if (controller.text.isNotEmpty) {
onReply(comment.id, controller.text);
Navigator.pop(ctx);
}
},
child: const Text('Отправить'),
),
],
),
);
}
}