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

448 lines
15 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/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:flutter_svg/flutter_svg.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;
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++)
_commentBuilder(
isReply: true,
comment: _comment.replies[i],
),
],
);
}
Widget _commentBuilder({required comment, bool isReply = false}) => Container(
margin: EdgeInsets.only(right: isReply ? 24.0 : 0.0),
decoration: BoxDecoration(
border: Border(
right: isReply
? BorderSide(
color: Theme.of(context).colorScheme.caption,
width: 3.0,
)
: BorderSide.none,
),
),
child: Padding(
padding: EdgeInsets.only(right: isReply ? 16.0 : 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
if (comment.user.photo == null)
const Icon(
DidvanIcons.avatar_light,
size: 50,
),
if (comment.user.photo != null)
SkeletonImage(
imageUrl: comment.user.photo,
height: 50,
width: 50,
borderRadius: DesignConfig.highBorderRadius,
),
const SizedBox(width: 6),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
comment.user.fullName,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Theme.of(context).colorScheme.caption),
),
const SizedBox(height: 4),
DidvanText(
DateTimeUtils.momentGenerator(comment.createdAt),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
color: const Color.fromARGB(255, 0, 126, 167),
),
],
),
const Spacer(),
DidvanIconButton(
size: 18,
gestureSize: 24,
icon: DidvanIcons.menu_light,
onPressed: () => _showCommentActions(comment),
),
],
),
const SizedBox(height: 8),
DidvanText(
comment.text,
color: Theme.of(context).colorScheme.caption,
),
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: [
_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,
),
),
const SizedBox(
width: 20,
),
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.bodySmall,
color: Theme.of(context).colorScheme.caption,
),
),
],
),
],
),
),
);
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(
mainAxisAlignment: MainAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
setState(() {
if (_likeValue) {
_likeCount--;
} else {
_likeCount++;
if (_dislikeValue) {
_dislikeValue = false;
_dislikeCount--;
}
}
_likeValue = !_likeValue;
});
widget.onFeedback(
_likeValue, _dislikeValue, _likeCount, _dislikeCount);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
transform: _likeValue
? (Matrix4.identity()..scale(1.2))
: Matrix4.identity(),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: RotationTransition(
turns:
Tween<double>(begin: 0.8, end: 1.0).animate(animation),
child: child,
),
);
},
child: SvgPicture.asset(
_likeValue
? 'lib/assets/icons/likefill.svg'
: 'lib/assets/icons/like.svg',
key: ValueKey(_likeValue),
color:
_likeValue ? const Color.fromARGB(255, 43, 178, 74) : null,
width: 16,
height: 16,
),
),
),
),
const SizedBox(width: 4),
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color:
_likeValue ? const Color.fromARGB(255, 43, 178, 74) : null,
fontWeight: _likeValue ? FontWeight.bold : FontWeight.normal,
),
child: DidvanText(
'پسندیدم',
color: _likeValue ? const Color.fromARGB(255, 43, 178, 74) : null,
style: const TextStyle(fontSize: 11),
),
),
const SizedBox(
width: 5,
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 250),
transitionBuilder: (Widget child, Animation<double> animation) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.5),
end: const Offset(0, 0),
).animate(animation),
child: FadeTransition(opacity: animation, child: child),
);
},
child: DidvanText('(${_likeCount.toString()})',
key: ValueKey(_likeCount),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 11,
color: _likeValue
? const Color.fromARGB(255, 43, 178, 74)
: null,
)),
),
const SizedBox(width: 12),
GestureDetector(
onTap: () {
setState(() {
if (_dislikeValue) {
_dislikeCount--;
} else {
_dislikeCount++;
if (_likeValue) {
_likeValue = false;
_likeCount--;
}
}
_dislikeValue = !_dislikeValue;
});
widget.onFeedback(
_likeValue, _dislikeValue, _likeCount, _dislikeCount);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
transform: _dislikeValue
? (Matrix4.identity()..scale(1.2))
: Matrix4.identity(),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation,
child: RotationTransition(
turns:
Tween<double>(begin: 0.8, end: 1.0).animate(animation),
child: child,
),
);
},
child: SvgPicture.asset(
_dislikeValue
? 'lib/assets/icons/dislike_fill.svg'
: 'lib/assets/icons/dislike.svg',
key: ValueKey(_dislikeValue),
color: _dislikeValue
? const Color.fromARGB(255, 196, 18, 18)
: null,
width: 16,
height: 16,
),
),
),
),
const SizedBox(width: 4),
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: _dislikeValue
? const Color.fromARGB(255, 196, 18, 18)
: null,
fontWeight: _dislikeValue ? FontWeight.bold : FontWeight.normal,
),
child: DidvanText(
'نپسندیدم',
color:
_dislikeValue ? const Color.fromARGB(255, 196, 18, 18) : null,
fontSize: 11,
),
),
const SizedBox(
width: 5,
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 250),
transitionBuilder: (Widget child, Animation<double> animation) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.5),
end: const Offset(0, 0),
).animate(animation),
child: FadeTransition(opacity: animation, child: child),
);
},
child: DidvanText(
'(${_dislikeCount.toString()})',
key: ValueKey(_dislikeCount),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 11,
),
color:
_dislikeValue ? const Color.fromARGB(255, 196, 18, 18) : null,
),
),
const SizedBox(width: 4),
],
);
}
}