UNPKG

@gs-rumana/chatify

Version:

A robust, feature-rich chat system package for Socket.IO with real-time messaging, groups, typing indicators, online status, and message delivery receipts

185 lines (157 loc) 5.03 kB
import { io, Socket } from 'socket.io-client'; import { User, Message, Group, TypingIndicator } from './types.js'; export interface ChatClientOptions { url?: string; auth?: { token?: string; }; autoConnect?: boolean; transports?: string[]; } export interface ChatClientEvents { 'connect': () => void; 'disconnect': () => void; 'message:receive': (message: Message) => void; 'message:delivered': (messageId: string, userId: string) => void; 'message:read': (messageId: string, userId: string) => void; 'typing:indicator': (indicator: TypingIndicator) => void; 'status:online': (userId: string) => void; 'status:offline': (userId: string) => void; 'status:update': (users: User[]) => void; 'group:joined': (group: Group) => void; 'group:member_joined': (data: { groupId: string; user: User }) => void; 'group:member_left': (data: { groupId: string; userId: string }) => void; 'error': (error: Error) => void; } export class ChatClient { private socket: Socket; private user?: User; private typingTimer?: NodeJS.Timeout; constructor(options: ChatClientOptions = {}) { this.socket = io(options.url || 'http://localhost:3000', { auth: options.auth, autoConnect: options.autoConnect !== false, transports: options.transports || ['websocket', 'polling'] }); this.setupEventListeners(); } private setupEventListeners(): void { this.socket.on('connect', () => { console.log('Connected to chat server'); }); this.socket.on('disconnect', () => { console.log('Disconnected from chat server'); }); } public async authenticate(token: string): Promise<{ success: boolean; user?: User; error?: string }> { return new Promise((resolve) => { this.socket.emit('auth:login', { token }, (response: any) => { if (response.success) { this.user = response.user; } resolve(response); }); }); } public async sendMessage( content: string, groupId?: string, messageType: 'text' | 'image' | 'file' = 'text', metadata?: Record<string, any> ): Promise<{ success: boolean; message?: Message; error?: string }> { return new Promise((resolve) => { this.socket.emit('message:send', { content, groupId, messageType, metadata }, (response: any) => { resolve(response); }); }); } public async createGroup( name: string, description?: string, isPrivate: boolean = false, members?: string[] ): Promise<{ success: boolean; group?: Group; error?: string }> { return new Promise((resolve) => { this.socket.emit('group:create', { name, description, isPrivate, members }, (response: any) => { resolve(response); }); }); } public async joinGroup(groupId: string): Promise<{ success: boolean; group?: Group; error?: string }> { return new Promise((resolve) => { this.socket.emit('group:join', { groupId }, (response: any) => { resolve(response); }); }); } public async leaveGroup(groupId: string): Promise<{ success: boolean; error?: string }> { return new Promise((resolve) => { this.socket.emit('group:leave', { groupId }, (response: any) => { resolve(response); }); }); } public startTyping(groupId?: string): void { this.socket.emit('typing:start', { groupId }); // Auto-stop typing after 3 seconds if (this.typingTimer) { clearTimeout(this.typingTimer); } this.typingTimer = setTimeout(() => { this.stopTyping(groupId); }, 3000); } public stopTyping(groupId?: string): void { this.socket.emit('typing:stop', { groupId }); if (this.typingTimer) { clearTimeout(this.typingTimer); this.typingTimer = undefined; } } public markMessageAsDelivered(messageId: string): void { this.socket.emit('message:delivered', { messageId }); } public markMessageAsRead(messageId: string): void { this.socket.emit('message:read', { messageId }); } public getOnlineUsers(): Promise<User[]> { return new Promise((resolve) => { this.socket.emit('status:get_online', (users: User[]) => { resolve(users); }); }); } public on<K extends keyof ChatClientEvents>(event: K, listener: ChatClientEvents[K]): this { this.socket.on(event as any, listener as any); return this; } public off<K extends keyof ChatClientEvents>(event: K, listener?: ChatClientEvents[K]): this { this.socket.off(event as any, listener as any); return this; } public disconnect(): void { this.socket.disconnect(); } public connect(): void { this.socket.connect(); } public get isConnected(): boolean { return this.socket.connected; } public get currentUser(): User | undefined { return this.user; } } export function createChatClient(options: ChatClientOptions = {}): ChatClient { return new ChatClient(options); }