-
Notifications
You must be signed in to change notification settings - Fork 215
feat: implement delete message feature in chat room #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 2 commits
d5b69f3
408686e
82dfb67
c665b86
1cc4c55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,42 @@ class RoomChatController extends GetxController { | |
| log(appwriteUpcommingRoom.toString()); | ||
| } | ||
|
|
||
| /// delete method | ||
| Future<void> deleteMessage(String messageId) async { | ||
| try { | ||
| Message messageToDelete = messages.firstWhere( | ||
| (msg) => msg.messageId == messageId, | ||
| ); | ||
| messageToDelete = messageToDelete.copyWith(content: '', isdeleted: true); | ||
|
|
||
| await databases.updateDocument( | ||
| databaseId: masterDatabaseId, | ||
| collectionId: chatMessagesCollectionId, | ||
| documentId: messageId, | ||
| data: messageToDelete.toJsonForUpload(), | ||
| ); | ||
| if (appwriteUpcommingRoom != null) { | ||
| log('Sending notification for deleted message'); | ||
| var body = json.encode({ | ||
| 'roomId': appwriteUpcommingRoom?.id, | ||
| 'payload': { | ||
| 'title': 'Message Deleted in ${appwriteUpcommingRoom?.name}', | ||
| 'body': '${messageToDelete.creatorName} deleted a message', | ||
| }, | ||
| }); | ||
| var results = await functions.createExecution( | ||
| functionId: sendMessageNotificationFunctionID, | ||
| body: body.toString(), | ||
| ); | ||
| log(results.status); | ||
| } | ||
| log('Message deleted successfully'); | ||
| } catch (e) { | ||
| log('Error deleting message: $e'); | ||
| return; | ||
| } | ||
|
||
| } | ||
|
||
|
|
||
| Future<void> loadMessages() async { | ||
| messages.clear(); | ||
| var queries = [ | ||
|
|
@@ -98,6 +134,7 @@ class RoomChatController extends GetxController { | |
| isEdited: false, | ||
| content: content, | ||
| creationDateTime: DateTime.now(), | ||
| isdeleted: false, | ||
| ); | ||
|
|
||
| await databases.createDocument( | ||
|
|
@@ -148,6 +185,7 @@ class RoomChatController extends GetxController { | |
| updatedMessage = updatedMessage.copyWith( | ||
| content: newContent, | ||
| isEdited: true, | ||
| isdeleted: false, | ||
| ); | ||
|
|
||
|
Comment on lines
171
to
176
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t resurrect deleted messages on edit Editing currently forces updatedMessage = updatedMessage.copyWith(
content: newContent,
isEdited: true,
- isdeleted: false,
);
+ if (updatedMessage.isdeleted) {
+ log('Editing a deleted message is not allowed');
+ return;
+ }
🤖 Prompt for AI Agents |
||
| try { | ||
|
|
@@ -247,6 +285,7 @@ class RoomChatController extends GetxController { | |
| messages[index] = messages[index].copyWith( | ||
| content: updatedMessage.content, | ||
| isEdited: updatedMessage.isEdited, | ||
| isdeleted: updatedMessage.isdeleted, | ||
| ); | ||
| if (appwriteRoom != null) { | ||
| auth.flutterLocalNotificationsPlugin.show( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,12 +112,18 @@ class _RoomChatScreenState extends State<RoomChatScreen> { | |
| onEditMessage: (String newContent) async { | ||
| await updateMessage(index, newContent); | ||
| }, | ||
| onDeleteMessage: (String messageId) async { | ||
| await chatController.deleteMessage(messageId); | ||
| }, | ||
| replytoMessage: (Message message) => | ||
| chatController.setReplyingTo(message), | ||
| canEdit: | ||
| auth.appwriteUser.$id == | ||
| chatController.messages[index].creatorId && | ||
| !chatController.messages[index].isEdited, | ||
| !chatController.messages[index].isdeleted, | ||
|
||
| canDelete: | ||
| auth.appwriteUser.$id == | ||
| chatController.messages[index].creatorId, | ||
| ); | ||
| }, | ||
| ), | ||
|
|
@@ -138,6 +144,8 @@ class ChatMessageItem extends StatefulWidget { | |
| final void Function(int) onTapReply; | ||
| final void Function(String) onEditMessage; | ||
| final void Function(Message) replytoMessage; | ||
| final void Function(String) onDeleteMessage; | ||
| final bool canDelete; | ||
| final bool canEdit; | ||
|
|
||
| const ChatMessageItem({ | ||
|
|
@@ -147,6 +155,8 @@ class ChatMessageItem extends StatefulWidget { | |
| required this.onEditMessage, | ||
| required this.replytoMessage, | ||
| required this.canEdit, | ||
| required this.onDeleteMessage, | ||
| required this.canDelete, | ||
| }); | ||
|
|
||
| @override | ||
|
|
@@ -156,6 +166,7 @@ class ChatMessageItem extends StatefulWidget { | |
| class ChatMessageItemState extends State<ChatMessageItem> { | ||
| bool isEditing = false; | ||
| late TextEditingController _editingController; | ||
|
|
||
| double _dragOffset = 0.0; | ||
| @override | ||
| void initState() { | ||
|
|
@@ -192,13 +203,78 @@ class ChatMessageItemState extends State<ChatMessageItem> { | |
| _editingController.text = widget.message.content; | ||
| } | ||
|
|
||
| void _showMessageContextMenu(BuildContext context) { | ||
| showModalBottomSheet( | ||
| context: context, | ||
| backgroundColor: Colors.transparent, | ||
| builder: (context) { | ||
| return Container( | ||
| decoration: BoxDecoration( | ||
| color: Theme.of(context).colorScheme.surface, | ||
| borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), | ||
| ), | ||
| child: Wrap( | ||
| children: [ | ||
| if (widget.canDelete && !widget.message.isdeleted) | ||
| ///delete option | ||
| ListTile( | ||
| leading: const Icon( | ||
| Icons.delete, | ||
| color: Color.fromARGB(255, 199, 169, 166), | ||
| ), | ||
| title: const Text('Delete'), | ||
| onTap: () { | ||
| Navigator.pop(context); | ||
| _confirmDelete(context); | ||
| }, | ||
| ), | ||
|
|
||
| ///cancel option | ||
| ListTile( | ||
| leading: const Icon(Icons.close), | ||
| title: const Text('Cancel'), | ||
| onTap: () => Navigator.pop(context), | ||
| ), | ||
| ], | ||
| ), | ||
| ); | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| void _confirmDelete(BuildContext context) { | ||
| showDialog( | ||
| context: context, | ||
| builder: (context) => AlertDialog( | ||
| title: const Text('Delete Message'), | ||
| content: const Text('Are you sure you want to delete this message?'), | ||
| actions: [ | ||
| TextButton( | ||
| onPressed: () => Navigator.pop(context), | ||
| child: const Text('Cancel'), | ||
| ), | ||
| TextButton( | ||
| onPressed: () { | ||
| Navigator.pop(context); | ||
| widget.onDeleteMessage(widget.message.messageId); | ||
| }, | ||
| child: const Text('Delete'), | ||
| ), | ||
| ], | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return LayoutBuilder( | ||
| builder: (context, constraints) { | ||
| return Stack( | ||
| children: [ | ||
| GestureDetector( | ||
| onLongPress: widget.canDelete | ||
| ? () => _showMessageContextMenu(context) | ||
| : null, | ||
| onHorizontalDragUpdate: (details) { | ||
| if (_dragOffset + details.delta.dx > 0.0 && | ||
| _dragOffset + details.delta.dx < 100) { | ||
|
|
@@ -227,8 +303,11 @@ class ChatMessageItemState extends State<ChatMessageItem> { | |
| child: Container( | ||
| width: double.infinity, | ||
| padding: const EdgeInsets.all(8.0), | ||
|
|
||
| decoration: BoxDecoration( | ||
| color: Theme.of(context).colorScheme.primary, | ||
| color: widget.message.isdeleted | ||
| ? Colors.grey[300] | ||
| : Theme.of(context).colorScheme.primary, | ||
| borderRadius: BorderRadius.circular(8.0), | ||
| ), | ||
| child: Column( | ||
|
|
@@ -252,13 +331,20 @@ class ChatMessageItemState extends State<ChatMessageItem> { | |
| widget.message.creatorName.capitalizeFirst!, | ||
| style: TextStyle( | ||
| fontWeight: FontWeight.bold, | ||
| color: Theme.of( | ||
| context, | ||
| ).colorScheme.secondary, | ||
|
|
||
| color: | ||
| widget | ||
| .message | ||
| .isdeleted // turns grey after getting deleted | ||
| ? Colors.grey[700] | ||
| : Theme.of( | ||
| context, | ||
| ).colorScheme.secondary, | ||
| ), | ||
| ), | ||
| const SizedBox(height: 5), | ||
| if (widget.message.replyTo != null) | ||
| if (widget.message.replyTo != null && | ||
| !widget.message.isdeleted) | ||
| GestureDetector( | ||
| onTap: () => widget.onTapReply( | ||
| widget.message.replyTo!.index, | ||
|
|
@@ -297,7 +383,8 @@ class ChatMessageItemState extends State<ChatMessageItem> { | |
| ), | ||
| ), | ||
| ), | ||
| if (isEditing) | ||
| if (isEditing && !widget.message.isdeleted) | ||
| ///the message to edit it AND the message is not deleted | ||
| Focus( | ||
| onKeyEvent: (node, event) { | ||
| if (event.logicalKey == | ||
|
|
@@ -326,6 +413,27 @@ class ChatMessageItemState extends State<ChatMessageItem> { | |
| ), | ||
| ), | ||
| ) | ||
| else if (widget.message.isdeleted) | ||
| ///The message has been deleted (`isDeleted = true`) | ||
| Row( | ||
| children: [ | ||
| Icon( | ||
| Icons.delete_outline, | ||
| color: Colors.grey[600], | ||
| size: 18, | ||
| ), | ||
|
||
| const SizedBox(width: 8), | ||
| Expanded( | ||
| child: Text( | ||
| 'This message was deleted', | ||
| style: TextStyle( | ||
| color: Colors.grey[600], | ||
| fontStyle: FontStyle.italic, | ||
| ), | ||
| ), | ||
| ), | ||
| ], | ||
| ) | ||
| else | ||
| Row( | ||
| children: [ | ||
|
|
@@ -350,6 +458,7 @@ class ChatMessageItemState extends State<ChatMessageItem> { | |
| ), | ||
| ], | ||
| ), | ||
|
|
||
| const SizedBox(height: 5), | ||
| Text( | ||
| widget.message.creationDateTime | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to add tests for this function