120 lines
3.8 KiB
Dart
120 lines
3.8 KiB
Dart
// Author: fruit.com.cn
|
|
// 评论楼中楼:展开回复、继续加载,没有下一页时给出结束提示。
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../../core/data/models/comment.dart';
|
|
import '../../../core/data/services/comment_controller.dart';
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../l10n/app_localizations.dart';
|
|
import 'comment_tile.dart';
|
|
|
|
const double _replyIndent = 52;
|
|
|
|
/// 一条评论及其回复。
|
|
class CommentThread extends StatelessWidget {
|
|
const CommentThread({
|
|
super.key,
|
|
required this.comment,
|
|
required this.controller,
|
|
required this.handlers,
|
|
});
|
|
|
|
final DramaComment comment;
|
|
final CommentController controller;
|
|
final CommentInteractionHandlers handlers;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final replies = controller.repliesFor(comment.id);
|
|
final expanded = controller.repliesExpanded(comment.id);
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
CommentTile(comment: comment, handlers: handlers),
|
|
if (comment.replyCount > 0 || replies.isNotEmpty) _toggle(context, expanded),
|
|
if (expanded) _replies(context, replies),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _toggle(BuildContext context, bool expanded) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: _replyIndent, bottom: 4),
|
|
child: TextButton(
|
|
key: Key('comment-replies-${comment.id}'),
|
|
onPressed: () => _toggleReplies(context),
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: AppColors.muted,
|
|
padding: EdgeInsets.zero,
|
|
minimumSize: const Size(0, 24),
|
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(width: 16, height: 1, color: AppColors.border),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
expanded ? l10n.hideReplies : l10n.viewReplies(comment.replyCount),
|
|
style: const TextStyle(
|
|
color: AppColors.muted,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
const SizedBox(width: 4),
|
|
Icon(
|
|
expanded
|
|
? Icons.keyboard_arrow_up_rounded
|
|
: Icons.keyboard_arrow_down_rounded,
|
|
size: 14,
|
|
color: AppColors.muted,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _replies(BuildContext context, List<DramaComment> replies) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: _replyIndent),
|
|
child: Column(
|
|
children: [
|
|
if (controller.repliesLoading(comment.id))
|
|
const Padding(
|
|
padding: EdgeInsets.symmetric(vertical: 8),
|
|
child: CircularProgressIndicator(),
|
|
),
|
|
for (final reply in replies)
|
|
CommentTile(comment: reply, handlers: handlers, isReply: true),
|
|
if (controller.repliesHasMore(comment.id))
|
|
TextButton(
|
|
onPressed: () => controller.loadMoreReplies(comment.id),
|
|
child: Text(l10n.loadMore),
|
|
)
|
|
else if (replies.isNotEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Text(
|
|
l10n.noMoreItems,
|
|
style: const TextStyle(color: AppColors.textHint, fontSize: 12),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _toggleReplies(BuildContext context) async {
|
|
if (controller.repliesExpanded(comment.id)) {
|
|
controller.hideReplies(comment.id);
|
|
return;
|
|
}
|
|
await controller.loadReplies(comment.id);
|
|
}
|
|
}
|