diff --git a/app/mobile/lib/models/comment.dart b/app/mobile/lib/models/comment.dart index af1624c9..c453bc83 100644 --- a/app/mobile/lib/models/comment.dart +++ b/app/mobile/lib/models/comment.dart @@ -1,13 +1,25 @@ class CommentData { - final String user; + final String username; final String commentText; + final String commentId; + final String dateTime; + final String userId; - CommentData({required this.user, required this.commentText}); + CommentData({ + required this.username, + required this.commentText, + required this.commentId, + required this.dateTime, + required this.userId, + }); factory CommentData.fromJson(Map json) { return CommentData( - user: json['user']['username'], + userId: json['user']['id'], + username: json['user']['username'], commentText: json['description'], + commentId: json['id'], + dateTime: json['created_date'], ); } } diff --git a/app/mobile/lib/services/ReportService.dart b/app/mobile/lib/services/ReportService.dart new file mode 100644 index 00000000..3aed020d --- /dev/null +++ b/app/mobile/lib/services/ReportService.dart @@ -0,0 +1,23 @@ +import 'package:dio/dio.dart'; +import 'package:mobile_app/services/apiService.dart'; + +class ReportService { + static Future report(String userId, String reason) async { + String reportEndpoint = '/user/report/$userId'; + + final Map data = { + 'reason': reason, + }; + + try { + final Response response = await ApiService.dio.post( + reportEndpoint, + data: data, + ); + return response.statusCode == 201; + } catch (e) { + print('$e'); + return false; + } + } +} diff --git a/app/mobile/lib/services/createCommentService.dart b/app/mobile/lib/services/createCommentService.dart index c3c96ad2..3295a37d 100644 --- a/app/mobile/lib/services/createCommentService.dart +++ b/app/mobile/lib/services/createCommentService.dart @@ -18,4 +18,16 @@ class PostCommentService { rethrow; } } -} \ No newline at end of file + + static Future deleteComment(String commentId) async { + final String commentEndpoint = '/comment/$commentId'; + + try { + final Response response = await ApiService.dio.delete(commentEndpoint); + return response.statusCode == 200; + } catch (e) { + print('Error: $e'); + return false; + } + } +} diff --git a/app/mobile/lib/view/login/loginScreen.dart b/app/mobile/lib/view/login/loginScreen.dart index 1d5a7a60..32e9e6fc 100644 --- a/app/mobile/lib/view/login/loginScreen.dart +++ b/app/mobile/lib/view/login/loginScreen.dart @@ -80,6 +80,8 @@ class _LoginScreenState extends State { if (loggedInUserData.statusCode == 200) { AppState.isModerator = false; AppState.loggedInUserId = loggedInUserData.data['id']; + // TODO: insallah dogrudur + AppState.loggedInUserUsername = loggedInUserData.data['username']; AppState.isGuest = false; print("loginScreen.login: ${AppState.loggedInUserId}"); } else { diff --git a/app/mobile/lib/view/pollView/commentWidget.dart b/app/mobile/lib/view/pollView/commentWidget.dart index 57779c3c..d580923f 100644 --- a/app/mobile/lib/view/pollView/commentWidget.dart +++ b/app/mobile/lib/view/pollView/commentWidget.dart @@ -1,27 +1,154 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:mobile_app/services/ReportService.dart'; +import 'package:mobile_app/services/createCommentService.dart'; +import 'package:mobile_app/view/helpers/dateTime.dart'; +import 'package:mobile_app/view/pollView/clickableUsername.dart'; +import 'package:mobile_app/view/state.dart'; import '../constants.dart'; -class CommentWidget extends StatelessWidget { - final String user; +class CommentWidget extends StatefulWidget { + final String username; + final String userId; final String commentText; + final String commentId; + final String dateTime; + final void Function() parentSetState; - const CommentWidget( - {super.key, required this.user, required this.commentText}); + const CommentWidget({ + super.key, + required this.username, + required this.commentText, + required this.commentId, + required this.dateTime, + required this.userId, + required this.parentSetState, + }); + + @override + State createState() => _CommentWidgetState(); +} + +class _CommentWidgetState extends State { + void _deleteComment() async { + await PostCommentService.deleteComment(widget.commentId); + widget.parentSetState(); + } + + void report(String reason) async { + var result = await ReportService.report(widget.userId, reason); + // show a dialog to inform the user about the result + if (!context.mounted) return; + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Report User'), + content: Text(result + ? 'User reported successfully' + : 'User could not be reported'), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('OK'), + ), + ], + ); + }, + ); + } + + // a dialog to show input the reason for reporting + void showReportDialog(BuildContext context) { + final TextEditingController reasonController = TextEditingController(); + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Report User'), + content: TextField( + controller: reasonController, + decoration: const InputDecoration( + hintText: 'Reason', + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + report(reasonController.text); + Navigator.pop(context); + }, + child: const Text('Report'), + ), + ], + ); + }, + ); + } @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.fromLTRB(8, 0, 8, 6), child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - border: Border.all(color: navy), - color: gray, + borderRadius: BorderRadius.circular(10.0), + border: Border.all(color: gray, width: 2), + color: whitish, ), - child: ListTile( - title: Text(user), - subtitle: Text(commentText), + child: Stack( + children: [ + ListTile( + title: ClickableUsername( + username: widget.username, + displayName: widget.username, + ), + subtitle: Text(widget.commentText), + ), + Positioned( + top: 12, + right: 60, + child: Text( + DateFormat('MMM d\'th\', HH:mm') + .format(DateTime.parse(widget.dateTime)), + ), + ), + if (widget.username == AppState.loggedInUserUsername) + Positioned( + top: -4, + right: -4, + child: IconButton( + icon: const Icon(Icons.delete), + color: Colors.grey.shade700, + onPressed: () { + _deleteComment(); + print("delete onpressed"); + }, + ), + ) + else + Positioned( + top: -4, + right: -4, + child: IconButton( + color: Colors.grey.shade700, + icon: const Icon(Icons.report), + onPressed: () { + showReportDialog(context); + print("report onpressed"); + }, + ), + ) + ], ), ), ); diff --git a/app/mobile/lib/view/pollView/pollView.dart b/app/mobile/lib/view/pollView/pollView.dart index de2a4210..211ff317 100644 --- a/app/mobile/lib/view/pollView/pollView.dart +++ b/app/mobile/lib/view/pollView/pollView.dart @@ -50,7 +50,9 @@ class PollPage extends StatefulWidget { required this.dateTime, required this.isSettled, this.approvedStatus, - required this.chosenVoteIndex, required this.annotationIndices, required this.annotationTexts, + required this.chosenVoteIndex, + required this.annotationIndices, + required this.annotationTexts, }); @override @@ -89,9 +91,9 @@ class _PollPageState extends State { pollId: widget.isSettled == 0 ? widget.pollId : "", ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: buildRichText(widget.postTitle, widget.annotationIndices, widget.annotationTexts) - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: buildRichText(widget.postTitle, widget.annotationIndices, + widget.annotationTexts)), TagListWidget(tags: widget.tags, tagColors: widget.tagColors), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -120,7 +122,8 @@ class _PollPageState extends State { Row( children: [ LikeCountWidget(likeCount: widget.likeCount), - DateTimeWidget(dateTime: DateTime.parse(widget.dateTime), color: blue), + DateTimeWidget( + dateTime: DateTime.parse(widget.dateTime), color: blue), ], ), CommentEntryFieldWidget( @@ -139,7 +142,9 @@ class _PollPageState extends State { // If we run into an error, display it to the user return Text('Error: ${snapshot.error}'); } else { - var fetchedComments = snapshot.hasData ? snapshot.data! : []; + var fetchedComments = snapshot.hasData + ? snapshot.data! + : [] as List; // Whether we have data or not, display the number of fetchedComments int commentCount = fetchedComments.length; @@ -155,8 +160,14 @@ class _PollPageState extends State { if (snapshot.hasData) ...fetchedComments .map((comment) => CommentWidget( - user: comment.user, + parentSetState: () { + setState(() {}); + }, + username: comment.username, commentText: comment.commentText, + commentId: comment.commentId, + dateTime: comment.dateTime, + userId: comment.userId, )) .toList(), // If there's no data, there's nothing else to add to the column @@ -188,9 +199,8 @@ class _PollPageState extends State { print("pressed like"); } - RichText buildRichText(String fullText, List> indices, List annotationTexts) { - - + RichText buildRichText( + String fullText, List> indices, List annotationTexts) { List textSpans = []; int previousIndex = 0; @@ -220,7 +230,8 @@ class _PollPageState extends State { ..onTap = () { // Handle tap on the underlined text _showPopup(context, annotationText); - print('Tapped on underlined text from index $startIndex to $endIndex!'); + print( + 'Tapped on underlined text from index $startIndex to $endIndex!'); }, ), ); @@ -320,8 +331,6 @@ class CommentEntryFieldWidget extends StatelessWidget { } } - - class LikeCountWidget extends StatelessWidget { final int likeCount; diff --git a/app/mobile/lib/view/state.dart b/app/mobile/lib/view/state.dart index e733bec6..680d9267 100644 --- a/app/mobile/lib/view/state.dart +++ b/app/mobile/lib/view/state.dart @@ -3,12 +3,14 @@ import 'dart:math'; class AppState { static bool isModerator = false; static String loggedInUserId = ''; + static String loggedInUserUsername = ''; static Random random = Random(); static bool isGuest = true; static void logout() { isModerator = false; loggedInUserId = ''; + loggedInUserUsername = ''; isGuest = true; } }