import 'package:common_utils/common_utils.dart'; import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:news_app/main.dart'; import 'package:news_app/model/new_comment_model.dart'; import 'package:news_app/model/user_model.dart'; import 'package:news_app/util/keyboard_util.dart'; import 'package:news_app/util/log.util.dart'; import 'package:news_app/widget/empty_2_widget.dart'; import '../../provider/common_provider.dart'; import 'comment_input_bar_widget.dart'; import 'comment_item_widget.dart'; /// @author: bo.zeng /// @email: cnhbwds@gmail.com /// @date: 2025 2025/4/17 11:15 /// @description: enum CommentType { video, topic, activity, news, activityVideo } final commentProvider = NotifierProvider.family( () => CommentNotifier(), ); final keyboardProvider = StateProvider((ref) => false); class CommentPage extends ConsumerStatefulWidget { final CommentType type; final String articleId; const CommentPage({super.key, required this.type, required this.articleId}); @override ConsumerState createState() => _CommentPageSate(); } class _CommentPageSate extends ConsumerState with WidgetsBindingObserver { final FocusNode _focusNode = FocusNode(); int _pageSize = 1; @override void initState() { super.initState(); final notifier = ref.read(commentProvider(widget.type).notifier); WidgetsBinding.instance.addObserver(this); notifier.fetchComment(page: _pageSize, articleId: widget.articleId); ref.listenManual(commentProvider(widget.type), (previous, next) { _currentTotal = next.total ?? 0; }); } @override void didChangeMetrics() { super.didChangeMetrics(); Future.delayed(Duration(milliseconds: 500), () { if (!mounted) return; final bottomInset = View.of(context).viewInsets.bottom; if (bottomInset > 0) { ref.read(keyboardProvider.notifier).state = true; } else { ref.read(keyboardProvider.notifier).state = false; _focusNode.unfocus(); } }); } @override void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); if (KeyboardUtils.isKeyboardVisible(_focusNode)) { KeyboardUtils.hideKeyboard(_focusNode); } _focusNode.dispose(); } int commentIndex = 0; String replyName = ''; void replyComment(int index, String reply) { KeyboardUtils.showKeyboard(context, _focusNode); consoleLog("index:$index reply:$reply"); commentIndex = index; replyName = reply; } void longTapAction(int index) { showCupertinoDialog( context: context, builder: (context) => CupertinoAlertDialog( title: const Text("举报该评论"), content: const Text("您确定要执行此操作吗?"), actions: [ CupertinoDialogAction( child: const Text("取消"), onPressed: () => Navigator.pop(context), ), CupertinoDialogAction( isDestructiveAction: true, // 红色警示按钮 child: const Text("确定"), onPressed: () { reportComment(index); Navigator.pop(context); // 执行删除操作 }, ), ], ), ); } void reportComment(int index) async { final comments = ref.watch(commentProvider(widget.type)); final model = comments.records![index]; consoleLog( "${model.fromUser?.memberId},${model.commentId},${widget.type.toString()}", ); KeyboardUtils.hideKeyboard(_focusNode); final notifier = ref.read(commentProvider(widget.type).notifier); var typeStr = "article"; if (widget.type == CommentType.activity) { typeStr = "activity"; } else if (widget.type == CommentType.video) { typeStr = "video"; } else if (widget.type == CommentType.topic) { typeStr = "topic"; } await notifier.reportComment(typeStr, model.contentId ?? ''); _pageSize = 1; notifier.fetchComment(page: _pageSize, articleId: widget.articleId); } void sendComment(String content, List images) { KeyboardUtils.hideKeyboard(_focusNode); consoleLog("replyName:$replyName content:$content images:$images"); UserModel user = ref.read(globalUserProvider); final notifier = ref.read(commentProvider(widget.type).notifier); if (replyName.isEmpty) { //新增评论 notifier.addComment( widget.articleId, Comment( fromUser: FromUser( memberId: "1", nickname: user.nickName, avatar: user.avatar, ), content: content, createTime: DateUtil.getNowDateStr(), resourceUrls: images.isNotEmpty ? images : null, ), ); } else { //回复评论 SubComment subComment = SubComment( fromUser: FromUser( memberId: "", //当前户Id nickname: user.nickName, avatar: user.avatar, ), content: content, createTime: DateUtil.getNowDateStr(), ); notifier.addReply(widget.articleId, commentIndex, subComment); replyName = ''; commentIndex = 0; } } int _currentTotal = 0; int _getItemLength(NewCommentModel comments) { int length = (comments.records != null && comments.records!.isNotEmpty) ? comments.records!.length : 0; return length; } @override Widget build(BuildContext context) { final comments = ref.watch(commentProvider(widget.type)); double bottomSize = ref.watch(keyboardProvider) ? KeyboardUtils.keyboardHeight(context) : 0; return Stack( children: [ comments.records?.isEmpty == true ? Empty2widget() : EasyRefresh.builder( onRefresh: () async { return IndicatorResult.none; }, onLoad: () async { final newData = ref.read(commentProvider(widget.type)); int total = newData.total ?? 0; if (_currentTotal >= total) { return IndicatorResult.noMore; } else { _pageSize += 1; await ref .read(commentProvider(widget.type).notifier) .fetchComment( page: _pageSize, articleId: widget.articleId, ); return IndicatorResult.success; } }, header: const NotRefreshHeader(), // 空的 header childBuilder: (context, physics) { return ListView.separated( physics: physics, itemCount: _getItemLength(comments), padding: EdgeInsets.only(bottom: 120.h), itemBuilder: (context, index) { final comment = comments.records; if (comment == null || index >= comment.length) { return const SizedBox.shrink(); // 防止越界构建 } return CommentItemWidget( replyComment, longTapAction, comment: comments.records![index], commentIndex: index, ); }, separatorBuilder: (_, __) => SizedBox(height: 20.h), ); }, ), Positioned( bottom: bottomSize, left: 0, right: 0, child: CommentInputBarWidget( _focusNode, sendComment, widget.articleId, showImageUpload: widget.type == CommentType.topic, // 仅指定话题显示图片上传 ), ), ], ); } }