Skip to content

Commit fdf2eed

Browse files
authored
Merge pull request #149 from isaaclins/copilot/add-in-room-text-chat
Add In-Room Text Chat: Real-Time Messaging for Collaboration Rooms
2 parents 84b37de + dbd30e7 commit fdf2eed

File tree

9 files changed

+1055
-4
lines changed

9 files changed

+1055
-4
lines changed

CHAT_FEATURE.md

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
# In-Room Text Chat Feature
2+
3+
## Overview
4+
5+
This feature adds real-time text chat functionality to Excalidraw collaboration rooms. Users can send and receive messages in real-time, with chat history automatically provided to new users when they join a room.
6+
7+
## Features
8+
9+
### Server-Side (Go)
10+
- **In-memory storage**: Chat messages are stored per room in memory
11+
- **Message limit**: Maximum 1000 messages per room to prevent memory issues
12+
- **Chat history**: New users receive full chat history when joining a room
13+
- **Auto cleanup**: Chat history is automatically cleared when a room becomes empty
14+
- **Thread-safe**: All chat operations use mutex locks for concurrent access
15+
16+
### Client-Side (TypeScript/React)
17+
- **Chat panel UI**: Expandable/collapsible panel in the bottom-right corner
18+
- **Real-time messaging**: Instant message delivery to all room participants
19+
- **Message formatting**: Shows sender, content, and timestamp
20+
- **Visual distinction**: Own messages highlighted differently from others
21+
- **Keyboard shortcuts**: Press Enter to send messages
22+
- **Auto-scroll**: Automatically scrolls to latest messages
23+
- **State management**: Chat clears when switching rooms
24+
25+
## Architecture
26+
27+
### WebSocket Events
28+
29+
#### Client → Server
30+
- **`server-chat-message`**: Client sends a chat message to the room
31+
```typescript
32+
{
33+
roomId: string,
34+
messageData: {
35+
id: string,
36+
content: string
37+
}
38+
}
39+
```
40+
41+
#### Server → Client
42+
- **`client-chat-message`**: Server broadcasts a message to all room members
43+
```typescript
44+
{
45+
id: string,
46+
roomId: string,
47+
sender: string,
48+
content: string,
49+
timestamp: number
50+
}
51+
```
52+
53+
- **`chat-history`**: Server sends chat history to newly joined user
54+
```typescript
55+
Array<{
56+
id: string,
57+
roomId: string,
58+
sender: string,
59+
content: string,
60+
timestamp: number
61+
}>
62+
```
63+
64+
### Data Flow
65+
66+
```
67+
User A Server User B
68+
| | |
69+
|--- server-chat-message -->| |
70+
| (roomId, message) | |
71+
| | |
72+
| [Store in memory] |
73+
| [Limit to 1000] |
74+
| | |
75+
|<-- client-chat-message --| |
76+
| |--- client-chat-message -->|
77+
| | (message) |
78+
| | |
79+
| [User B joins] |
80+
| |<--- join-room ----------|
81+
| | |
82+
| |--- chat-history -------->|
83+
| | (all messages) |
84+
```
85+
86+
## Implementation Details
87+
88+
### Server (Go)
89+
90+
**File**: `excalidraw-server/handlers/websocket/collab.go`
91+
92+
Key functions:
93+
- `addChatMessage(roomID, message)`: Adds message to room's history
94+
- `getChatHistory(roomID)`: Retrieves chat history for a room
95+
- `clearChatHistory(roomID)`: Clears history when room is empty
96+
- `handleChatMessage(socket, srv, datas)`: Processes incoming chat messages
97+
98+
Data structures:
99+
```go
100+
type ChatMessage struct {
101+
ID string `json:"id"`
102+
RoomID string `json:"roomId"`
103+
Sender string `json:"sender"`
104+
Content string `json:"content"`
105+
Timestamp int64 `json:"timestamp"`
106+
}
107+
108+
const maxChatMessagesPerRoom = 1000
109+
110+
var (
111+
chatHistory = make(map[string][]ChatMessage)
112+
chatHistoryMutex sync.RWMutex
113+
)
114+
```
115+
116+
### Client (TypeScript/React)
117+
118+
**Files**:
119+
- `excalidraw-app/src/lib/websocket.ts`: CollaborationClient chat methods
120+
- `excalidraw-app/src/components/ChatPanel.tsx`: Chat UI component
121+
- `excalidraw-app/src/components/ChatPanel.css`: Chat styling
122+
- `excalidraw-app/src/components/ExcalidrawWrapper.tsx`: Integration
123+
124+
Key methods in CollaborationClient:
125+
```typescript
126+
sendChatMessage(messageId: string, content: string): void
127+
onChatMessage(callback: (message: ChatMessage) => void): void
128+
onChatHistory(callback: (messages: ChatMessage[]) => void): void
129+
```
130+
131+
ChatPanel component features:
132+
- Collapsible header with message count
133+
- Scrollable message list
134+
- Input field with send button
135+
- Timestamp formatting
136+
- Sender identification (You vs socket ID)
137+
- Auto-scroll to bottom
138+
139+
## Testing
140+
141+
### Unit Tests
142+
143+
**App Tests** (`excalidraw-app/src/lib/__tests__/websocket.test.ts`):
144+
- ✅ Send chat message
145+
- ✅ Prevent sending when not connected
146+
- ✅ Handle receiving chat messages
147+
- ✅ Handle receiving chat history
148+
- ✅ Handle empty content
149+
- ✅ Handle special characters
150+
151+
**Server Tests** (`excalidraw-server/handlers/websocket/collab_test.go`):
152+
- ✅ Add chat message
153+
- ✅ Message limit enforcement
154+
- ✅ Empty room chat history
155+
- ✅ Clear chat history
156+
- ✅ Multiple rooms isolation
157+
- ✅ Concurrent access safety
158+
- ✅ Special character handling
159+
160+
### Integration Test
161+
162+
A Node.js integration test demonstrates:
163+
- Two clients connecting to the server
164+
- Both clients joining the same room
165+
- Message exchange between clients
166+
- Chat history delivery to new users
167+
168+
**Test Results**:
169+
```
170+
✅ Connections: Both clients connected
171+
✅ Room joins: Both clients joined successfully
172+
✅ Messages received: 4 messages exchanged
173+
✅ Chat functionality working!
174+
175+
Messages exchanged:
176+
1. [Client 2] Hello from Client 1!
177+
2. [Client 1] Hello from Client 1!
178+
3. [Client 2] Hi Client 1! This is Client 2.
179+
4. [Client 1] Hi Client 1! This is Client 2.
180+
```
181+
182+
### Test Coverage
183+
184+
- **App**: 140 tests pass (6 new chat tests)
185+
- **Server**: All existing tests + 7 new chat tests pass
186+
- **Build**: Both app and server build successfully
187+
- **Linting**: No errors
188+
189+
## Usage
190+
191+
### For Users
192+
193+
1. **Connect to a room**: Use the connection dialog to join a collaboration room
194+
2. **Open chat**: Look for the chat panel in the bottom-right corner
195+
3. **Expand panel**: Click the header to expand/collapse
196+
4. **Send message**: Type in the input field and press Enter or click Send
197+
5. **View history**: New messages appear at the bottom, scroll to see older ones
198+
199+
### For Developers
200+
201+
**Starting a chat conversation**:
202+
```typescript
203+
const collab = api.getCollaborationClient();
204+
collab.sendChatMessage('unique-id', 'Hello world!');
205+
```
206+
207+
**Listening for messages**:
208+
```typescript
209+
collab.onChatMessage((message) => {
210+
console.log(`${message.sender}: ${message.content}`);
211+
});
212+
```
213+
214+
**Accessing chat history**:
215+
```typescript
216+
collab.onChatHistory((messages) => {
217+
console.log(`Received ${messages.length} messages`);
218+
});
219+
```
220+
221+
## Configuration
222+
223+
### Server
224+
225+
No configuration needed. Chat is automatically enabled with:
226+
- In-memory storage
227+
- 1000 message limit per room
228+
- Automatic cleanup on room empty
229+
230+
### Client
231+
232+
Chat panel automatically appears when:
233+
- User is connected to a server
234+
- User has joined a room
235+
236+
## Security Considerations
237+
238+
1. **Content sanitization**: Messages are displayed as plain text (no HTML rendering)
239+
2. **Input validation**: Server validates all message fields
240+
3. **Rate limiting**: Consider adding rate limits in production
241+
4. **Message size**: Consider adding max message length limit
242+
5. **Persistence**: Messages are ephemeral (not persisted to database)
243+
244+
## Future Enhancements
245+
246+
Potential improvements:
247+
- [ ] Message persistence to database
248+
- [ ] Markdown/link support in messages
249+
- [ ] User mentions (@username)
250+
- [ ] Read receipts
251+
- [ ] Typing indicators
252+
- [ ] Message editing/deletion
253+
- [ ] File attachments
254+
- [ ] Message search
255+
- [ ] User blocking/reporting
256+
- [ ] Emoji picker
257+
- [ ] Message reactions
258+
- [ ] Thread replies
259+
260+
## Troubleshooting
261+
262+
### Chat panel not appearing
263+
- Ensure you're connected to a server
264+
- Verify you've joined a room
265+
- Check browser console for errors
266+
267+
### Messages not sending
268+
- Verify server connection is active
269+
- Check that room ID is valid
270+
- Ensure content is not empty
271+
272+
### Messages not receiving
273+
- Verify WebSocket connection is stable
274+
- Check server logs for errors
275+
- Ensure both clients are in same room
276+
277+
### Chat history not loading
278+
- Verify server is running and accessible
279+
- Check that room was not empty when joining
280+
- Look for errors in server logs
281+
282+
## Related Files
283+
284+
### Server
285+
- `excalidraw-server/handlers/websocket/collab.go` - Main chat implementation
286+
- `excalidraw-server/handlers/websocket/collab_test.go` - Chat tests
287+
288+
### Client
289+
- `excalidraw-app/src/lib/websocket.ts` - WebSocket client methods
290+
- `excalidraw-app/src/lib/__tests__/websocket.test.ts` - Client tests
291+
- `excalidraw-app/src/components/ChatPanel.tsx` - UI component
292+
- `excalidraw-app/src/components/ChatPanel.css` - Styling
293+
- `excalidraw-app/src/components/ExcalidrawWrapper.tsx` - Integration
294+
295+
## License
296+
297+
Same as main project (MIT)

0 commit comments

Comments
 (0)