Skip to content

Instantly share code, notes, and snippets.

@abirc8010
Last active January 20, 2025 20:12
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { isSameDay, format } from 'date-fns';
import {
Box,
Sidebar,
Popup,
ActionButton,
Icon,
useTheme
} from '@embeddedchat/ui-elements';
import { MessageDivider } from '../../Message/MessageDivider';
import Message from '../../Message/Message';
import getMessageAggregatorStyles from './MessageAggregator.styles';
import { useMessageStore, useSidebarStore } from '../../../store';
import { useSetMessageList } from '../../../hooks/useSetMessageList';
import LoadingIndicator from './LoadingIndicator';
import NoMessagesIndicator from './NoMessageIndicator';
import FileDisplay from '../../FileMessage/FileMessage';
import useSetExclusiveState from '../../../hooks/useSetExclusiveState';
import { useRCContext } from '../../../context/RCInstance';
export const MessageAggregator = ({
title,
iconName,
noMessageInfo,
shouldRender,
fetchedMessageList,
loadMoreMessages, // A function to load more messages taken as prop
filterProps,
searchProps,
searchFiltered,
type = 'message',
viewType = 'Sidebar',
}) => {
const { theme } = useTheme
const styles = getMessageAggregatorStyles(theme);
const setExclusiveState = useSetExclusiveState();
const { ECOptions } = useRCContext();
const showRoles = ECOptions?.showRoles;
const messages = useMessageStore((state) => state.messages);
const threadMessages = useMessageStore((state) => state.threadMessages) || [];
const allMessages = useMemo(
() => [...messages, ...[...threadMessages].reverse()],
[messages, threadMessages]
);
const [messageRendered, setMessageRendered] = useState(false);
const { loading, messageList } = useSetMessageList(
fetchedMessageList || searchFiltered || allMessages,
shouldRender
);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const [currentMessageList, setCurrentMessageList] = useState(messageList);
useEffect(() => {
setCurrentMessageList(messageList);
}, [messageList]);
const handleScroll = useCallback(
(e) => {
const { scrollTop, scrollHeight, clientHeight } = e.target;
if (scrollHeight - scrollTop - clientHeight <= 100 && !isLoadingMore) {
setIsLoadingMore(true);
loadMoreMessages().finally(() => {
setIsLoadingMore(false);
});
}
},
[isLoadingMore, loadMoreMessages]
);
const noMessages = currentMessageList?.length === 0 || !messageRendered;
const ViewComponent = viewType === 'Popup' ? Popup : Sidebar;
return (
<ViewComponent
title={title}
iconName={iconName}
filterProps={filterProps}
searchProps={searchProps}
onClose={() => setExclusiveState(null)}
style={{
width: '400px',
padding: 0,
zIndex: window.innerWidth <= 780 ? 1 : null,
}}
{...(viewType === 'Popup'
? {
isPopupHeader: true,
}
: {})}
>
{loading ? (
<LoadingIndicator />
) : (
<Box
css={[
styles.listContainerStyles,
noMessages && styles.noMessageStyles,
]}
onScroll={handleScroll}
style={{ overflowY: 'auto' }}
>
{noMessages && (
<NoMessagesIndicator iconName={iconName} message={noMessageInfo} />
)}
{[...new Map(currentMessageList.map((msg) => [msg._id, msg])).values()].map(
(msg, index, arr) => {
const newDay = !arr[index - 1] || isSameDay(new Date(msg.ts), new Date(arr[index - 1]?.ts));
if (!messageRendered && shouldRender(msg)) {
setMessageRendered(true);
}
return (
<React.Fragment key={msg._id}>
{type === 'message' && newDay && (
<MessageDivider>
{format(new Date(msg.ts), 'MMMM d, yyyy')}
</MessageDivider>
)}
{type === 'file' ? (
<FileDisplay
key={`${msg._id}-aggregated`}
fileMessage={msg}
/>
) : (
<Box
position="relative"
style={{
display: 'flex',
}}
>
<Message
key={`${msg._id}-aggregated`}
message={msg}
newDay={false}
type="default"
showAvatar
showToolbox={false}
showRoles={showRoles}
isInSidebar
style={{
flex: 1,
padding: 0,
marginLeft: '15px',
minWidth: 0,
}}
/>
<ActionButton
square
ghost
onClick={() => console.log('Jump to message:', msg)}
css={{
position: 'relative',
zIndex: 10,
marginRight: '5px',
}}
>
<Icon name="arrow-back" size="1.25rem" />
</ActionButton>
</Box>
)}
</React.Fragment>
);
}
)}
{isLoadingMore && (
<Box
css={{
display: 'flex',
justifyContent: 'center',
padding: '10px',
}}
>
<LoadingIndicator />
</Box>
)}
</Box>
)}
</ViewComponent>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment