useChat.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. 'use client';
  2. import { useState, useEffect, useCallback, useRef } from 'react';
  3. import { useSignalRContext } from '@/contexts/signalrProvider';
  4. import type { ChatMessage } from '@/types/chat';
  5. export type SystemMessage = {
  6. id: number;
  7. content: string;
  8. receivedAt: string;
  9. };
  10. export type ChatParticipant = {
  11. memberName: string;
  12. isGuest: boolean;
  13. };
  14. let systemMsgId = 0;
  15. export default function useChat() {
  16. const { chatConnection, chatConnected } = useSignalRContext();
  17. const [messages, setMessages] = useState<ChatMessage[]>([]);
  18. const [systemMessages, setSystemMessages] = useState<SystemMessage[]>([]);
  19. const [participantCount, setParticipantCount] = useState(0);
  20. const [participants, setParticipants] = useState<ChatParticipant[]>([]);
  21. const prevConnectionRef = useRef<typeof chatConnection>(null);
  22. useEffect(() => {
  23. if (!chatConnection || !chatConnected) return;
  24. // 같은 connection 객체면 중복 등록 방지
  25. if (prevConnectionRef.current === chatConnection) return;
  26. prevConnectionRef.current = chatConnection;
  27. chatConnection.on('ReceiveHistory', (history: ChatMessage[]) => {
  28. setMessages(history);
  29. });
  30. chatConnection.on('ReceiveMessage', (message: ChatMessage) => {
  31. setMessages((prev) => [...prev, message]);
  32. });
  33. chatConnection.on('ReceiveSystemMessage', (content: string) => {
  34. setSystemMessages((prev) => [
  35. ...prev,
  36. { id: ++systemMsgId, content, receivedAt: new Date().toISOString() }
  37. ]);
  38. });
  39. chatConnection.on('ReceiveParticipantCount', (count: number) => {
  40. setParticipantCount(count);
  41. });
  42. chatConnection.on('ReceiveParticipants', (list: ChatParticipant[]) => {
  43. setParticipants(list);
  44. });
  45. // 핸들러 등록 후 명시적으로 히스토리 + 접속자 수 요청
  46. chatConnection.invoke('RequestHistory').catch(() => {});
  47. chatConnection.invoke('RequestParticipantCount').catch(() => {});
  48. return () => {
  49. chatConnection.off('ReceiveHistory');
  50. chatConnection.off('ReceiveMessage');
  51. chatConnection.off('ReceiveSystemMessage');
  52. chatConnection.off('ReceiveParticipantCount');
  53. chatConnection.off('ReceiveParticipants');
  54. prevConnectionRef.current = null;
  55. };
  56. }, [chatConnection, chatConnected]);
  57. const sendMessage = useCallback(async (content: string) => {
  58. if (!chatConnection || !chatConnected) return;
  59. const trimmed = content.trim();
  60. if (!trimmed || trimmed.length > 500) return;
  61. try {
  62. await chatConnection.invoke('SendMessage', trimmed);
  63. } catch (error) {
  64. console.error('메시지 전송 실패:', error);
  65. }
  66. }, [chatConnection, chatConnected]);
  67. const clearMessages = useCallback(() => {
  68. setMessages([]);
  69. setSystemMessages([]);
  70. }, []);
  71. const refreshChat = useCallback(async () => {
  72. if (!chatConnection || !chatConnected) return;
  73. setMessages([]);
  74. setSystemMessages([]);
  75. try {
  76. await chatConnection.invoke('RequestHistory');
  77. await chatConnection.invoke('RequestParticipantCount');
  78. } catch {
  79. // ignore
  80. }
  81. }, [chatConnection, chatConnected]);
  82. const requestParticipants = useCallback(async () => {
  83. if (!chatConnection || !chatConnected) return;
  84. try {
  85. await chatConnection.invoke('RequestParticipants');
  86. } catch {
  87. // ignore
  88. }
  89. }, [chatConnection, chatConnected]);
  90. return {
  91. messages,
  92. systemMessages,
  93. participantCount,
  94. participants,
  95. sendMessage,
  96. clearMessages,
  97. refreshChat,
  98. requestParticipants,
  99. chatConnected
  100. };
  101. }