Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bc58205
[Feat] : AI 프리토킹(Speaking) 기능 구현 (#195)
hye-inA Jan 23, 2026
52ceba7
[FEAT] SSE 기반 실시간 알림 시스템 연동 (#197) (#198)
DDINGJOO Jan 24, 2026
232f3c9
fix : 연속 선언된 변수 t 제거 (#199)
hye-inA Jan 24, 2026
39c2c5d
Merge branch 'prod' into develop
hye-inA Jan 24, 2026
05e7928
refactor : AI 말하기 routing 페이지 수정 (#202)
hye-inA Jan 24, 2026
f4114e0
Merge branch 'prod' into develop
hye-inA Jan 24, 2026
3944fc1
refactor : AI 말하기 routing 페이지 수정 (#203)
hye-inA Jan 24, 2026
f8ea2ed
[FEAT] 채팅 슬래시 명령어 시스템 구현 (#200) (#204)
DDINGJOO Jan 24, 2026
ba1cc05
[FEAT] 영어 끝말잇기(Word Chain) 게임 구현 (#205) (#206)
DDINGJOO Jan 24, 2026
d979845
[FIX] 채팅 슬래시 명령어 버그 수정 (#207)
DDINGJOO Jan 24, 2026
44fa7d9
refactor : 로그인/인증 로직 정상화 및 채팅 서버 연결 및 메인 화면 헤더 프로필 상태 표시 (#208)
hye-inA Jan 25, 2026
100c283
[FIX] 끝말잇기 게임 버그 수정 및 UI 개선 (#210)
DDINGJOO Jan 25, 2026
36d3c01
[FIX] 끝말잇기 게임 버그 수정 및 UI 개선 (#211)
DDINGJOO Jan 25, 2026
ee93158
feat : 사용자 프로필 닉네임 표시 구현 (#212)
hye-inA Jan 25, 2026
83a9a18
feature : 채팅 화면 컴포넌트에 닉네임 필드 추가 (#213)
hye-inA Jan 26, 2026
43eb0f5
Feature : OPIc 모의고사 테스트 진행(녹음/제출) 및 세션 완료 기능 구현 (#216)
hye-inA Jan 27, 2026
956b6e0
featrue : OPIC 말하기연습 전체 세션에 대한 피드백 리포트 화면 구현 (#218)
hye-inA Jan 27, 2026
7cc571b
[STYLE] Adjust formatting for improved readability and consistency
DDINGJOO Jan 28, 2026
e092df6
Merge origin/prod into develop - resolve conflicts
DDINGJOO Jan 28, 2026
59bbfc9
fix: 채팅 시스템 명령어 및 투표 UI 표시 버그 수정
DDINGJOO Jan 28, 2026
2d3ff35
fix: 투표 결과 및 투표 알림 메시지 표시 추가
DDINGJOO Jan 28, 2026
00bc0e0
debug: 시스템 명령어 로그 추가 및 displayText 추출 개선
DDINGJOO Jan 28, 2026
5009ae2
debug: system_command 케이스 로그 추가
DDINGJOO Jan 28, 2026
089c88d
debug: poll end 메시지 추가 로그
DDINGJOO Jan 28, 2026
7d5d2ec
Merge branch 'prod' into develop - resolve conflicts
DDINGJOO Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/domains/freetalk/components/ChatRoomModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,39 @@ const ChatRoomModal = ({ open, onClose, room, onLeave }) => {
)
}

// 투표 관련 메시지 (POLL_CREATE, POLL_VOTE, POLL_END)
if (message.messageType === 'POLL_CREATE' || message.messageType === 'poll_create' ||
message.messageType === 'POLL_VOTE' || message.messageType === 'poll_vote' ||
message.messageType === 'POLL_END' || message.messageType === 'poll_end') {
return (
<Box key={message.id} sx={{ width: '100%', display: 'flex', justifyContent: 'center', py: 0.5 }}>
<Paper
elevation={0}
sx={{
px: 2,
py: 1,
bgcolor: isDark ? 'rgba(139, 92, 246, 0.1)' : '#f5f3ff',
border: isDark ? '1px solid rgba(139, 92, 246, 0.3)' : '1px solid rgba(139, 92, 246, 0.2)',
borderRadius: 2,
maxWidth: '85%',
}}
>
<Typography
variant="body2"
sx={{
whiteSpace: 'pre-wrap',
color: isDark ? '#e9d5ff' : '#6b21a8',
fontWeight: 500,
fontSize: '0.8rem',
}}
>
{message.content}
</Typography>
</Paper>
</Box>
)
}

return (
<Box
key={message.id}
Expand Down
51 changes: 41 additions & 10 deletions src/domains/freetalk/hooks/useChatWebSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,10 @@ export function useChatWebSocket(roomId, userId) {
const pollMessage = {
id: `poll-${pollData.pollId}`,
messageType: 'POLL_CREATE',
userId: pollData.creatorId,
createdAt: pollData.createdAt || new Date().toISOString(),
isOwn: pollData.creatorId === userId,
userId: pollData.creatorId || pollData.createdBy,
content: data.content,
createdAt: data.createdAt || pollData.createdAt || new Date().toISOString(),
isOwn: (pollData.creatorId || pollData.createdBy) === userId,
data: pollData,
}
setMessages((prev) => [...prev, pollMessage])
Expand All @@ -107,8 +108,8 @@ export function useChatWebSocket(roomId, userId) {
onPollVote: (data) => {
console.log('[useChatWebSocket] Poll vote:', data)
const voteData = data.data || data
setMessages((prev) =>
prev.map((msg) => {
setMessages((prev) => {
const updated = prev.map((msg) => {
if (msg.id === `poll-${voteData.pollId}` && msg.data) {
return {
...msg,
Expand All @@ -120,14 +121,26 @@ export function useChatWebSocket(roomId, userId) {
}
return msg
})
)
if (data.content) {
updated.push({
id: `poll-vote-${Date.now()}`,
messageType: 'POLL_VOTE',
userId: voteData.voterId || voteData.userId,
content: data.content,
createdAt: data.createdAt || new Date().toISOString(),
data: voteData,
})
}
return updated
})
},

onPollEnd: (data) => {
console.log('[useChatWebSocket] Poll ended:', data)
console.log('[useChatWebSocket] Poll end content:', data.content)
const endData = data.data || data
setMessages((prev) =>
prev.map((msg) => {
setMessages((prev) => {
const updated = prev.map((msg) => {
if (msg.id === `poll-${endData.pollId}` && msg.data) {
return {
...msg,
Expand All @@ -140,7 +153,21 @@ export function useChatWebSocket(roomId, userId) {
}
return msg
})
)
console.log('[useChatWebSocket] Adding poll end message, content exists:', !!data.content)
if (data.content) {
const pollEndMsg = {
id: `poll-end-${endData.pollId}`,
messageType: 'POLL_END',
userId: endData.endedBy,
content: data.content,
createdAt: data.createdAt || new Date().toISOString(),
data: endData,
}
console.log('[useChatWebSocket] Poll end message:', pollEndMsg)
updated.push(pollEndMsg)
}
return updated
})
},

onClearChat: (data) => {
Expand All @@ -166,16 +193,20 @@ export function useChatWebSocket(roomId, userId) {
},

onSystemCommand: (data) => {
console.log('[useChatWebSocket] System command received:', data)
const commandData = data.data || {}
const displayText = data.content || data.message || commandData.content || commandData.message || commandData.displayText || ''
console.log('[useChatWebSocket] System command displayText:', displayText)
const commandMessage = {
id: `syscmd-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
messageType: 'SYSTEM_COMMAND',
userId: commandData.userId || commandData.nickname || data.userId,
content: displayText,
createdAt: data.createdAt || new Date().toISOString(),
data: {
commandType: commandData.type || 'help',
userId: commandData.userId || commandData.nickname,
displayText: data.content || data.message || '',
displayText: displayText,
result: typeof commandData.result === 'object'
? commandData.result
: { value: commandData.result },
Expand Down
2 changes: 2 additions & 0 deletions src/domains/freetalk/services/chatWebSocketService.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ class ChatWebSocketConnection {
case 'system_command':
case 'SYSTEM_COMMAND':
// 시스템 명령어 응답 (예: /dice, /coin, /random, /members, /help 등)
console.log('[ChatWebSocket] System command received:', data)
console.log('[ChatWebSocket] onSystemCommand callback exists:', !!this.callbacks.onSystemCommand)
this.callbacks.onSystemCommand?.(data)
break
case 'poll_create':
Expand Down
41 changes: 41 additions & 0 deletions src/domains/games/components/WaitingChat.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {Avatar, Box, IconButton, Paper, TextField, Typography} from '@mui/materi
import {Send as SendIcon} from '@mui/icons-material'
import {GAME_COLORS} from '../theme/gameTheme'
import {useThemeMode} from '../../../contexts/ThemeContext'
import SystemCommandMessage from '../../freetalk/components/SystemCommandMessage'
import {MessageType} from '../../freetalk/types/chatCommandTypes'

const WaitingChat = ({ messages, onSendMessage, currentUserId, disabled }) => {
const {mode} = useThemeMode()
Expand Down Expand Up @@ -62,6 +64,45 @@ const WaitingChat = ({ messages, onSendMessage, currentUserId, disabled }) => {
const isOwn = message.userId === currentUserId
const isSystem = message.isSystem

// 시스템 명령어 메시지 (SYSTEM_COMMAND) - /dice, /coin 등
if (message.messageType === MessageType.SYSTEM_COMMAND || message.messageType === 'SYSTEM_COMMAND') {
return (
<SystemCommandMessage key={message.id} data={message.data} />
)
}

// 투표 관련 메시지 (POLL_CREATE, POLL_VOTE, POLL_END)
if (message.messageType === 'POLL_CREATE' || message.messageType === 'poll_create' ||
message.messageType === 'POLL_VOTE' || message.messageType === 'poll_vote' ||
message.messageType === 'POLL_END' || message.messageType === 'poll_end') {
return (
<Box key={message.id} sx={{ width: '100%', display: 'flex', justifyContent: 'center', py: 1 }}>
<Paper
elevation={0}
sx={{
px: 3,
py: 1.5,
bgcolor: isDark ? 'rgba(139, 92, 246, 0.1)' : '#f5f3ff',
border: isDark ? '1px solid rgba(139, 92, 246, 0.3)' : '1px solid rgba(139, 92, 246, 0.2)',
borderRadius: 3,
maxWidth: '85%',
}}
>
<Typography
variant="body2"
sx={{
whiteSpace: 'pre-wrap',
color: isDark ? '#e9d5ff' : '#6b21a8',
fontWeight: 500,
}}
>
{message.content}
</Typography>
</Paper>
</Box>
)
}

if (isSystem) {
return (
<Box key={message.id} sx={{ textAlign: 'center', py: 0.5 }}>
Expand Down