From 7feca652d8fff9a4a73c586300558867403cecf3 Mon Sep 17 00:00:00 2001 From: SangHyun Kim Date: Sat, 31 Aug 2024 14:18:25 +0900 Subject: [PATCH] =?UTF-8?q?=EB=AC=B8=EC=9E=90=20AppString.dart=EC=97=90?= =?UTF-8?q?=EC=84=9C=20import=20=ED=95=B4=EC=84=9C=20=EC=93=B0=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/widget/CommentBottomSheetWidget.dart | 128 ++++++----- .../post/widget/CommentItemWidget.dart | 209 +++++++++--------- .../post/widget/DeleteConfirmationDialog.dart | 40 ---- .../DeleteConfirmationDialogWidget.dart | 37 ++++ .../post/widget/EditCommentDialog.dart | 50 ----- .../post/widget/EditCommentDialogWidget.dart | 44 ++++ .../post/widget/PostListViewWidget.dart | 6 +- ...eleton.dart => ShimmerSkeletonWidget.dart} | 4 +- lib/utils/AppStrings.dart | 12 + 9 files changed, 278 insertions(+), 252 deletions(-) delete mode 100644 lib/feature/post/widget/DeleteConfirmationDialog.dart create mode 100644 lib/feature/post/widget/DeleteConfirmationDialogWidget.dart delete mode 100644 lib/feature/post/widget/EditCommentDialog.dart create mode 100644 lib/feature/post/widget/EditCommentDialogWidget.dart rename lib/feature/post/widget/{ShimmerSkeleton.dart => ShimmerSkeletonWidget.dart} (89%) diff --git a/lib/feature/post/widget/CommentBottomSheetWidget.dart b/lib/feature/post/widget/CommentBottomSheetWidget.dart index 9e5a94c..6e7ecd4 100644 --- a/lib/feature/post/widget/CommentBottomSheetWidget.dart +++ b/lib/feature/post/widget/CommentBottomSheetWidget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../provider/CommentProvider.dart'; +import '../provider/UserInfoProvider.dart'; class CommentBottomSheetWidget extends ConsumerWidget { final String postID; @@ -16,65 +17,84 @@ class CommentBottomSheetWidget extends ConsumerWidget { final TextEditingController commentController = TextEditingController(); const userID = 'eztqDqrvEXDc8nqnnrB8'; // 임시 사용자 ID - return FractionallySizedBox( - heightFactor: 0.75, // 모달의 높이를 75%로 설정 - child: Padding( - padding: const EdgeInsets.all(16.0), // 패딩 추가 - child: Column( - children: [ - // '댓글' 제목 - const Text( - '댓글', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), // 제목과 댓글 리스트 사이 간격 - // 댓글 리스트 - Expanded( - child: commentList.when( - data: (comments) { - return ListView.builder( - itemCount: comments.length, - itemBuilder: (context, index) { - final comment = comments[index]; - return CommentItemWidget(comment: comment); - }, - ); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stackTrace) => Center(child: Text('Error: $error')), - ), - ), - const SizedBox(height: 8), // 댓글 리스트와 입력 필드 사이 간격 - // 댓글 입력 필드 - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), // 입력 필드의 좌우 패딩 - child: TextField( - controller: commentController, - decoration: InputDecoration( - hintText: '댓글 추가...', - suffixIcon: IconButton( - icon: const Icon(Icons.send), - onPressed: () async { - final content = commentController.text.trim(); - if (content.isNotEmpty) { - await commentNotifier.addComment(content, userID); - commentController.clear(); - } + final postUserInfo = ref.watch(postUserInfoProvider(userID)); + + return postUserInfo.when( + data: (userInfo) { + return FractionallySizedBox( + heightFactor: 0.75, // 모달의 높이를 75%로 설정 + child: Padding( + padding: const EdgeInsets.all(16.0), // 패딩 추가 + child: Column( + children: [ + // '댓글' 제목 + const Text( + '댓글', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), // 제목과 댓글 리스트 사이 간격 + // 댓글 리스트 + Expanded( + child: commentList.when( + data: (comments) { + return ListView.builder( + itemCount: comments.length, + itemBuilder: (context, index) { + final comment = comments[index]; + return CommentItemWidget( + comment: comment, + userName: userInfo.name, + userProfileImageUrl: userInfo.profileImageUrl, + postID: postID, + onDelete: (commentID) async { + await commentNotifier.deleteComment(commentID); + }, + onEdit: (commentID, newContent) async { + await commentNotifier.updateComment(commentID, newContent); + }, + ); + }, + ); }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => Center(child: Text('Error: $error')), ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(15), + ), + const SizedBox(height: 8), // 댓글 리스트와 입력 필드 사이 간격 + // 댓글 입력 필드 + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), // 입력 필드의 좌우 패딩 + child: TextField( + controller: commentController, + decoration: InputDecoration( + hintText: '댓글 추가...', + suffixIcon: IconButton( + icon: const Icon(Icons.send), + onPressed: () async { + final content = commentController.text.trim(); + if (content.isNotEmpty) { + await commentNotifier.addComment(content, userID); + commentController.clear(); + } + }, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(15), + ), + ), ), ), - ), + ], ), - ], - ), - ), + ), + ); + }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stackTrace) => const Center(child: Text('Error loading user info')), ); } } diff --git a/lib/feature/post/widget/CommentItemWidget.dart b/lib/feature/post/widget/CommentItemWidget.dart index 1253f44..6499f23 100644 --- a/lib/feature/post/widget/CommentItemWidget.dart +++ b/lib/feature/post/widget/CommentItemWidget.dart @@ -1,127 +1,130 @@ import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../../../model/CommentModel.dart'; -import '../provider/UserInfoProvider.dart'; -import 'DeleteConfirmationDialog.dart'; -import 'EditCommentDialog.dart'; +import 'DeleteConfirmationDialogWidget.dart'; +import 'EditCommentDialogWidget.dart'; -class CommentItemWidget extends ConsumerWidget { +class CommentItemWidget extends StatelessWidget { final CommentModel comment; + final String userName; + final String userProfileImageUrl; + final String postID; + final void Function(String commentID) onDelete; + final void Function(String commentID, String newContent) onEdit; - const CommentItemWidget({super.key, required this.comment}); + const CommentItemWidget({ + super.key, + required this.comment, + required this.userName, + required this.userProfileImageUrl, + required this.postID, + required this.onDelete, + required this.onEdit, + }); @override - Widget build(BuildContext context, WidgetRef ref) { - final postUserInfo = ref.watch(postUserInfoProvider(comment.userID)); + Widget build(BuildContext context) { final TextEditingController editController = TextEditingController(text: comment.content); - return postUserInfo.when( - data: (userInfo) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CircleAvatar( - backgroundImage: NetworkImage(userInfo.profileImageUrl), - radius: 20, - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CircleAvatar( + backgroundImage: NetworkImage(userProfileImageUrl), + radius: 20, + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( children: [ - Row( - children: [ - Text( - userInfo.name, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(width: 8), - Text( - DateFormat.yMMMd().format(comment.createdAt), - style: const TextStyle( - fontSize: 12, - color: Colors.grey, - ), - ), - const Spacer(), - PopupMenuButton( - icon: const Icon(Icons.more_vert), - onSelected: (value) { - if (value == 'edit') { - showDialog( - context: context, - builder: (context) => EditCommentDialog( - commentID: comment.commentID, - postID: comment.postID, - ref: ref, - editController: editController, - ), - ); - } else if (value == 'delete') { - showDialog( - context: context, - builder: (context) => DeleteConfirmationDialog( - commentID: comment.commentID, - postID: comment.postID, - ref: ref, - ), - ); - } - }, - itemBuilder: (BuildContext context) => >[ - const PopupMenuItem( - value: 'edit', - child: Text('수정'), + Text( + userName, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + const SizedBox(width: 8), + Text( + DateFormat.yMMMd().format(comment.createdAt), + style: const TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + const Spacer(), + PopupMenuButton( + icon: const Icon(Icons.more_vert), + onSelected: (value) { + if (value == 'edit') { + showDialog( + context: context, + builder: (context) => EditCommentDialogWidget( + commentID: comment.commentID, + postID: postID, + editController: editController, + onEdit: onEdit, // onEdit 콜백 전달 ), - const PopupMenuItem( - value: 'delete', - child: Text('삭제'), + ); + } else if (value == 'delete') { + showDialog( + context: context, + builder: (context) => DeleteConfirmationDialogWidget( + commentID: comment.commentID, + onDelete: onDelete, // onDelete 콜백 전달 ), - ], - ), - ], - ), - const SizedBox(height: 4), - Text(comment.content), - const SizedBox(height: 8), - Row( - children: [ - IconButton( - icon: const Icon(Icons.thumb_up_alt_outlined, size: 20), - onPressed: () { - // 좋아요 버튼 기능 - }, - ), - IconButton( - icon: const Icon(Icons.thumb_down_alt_outlined, size: 20), - onPressed: () { - // 싫어요 버튼 기능 - }, + ); + } + }, + itemBuilder: (BuildContext context) => >[ + const PopupMenuItem( + value: 'edit', + child: Text('수정'), ), - IconButton( - icon: const Icon(Icons.comment_outlined, size: 20), - onPressed: () { - // 댓글 버튼 기능 - }, + const PopupMenuItem( + value: 'delete', + child: Text('삭제'), ), ], + ) + ], + ), + const SizedBox(height: 4), + Text(comment.content), + const SizedBox(height: 8), + Row( + children: [ + IconButton( + icon: const Icon(Icons.thumb_up_alt_outlined, size: 20), + onPressed: () { + // 좋아요 버튼 기능 + }, + ), + IconButton( + icon: const Icon(Icons.thumb_down_alt_outlined, size: 20), + onPressed: () { + // 싫어요 버튼 기능 + }, + ), + IconButton( + icon: const Icon(Icons.comment_outlined, size: 20), + onPressed: () { + // 댓글 버튼 기능 + }, ), ], ), - ), - ], + ], + ), ), - ); - }, - loading: () => const CircularProgressIndicator(), - error: (error, stackTrace) => const Icon(Icons.error), + ], + ), ); } } diff --git a/lib/feature/post/widget/DeleteConfirmationDialog.dart b/lib/feature/post/widget/DeleteConfirmationDialog.dart deleted file mode 100644 index 9077de7..0000000 --- a/lib/feature/post/widget/DeleteConfirmationDialog.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../provider/CommentProvider.dart'; - -class DeleteConfirmationDialog extends StatelessWidget { - final String commentID; - final String postID; - final WidgetRef ref; - - const DeleteConfirmationDialog({ - super.key, - required this.commentID, - required this.postID, - required this.ref, - }); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: const Text('댓글 삭제'), - content: const Text('이 댓글을 삭제하시겠습니까?'), - actions: [ - TextButton( - child: const Text('취소'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: const Text('삭제'), - onPressed: () async { - await ref.read(commentNotifierProvider(postID)).deleteComment(commentID); - Navigator.of(context).pop(); - }, - ), - ], - ); - } -} diff --git a/lib/feature/post/widget/DeleteConfirmationDialogWidget.dart b/lib/feature/post/widget/DeleteConfirmationDialogWidget.dart new file mode 100644 index 0000000..37a7571 --- /dev/null +++ b/lib/feature/post/widget/DeleteConfirmationDialogWidget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/AppStrings.dart'; + +class DeleteConfirmationDialogWidget extends StatelessWidget { + final String commentID; + final void Function(String commentID) onDelete; + + const DeleteConfirmationDialogWidget({ + super.key, + required this.commentID, + required this.onDelete, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text(AppStrings.deleteCommentTitle), + content: const Text(AppStrings.deleteCommentConfirmation), + actions: [ + TextButton( + child: const Text(AppStrings.deleteCommentCancel), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text(AppStrings.deleteCommentConfirm), + onPressed: () { + onDelete(commentID); + Navigator.of(context).pop(); + }, + ), + ], + ); + } +} diff --git a/lib/feature/post/widget/EditCommentDialog.dart b/lib/feature/post/widget/EditCommentDialog.dart deleted file mode 100644 index 15111c1..0000000 --- a/lib/feature/post/widget/EditCommentDialog.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../provider/CommentProvider.dart'; - -class EditCommentDialog extends StatelessWidget { - final String commentID; - final String postID; - final WidgetRef ref; - final TextEditingController editController; - - const EditCommentDialog({ - super.key, - required this.commentID, - required this.postID, - required this.ref, - required this.editController, - }); - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: const Text('댓글 수정'), - content: TextField( - controller: editController, - decoration: const InputDecoration( - hintText: '댓글 수정...', - ), - ), - actions: [ - TextButton( - child: const Text('취소'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: const Text('저장'), - onPressed: () async { - final newContent = editController.text.trim(); - if (newContent.isNotEmpty) { - await ref.read(commentNotifierProvider(postID)).updateComment(commentID, newContent); - Navigator.of(context).pop(); - } - }, - ), - ], - ); - } -} diff --git a/lib/feature/post/widget/EditCommentDialogWidget.dart b/lib/feature/post/widget/EditCommentDialogWidget.dart new file mode 100644 index 0000000..459545f --- /dev/null +++ b/lib/feature/post/widget/EditCommentDialogWidget.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/AppStrings.dart'; + +class EditCommentDialogWidget extends StatelessWidget { + final String commentID; + final String postID; + final TextEditingController editController; + final void Function(String commentID, String newContent) onEdit; + + const EditCommentDialogWidget({ + super.key, + required this.commentID, + required this.postID, + required this.editController, + required this.onEdit, + }); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text(AppStrings.editCommentTitle), + content: TextField( + controller: editController, + decoration: const InputDecoration(hintText: AppStrings.editCommentHint), + ), + actions: [ + TextButton( + child: const Text(AppStrings.editCommentCancel), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text(AppStrings.editCommentConfirm), + onPressed: () { + onEdit(commentID, editController.text); + Navigator.of(context).pop(); + }, + ), + ], + ); + } +} diff --git a/lib/feature/post/widget/PostListViewWidget.dart b/lib/feature/post/widget/PostListViewWidget.dart index adb237f..ed04036 100644 --- a/lib/feature/post/widget/PostListViewWidget.dart +++ b/lib/feature/post/widget/PostListViewWidget.dart @@ -6,7 +6,7 @@ import 'package:blueberry_flutter_template/utils/Talker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../provider/UserInfoProvider.dart'; -import 'ShimmerSkeleton.dart'; +import 'ShimmerSkeletonWidget.dart'; class PostListViewWidget extends ConsumerWidget { const PostListViewWidget({super.key}); @@ -61,7 +61,7 @@ class PostListViewWidget extends ConsumerWidget { }, ); }, - loading: () => const ShimmerSkeleton(), + loading: () => const ShimmerSkeletonWidget(), error: (error, stackTrace) { talker.error('Error loading user info: $error'); return Center(child: Text('Error: $error')); @@ -70,7 +70,7 @@ class PostListViewWidget extends ConsumerWidget { }, ); }, - loading: () => const ShimmerSkeleton(), + loading: () => const ShimmerSkeletonWidget(), error: (error, stackTrace) { talker.error('Error loading posts: $error'); return Center(child: Text('Error: $error')); diff --git a/lib/feature/post/widget/ShimmerSkeleton.dart b/lib/feature/post/widget/ShimmerSkeletonWidget.dart similarity index 89% rename from lib/feature/post/widget/ShimmerSkeleton.dart rename to lib/feature/post/widget/ShimmerSkeletonWidget.dart index 247f4f6..2c5c7aa 100644 --- a/lib/feature/post/widget/ShimmerSkeleton.dart +++ b/lib/feature/post/widget/ShimmerSkeletonWidget.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; -class ShimmerSkeleton extends StatelessWidget { - const ShimmerSkeleton({super.key}); +class ShimmerSkeletonWidget extends StatelessWidget { + const ShimmerSkeletonWidget({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/utils/AppStrings.dart b/lib/utils/AppStrings.dart index 29bdabe..d0a6416 100644 --- a/lib/utils/AppStrings.dart +++ b/lib/utils/AppStrings.dart @@ -1,6 +1,18 @@ class AppStrings { static const String appTitle = '블루베리 플러터 템플릿'; + // EditCommentDialogWidget.dart + static const String editCommentTitle = '댓글 수정'; + static const String editCommentHint = '댓글을 수정하세요'; + static const String editCommentCancel = '취소'; + static const String editCommentConfirm = '수정'; + + // DeleteConfirmationDialogWidget.dart + static const String deleteCommentTitle = '댓글 삭제'; + static const String deleteCommentConfirmation = '이 댓글을 삭제하시겠습니까?'; + static const String deleteCommentCancel = '취소'; + static const String deleteCommentConfirm = '삭제'; + //MatchFilterWidget.dart static const String filterTitle = '어떤 친구를 만날까요?'; static const String filterSubmitButtonText = '만나기';