Skip to content

Commit 90f7d8e

Browse files
authored
Merge pull request #31 from Nexters/feat/#18
[Feat/#18] 채팅 페이지 기능 QA
2 parents 87f78c6 + 0587dc8 commit 90f7d8e

18 files changed

+347
-113
lines changed

package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"react-textarea-autosize": "^8.5.7",
2626
"styled-components": "^6.1.14",
2727
"styled-reset": "^4.5.2",
28+
"use-stick-to-bottom": "^1.0.44",
2829
"zod": "^3.24.1"
2930
},
3031
"devDependencies": {

src/chat/components/AcceptRejectButtons.tsx

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
import { useChatMessagesContext } from "@/chat/hooks/useChatMessagesStore";
2-
import { useSendChatMessage } from "@/chat/hooks/useSendChatMesasge";
2+
import { useSendChatMessage } from "@/chat/hooks/useSendChatMessage";
33
import { delay } from "@/shared/utils/delay";
44
import { useParams } from "next/navigation";
5+
import { useState } from "react";
56
import { css } from "styled-components";
7+
import { useTextFieldInChatDisplayContext } from "../hooks/useTextFieldInChatDisplayStore";
68
import ChipButton from "./ChipButton";
7-
8-
type Props = {
9-
open: boolean;
10-
};
11-
12-
export default function AcceptRejectButtons({ open }: Props) {
13-
const { addMessage, deleteMessage } = useChatMessagesContext();
14-
const { mutate: sendChatMessage, isPending: isSendingChatMessage } = useSendChatMessage();
9+
export default function AcceptRejectButtons() {
10+
const { addMessage, deleteMessage, editMessage, state: messages } = useChatMessagesContext();
11+
const { mutate: sendChatMessage } = useSendChatMessage();
12+
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
13+
const {
14+
enable: enableTextField,
15+
disable: disableTextField,
16+
hide: hideTextField,
17+
focus: focusTextField,
18+
} = useTextFieldInChatDisplayContext();
1519
const { chatId } = useParams<{ chatId: string }>();
1620

1721
const rejectMessage = "아니, 얘기 더 들어봐";
1822
const acceptMessage = "좋아! 타로 볼래";
1923

24+
const isSystemRepliedQuestion = messages[messages.length - 1]?.type === "SYSTEM_TAROT_QUESTION_REPLY";
25+
2026
if (!chatId) throw new Error("chatId가 Dynamic Route에서 전달 되어야 합니다.");
2127

2228
const handleAcceptClick = async () => {
29+
setIsButtonDisabled(true);
30+
hideTextField();
2331
addMessage({
2432
messageId: Math.random(),
2533
type: "USER_NORMAL",
@@ -46,21 +54,34 @@ export default function AcceptRejectButtons({ open }: Props) {
4654
intent: "TAROT_ACCEPT",
4755
},
4856
{
49-
onSuccess: (data) => {
57+
onSuccess: async (data) => {
5058
deleteMessage(loadingMessageId);
5159

5260
addMessage({
5361
messageId: data.messageId,
5462
type: data.type,
5563
sender: data.sender,
56-
answers: data.answers,
64+
answers: [data.answers[0]],
5765
});
66+
67+
for (let index = 1; index < data.answers.length; index++) {
68+
await delay(1000);
69+
editMessage({
70+
messageId: data.messageId,
71+
type: data.type,
72+
sender: data.sender,
73+
answers: data.answers.slice(0, index + 1),
74+
});
75+
}
5876
},
5977
}
6078
);
79+
setIsButtonDisabled(false);
6180
};
6281

6382
const handleRejectClick = async () => {
83+
setIsButtonDisabled(true);
84+
disableTextField();
6485
addMessage({
6586
messageId: Math.random(),
6687
type: "USER_NORMAL",
@@ -87,21 +108,35 @@ export default function AcceptRejectButtons({ open }: Props) {
87108
intent: "TAROT_DECLINE",
88109
},
89110
{
90-
onSuccess: (data) => {
111+
onSuccess: async (data) => {
91112
deleteMessage(loadingMessageId);
92113

93114
addMessage({
94115
messageId: data.messageId,
95116
type: data.type,
96117
sender: data.sender,
97-
answers: data.answers,
118+
answers: [data.answers[0]],
98119
});
120+
121+
for (let index = 1; index < data.answers.length; index++) {
122+
await delay(1000);
123+
editMessage({
124+
messageId: data.messageId,
125+
type: data.type,
126+
sender: data.sender,
127+
answers: data.answers.slice(0, index + 1),
128+
});
129+
}
130+
enableTextField();
131+
await delay(1);
132+
focusTextField();
99133
},
100134
}
101135
);
136+
setIsButtonDisabled(false);
102137
};
103138

104-
if (!open) return null;
139+
if (!isSystemRepliedQuestion) return null;
105140

106141
return (
107142
<div
@@ -111,10 +146,10 @@ export default function AcceptRejectButtons({ open }: Props) {
111146
margin-top: 76px;
112147
`}
113148
>
114-
<ChipButton type="button" disabled={isSendingChatMessage} color="primary02" onClick={handleAcceptClick}>
149+
<ChipButton type="button" disabled={isButtonDisabled} color="primary02" onClick={handleAcceptClick}>
115150
{acceptMessage}
116151
</ChipButton>
117-
<ChipButton type="button" disabled={isSendingChatMessage} color="grey30" onClick={handleRejectClick}>
152+
<ChipButton type="button" disabled={isButtonDisabled} color="grey30" onClick={handleRejectClick}>
118153
{rejectMessage}
119154
</ChipButton>
120155
</div>

src/chat/components/Chat.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
"use client";
22

33
import { ChatMessagesProvider } from "@/chat/hooks/useChatMessagesStore";
4-
import ChatHeader from "./ChatHeader";
4+
import { TextFieldInChatDisplayProvider } from "@/chat/hooks/useTextFieldInChatDisplayStore";
55
import ChatRoom from "./ChatRoom";
66

77
export default function Chat() {
88
// TODO: 채팅 메세지 목록 프리페치 SSR 필요
99
return (
10-
<>
11-
<ChatHeader />
12-
<ChatMessagesProvider>
10+
<ChatMessagesProvider>
11+
<TextFieldInChatDisplayProvider>
1312
<ChatRoom />
14-
</ChatMessagesProvider>
15-
</>
13+
</TextFieldInChatDisplayProvider>
14+
</ChatMessagesProvider>
1615
);
1716
}

src/chat/components/ChatBubble.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export default function ChatBubble({ sender, message, card, loading }: Props) {
8585
background-color: ${({ theme }) => theme.colors.grey10};
8686
${({ theme }) => theme.fonts.body3}
8787
border-radius: 8px;
88+
width: fit-content;
8889
max-width: 260px;
8990
color: ${({ theme }) => theme.colors.grey90};
9091
white-space: pre-wrap;

src/chat/components/ChatBubbleGroup.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import ChatBubble from "./ChatBubble";
55

66
type Props = {
77
message: MessageType;
8-
isJustSent: boolean;
98
};
109

1110
export default function ChatBubbleGroup({ message }: Props) {
12-
// TODO: 응답을 새로 받은 경우에만 메세지를 순차적으로 렌더링
1311
const renderMessage = (message: MessageType) => {
1412
if (message.tarotName) {
1513
return <ChatBubble key={message.messageId} sender={"SYSTEM"} card={message.tarotName} />;

src/chat/components/ChatHeader.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,28 @@ import KebobMenuIcon from "@/shared/assets/icons/kebab-menu.svg";
44
import BottomSheet from "@/shared/components/BottomSheet";
55
import HeaderContent from "@/shared/components/HeaderContent";
66
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
7+
import { useRouter } from "next/navigation";
78
import { css } from "styled-components";
9+
import { useCreateChatRoom } from "../hooks/useCreateChatRoom";
810

911
export default function ChatHeader() {
12+
const { mutate: createChatRoom } = useCreateChatRoom();
13+
const router = useRouter();
14+
15+
const handleResetChatClick = () => {
16+
createChatRoom(undefined, {
17+
onSuccess: (data) => {
18+
router.push(`/chats/${data.roomId}`);
19+
},
20+
});
21+
};
22+
1023
return (
1124
<HeaderContent
12-
divider
25+
/**
26+
* FIXME: 구분선이 추가되어야 하나 쌓임 맥락에 의해 전체 화면 오버플로우 되지 않는 문제
27+
* 레이아웃 구조 개선 후 divider prop 적용 필요
28+
* */
1329
sticky
1430
endAction={
1531
<BottomSheet.Root>
@@ -47,8 +63,9 @@ export default function ChatHeader() {
4763
<button type="button">친구에게 타로냥 알리기</button>
4864
</li>
4965
<li>
50-
{/* TODO: 메뉴 버튼 액션 추가 */}
51-
<button type="button">새 대화 시작하기</button>
66+
<button type="button" onClick={handleResetChatClick}>
67+
새 대화 시작하기
68+
</button>
5269
</li>
5370
</ul>
5471
</BottomSheet.Content>

0 commit comments

Comments
 (0)