From 000136b4f01ba9b72a398ec82e8d7ca71e206981 Mon Sep 17 00:00:00 2001 From: Nikhil Kothari Date: Fri, 11 Oct 2024 19:43:11 +0530 Subject: [PATCH] feat: show system messages when channel members are changed --- .../RemoveChannelMemberModal.tsx | 2 +- .../feature/chat/ChatMessage/MessageItem.tsx | 3 +- .../chat/ChatMessage/SystemMessageBlock.tsx | 15 ++++ .../feature/chat/ChatStream/ChatStream.tsx | 3 + .../feature/chat/ChatStream/useChatStream.ts | 4 +- .../raven_channel_member.py | 87 +++++++++++++++++++ .../doctype/raven_message/raven_message.json | 4 +- .../doctype/raven_message/raven_message.py | 4 +- types/Messaging/Message.ts | 9 +- 9 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/feature/chat/ChatMessage/SystemMessageBlock.tsx diff --git a/frontend/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx b/frontend/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx index 99f56ed74..0ec70fc24 100644 --- a/frontend/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx +++ b/frontend/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx @@ -56,7 +56,7 @@ export const RemoveChannelMemberModal = ({ onClose, member }: RemoveChannelMembe - This person will no longer have access to the channel and can only rejoin by invitation. + This person will no longer have access to the channel. diff --git a/frontend/src/components/feature/chat/ChatMessage/MessageItem.tsx b/frontend/src/components/feature/chat/ChatMessage/MessageItem.tsx index 93e7f5364..fd36528cd 100644 --- a/frontend/src/components/feature/chat/ChatMessage/MessageItem.tsx +++ b/frontend/src/components/feature/chat/ChatMessage/MessageItem.tsx @@ -144,8 +144,7 @@ export const MessageItem = ({ message, setDeleteMessage, isHighlighted, onReplyM data-[state=open]:shadow-sm transition-colors px-1 - py-2 - sm:p-2 + py-1.5 rounded-md`, isHighlighted ? 'bg-yellow-50 hover:bg-yellow-50 dark:bg-yellow-300/20 dark:hover:bg-yellow-300/20' : !isDesktop && isHovered ? 'bg-gray-2 dark:bg-gray-3' : '', isEmojiPickerOpen ? 'bg-gray-2 dark:bg-gray-3' : '')}> diff --git a/frontend/src/components/feature/chat/ChatMessage/SystemMessageBlock.tsx b/frontend/src/components/feature/chat/ChatMessage/SystemMessageBlock.tsx new file mode 100644 index 000000000..810cca0ab --- /dev/null +++ b/frontend/src/components/feature/chat/ChatMessage/SystemMessageBlock.tsx @@ -0,0 +1,15 @@ +import { Flex, Text } from '@radix-ui/themes' +import { SystemMessage } from '../../../../../../types/Messaging/Message' +import { DateTooltipShort } from './Renderers/DateTooltip' + +const SystemMessageBlock = ({ message }: { message: SystemMessage }) => { + return ( + + + {message.text} + + + ) +} + +export default SystemMessageBlock \ No newline at end of file diff --git a/frontend/src/components/feature/chat/ChatStream/ChatStream.tsx b/frontend/src/components/feature/chat/ChatStream/ChatStream.tsx index 4b8e6977b..e21c5306d 100644 --- a/frontend/src/components/feature/chat/ChatStream/ChatStream.tsx +++ b/frontend/src/components/feature/chat/ChatStream/ChatStream.tsx @@ -16,6 +16,7 @@ import { ErrorBanner } from '@/components/layout/AlertBanner' import { ForwardMessageDialog, useForwardMessage } from '../ChatMessage/MessageActions/ForwardMessage' import AttachFileToDocumentDialog, { useAttachFileToDocument } from '../ChatMessage/MessageActions/AttachFileToDocument' import { ReactionAnalyticsDialog, useMessageReactionAnalytics } from '../ChatMessage/MessageActions/MessageReactionAnalytics' +import SystemMessageBlock from '../ChatMessage/SystemMessageBlock' /** * Anatomy of a message @@ -132,6 +133,8 @@ const ChatStream = ({ channelID, replyToMessage, showThreadButton = true }: Prop return {message.creation} + } else if (message.message_type === 'System') { + return } else { return
diff --git a/frontend/src/components/feature/chat/ChatStream/useChatStream.ts b/frontend/src/components/feature/chat/ChatStream/useChatStream.ts index 49a55b201..b6dd1c231 100644 --- a/frontend/src/components/feature/chat/ChatStream/useChatStream.ts +++ b/frontend/src/components/feature/chat/ChatStream/useChatStream.ts @@ -462,7 +462,9 @@ const useChatStream = (channelID: string, scrollRef: MutableRefObject 0 @@ -61,6 +69,40 @@ def after_delete(self): ) frappe.db.set_value("Raven Channel Member", first_member.name, "is_admin", 1) + first_member_name = frappe.get_cached_value("Raven User", first_member.user_id, "full_name") + + # Add a system message to the channel mentioning the new admin + frappe.get_doc( + { + "doctype": "Raven Message", + "channel_id": self.channel_id, + "message_type": "System", + "text": f"{member_name} was removed by {current_user_name} and {first_member_name} is the new admin of this channel.", + } + ).insert() + else: + # If the member who left is the current user, then add a system message to the channel mentioning that the user left + if member_name == current_user_name: + # Add a system message to the channel mentioning the member who left + frappe.get_doc( + { + "doctype": "Raven Message", + "channel_id": self.channel_id, + "message_type": "System", + "text": f"{member_name} left.", + } + ).insert(ignore_permissions=True) + else: + # Add a system message to the channel mentioning the member who left + frappe.get_doc( + { + "doctype": "Raven Message", + "channel_id": self.channel_id, + "message_type": "System", + "text": f"{current_user_name} removed {member_name}.", + } + ).insert() + def on_trash(self): # if the leaving member is admin, then the first member becomes new admin if ( @@ -119,6 +161,33 @@ def after_insert(self): if not is_direct_message and self.allow_notifications: subscribe_user_to_topic(self.channel_id, self.user_id) + if not is_direct_message: + + is_thread = self.is_thread() + + # Send a system message to the channel mentioning the member who joined + if not is_thread: + member_name = frappe.get_cached_value("Raven User", self.user_id, "full_name") + if self.user_id == frappe.session.user: + frappe.get_doc( + { + "doctype": "Raven Message", + "channel_id": self.channel_id, + "message_type": "System", + "text": f"{member_name} joined.", + } + ).insert() + else: + current_user_name = frappe.get_cached_value("Raven User", frappe.session.user, "full_name") + frappe.get_doc( + { + "doctype": "Raven Message", + "channel_id": self.channel_id, + "message_type": "System", + "text": f"{current_user_name} added {member_name}.", + } + ).insert() + def on_update(self): """ Check if the notification preference is changed and update the subscription @@ -134,9 +203,27 @@ def on_update(self): else: unsubscribe_user_to_topic(self.channel_id, self.user_id) + if self.has_value_changed("is_admin") and not self.flags.in_insert and not self.is_thread(): + # Send a system message to the channel mentioning the member who became admin + member_name = frappe.get_cached_value("Raven User", self.user_id, "full_name") + text = ( + f"{member_name} is now an admin." if self.is_admin else f"{member_name} is no longer an admin." + ) + frappe.get_doc( + { + "doctype": "Raven Message", + "channel_id": self.channel_id, + "message_type": "System", + "text": text, + } + ).insert() + def get_admin_count(self): return frappe.db.count("Raven Channel Member", {"channel_id": self.channel_id, "is_admin": 1}) + def is_thread(self): + return frappe.get_cached_value("Raven Channel", self.channel_id, "is_thread") + def on_doctype_update(): """ diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.json b/raven/raven_messaging/doctype/raven_message/raven_message.json index 860d1b08a..19f5e3dcc 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.json +++ b/raven/raven_messaging/doctype/raven_message/raven_message.json @@ -65,7 +65,7 @@ "fieldname": "message_type", "fieldtype": "Select", "label": "Message Type", - "options": "Text\nImage\nFile\nPoll" + "options": "Text\nImage\nFile\nPoll\nSystem" }, { "fieldname": "message_reactions", @@ -191,7 +191,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-08-16 16:09:36.292391", + "modified": "2024-10-11 18:42:44.984706", "modified_by": "Administrator", "module": "Raven Messaging", "name": "Raven Message", diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.py b/raven/raven_messaging/doctype/raven_message/raven_message.py index 12b078ee3..4d40b27be 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.py +++ b/raven/raven_messaging/doctype/raven_message/raven_message.py @@ -45,7 +45,7 @@ class RavenMessage(Document): linked_message: DF.Link | None mentions: DF.Table[RavenMention] message_reactions: DF.JSON | None - message_type: DF.Literal["Text", "Image", "File", "Poll"] + message_type: DF.Literal["Text", "Image", "File", "Poll", "System"] poll_id: DF.Link | None replied_message_details: DF.JSON | None text: DF.LongText | None @@ -55,7 +55,7 @@ class RavenMessage(Document): def before_validate(self): try: - if self.text: + if self.text and not self.message_type == "System": content = html2text(self.text) # Remove trailing new line characters and white spaces self.content = content.rstrip() diff --git a/types/Messaging/Message.ts b/types/Messaging/Message.ts index 37c5c7f29..0a50a860a 100644 --- a/types/Messaging/Message.ts +++ b/types/Messaging/Message.ts @@ -1,4 +1,4 @@ -export type Message = FileMessage | TextMessage | ImageMessage | PollMessage +export type Message = FileMessage | TextMessage | ImageMessage | PollMessage | SystemMessage export interface BaseMessage { name: string, @@ -7,7 +7,7 @@ export interface BaseMessage { channel_id: string, creation: string, modified: string, - message_type: 'Text' | 'File' | 'Image' | 'Poll', + message_type: 'Text' | 'File' | 'Image' | 'Poll' | 'System', message_reactions?: string | null, is_continuation: 1 | 0 is_reply: 1 | 0 @@ -53,6 +53,11 @@ export interface PollMessage extends BaseMessage { content?: string } +export interface SystemMessage extends BaseMessage { + message_type: 'System', + text: string, +} + export type DateBlock = { block_type: 'date', data: string