From 693bd66d17f30e29d4bcd5893e7c182ad1ece593 Mon Sep 17 00:00:00 2001 From: rob Date: Sun, 21 Apr 2024 20:03:12 -0400 Subject: [PATCH] delete message --- app/controllers/chat.js | 20 ++++++++++++++++++++ app/services/chat.js | 10 ++++++++++ app/views/chat/components/message.pug | 16 ++++++++++++++-- client/js/chat-client.js | 18 ++++++++++++++++++ client/static/sfx/message-deleted.mp3 | Bin 0 -> 23405 bytes 5 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 client/static/sfx/message-deleted.mp3 diff --git a/app/controllers/chat.js b/app/controllers/chat.js index 12eb89a..4f962f4 100644 --- a/app/controllers/chat.js +++ b/app/controllers/chat.js @@ -115,6 +115,12 @@ export default class ChatController extends SiteController { this.getRoomView.bind(this), ); + router.delete( + '/message/:messageId', + // limiterService.create(limiterService.config.chat.deleteChatMessage), + this.deleteChatMessage.bind(this), + ); + router.delete( '/room/:roomId', // limiterService.create(limiterService.config.chat.deleteRoom), @@ -258,6 +264,20 @@ export default class ChatController extends SiteController { } } + async deleteChatMessage (req, res) { + const { chat: chatService } = this.dtp.services; + try { + await chatService.removeMessage(res.locals.message); + res.status(200).json({ success: true }); + } catch (error) { + this.log.error('failed to destroy chat room', { error }); + res.status(error.statusCode || 500).json({ + success: false, + message: error.message, + }); + } + } + async deleteRoom (req, res) { const { chat: chatService } = this.dtp.services; try { diff --git a/app/services/chat.js b/app/services/chat.js index 6d9245f..4a434d8 100644 --- a/app/services/chat.js +++ b/app/services/chat.js @@ -550,5 +550,15 @@ export default class ChatService extends SiteService { this.log.debug('removing chat message', { messageId: message._id }); await ChatMessage.deleteOne({ _id: message._id }); + + const displayList = this.createDisplayList('remove-chat-message'); + displayList.removeElement(`.chat-message[data-message-id="${message._id}"]`); + + this.dtp.emitter + .to(message.channel._id.toString()) + .emit('chat-control', { + displayList, + audio: { playSound: 'message-remove' }, + }); } } \ No newline at end of file diff --git a/app/views/chat/components/message.pug b/app/views/chat/components/message.pug index 65176eb..e5ff282 100644 --- a/app/views/chat/components/message.pug +++ b/app/views/chat/components/message.pug @@ -57,7 +57,7 @@ mixin renderChatMessage (message) +renderReactionBar(message) .message-menu - div(uk-grid).uk-grid-small.uk-flex-middle + .uk-flex.uk-flex-middle .uk-width-auto button( type="button", @@ -82,6 +82,14 @@ mixin renderChatMessage (message) uk-tooltip="React with smiley" onclick="return dtp.app.toggleMessageReaction(event);", ).message-menu-button 😃 + .uk-width-auto + button( + type="button", + data-message-id= message._id, + data-emoji="🤣", + uk-tooltip="React with laugh" + onclick="return dtp.app.toggleMessageReaction(event);", + ).message-menu-button 🤣 .uk-width-auto button( type="button", @@ -112,4 +120,8 @@ mixin renderChatMessage (message) li a(href="") Edit li - a(href="") Delete \ No newline at end of file + a( + href="", + data-message-id= message._id, + onclick="return dtp.app.deleteChatMessage(event);", + ) Delete \ No newline at end of file diff --git a/client/js/chat-client.js b/client/js/chat-client.js index ac72634..b9bbb82 100644 --- a/client/js/chat-client.js +++ b/client/js/chat-client.js @@ -25,6 +25,7 @@ export class ChatApp extends DtpApp { static get SFX_CHAT_MESSAGE ( ) { return 'chat-message'; } static get SFX_CHAT_REACTION ( ) { return 'reaction'; } static get SFX_CHAT_REACTION_REMOVE ( ) { return 'reaction-remove'; } + static get SFX_CHAT_MESSAGE_REMOVE ( ) { return 'message-remove'; } constructor (user) { super(DTP_COMPONENT_NAME, user); @@ -75,6 +76,7 @@ export class ChatApp extends DtpApp { this.audio.loadSound(ChatApp.SFX_CHAT_MESSAGE, '/static/sfx/chat-message.mp3'), this.audio.loadSound(ChatApp.SFX_CHAT_REACTION, '/static/sfx/reaction.mp3'), this.audio.loadSound(ChatApp.SFX_CHAT_REACTION_REMOVE, '/static/sfx/reaction-remove.mp3'), + this.audio.loadSound(ChatApp.SFX_CHAT_MESSAGE_REMOVE, '/static/sfx/message-deleted.mp3'), ]); } catch (error) { this.log.error('startAudio', 'failed to load sound', { error }); @@ -262,6 +264,22 @@ export class ChatApp extends DtpApp { return true; } + async deleteChatMessage (event) { + const target = event.currentTarget || event.target; + const messageId = target.getAttribute('data-message-id'); + + event.preventDefault(); + event.stopPropagation(); + + try { + const response = await fetch(`/chat/message/${messageId}`, { method: 'DELETE' }); + await this.processResponse(response); + } catch (error) { + this.log.error('deleteChatMessage', 'failed to delete chat message', { error }); + UIkit.modal.alert(`Failed to delete chat message: ${error.message}`); + } + } + async toggleMessageReaction (event) { const target = event.currentTarget || event.target; const messageId = target.getAttribute('data-message-id'); diff --git a/client/static/sfx/message-deleted.mp3 b/client/static/sfx/message-deleted.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..21237e380e2d811195a75f37e5570ecdd24cd56d GIT binary patch literal 23405 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 literal 0 HcmV?d00001