Skip to content

Commit f1ebb05

Browse files
authored
Merge pull request #315 from Soohyuniii/feat-314-openchat-profile-image
Feat 314 openchat profile image
2 parents 330cf98 + 3eb75db commit f1ebb05

File tree

2 files changed

+185
-20
lines changed

2 files changed

+185
-20
lines changed

src/pages/Chat.tsx

Lines changed: 123 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import ChatMessage from "@/components/ChatMessage";
1616
import ChatActionBar from "@/components/ChatActionBar";
1717
import TipsMenuContainer from "@/components/tips/TipsMenuContainer";
1818
import pickMbtiImage from "@/utils/pickMbtiImage";
19+
import pickMbtiProfileImage from "@/utils/pickMbtiProfileImage";
1920
import websocketService from "@/services/websocket";
2021
import { getOpenChatMessages } from "@/api/openChat";
2122
import { WebSocketMessage } from "@/types/openChat";
@@ -71,6 +72,8 @@ const Chat = () => {
7172
openChatTitle || (name ? `${name}과 대화` : `${mbti}와 대화`);
7273
const assistantImgUrl = pickMbtiImage(mbti);
7374
const storageKey = `chatMessages_${id}`;
75+
const topicChatStorageKey = `topicChatMessages_${openChatId}_${nickname}`;
76+
const topicChatMbtiCacheKey = `topicChatMbti_${openChatId}`;
7477

7578
const isTopicChat = mode === "topicChat";
7679

@@ -133,23 +136,59 @@ const Chat = () => {
133136
wsMessage.message
134137
);
135138

136-
const newMessage: Message = {
137-
role: "assistant",
138-
content: wsMessage.message,
139-
nickname: wsMessage.nickname,
140-
mbti: wsMessage.mbti || undefined,
141-
messageType: "text"
142-
};
139+
// 웹소켓에서 받은 mbti가 없으면 기존 메시지에서 찾아서 사용
140+
setMessages((prev) => {
141+
// 같은 닉네임의 이전 메시지에서 mbti 찾기
142+
const existingMsg = prev
143+
.slice()
144+
.reverse()
145+
.find((m) => m.nickname === wsMessage.nickname);
146+
// localStorage에서 mbti 찾기
147+
let cachedMbti = null;
148+
if (topicChatMbtiCacheKey && wsMessage.nickname) {
149+
const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey);
150+
if (mbtiCache) {
151+
try {
152+
const cacheData = JSON.parse(mbtiCache);
153+
cachedMbti = cacheData[wsMessage.nickname];
154+
} catch (e) {
155+
console.error("Failed to parse mbti cache", e);
156+
}
157+
}
158+
}
159+
160+
const userMbti =
161+
wsMessage.mbti ?? existingMsg?.mbti ?? cachedMbti ?? undefined;
162+
163+
// mbti가 있으면 localStorage에 저장
164+
if (userMbti && topicChatMbtiCacheKey && wsMessage.nickname) {
165+
const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey);
166+
const cacheData = mbtiCache ? JSON.parse(mbtiCache) : {};
167+
cacheData[wsMessage.nickname] = userMbti;
168+
localStorage.setItem(
169+
topicChatMbtiCacheKey,
170+
JSON.stringify(cacheData)
171+
);
172+
}
173+
174+
const newMessage: Message = {
175+
role: "assistant",
176+
content: wsMessage.message,
177+
nickname: wsMessage.nickname ?? undefined,
178+
mbti: userMbti,
179+
messageType: "text"
180+
};
143181

144-
setMessages((prev) => [...prev, newMessage]);
182+
return [...prev, newMessage];
183+
});
145184
}
146185

147186
// 예상치 못한 메시지 형식 로그
148187
else {
149188
console.warn("알 수 없는 WebSocket 메시지:", wsMessage);
150189
}
151190
},
152-
[nickname]
191+
[nickname, topicChatMbtiCacheKey]
153192
);
154193

155194
useEffect(() => {
@@ -180,14 +219,65 @@ const Chat = () => {
180219
role: msg.nickname === nickname ? "user" : "assistant",
181220
content: msg.message,
182221
nickname: msg.nickname,
183-
mbti: msg.mbti || undefined,
222+
mbti: msg.mbti ?? undefined,
184223
messageType: msg.messageType || "text"
185224
};
186225
}
187226
);
188227

189228
// 메시지 순서: API에서 최신순으로 오므로 reverse()로 시간순 정렬
190-
setMessages(convertedMessages.reverse());
229+
const apiMessages = convertedMessages.reverse();
230+
231+
// sessionStorage에서 이전 MBTI 정보 가져오기
232+
let cachedMessages: Message[] = [];
233+
if (topicChatStorageKey) {
234+
const storedMessage = sessionStorage.getItem(topicChatStorageKey);
235+
if (storedMessage) {
236+
cachedMessages = JSON.parse(storedMessage);
237+
}
238+
}
239+
240+
// API 메시지에 mbti가 없으면 캐시에서 찾아서 채우기
241+
const enhancedMessages = apiMessages.map((msg) => {
242+
if (!msg.mbti && msg.nickname) {
243+
// sessionStorage에서 찾기 (우선순위 1)
244+
const cachedMsg = cachedMessages
245+
.slice()
246+
.reverse()
247+
.find((m) => m.nickname === msg.nickname);
248+
249+
// localStorage에서 찾기 (우선순위 2)
250+
let localStorageMbti = null;
251+
if (topicChatMbtiCacheKey) {
252+
const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey);
253+
if (mbtiCache) {
254+
try {
255+
const cacheData = JSON.parse(mbtiCache);
256+
localStorageMbti = cacheData[msg.nickname];
257+
} catch (e) {
258+
console.error("Failed to parse mbti cache", e);
259+
}
260+
}
261+
}
262+
263+
const foundMbti = cachedMsg?.mbti ?? localStorageMbti;
264+
if (foundMbti) {
265+
return { ...msg, mbti: foundMbti };
266+
}
267+
} else if (msg.mbti && topicChatMbtiCacheKey && msg.nickname) {
268+
// mbti가 있으면 localStorage에 저장
269+
const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey);
270+
const cacheData = mbtiCache ? JSON.parse(mbtiCache) : {};
271+
cacheData[msg.nickname] = msg.mbti;
272+
localStorage.setItem(
273+
topicChatMbtiCacheKey,
274+
JSON.stringify(cacheData)
275+
);
276+
}
277+
return msg;
278+
});
279+
280+
setMessages(enhancedMessages);
191281
} else {
192282
setMessages([]);
193283
}
@@ -281,17 +371,27 @@ const Chat = () => {
281371
if (storedMessage) {
282372
setMessages(JSON.parse(storedMessage));
283373
}
374+
} else if (isTopicChat && topicChatStorageKey) {
375+
// sessionStorage에서 로드 (탭이 닫히지 않은 경우)
376+
const storedMessage = sessionStorage.getItem(topicChatStorageKey);
377+
if (storedMessage) {
378+
setMessages(JSON.parse(storedMessage));
379+
}
380+
// sessionStorage가 없으면 API에서 로드된 데이터 사용
284381
}
285382
};
286383

287384
fetchMessages();
288-
}, [mode, id, storageKey, isTopicChat]);
385+
}, [mode, id, storageKey, isTopicChat, topicChatStorageKey]);
289386

290387
useEffect(() => {
291388
if (mode !== "virtualFriend" && !isTopicChat) {
292389
sessionStorage.setItem(storageKey, JSON.stringify(messages));
390+
} else if (isTopicChat && topicChatStorageKey) {
391+
// topicChat은 sessionStorage에 저장 (탭이 닫히면 날아감)
392+
sessionStorage.setItem(topicChatStorageKey, JSON.stringify(messages));
293393
}
294-
}, [messages, mode, storageKey, isTopicChat]);
394+
}, [messages, mode, storageKey, topicChatStorageKey, isTopicChat]);
295395

296396
useEffect(() => {
297397
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
@@ -463,13 +563,16 @@ const Chat = () => {
463563
<div className="mr-[9px] shrink-0">
464564
{isTopicChat && msg.nickname ? (
465565
<div className="text-center">
466-
<div className="flex h-[36px] w-[36px] items-center justify-center rounded-full bg-primary-pale text-xs font-bold text-primary-normal">
467-
{msg.nickname.charAt(0)}
468-
</div>
469-
{msg.mbti && (
470-
<span className="mt-1 text-xs text-gray-500">
471-
{msg.mbti}
472-
</span>
566+
{msg.mbti ? (
567+
<img
568+
src={pickMbtiProfileImage(msg.mbti)}
569+
alt="MBTI Profile"
570+
className="h-[36px] w-[36px] rounded-full border border-gray-200 object-cover"
571+
/>
572+
) : (
573+
<div className="flex h-[36px] w-[36px] items-center justify-center rounded-full bg-primary-pale text-xs font-bold text-primary-normal">
574+
{msg.nickname.charAt(0)}
575+
</div>
473576
)}
474577
</div>
475578
) : (

src/utils/pickMbtiProfileImage.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const pickMbtiProfileImage = (mbti?: string) => {
2+
if (!mbti) return "";
3+
4+
let mbtiProfileImage = "";
5+
6+
switch (mbti) {
7+
case "ISTJ":
8+
mbtiProfileImage = "/image/ISTJ_profile.png";
9+
break;
10+
case "ISFJ":
11+
mbtiProfileImage = "/image/ISFJ_profile.png";
12+
break;
13+
case "INFJ":
14+
mbtiProfileImage = "/image/INFJ_profile.png";
15+
break;
16+
case "INTJ":
17+
mbtiProfileImage = "/image/INTJ_profile.png";
18+
break;
19+
case "ISTP":
20+
mbtiProfileImage = "/image/ISTP_profile.png";
21+
break;
22+
case "ISFP":
23+
mbtiProfileImage = "/image/ISFP_profile.png";
24+
break;
25+
case "INFP":
26+
mbtiProfileImage = "/image/INFP_profile.png";
27+
break;
28+
case "INTP":
29+
mbtiProfileImage = "/image/INTP_profile.png";
30+
break;
31+
case "ESTP":
32+
mbtiProfileImage = "/image/ESTP_profile.png";
33+
break;
34+
case "ESFP":
35+
mbtiProfileImage = "/image/ESFP_profile.png";
36+
break;
37+
case "ENFP":
38+
mbtiProfileImage = "/image/ENFP_profile.png";
39+
break;
40+
case "ENTP":
41+
mbtiProfileImage = "/image/ENTP_profile.png";
42+
break;
43+
case "ESTJ":
44+
mbtiProfileImage = "/image/ESTJ_profile.png";
45+
break;
46+
case "ESFJ":
47+
mbtiProfileImage = "/image/ESFJ_profile.png";
48+
break;
49+
case "ENFJ":
50+
mbtiProfileImage = "/image/ENFJ_profile.png";
51+
break;
52+
case "ENTJ":
53+
mbtiProfileImage = "/image/ENTJ_profile.png";
54+
break;
55+
default:
56+
break;
57+
}
58+
59+
return mbtiProfileImage;
60+
};
61+
62+
export default pickMbtiProfileImage;

0 commit comments

Comments
 (0)