didvan-app/lib/views/comments/widgets/comment.dart

350 lines
12 KiB
Dart

import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/comment/comment.dart';
import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/comments/comments_state.dart';
import 'package:didvan/views/widgets/menu_item.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Comment extends StatefulWidget {
final FocusNode focusNode;
final CommentData comment;
const Comment({
Key? key,
required this.focusNode,
required this.comment,
}) : super(key: key);
@override
State<Comment> createState() => CommentState();
}
class CommentState extends State<Comment> {
late final CommentsState state;
bool _showSubComments = false;
CommentData get _comment => widget.comment;
@override
void initState() {
state = context.read<CommentsState>();
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
_commentBuilder(comment: _comment),
if (_comment.replies.isNotEmpty) const SizedBox(height: 16),
for (var i = 0; i < _comment.replies.length; i++)
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: _showSubComments,
child: _commentBuilder(
isReply: true,
comment: _comment.replies[i],
),
),
],
);
}
Widget _commentBuilder({required comment, bool isReply = false}) => Container(
decoration: BoxDecoration(
border: Border(
right: isReply
? BorderSide(color: Theme.of(context).colorScheme.caption)
: BorderSide.none,
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isReply) const SizedBox(width: 12),
if (comment.user.photo == null)
const Icon(DidvanIcons.avatar_light),
if (comment.user.photo != null)
SkeletonImage(
imageUrl: comment.user.photo,
height: 24,
width: 24,
borderRadius: DesignConfig.highBorderRadius,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
DidvanText(
comment.user.fullName,
style: Theme.of(context).textTheme.bodyLarge,
),
const Spacer(),
DidvanText(
DateTimeUtils.momentGenerator(comment.createdAt),
style: Theme.of(context).textTheme.bodySmall,
color: Theme.of(context).colorScheme.caption,
),
const SizedBox(width: 4),
DidvanIconButton(
size: 18,
gestureSize: 24,
icon: DidvanIcons.menu_light,
onPressed: () => _showCommentActions(comment),
),
],
),
const SizedBox(height: 8),
if (isReply)
DidvanText(
'پاسخ به ${comment.toUser.fullName}',
style: Theme.of(context).textTheme.bodySmall,
color: Theme.of(context).colorScheme.caption,
),
const SizedBox(height: 8),
DidvanText(comment.text),
const SizedBox(height: 8),
if (comment.status == 2)
Row(
children: [
Icon(
Icons.circle,
color: Theme.of(context)
.colorScheme
.secondary
.withValues(alpha: 0.3),
size: 18,
),
const SizedBox(width: 4),
DidvanText(
'در انتظار تایید',
color: Theme.of(context)
.colorScheme
.secondary
.withValues(alpha: 0.3),
),
],
),
const SizedBox(height: 8),
if (_comment.status != 2)
Row(
children: [
InkWrapper(
onPressed: () {
state.commentId = _comment.id;
state.replyingTo = comment.user;
state.showReplyBox = true;
state.update();
widget.focusNode.requestFocus();
},
child: DidvanText(
'پاسخ',
style: Theme.of(context).textTheme.bodyLarge,
color: Theme.of(context).colorScheme.primary,
),
),
if (!isReply) const SizedBox(width: 20),
if (!isReply && comment.replies.isNotEmpty)
InkWrapper(
onPressed: () => setState(
() => _showSubComments = !_showSubComments,
),
child: Row(
children: [
DidvanText(
'پاسخ‌ها(${comment.replies.length})',
style: Theme.of(context).textTheme.bodyLarge,
color: Theme.of(context).colorScheme.primary,
),
AnimatedRotation(
duration: DesignConfig.lowAnimationDuration,
turns: _showSubComments ? 0.5 : 0,
child: Icon(
DidvanIcons.angle_down_regular,
color:
Theme.of(context).colorScheme.primary,
),
),
],
),
),
const Spacer(),
_FeedbackButtons(
likeCount: comment.feedback.like,
dislikeCount: comment.feedback.dislike,
likeValue: comment.liked,
dislikeValue: comment.disliked,
onFeedback:
(like, dislike, likeCount, dislikeCount) =>
state.feedback(
id: _comment.id,
like: like,
dislike: dislike,
likeCount: likeCount,
dislikeCount: dislikeCount,
replyId: isReply ? comment.id : null,
),
),
],
),
],
),
),
],
),
);
Future<void> _showCommentActions(comment) async {
ActionSheetUtils(context).showBottomSheet(
data: ActionSheetData(
title: 'گزینه‌های نظر',
content: Column(
children: [
if (comment.user.id != context.read<UserProvider>().user.id)
MenuOption(
title: 'گزارش محتوای نامناسب',
onTap: () {
state.reportComment(comment.id);
ActionSheetUtils(context).pop();
},
icon: DidvanIcons.alert_regular,
),
if (comment.user.id == context.read<UserProvider>().user.id)
MenuOption(
title: 'حذف نظر',
color: Theme.of(context).colorScheme.secondary,
onTap: () {
state.deleteComment(
comment.id,
comment.status,
comment.runtimeType == Reply ? _comment.id : null,
);
ActionSheetUtils(context).pop();
},
icon: DidvanIcons.trash_solid,
),
],
),
hasConfirmButton: false,
hasDismissButton: false,
),
);
}
}
class _FeedbackButtons extends StatefulWidget {
final int likeCount;
final int dislikeCount;
final bool likeValue;
final bool dislikeValue;
final void Function(bool like, bool dislike, int likeCount, int dislikeCount)
onFeedback;
const _FeedbackButtons({
Key? key,
required this.onFeedback,
required this.likeCount,
required this.dislikeCount,
required this.likeValue,
required this.dislikeValue,
}) : super(key: key);
@override
State<_FeedbackButtons> createState() => _FeedbackButtonsState();
}
class _FeedbackButtonsState extends State<_FeedbackButtons> {
late bool _likeValue;
late bool _dislikeValue;
late int _likeCount;
late int _dislikeCount;
@override
void initState() {
_likeValue = widget.likeValue;
_dislikeValue = widget.dislikeValue;
_likeCount = widget.likeCount;
_dislikeCount = widget.dislikeCount;
super.initState();
}
@override
Widget build(BuildContext context) {
return Row(
children: [
DidvanText(
_likeCount.toString(),
style: Theme.of(context).textTheme.bodySmall,
color: Theme.of(context).colorScheme.caption,
),
const SizedBox(width: 4),
DidvanIconButton(
icon: _likeValue ? DidvanIcons.like_solid : DidvanIcons.like_regular,
color: _likeValue ? Theme.of(context).colorScheme.primary : null,
gestureSize: 24,
onPressed: () {
setState(() {
if (_likeValue) {
_likeCount--;
} else {
_likeCount++;
if (_dislikeValue) {
_dislikeValue = false;
_dislikeCount--;
}
}
_likeValue = !_likeValue;
});
widget.onFeedback(
_likeValue, _dislikeValue, _likeCount, _dislikeCount);
},
),
const SizedBox(width: 16),
DidvanText(
_dislikeCount.toString(),
style: Theme.of(context).textTheme.bodySmall,
color: Theme.of(context).colorScheme.caption,
),
const SizedBox(width: 4),
DidvanIconButton(
icon: _dislikeValue
? DidvanIcons.dislike_solid
: DidvanIcons.dislike_regular,
color: _dislikeValue ? Theme.of(context).colorScheme.secondary : null,
gestureSize: 24,
onPressed: () {
setState(() {
if (_dislikeValue) {
_dislikeCount--;
} else {
_dislikeCount++;
if (_likeValue) {
_likeValue = false;
_likeCount--;
}
}
_dislikeValue = !_dislikeValue;
});
widget.onFeedback(
_likeValue, _dislikeValue, _likeCount, _dislikeCount);
},
),
],
);
}
}