Skip to content

Commit

Permalink
Merge pull request #50 from sendbird/feat/AC-438/group-short-term-mes…
Browse files Browse the repository at this point in the history
…sage

feat: Merge sender profile when message is sent in short span of time
  • Loading branch information
AhyoungRyu authored Sep 8, 2023
2 parents 48918eb + 47f3187 commit efd3392
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 31 deletions.
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"dependencies": {
"@sendbird/chat": "^4.9.8",
"@sendbird/uikit-react": "^3.6.7",
"@sendbird/uikit-react": "^3.6.8",
"dompurify": "^3.0.4",
"polished": "^2.3.1",
"react-code-blocks": "^0.1.0",
Expand Down
59 changes: 41 additions & 18 deletions src/components/BotMessageWithBodyInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,36 +55,59 @@ type Props = {
botUser: User;
message: UserMessage;
bodyComponent: ReactNode;
chainTop: boolean;
chainBottom: boolean;
messageCount?: number;
zIndex?: number;
bodyStyle?: object;
};

const ImageContainer = styled.div``;

const EmptyImageContainer = styled.div`
width: 30px;
`;

export default function BotMessageWithBodyInput(props: Props) {
const { botUser, message, bodyComponent, messageCount, zIndex, bodyStyle } =
props;
const {
botUser,
message,
bodyComponent,
messageCount,
zIndex,
bodyStyle,
chainTop,
chainBottom,
} = props;

const nonChainedMessage = chainTop == null && chainBottom == null;
const displayProfileImage = nonChainedMessage || chainBottom;
const displaySender = nonChainedMessage || chainTop;
return (
<Root style={{ zIndex: messageCount === 1 && zIndex ? zIndex : 0 }}>
<ImageContainer>
<img
src={botMessageImage}
alt="botProfileImage"
style={{
height: '28px',
}}
/>
</ImageContainer>
{displayProfileImage ? (
<ImageContainer>
<img
src={botMessageImage}
alt="botProfileImage"
style={{
height: '28px',
}}
/>
</ImageContainer>
) : (
<EmptyImageContainer />
)}
<BodyContainer style={bodyStyle ?? {}}>
<Sender
style={{
textAlign: 'left',
}}
>
{botUser.nickname}
</Sender>
{displaySender && (
<Sender
style={{
textAlign: 'left',
}}
>
{botUser.nickname}
</Sender>
)}
{bodyComponent}
<ReactionContainer message={message} />
</BodyContainer>
Expand Down
11 changes: 11 additions & 0 deletions src/components/CustomChannelComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import DynamicRepliesPanel from './DynamicRepliesPanel';
import { useConstantState } from '../context/ConstantContext';
import { useScrollOnStreaming } from '../hooks/useScrollOnStreaming';
import { isSpecialMessage, scrollUtil } from '../utils';
import { groupMessagesByShortSpanTime } from '../utils/messages';

const Root = styled.div<{ hidePlaceholder: boolean; height: string }>`
height: ${({ height }) => height};
Expand Down Expand Up @@ -120,6 +121,11 @@ export function CustomChannelComponent(props: CustomChannelComponentProps) {
}
}, [lastMessage?.messageId]);

const grouppedMessages = useMemo(
() => groupMessagesByShortSpanTime(allMessages),
[allMessages.length]
);

return (
<Root hidePlaceholder={startingPagePlaceHolder} height={'100%'}>
<ChannelUI
Expand Down Expand Up @@ -149,13 +155,18 @@ export function CustomChannelComponent(props: CustomChannelComponentProps) {
);
}}
renderMessage={({ message }: { message: EveryMessage }) => {
const grouppedMessage = grouppedMessages.find(
(m) => m.messageId == message.messageId
);
return (
<>
<CustomMessage
message={message}
activeSpinnerId={activeSpinnerId}
botUser={botUser}
lastMessageRef={lastMessageRef}
chainTop={grouppedMessage?.chaintop}
chainBottom={grouppedMessage?.chainBottom}
/>
{message.messageId === lastMessage.messageId &&
dynamicReplyOptions.length > 0 && (
Expand Down
17 changes: 16 additions & 1 deletion src/components/CustomMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,19 @@ type Props = {
activeSpinnerId: number;
botUser: User;
lastMessageRef: React.RefObject<HTMLDivElement>;
chainTop?: boolean;
chainBottom?: boolean;
};

export default function CustomMessage(props: Props) {
const { message, activeSpinnerId, botUser, lastMessageRef } = props;
const {
message,
activeSpinnerId,
botUser,
lastMessageRef,
chainTop,
chainBottom,
} = props;
const { replacementTextList } = useConstantState();

const { allMessages } = useChannelContext();
Expand Down Expand Up @@ -63,6 +72,8 @@ export default function CustomMessage(props: Props) {
messageCount={allMessages.length}
zIndex={30}
bodyStyle={{ maxWidth: '255px', width: 'calc(100% - 98px)' }}
chainTop={chainTop}
chainBottom={chainBottom}
/>
</div>
);
Expand All @@ -81,6 +92,8 @@ export default function CustomMessage(props: Props) {
}
bodyStyle={{ maxWidth: '320px', width: 'calc(100% - 98px)' }}
messageCount={allMessages.length}
chainTop={chainTop}
chainBottom={chainBottom}
/>
);
}
Expand Down Expand Up @@ -110,6 +123,8 @@ export default function CustomMessage(props: Props) {
tokens={tokens}
/>
}
chainTop={chainTop}
chainBottom={chainBottom}
/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts → src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LOCAL_MESSAGE_CUSTOM_TYPE } from './const';
import { LOCAL_MESSAGE_CUSTOM_TYPE } from '../const';

export function uuid() {
let d = new Date().getTime();
Expand Down
50 changes: 50 additions & 0 deletions src/utils/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// eslint-disable-next-line import/no-unresolved
import { EveryMessage } from 'SendbirdUIKitGlobal';

const TIME_SPAN = 3 * 60 * 1000;
/**
* Function to group messages based on their creation time
*
* @param {EveryMessage[]} messages - Array of messages to group
* @returns {EveryMessage[]} - Array of messages grouped by creation time
*/
export function groupMessagesByShortSpanTime(
messages: EveryMessage[]
): EveryMessage[] {
// Create an object to group messages based on their creation time
const groupedMessagesByCreatedAt = messages.reduce((groups, message) => {
const { createdAt } = message;
// Get the key of the previous group
const prevKey = Object.keys(groups)[Object.keys(groups).length - 1];

// Check if the time difference between the current message and the previous one is within 3 minutes
if (prevKey && message.createdAt - Number(prevKey) <= TIME_SPAN) {
// Add the message to the existing group
return {
...groups,
[prevKey]: [...(groups[prevKey] ?? []), message],
};
} else {
// Create a new group for the current message
return {
...groups,
[createdAt]: [message],
};
}
}, {});

// Flatten the grouped messages and add chain indicators
return Object.values(groupedMessagesByCreatedAt).flatMap(
(messages: EveryMessage[]) => {
if (messages.length > 1) {
// Add chain indicators to the first and last messages in the group
return messages.map((message, index) => ({
...message,
chaintop: index === 0,
chainBottom: index === messages.length - 1,
}));
}
return messages;
}
);
}

0 comments on commit efd3392

Please sign in to comment.