From 41b5d59df0da57fca0567b43f287a2333b74a9af Mon Sep 17 00:00:00 2001 From: Wenhao Ji Date: Sat, 21 Dec 2024 11:37:59 -0800 Subject: [PATCH] Refactored `useChatRoom` hook --- src/components/ScoreBoardAndToggle.tsx | 2 +- src/lib/useChatRoom.ts | 40 +++--------------------- src/lib/useMessages.test.ts | 28 +++++++++++++++++ src/lib/useMessages.ts | 43 ++++++++++++++++++++++++++ src/lib/useNames.test.ts | 25 +++++++++++++++ src/lib/useNames.ts | 30 ++++++++++++++++++ 6 files changed, 131 insertions(+), 37 deletions(-) create mode 100644 src/lib/useMessages.test.ts create mode 100644 src/lib/useMessages.ts create mode 100644 src/lib/useNames.test.ts create mode 100644 src/lib/useNames.ts diff --git a/src/components/ScoreBoardAndToggle.tsx b/src/components/ScoreBoardAndToggle.tsx index 2b57639..14d8480 100644 --- a/src/components/ScoreBoardAndToggle.tsx +++ b/src/components/ScoreBoardAndToggle.tsx @@ -108,7 +108,7 @@ export default function ScoreBoardAndToggle(props: { { - Array.from(props.scoreBoard.entries()).sort(([p1, s1], [p2, s2]) => s2 - s1).map(([player, score], i) => + Array.from(props.scoreBoard.entries()).sort(([, s1], [, s2]) => s2 - s1).map(([player, score], i) => {props.names.get(player) ?? '-'} diff --git a/src/lib/useChatRoom.ts b/src/lib/useChatRoom.ts index c0432d4..5e45ca8 100644 --- a/src/lib/useChatRoom.ts +++ b/src/lib/useChatRoom.ts @@ -1,7 +1,8 @@ -import {useEffect, useState} from "react"; import {Chat} from "./setup"; import {ChatRoomEvents} from "./ChatRoom"; import {EventListener} from "./types"; +import useNames from "./useNames"; +import useMessages from "./useMessages"; export interface Message { type: 'message'; @@ -21,41 +22,8 @@ export interface ChatRoomLike { export default function useChatRoom( chatRoom: ChatRoomLike = Chat, ) { - const [messages, setMessages] = useState([]); - - useEffect(() => { - const textListener = (text: string, whose: string) => { - setMessages(prev => [ - ...prev, - { - type: 'message', - text, - whose, - timestamp: Date.now(), - }, - ]); - }; - chatRoom.listener.on('text', textListener); - return () => { - chatRoom.listener.off('text', textListener); - } - }, [chatRoom.listener]); - - const [names, setNames] = useState(new Map()); - - useEffect(() => { - const nameListener = (name: string, whose: string) => { - setNames(prev => { - const next = new Map(prev); - next.set(whose, name); - return next; - }); - }; - chatRoom.listener.on('name', nameListener); - return () => { - chatRoom.listener.off('name', nameListener); - } - }, [chatRoom.listener]); + const messages = useMessages(chatRoom); + const names = useNames(chatRoom); const setMyName = (name: string) => { chatRoom.setMyName(name); diff --git a/src/lib/useMessages.test.ts b/src/lib/useMessages.test.ts new file mode 100644 index 0000000..eab2f15 --- /dev/null +++ b/src/lib/useMessages.test.ts @@ -0,0 +1,28 @@ +import {renderHook, waitFor} from "@testing-library/react"; +import useMessages, {ChatRoomLike} from "./useMessages"; +import EventEmitter from "eventemitter3"; +import {ChatRoomEvents} from "./ChatRoom"; + +describe('useMessages', () => { + test('messages are returned', async () => { + const listener = new EventEmitter(); + const mockChatRoom: ChatRoomLike = { + listener, + }; + const { result } = renderHook(() => useMessages(mockChatRoom)); + + listener.emit('text', 'ABC', 'player1'); + listener.emit('text', 'XYZ', 'player2'); + + await waitFor(() => { + expect(result.current.length).toBe(2); + }); + const messages = result.current; + expect(messages[0].text).toBe('ABC'); + expect(messages[0].whose).toBe('player1'); + expect(messages[0].timestamp).toBeDefined(); + expect(messages[1].text).toBe('XYZ'); + expect(messages[1].whose).toBe('player2'); + expect(messages[1].timestamp).toBeDefined(); + }); +}); diff --git a/src/lib/useMessages.ts b/src/lib/useMessages.ts new file mode 100644 index 0000000..6ece213 --- /dev/null +++ b/src/lib/useMessages.ts @@ -0,0 +1,43 @@ +import {useEffect, useState} from "react"; +import {Chat} from "./setup"; +import {EventListener} from "./types"; +import {ChatRoomEvents} from "./ChatRoom"; + +export interface Message { + type: 'message'; + text: string; + whose: string; + timestamp: number; +} + +export type Messages = Message[]; + +export interface ChatRoomLike { + listener: EventListener; +} + +export default function useMessages( + chatRoom: ChatRoomLike = Chat, +) { + const [messages, setMessages] = useState([]); + + useEffect(() => { + const textListener = (text: string, whose: string) => { + setMessages(prev => [ + ...prev, + { + type: 'message', + text, + whose, + timestamp: Date.now(), + }, + ]); + }; + chatRoom.listener.on('text', textListener); + return () => { + chatRoom.listener.off('text', textListener); + } + }, [chatRoom.listener]); + + return messages; +} diff --git a/src/lib/useNames.test.ts b/src/lib/useNames.test.ts new file mode 100644 index 0000000..cb8aaf4 --- /dev/null +++ b/src/lib/useNames.test.ts @@ -0,0 +1,25 @@ +import {renderHook, waitFor} from "@testing-library/react"; +import EventEmitter from "eventemitter3"; +import {ChatRoomEvents} from "./ChatRoom"; +import useNames, {ChatRoomLike} from "./useNames"; + +describe('useNames', () => { + test('names are updated', async () => { + const listener = new EventEmitter(); + const mockChatRoom: ChatRoomLike = { + listener, + }; + const { result } = renderHook(() => useNames(mockChatRoom)); + + listener.emit('name', 'Alice', 'player1') + listener.emit('name', 'Bob', 'player2'); + + await waitFor(() => { + expect(result.current.size).toBe(2); + }); + + const names = result.current; + expect(names.get('player1')).toBe('Alice'); + expect(names.get('player2')).toBe('Bob'); + }); +}); diff --git a/src/lib/useNames.ts b/src/lib/useNames.ts new file mode 100644 index 0000000..52b8138 --- /dev/null +++ b/src/lib/useNames.ts @@ -0,0 +1,30 @@ +import {useEffect, useState} from "react"; +import {Chat} from "./setup"; +import {ChatRoomEvents} from "./ChatRoom"; +import {EventListener} from "./types"; + +export interface ChatRoomLike { + listener: EventListener; +} + +export default function useNames( + chatRoom: ChatRoomLike = Chat, +) { + const [names, setNames] = useState(new Map()); + + useEffect(() => { + const nameListener = (name: string, whose: string) => { + setNames(prev => { + const next = new Map(prev); + next.set(whose, name); + return next; + }); + }; + chatRoom.listener.on('name', nameListener); + return () => { + chatRoom.listener.off('name', nameListener); + } + }, [chatRoom.listener]); + + return names; +}