From 620a1b6ced57f1b0c4a17b2eadde39aff33f5cd0 Mon Sep 17 00:00:00 2001 From: bang9 Date: Mon, 26 Feb 2024 00:32:27 +0900 Subject: [PATCH 1/4] fix: message render structure --- .../components/MessageInputWrapper/index.tsx | 7 +- .../Channel/components/MessageList/index.tsx | 174 +++++++----------- .../GroupChannelUI/GroupChannelUIView.tsx | 44 ++++- .../components/Message/MessageView.tsx | 127 +++++++------ .../components/MessageInputWrapper/index.tsx | 8 +- .../components/MessageList/index.tsx | 53 +++--- 6 files changed, 205 insertions(+), 208 deletions(-) diff --git a/src/modules/Channel/components/MessageInputWrapper/index.tsx b/src/modules/Channel/components/MessageInputWrapper/index.tsx index 024a93357..cae43fe01 100644 --- a/src/modules/Channel/components/MessageInputWrapper/index.tsx +++ b/src/modules/Channel/components/MessageInputWrapper/index.tsx @@ -4,14 +4,15 @@ import { getSuggestedReplies } from '../../../../utils'; import MessageInputWrapperView from '../../../GroupChannel/components/MessageInputWrapper/MessageInputWrapperView'; import { useChannelContext } from '../../context/ChannelProvider'; import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; +import { GroupChannelUIBasicProps } from '../../../GroupChannel/components/GroupChannelUI/GroupChannelUIView'; export interface MessageInputWrapperProps { value?: string; disabled?: boolean; - renderFileUploadIcon?: () => React.ReactElement; - renderVoiceMessageIcon?: () => React.ReactElement; - renderSendMessageIcon?: () => React.ReactElement; acceptableMimeTypes?: string[]; + renderFileUploadIcon?: GroupChannelUIBasicProps['renderFileUploadIcon']; + renderVoiceMessageIcon?: GroupChannelUIBasicProps['renderVoiceMessageIcon']; + renderSendMessageIcon?: GroupChannelUIBasicProps['renderSendMessageIcon']; } export const MessageInputWrapper = (props: MessageInputWrapperProps) => { diff --git a/src/modules/Channel/components/MessageList/index.tsx b/src/modules/Channel/components/MessageList/index.tsx index f27cd6fc1..8359288b3 100644 --- a/src/modules/Channel/components/MessageList/index.tsx +++ b/src/modules/Channel/components/MessageList/index.tsx @@ -29,12 +29,12 @@ const SCROLL_BOTTOM_PADDING = 50; export type MessageListProps = GroupChannelMessageListProps; export const MessageList = ({ className = '', - renderMessage, + renderMessage = (props) => , renderMessageContent, - renderPlaceholderEmpty, renderCustomSeparator, - renderPlaceholderLoader, - renderFrozenNotification, + renderPlaceholderLoader = () => , + renderPlaceholderEmpty = () => , + renderFrozenNotification = () => , }: MessageListProps) => { const { allMessages, @@ -62,9 +62,7 @@ export const MessageList = ({ } = useChannelContext(); const store = useSendbirdStateContext(); - const allMessagesFiltered = (typeof filterMessageList === 'function') - ? allMessages.filter((filterMessageList as (message: EveryMessage) => boolean)) - : allMessages; + const allMessagesFiltered = typeof filterMessageList === 'function' ? allMessages.filter(filterMessageList as (message: EveryMessage) => boolean) : allMessages; const markAsReadScheduler = store.config.markAsReadScheduler; const [isScrollBottom, setIsScrollBottom] = useState(false); @@ -80,11 +78,7 @@ export const MessageList = ({ return; } - const { - scrollTop, - clientHeight, - scrollHeight, - } = element; + const { scrollTop, clientHeight, scrollHeight } = element; if (hasMorePrev && isAboutSame(scrollTop, 0, SCROLL_BUFFER)) { onScrollCallback(callback); @@ -94,10 +88,7 @@ export const MessageList = ({ onScrollDownCallback(callback); } - if (!disableMarkAsRead - && isAboutSame(clientHeight + scrollTop, scrollHeight, SCROLL_BUFFER) - && !!currentGroupChannel - ) { + if (!disableMarkAsRead && isAboutSame(clientHeight + scrollTop, scrollHeight, SCROLL_BUFFER) && !!currentGroupChannel) { messagesDispatcher({ type: messageActionTypes.MARK_AS_READ, payload: { channel: currentGroupChannel }, @@ -124,8 +115,7 @@ export const MessageList = ({ const current = scrollRef?.current; if (current) { const bottom = current.scrollHeight - current.scrollTop - current.offsetHeight; - if (scrollBottom < bottom - && (!isBottomMessageAffected || scrollBottom < SCROLL_BUFFER)) { + if (scrollBottom < bottom && (!isBottomMessageAffected || scrollBottom < SCROLL_BUFFER)) { // Move the scroll as much as the height of the message has changed current.scrollTop += bottom - scrollBottom; } @@ -165,22 +155,16 @@ export const MessageList = ({ const { scrollToBottomHandler, scrollBottom } = useSetScrollToBottom({ loading }); if (loading) { - return (typeof renderPlaceholderLoader === 'function') - ? renderPlaceholderLoader() - : ; + return renderPlaceholderLoader(); } + if (allMessagesFiltered.length < 1) { - if (renderPlaceholderEmpty && typeof renderPlaceholderEmpty === 'function') { - return renderPlaceholderEmpty(); - } - return ; + return renderPlaceholderEmpty(); } return ( <> - { - !isScrolled && - } + {!isScrolled && }
@@ -193,86 +177,63 @@ export const MessageList = ({ onScrollReachedEndDetector(e); }} > - { - allMessagesFiltered.map((m, idx) => { - const { - chainTop, - chainBottom, - hasSeparator, - } = getMessagePartsInfo({ - allMessages: allMessagesFiltered, - replyType, - isMessageGroupingEnabled, - currentIndex: idx, - currentMessage: m, - currentChannel: currentGroupChannel, - }); - const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId; - return ( - - - - ); - }) - } - { - localMessages.map((m, idx) => { - const { - chainTop, - chainBottom, - } = getMessagePartsInfo({ - allMessages: allMessagesFiltered, - replyType, - isMessageGroupingEnabled, - currentIndex: idx, - currentMessage: m, - currentChannel: currentGroupChannel, - }); - const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId; - return ( - - - - ); - }) - } - { - !hasMoreNext + {allMessagesFiltered.map((m, idx) => { + const { chainTop, chainBottom, hasSeparator } = getMessagePartsInfo({ + allMessages: allMessagesFiltered, + replyType, + isMessageGroupingEnabled, + currentIndex: idx, + currentMessage: m, + currentChannel: currentGroupChannel, + }); + const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId; + return ( + + {renderMessage({ + handleScroll: moveScroll, + message: m as EveryMessage, + hasSeparator, + chainTop, + chainBottom, + renderMessageContent, + renderCustomSeparator, + })} + + ); + })} + {localMessages.map((m, idx) => { + const { chainTop, chainBottom } = getMessagePartsInfo({ + allMessages: allMessagesFiltered, + replyType, + isMessageGroupingEnabled, + currentIndex: idx, + currentMessage: m, + currentChannel: currentGroupChannel, + }); + const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId; + return ( + + {renderMessage({ + handleScroll: moveScroll, + message: m as EveryMessage, + chainTop, + chainBottom, + renderMessageContent, + renderCustomSeparator, + })} + + ); + })} + {!hasMoreNext && store?.config?.groupChannel?.enableTypingIndicator - && store?.config?.groupChannel?.typingIndicatorTypes?.has(TypingIndicatorType.Bubble) - && - } + && store?.config?.groupChannel?.typingIndicatorTypes?.has(TypingIndicatorType.Bubble) && ( + + )} {/* show frozen notifications, */} {/* show new message notifications, */}
- { - currentGroupChannel?.isFrozen && ( - renderFrozenNotification - ? renderFrozenNotification() - : - ) - } + {currentGroupChannel?.isFrozen && renderFrozenNotification()} { /** * Show unread count IFF scroll is not bottom or is bottom but hasNext is true. @@ -309,12 +270,7 @@ export const MessageList = ({ tabIndex={0} role="button" > - +
) } diff --git a/src/modules/GroupChannel/components/GroupChannelUI/GroupChannelUIView.tsx b/src/modules/GroupChannel/components/GroupChannelUI/GroupChannelUIView.tsx index e5bb4ab51..721e4c377 100644 --- a/src/modules/GroupChannel/components/GroupChannelUI/GroupChannelUIView.tsx +++ b/src/modules/GroupChannel/components/GroupChannelUI/GroupChannelUIView.tsx @@ -15,24 +15,66 @@ import type { MessageContentProps } from '../../../../ui/MessageContent'; export interface GroupChannelUIBasicProps { // Components + /** + * A function that customizes the rendering of a loading placeholder component. + */ renderPlaceholderLoader?: () => React.ReactElement; + /** + * A function that customizes the rendering of a invalid placeholder component. + */ renderPlaceholderInvalid?: () => React.ReactElement; + /** + * A function that customizes the rendering of an empty placeholder component when there are no messages in the channel. + */ renderPlaceholderEmpty?: () => React.ReactElement; + /** + * A function that customizes the rendering of a header component. + */ renderChannelHeader?: (props: GroupChannelHeaderProps) => React.ReactElement; + /** + * A function that customizes the rendering of a message list component. + */ renderMessageList?: (props: GroupChannelMessageListProps) => React.ReactElement; + /** + * A function that customizes the rendering of a message input component. + */ renderMessageInput?: () => React.ReactElement; // Sub components // MessageList + /** + * A function that customizes the rendering of each message component in the message list component. + */ renderMessage?: (props: RenderMessageParamsType) => React.ReactElement; + /** + * A function that customizes the rendering of the content portion of each message component. + */ renderMessageContent?: (props: MessageContentProps) => React.ReactElement; + /** + * A function that customizes the rendering of a separator component between messages. + */ renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement; + /** + * A function that customizes the rendering of a frozen notification component when the channel is frozen. + */ renderFrozenNotification?: () => React.ReactElement; - // MessageInput + // MessageInput + /** + * A function that customizes the rendering of the file upload icon in the message input component. + */ renderFileUploadIcon?: () => React.ReactElement; + /** + * A function that customizes the rendering of the voice message icon in the message input component. + */ renderVoiceMessageIcon?: () => React.ReactElement; + /** + * A function that customizes the rendering of the send message icon in the message input component. + */ renderSendMessageIcon?: () => React.ReactElement; + /** + * A function that customizes the rendering of the typing indicator component. + */ renderTypingIndicator?: () => React.ReactElement; } diff --git a/src/modules/GroupChannel/components/Message/MessageView.tsx b/src/modules/GroupChannel/components/Message/MessageView.tsx index 8f798aff1..eb9ac465d 100644 --- a/src/modules/GroupChannel/components/Message/MessageView.tsx +++ b/src/modules/GroupChannel/components/Message/MessageView.tsx @@ -28,11 +28,27 @@ export interface MessageProps { chainBottom?: boolean; handleScroll?: (isBottomMessageAffected?: boolean) => void; - // for extending - renderMessage?: (props: RenderMessageParamsType) => React.ReactElement; + /** + * Customizes all child components of the message. + * */ + children?: React.ReactNode; + /** + * A function that customizes the rendering of the content portion of message component. + */ renderMessageContent?: (props: MessageContentProps) => React.ReactElement; + /** + * A function that customizes the rendering of a separator between messages. + */ renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement; + /** + * A function that customizes the rendering of the edit input portion of the message component. + * */ renderEditInput?: () => React.ReactElement; + /** + * @deprecated Please use `children` instead + * @description Customizes all child components of the message. + * */ + renderMessage?: (props: RenderMessageParamsType) => React.ReactElement; } export interface MessageViewProps extends MessageProps { @@ -78,6 +94,7 @@ const MessageView = (props: MessageViewProps) => { // MessageProps message, renderMessage, + children, renderMessageContent = (props) => , renderCustomSeparator, renderEditInput, @@ -227,40 +244,54 @@ const MessageView = (props: MessageViewProps) => { }, [animatedMessageId, messageScrollRef.current, message.messageId]); const renderedCustomSeparator = useMemo(() => renderCustomSeparator?.({ message }) ?? null, [message, renderCustomSeparator]); - const renderedMessage = useMemo(() => renderMessage?.(props), [message, renderMessage]); - if (renderedMessage) { + const renderChildren = () => { + if (children) { + return children; + } + + if (renderMessage) { + return renderMessage({ ...props, renderMessage: undefined }); + } + return ( -
- {/* date-separator */} + <> + {/* Message */} + {renderMessageContent({ + className: 'sendbird-message-hoc__message-content', + userId, + scrollToMessage, + channel, + message, + disabled: !isOnline, + chainTop, + chainBottom, + isReactionEnabled, + replyType, + threadReplySelectType, + nicknamesMap, + emojiContainer, + showEdit: setShowEdit, + showRemove: setShowRemove, + showFileViewer: setShowFileViewer, + resendMessage, + deleteMessage, + toggleReaction, + setQuoteMessage, + onReplyInThread: onReplyInThreadClick, + onQuoteMessageClick: onQuoteMessageClick, + onMessageHeightChange: handleScroll, + })} { - // TODO: Add message instance as a function parameter - hasSeparator - && (renderedCustomSeparator || ( - - - - )) + /** Suggested Replies */ + shouldRenderSuggestedReplies && } - {renderedMessage} -
+ {/* Modal */} + {showRemove && renderRemoveMessageModal({ message, onCancel: () => setShowRemove(false) })} + {showFileViewer && renderFileViewer({ message: message as FileMessage, onCancel: () => setShowFileViewer(false) })} + ); - } + }; if (showEdit && message?.isUserMessage?.()) { return ( @@ -373,39 +404,7 @@ const MessageView = (props: MessageViewProps) => { ))} - {/* Message */} - {renderMessageContent({ - className: 'sendbird-message-hoc__message-content', - userId, - scrollToMessage, - channel, - message, - disabled: !isOnline, - chainTop, - chainBottom, - isReactionEnabled, - replyType, - threadReplySelectType, - nicknamesMap, - emojiContainer, - showEdit: setShowEdit, - showRemove: setShowRemove, - showFileViewer: setShowFileViewer, - resendMessage, - deleteMessage, - toggleReaction, - setQuoteMessage, - onReplyInThread: onReplyInThreadClick, - onQuoteMessageClick: onQuoteMessageClick, - onMessageHeightChange: handleScroll, - })} - { - /** Suggested Replies */ - shouldRenderSuggestedReplies && - } - {/* Modal */} - {showRemove && renderRemoveMessageModal({ message, onCancel: () => setShowRemove(false) })} - {showFileViewer && renderFileViewer({ message: message as FileMessage, onCancel: () => setShowFileViewer(false) })} + {renderChildren()} ); }; diff --git a/src/modules/GroupChannel/components/MessageInputWrapper/index.tsx b/src/modules/GroupChannel/components/MessageInputWrapper/index.tsx index f773fa48e..5e0fc7861 100644 --- a/src/modules/GroupChannel/components/MessageInputWrapper/index.tsx +++ b/src/modules/GroupChannel/components/MessageInputWrapper/index.tsx @@ -4,13 +4,15 @@ import { useGroupChannelContext } from '../../context/GroupChannelProvider'; import { useIIFE } from '@sendbird/uikit-tools'; import { getSuggestedReplies, isSendableMessage } from '../../../../utils'; import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; +import { GroupChannelUIBasicProps } from '../GroupChannelUI/GroupChannelUIView'; export interface MessageInputWrapperProps { value?: string; disabled?: boolean; - renderFileUploadIcon?: () => React.ReactElement; - renderVoiceMessageIcon?: () => React.ReactElement; - renderSendMessageIcon?: () => React.ReactElement; + acceptableMimeTypes?: string[]; + renderFileUploadIcon?: GroupChannelUIBasicProps['renderFileUploadIcon']; + renderVoiceMessageIcon?: GroupChannelUIBasicProps['renderVoiceMessageIcon']; + renderSendMessageIcon?: GroupChannelUIBasicProps['renderSendMessageIcon']; } export const MessageInputWrapper = (props: MessageInputWrapperProps) => { diff --git a/src/modules/GroupChannel/components/MessageList/index.tsx b/src/modules/GroupChannel/components/MessageList/index.tsx index 776e3431f..2318accb4 100644 --- a/src/modules/GroupChannel/components/MessageList/index.tsx +++ b/src/modules/GroupChannel/components/MessageList/index.tsx @@ -4,7 +4,7 @@ import type { Member } from '@sendbird/chat/groupChannel'; import { useGroupChannelHandler } from '@sendbird/uikit-tools'; import type { CoreMessageType } from '../../../../utils'; -import { EveryMessage, RenderCustomSeparatorProps, RenderMessageParamsType, TypingIndicatorType } from '../../../../types'; +import { EveryMessage, TypingIndicatorType } from '../../../../types'; import PlaceHolder, { PlaceHolderTypes } from '../../../../ui/PlaceHolder'; import Icon, { IconColors, IconTypes } from '../../../../ui/Icon'; @@ -19,25 +19,26 @@ import { useScrollBehavior } from './hooks/useScrollBehavior'; import TypingIndicatorBubble from '../../../../ui/TypingIndicatorBubble'; import { useGroupChannelContext } from '../../context/GroupChannelProvider'; import { getComponentKeyFromMessage } from '../../context/utils'; -import type { MessageContentProps } from '../../../../ui/MessageContent'; +import { GroupChannelUIBasicProps } from '../GroupChannelUI/GroupChannelUIView'; export interface GroupChannelMessageListProps { className?: string; - renderMessage?: (props: RenderMessageParamsType) => React.ReactElement; - renderMessageContent?: (props: MessageContentProps) => React.ReactElement; - renderPlaceholderEmpty?: () => React.ReactElement; - renderCustomSeparator?: (props: RenderCustomSeparatorProps) => React.ReactElement; - renderPlaceholderLoader?: () => React.ReactElement; - renderFrozenNotification?: () => React.ReactElement; + renderMessage?: GroupChannelUIBasicProps['renderMessage']; + renderMessageContent?: GroupChannelUIBasicProps['renderMessageContent']; + renderCustomSeparator?: GroupChannelUIBasicProps['renderCustomSeparator']; + renderPlaceholderLoader?: GroupChannelUIBasicProps['renderPlaceholderLoader']; + renderPlaceholderEmpty?: GroupChannelUIBasicProps['renderPlaceholderEmpty']; + renderFrozenNotification?: GroupChannelUIBasicProps['renderFrozenNotification']; } export const MessageList = ({ className = '', - renderMessage, - renderPlaceholderEmpty, + renderMessage = (props) => , + renderMessageContent, renderCustomSeparator, - renderPlaceholderLoader, - renderFrozenNotification, + renderPlaceholderLoader = () => , + renderPlaceholderEmpty = () => , + renderFrozenNotification = () => , }: GroupChannelMessageListProps) => { const { channelUrl, @@ -88,9 +89,7 @@ export const MessageList = ({ const renderer = { frozenNotification() { if (!currentChannel || !currentChannel.isFrozen) return null; - - if (renderFrozenNotification) return renderFrozenNotification(); - return ; + return renderFrozenNotification(); }, unreadMessagesNotification() { if (isScrollBottomReached || !unreadSinceDate) return null; @@ -121,13 +120,11 @@ export const MessageList = ({ }; if (loading) { - if (renderPlaceholderLoader) return renderPlaceholderLoader(); - return ; + return renderPlaceholderLoader(); } if (messages.length === 0) { - if (renderPlaceholderEmpty) return renderPlaceholderEmpty(); - return ; + return renderPlaceholderEmpty(); } return ( @@ -148,15 +145,15 @@ export const MessageList = ({ const isOutgoingMessage = message.isUserMessage() && message.sender.userId === store.config.userId; return ( - + {renderMessage({ + handleScroll: onMessageContentSizeChanged, + message: message as EveryMessage, + hasSeparator, + chainTop, + chainBottom, + renderMessageContent, + renderCustomSeparator, + })} ); })} From c9ef668e5211564fef96256b359058281cf18c43 Mon Sep 17 00:00:00 2001 From: bang9 Date: Mon, 26 Feb 2024 00:42:47 +0900 Subject: [PATCH 2/4] chore: build fix --- src/modules/GroupChannel/components/Message/MessageView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/GroupChannel/components/Message/MessageView.tsx b/src/modules/GroupChannel/components/Message/MessageView.tsx index eb9ac465d..b41366ed9 100644 --- a/src/modules/GroupChannel/components/Message/MessageView.tsx +++ b/src/modules/GroupChannel/components/Message/MessageView.tsx @@ -251,7 +251,8 @@ const MessageView = (props: MessageViewProps) => { } if (renderMessage) { - return renderMessage({ ...props, renderMessage: undefined }); + const messageProps = { ...props, renderMessage: undefined }; + return renderMessage(messageProps); } return ( From 0fd1bd7a5a5833962758d1ddccd638bab3b30f02 Mon Sep 17 00:00:00 2001 From: bang9 Date: Mon, 26 Feb 2024 01:07:54 +0900 Subject: [PATCH 3/4] chore: keep backward compatability in Channel module --- .../Channel/components/ChannelUI/index.tsx | 4 ++ .../Channel/components/MessageList/index.tsx | 48 +++++++++++-------- .../components/Message/MessageView.tsx | 2 +- .../components/MessageList/index.tsx | 18 +++++++ 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/modules/Channel/components/ChannelUI/index.tsx b/src/modules/Channel/components/ChannelUI/index.tsx index c69530bfe..16338919e 100644 --- a/src/modules/Channel/components/ChannelUI/index.tsx +++ b/src/modules/Channel/components/ChannelUI/index.tsx @@ -9,6 +9,10 @@ import MessageInputWrapper from '../MessageInputWrapper'; export interface ChannelUIProps extends GroupChannelUIBasicProps { isLoading?: boolean; + /** + * Customizes all child components of the message component. + * */ + renderMessage?: GroupChannelUIBasicProps['renderMessage']; } const ChannelUI = (props: ChannelUIProps) => { diff --git a/src/modules/Channel/components/MessageList/index.tsx b/src/modules/Channel/components/MessageList/index.tsx index 8359288b3..2c85341ea 100644 --- a/src/modules/Channel/components/MessageList/index.tsx +++ b/src/modules/Channel/components/MessageList/index.tsx @@ -23,13 +23,19 @@ import { useOnScrollPositionChangeDetector } from '../../../../hooks/useOnScroll import { getMessagePartsInfo } from '../../../GroupChannel/components/MessageList/getMessagePartsInfo'; import { GroupChannelMessageListProps } from '../../../GroupChannel/components/MessageList'; +import { GroupChannelUIBasicProps } from '../../../GroupChannel/components/GroupChannelUI/GroupChannelUIView'; const SCROLL_BOTTOM_PADDING = 50; -export type MessageListProps = GroupChannelMessageListProps; +export interface MessageListProps extends GroupChannelMessageListProps { + /** + * Customizes all child components of the message component. + * */ + renderMessage?: GroupChannelUIBasicProps['renderMessage']; +} export const MessageList = ({ className = '', - renderMessage = (props) => , + renderMessage, renderMessageContent, renderCustomSeparator, renderPlaceholderLoader = () => , @@ -189,15 +195,17 @@ export const MessageList = ({ const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId; return ( - {renderMessage({ - handleScroll: moveScroll, - message: m as EveryMessage, - hasSeparator, - chainTop, - chainBottom, - renderMessageContent, - renderCustomSeparator, - })} + ); })} @@ -213,14 +221,16 @@ export const MessageList = ({ const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId; return ( - {renderMessage({ - handleScroll: moveScroll, - message: m as EveryMessage, - chainTop, - chainBottom, - renderMessageContent, - renderCustomSeparator, - })} + ); })} diff --git a/src/modules/GroupChannel/components/Message/MessageView.tsx b/src/modules/GroupChannel/components/Message/MessageView.tsx index b41366ed9..7fc012ce7 100644 --- a/src/modules/GroupChannel/components/Message/MessageView.tsx +++ b/src/modules/GroupChannel/components/Message/MessageView.tsx @@ -389,7 +389,7 @@ const MessageView = (props: MessageViewProps) => { isAnimated ? 'sendbird-msg-hoc__animated' : '', isHighlighted ? 'sendbird-msg-hoc__highlighted' : '', ])} - style={{ marginBottom: '2px' }} + style={children || renderMessage ? undefined : { marginBottom: '2px' }} data-sb-message-id={message.messageId} data-sb-created-at={message.createdAt} ref={messageScrollRef} diff --git a/src/modules/GroupChannel/components/MessageList/index.tsx b/src/modules/GroupChannel/components/MessageList/index.tsx index 2318accb4..7728d8abf 100644 --- a/src/modules/GroupChannel/components/MessageList/index.tsx +++ b/src/modules/GroupChannel/components/MessageList/index.tsx @@ -23,11 +23,29 @@ import { GroupChannelUIBasicProps } from '../GroupChannelUI/GroupChannelUIView'; export interface GroupChannelMessageListProps { className?: string; + /** + * A function that customizes the rendering of each message component in the message list component. + */ renderMessage?: GroupChannelUIBasicProps['renderMessage']; + /** + * A function that customizes the rendering of the content portion of each message component. + */ renderMessageContent?: GroupChannelUIBasicProps['renderMessageContent']; + /** + * A function that customizes the rendering of a separator component between messages. + */ renderCustomSeparator?: GroupChannelUIBasicProps['renderCustomSeparator']; + /** + * A function that customizes the rendering of a loading placeholder component. + */ renderPlaceholderLoader?: GroupChannelUIBasicProps['renderPlaceholderLoader']; + /** + * A function that customizes the rendering of an empty placeholder component when there are no messages in the channel. + */ renderPlaceholderEmpty?: GroupChannelUIBasicProps['renderPlaceholderEmpty']; + /** + * A function that customizes the rendering of a frozen notification component when the channel is frozen. + */ renderFrozenNotification?: GroupChannelUIBasicProps['renderFrozenNotification']; } From 3555d9f4eef26537e92993d578cccafe6b562b8f Mon Sep 17 00:00:00 2001 From: bang9 Date: Mon, 26 Feb 2024 15:33:07 +0900 Subject: [PATCH 4/4] fix: should check sendable instead of user message --- src/modules/GroupChannel/components/MessageList/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/GroupChannel/components/MessageList/index.tsx b/src/modules/GroupChannel/components/MessageList/index.tsx index 7728d8abf..26ce8d568 100644 --- a/src/modules/GroupChannel/components/MessageList/index.tsx +++ b/src/modules/GroupChannel/components/MessageList/index.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import type { Member } from '@sendbird/chat/groupChannel'; import { useGroupChannelHandler } from '@sendbird/uikit-tools'; -import type { CoreMessageType } from '../../../../utils'; +import { CoreMessageType, isSendableMessage } from '../../../../utils'; import { EveryMessage, TypingIndicatorType } from '../../../../types'; import PlaceHolder, { PlaceHolderTypes } from '../../../../ui/PlaceHolder'; @@ -160,7 +160,7 @@ export const MessageList = ({ currentMessage: message as CoreMessageType, currentChannel, }); - const isOutgoingMessage = message.isUserMessage() && message.sender.userId === store.config.userId; + const isOutgoingMessage = isSendableMessage(message) && message.sender.userId === store.config.userId; return ( {renderMessage({