Skip to content

Commit

Permalink
Merge pull request #386 from js43o/enhance-chatting-2
Browse files Browse the repository at this point in the history
[Feat] 모각코 참석/참석 취소 시 채팅 입장/퇴장 알림 메시지 전송
  • Loading branch information
js43o authored Dec 10, 2023
2 parents 71ae755 + 4803c91 commit 474494b
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,50 @@ import { sansRegular12, sansRegular14 } from '@/styles/font.css';

const isMine = style({});

export const container = style({
export const datetime = style([
sansRegular12,
{
padding: '0 0.4rem',
alignSelf: 'start',
color: vars.color.grayscale200,

selectors: {
[`${isMine}&`]: {
alignSelf: 'end',
},
},
},
]);

export { isMine };

export const notification = style([
sansRegular14,
{
display: 'flex',
justifyContent: 'center',
flexShrink: 0,
alignSelf: 'center',
padding: '0.4rem 1.6rem',
border: `1px solid ${vars.color.grayscale100}`,
borderRadius: '100rem',
margin: '1rem 0',
color: vars.color.grayscale200,
overflow: 'hidden auto',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
},
]);

export const talkContainer = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'stretch',
gap: '0.8rem',
width: '100%',
});

export const content = style([
export const talkContents = style([
sansRegular14,
{
display: 'flex',
Expand All @@ -22,33 +57,17 @@ export const content = style([
padding: '1.2rem 1.6rem',
border: `1px solid ${vars.color.grayscale200}`,
borderRadius: '0 0.8rem 0.8rem 0.8rem',
background: vars.color.grayscaleWhite,
background: vars.color.grayscale50,
lineHeight: '1.4',
wordBreak: 'break-word',

selectors: {
[`${isMine}&`]: {
borderRadius: '0.8rem 0 0.8rem 0.8rem',
border: `1px solid ${vars.color.morakGreen}`,
background: vars.color.subGreen,
alignSelf: 'end',
},
},
},
]);

export const datetime = style([
sansRegular12,
{
padding: '0 0.4rem',
alignSelf: 'start',
color: vars.color.grayscale200,

selectors: {
[`${isMine}&`]: {
alignSelf: 'end',
},
},
},
]);

export { isMine };
19 changes: 16 additions & 3 deletions app/frontend/src/components/Sidebar/Contents/Chatting/ChatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ChatMessage } from '@morak/chat/src/interface/message.interface';
import { useObserver } from '@/hooks/useObserver';

import * as styles from './index.css';
import { MemorizedNotificationItem } from './NotificationItem';
import { MemorizedTalkItem } from './TalkItem';

type ChatListProps = {
Expand All @@ -32,10 +33,18 @@ export function ChatList({
}

const { scrollTop, clientHeight, scrollHeight } = listElemRef.current;

if (scrollHeight === prevScrollHeightRef.current) {
return;
}

if (exposed) {
listElemRef.current.scrollTo({
top: scrollHeight - prevScrollHeightRef.current,
});
prevScrollHeightRef.current = scrollHeight;

return;
}

if (
Expand All @@ -45,9 +54,8 @@ export function ChatList({
listElemRef.current.scrollTo({
top: scrollHeight - clientHeight,
});
prevScrollHeightRef.current = scrollHeight;
}

prevScrollHeightRef.current = scrollHeight;
}, [chatItems, exposed]);

useEffect(() => {
Expand All @@ -63,7 +71,7 @@ export function ChatList({
const participantInfo = participants.find(
(participant) => participant.id === chatItem.user,
);
return (
return chatItem.messageType === 'talk' ? (
<MemorizedTalkItem
key={chatItem.date.toString()}
nickname={participantInfo?.nickname || ''}
Expand All @@ -72,6 +80,11 @@ export function ChatList({
date={chatItem.date}
isMine={chatItem.user === userId}
/>
) : (
<MemorizedNotificationItem
key={chatItem.date.toString()}
contents={chatItem.contents}
/>
);
})}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { Button, Textarea } from '@/components';
import * as styles from './index.css';

type ChattingFooterProps = {
sendMessage: (message: string) => void;
userId: string;
sendMessage: (message: string, userId: string) => void;
};

export function ChattingFooter({ sendMessage }: ChattingFooterProps) {
export function ChattingFooter({ userId, sendMessage }: ChattingFooterProps) {
const [message, setMessage] = useState('');

const onChangeTextarea = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
Expand All @@ -17,7 +18,7 @@ export function ChattingFooter({ sendMessage }: ChattingFooterProps) {

const handleSendMessage = () => {
if (/\S+/.test(message)) {
sendMessage(message);
sendMessage(message, userId);
setMessage('');
}
};
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import * as styles from './NotificationItem.css';
import { memo } from 'react';

export function NotificationItem() {
return <div className={styles.container} />;
import * as styles from './ChatItem.css';

type NotificationItemProps = {
contents: string;
};

function NotificationItem({ contents }: NotificationItemProps) {
return <div className={styles.notification}>{contents}</div>;
}

export const MemorizedNotificationItem = memo(NotificationItem);
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import dayjs from 'dayjs';

import { UserChip } from '@/components';

import * as styles from './TalkItem.css';
import * as styles from './ChatItem.css';

type TalkItemProps = {
nickname: string;
Expand All @@ -27,9 +27,9 @@ function TalkItem({
: wrappedDate.format('MM.DD h:mm A');

return (
<div className={styles.container}>
<div className={styles.talkContainer}>
{!isMine && <UserChip username={nickname} profileSrc={profilePicture} />}
<div className={`${styles.content} ${isMine && styles.isMine}`}>
<div className={`${styles.talkContents} ${isMine && styles.isMine}`}>
{contents}
</div>
<div className={`${styles.datetime} ${isMine && styles.isMine}`}>
Expand Down
13 changes: 13 additions & 0 deletions app/frontend/src/components/Sidebar/Contents/Chatting/index.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ export const header = style({
background: vars.color.grayscale50,
});

export const notParticipated = style([
sansRegular16,
{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
color: vars.color.grayscale200,
lineHeight: '1.4',
textAlign: 'center',
},
]);

export const participants = style([
sansRegular16,
{
Expand Down
78 changes: 71 additions & 7 deletions app/frontend/src/components/Sidebar/Contents/Chatting/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useEffect, useRef, useState } from 'react';

import { ResponseParticipant } from '@morak/apitype';

import { useChatting } from '@/hooks';
Expand All @@ -11,30 +13,92 @@ type ChattingProps = {
postId: string;
title: string;
participants: ResponseParticipant[];
userId: string;
currentUser: ResponseParticipant;
};

export function Chatting({
postId,
title,
participants,
userId,
currentUser,
}: ChattingProps) {
const { chatItems, sendMessage, fetchPrevMessages } = useChatting(
postId,
userId,
const [userData, setUserData] = useState(
participants.find((p) => p.providerId === currentUser.providerId),
);
const participatedRef = useRef(userData);
const {
chatItems,
resetChatItems,
sendMessage,
notifyToJoin,
notifyToLeave,
fetchPrevMessages,
joinRoom,
leaveRoom,
} = useChatting(postId);

useEffect(() => {
const participated = participants.find(
(p) => p.providerId === currentUser.providerId,
);
if (participated) {
if (!participatedRef.current) {
joinRoom(participated.id, () =>
notifyToJoin(participated.nickname, participated.id),
);
participatedRef.current = participated;
} else {
joinRoom(participated.id);
}
}

if (!participated && participatedRef.current) {
notifyToLeave(
participatedRef.current.nickname,
participatedRef.current.id,
);
resetChatItems();
participatedRef.current = undefined;
}

setUserData(participated);
}, [
participants,
currentUser,
resetChatItems,
notifyToJoin,
notifyToLeave,
joinRoom,
leaveRoom,
]);

useEffect(() => {
if (!userData) {
return;
}

return () => leaveRoom(userData.id);
}, [userData, leaveRoom]);

if (!userData) {
return (
<div className={styles.notParticipated}>
아직 해당 모각코에 참여하지 않았습니다!
<br />
채팅방에 입장하려면 먼저 모각코에 참석해 주세요.
</div>
);
}
return (
<div className={styles.container}>
<ChattingHeader title={title} participants={participants} />
<ChatList
chatItems={chatItems}
userId={userId}
userId={userData.id}
participants={participants}
fetchPrevMessages={fetchPrevMessages}
/>
<ChattingFooter sendMessage={sendMessage} />
<ChattingFooter userId={userData.id} sendMessage={sendMessage} />
</div>
);
}
Loading

0 comments on commit 474494b

Please sign in to comment.