|
| 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