Client documentation
Preact based real-time chat application client
Overview
The DockerChat client is a modern real-time chat application built with Preact that connects to a WebSocket server for instant messaging. It features both public room conversations and private direct messaging with file sharing capabilities.
Tech Stack
- Framework: Preact 10.26.9 with TypeScript
- Routing: preact-iso 2.9.2
- Build Tool: Vite 7.0.4
- Styling: CSS Modules + Custom CSS Variables
- Containerization: Docker multi-stage build with Node.js 20
Project Structure
client/
├── src/
│ ├── assets/icons/ # SVG icons (attach, send, settings, etc.)
│ ├── components/ # React components
│ │ ├── Header.tsx # Navigation header
│ │ ├── RoomList.tsx # Public rooms display
│ │ ├── chatWindow.tsx # Main chat interface
│ │ └── directMessages.tsx # Private message sidebar
│ ├── css/ # CSS modules
│ │ ├── chatWindow.module.css
│ │ ├── directMessages.module.css
│ │ └── roomsList.module.css
│ ├── pages/ # Route components
│ │ ├── Home/ # Main chat page
│ │ └── _404.tsx # Error page
│ ├── shared/ # Context providers & utilities
│ │ ├── authContext.tsx # Authentication management
│ │ ├── chatContext.tsx # Chat state management
│ │ ├── webSocketContext.tsx # WebSocket connection
│ │ ├── unreadMessagesContext.tsx # Notification system
│ │ ├── fileHelpers.tsx # File upload utilities
│ │ └── utils.ts # Crypto & utility functions
│ ├── config.ts # WebSocket configuration
│ ├── types.ts # TypeScript definitions
│ └── index.tsx # App entry point
├── Dockerfile # Multi-stage container build
└── package.jsonCore Architecture
Provider Pattern
The application uses a nested provider architecture for state management:
<SocketProvider>
<ClientProvider>
<UnreadProvider>
<ChatProvider>
<App />
</ChatProvider>
</UnreadProvider>
</ClientProvider>
</SocketProvider>Context Providers
1. SocketProvider (webSocketContext.tsx)
Manages WebSocket connection and message handling:
type SocketContextType = {
status: "connecting" | "open" | "closed" | "error";
messages: SocketMessage[];
sendMessage: (msg: any) => void;
};Features:
- Automatic connection to
ws://api.localhost:5001 - Message queuing when connection is unavailable
- Real-time message broadcasting
- Connection status tracking
2. ClientProvider (authContext.tsx)
Handles user authentication and identity:
type ClientContextType = {
username: string | null;
loading: boolean;
};Features:
- Username validation (3-16 chars, alphanumeric +
_-) - RSA key pair generation for message signing
- Automatic heartbeat every 30 seconds
- localStorage persistence for user data
3. UnreadProvider (unreadMessagesContext.tsx)
Manages unread message notifications:
interface UnreadContextType {
unreadCounts: UnreadCount;
incrementUnread: (chatId: string) => void;
clearUnread: (chatId: string) => void;
getTotalUnread: () => number;
getUnreadCount: (chatId: string) => number;
}Features:
- Per-chat unread counters
- Visual badge system
- Automatic clearing when viewing conversations
- localStorage persistence per user
4. ChatProvider (chatContext.tsx)
Core chat functionality and state:
type ChatContextType = {
rooms: Room[];
currentRoom: Room | null;
messages: Message[];
privateMessages: Record<string, Message[]>;
clients: Client[];
currentClient: Client | null;
// ... methods for chat operations
};Key Components
RoomList Component
Displays available public chat rooms:
- Room Creation: Click "+" button to create new rooms
- Room Selection: Click room avatar to join/leave
- Visual Indicators: Active room highlighting
- Room Info: Tooltips show client count and message count
// Room data structure
type Room = {
name: string;
clients: number;
messages: number;
created_at: string;
last_activity: string;
};DirectMessages Component
Manages private messaging interface:
- User List: Shows all connected clients
- Online Status: Green badge for online users
- Unread Badges: Red counters for new messages
- Last Seen: Timestamps for user activity
- Smart Sorting: Unread messages first, then by last activity
ChatWindow Component
Main chat interface with dual functionality:
Room Mode:
- Displays room messages chronologically
- File drag-and-drop support
- Message composer for text input
- Attach button for file selection
Private Mode:
- One-on-one conversations
- Same file sharing capabilities
- Separate message history per user
Features:
- Auto-scroll: Automatically scrolls to new messages
- File Validation: 10MB limit, type checking
- Drag & Drop: Visual overlay for file uploads
- Message Rendering: Different layouts for text/image/file messages
File Handling
Supported File Types
const acceptedTypes = [
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
'application/pdf', 'text/plain',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
];Upload Process
- File Selection: Drag & drop or file picker
- Validation: Size (10MB) and type checking
- Base64 Conversion: Convert to Base64 for transmission
- WebSocket Send: Authenticated message with file data
- UI Update: Immediate local display
File Message Rendering
- Images: Inline preview with download link
- Documents: File icon with download link
- Error Handling: Graceful fallback for missing content
Authentication Flow
Initial Registration
- Username Prompt: Validate and collect username
- Key Generation: Create RSA-2048 key pair
- Public Key Upload: Send to server for verification
- Storage: Save username and keys to localStorage
Message Authentication
- Message Preparation: Construct command with data
- Signature Creation: Sign with RSA private key
- Nonce Addition: Add timestamp and UUID for replay protection
- Transmission: Send authenticated message via WebSocket
// Authentication message format
{
command: string;
client_id: string;
nonce: string;
timestamp: number;
signature: string;
// ... additional data
}Styling System
CSS Architecture
- CSS Modules: Component-scoped styling
- CSS Variables: Theme-based color system
- Flexbox/Grid: Responsive layout system
- Dark Theme: Modern dark UI design
Key CSS Variables
:root {
--primary-color: #8775e9;
--border: rgba(255, 255, 255, 0.25);
--chat-sent-bubble-bg: #8775e9;
--chat-received-bubble-bg: #2d2d2d;
--message-composer-input-bg: #2d2d2d;
}Responsive Breakpoints
- Desktop: Sidebar (350px) + main chat area
- Mobile: Full-width single view (< 768px)
- Navigation: Hide/show sidebar on mobile
Development Workflow
Component Development
- Create component in appropriate directory
- Add corresponding CSS module
- Export from index file
- Import and use in parent components
State Management
- Use appropriate context provider for state
- Avoid prop drilling with context consumers
- Handle loading and error states
Styling Guidelines
- Use CSS modules for component styles
- Follow BEM-like naming conventions
- Utilize CSS variables for theming
- Ensure mobile responsiveness
Build & Deployment
Development Build
npm run dev # Start Vite dev server
npm run build # Production build
npm run preview # Preview production buildDocker Deployment
Multi-stage Dockerfile:
# Stage 1: Build
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Serve
FROM node:20-alpine AS serve
WORKDIR /app
RUN npm install -g http-server
COPY --from=build /app/dist/ /app
EXPOSE 80
CMD ["http-server", "-p", "80", "-c-1"]Build Commands:
docker build -t chat-client .
docker run -p 80:80 chat-clientConfiguration
WebSocket Settings
Update src/config.ts for different environments:
export const WS_CONFIG = {
HOST: "api.localhost", // Change for production
PORT: 5001, // WebSocket server port
};Environment Variables
The client adapts to different environments automatically. For production deployment, update the WebSocket host configuration to match your server setup.
Security Implementation
Client-Side Security
- RSA Digital Signatures: All authenticated commands are signed
- Nonce Protection: Prevents replay attacks
- File Validation: Strict file type and size limits
- No Sensitive Storage: Passwords/tokens not stored
Cryptographic Functions
// Key generation
generateKeyPair(): Promise<{ publicKeyPem: string, privateKey: CryptoKey }>
// Message signing
signMessage(privateKey: CryptoKey, message: string): Promise<string>
// Authenticated messaging
sendAuthenticatedMessage(sendMessage: Function, message: any): Promise<void>Performance Features
Message Optimization
- Duplicate Prevention: Tracks processed messages
- Memory Management: Limits processed message cache
- Efficient Updates: Only processes new messages
- Auto-scroll: Smooth scrolling to latest messages
File Handling
- Progressive Upload: Base64 streaming for large files
- Type Detection: MIME type validation
- Preview Generation: Automatic image previews
- Error Recovery: Graceful handling of upload failures
Browser Support
Required APIs
- WebSocket API: Real-time communication
- Web Crypto API: RSA key generation and signing
- File API: File upload and drag & drop
- Local Storage: User data persistence
Polyfills
No polyfills required for modern browsers (2021+). For older browser support, consider adding:
- WebSocket polyfill for IE
- Crypto API polyfill for legacy browsers
Troubleshooting
Common Issues
Connection Problems:
// Check WebSocket status
const { status } = useSocket();
console.log('WebSocket status:', status);Authentication Issues:
// Reset user authentication
localStorage.removeItem('username');
localStorage.removeItem('public_key');
localStorage.removeItem('private_key');
// Refresh pageFile Upload Errors:
- Verify file size < 10MB
- Check supported MIME types
- Ensure stable internet connection
- Check browser console for detailed errors
Debug Information
The application logs important events to browser console:
- WebSocket connection status
- Authentication state changes
- File upload progress
- Message processing events