chat-app/frontend/src/lib/socket.ts
Gal Podlipnik d2149bd99f
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m18s
cicd 14
2025-06-12 17:17:10 +02:00

172 lines
4.6 KiB
TypeScript

import { useChatStore } from '@/stores/chatStore';
import { useSocketStore } from '@/stores/socketStore';
import type { Message, MessageReaction } from '@/types';
import { io, type Socket } from 'socket.io-client';
import { toast } from 'sonner';
class SocketService {
private socket: Socket | null = null;
private listenersInitialized = false;
connect(token: string): Socket {
if (this.socket && this.socket.connected) {
return this.socket;
}
const socketUrl =
import.meta.env.VITE_SOCKET_URL || 'http://localhost:3000';
this.socket = io(socketUrl, {
auth: {
token,
},
});
if (!this.listenersInitialized) {
this.setupEventListeners();
this.listenersInitialized = true;
}
useSocketStore.getState().setSocket(this.socket);
return this.socket;
}
disconnect() {
if (this.socket) {
this.removeAllListeners();
this.socket.disconnect();
this.socket = null;
useSocketStore.getState().setSocket(null);
useSocketStore.getState().setConnected(false);
}
}
private removeAllListeners() {
if (!this.socket) return;
this.socket.removeAllListeners('new_message');
this.socket.removeAllListeners('message_reaction_updated');
this.socket.removeAllListeners('user_online');
this.socket.removeAllListeners('user_offline');
this.socket.removeAllListeners('rate_limit_exceeded');
this.socket.removeAllListeners('error');
this.socket.removeAllListeners('connect');
this.socket.removeAllListeners('disconnect');
this.socket.removeAllListeners('user_typing');
this.socket.removeAllListeners('user_stopped_typing');
}
getSocket(): Socket | null {
return this.socket;
}
private setupEventListeners() {
if (!this.socket) return;
this.removeAllListeners();
const { setConnected } = useSocketStore.getState();
const {
addMessage,
updateMessageReactions,
addOnlineUser,
removeOnlineUser,
addTypingUser,
removeTypingUser,
} = useChatStore.getState();
this.socket.on('connect', () => {
console.log('Connected to server');
setConnected(true);
});
this.socket.on('disconnect', () => {
console.log('Disconnected from server');
setConnected(false);
});
this.socket.on('new_message', (message: Message) => {
addMessage(message);
});
this.socket.on(
'message_reaction_updated',
(data: { messageId: string; reactions: MessageReaction[] }) => {
updateMessageReactions(data.messageId, data.reactions);
}
);
this.socket.on(
'user_online',
(data: { userId: string; username: string }) => {
addOnlineUser(data.userId);
}
);
this.socket.on(
'online_users',
(users: { id: string; username: string[] }[]) => {
const onlineUserIds = new Set(users.map((user) => user.id));
useChatStore.getState().setOnlineUsers(onlineUserIds);
}
);
this.socket.on(
'user_offline',
(data: { userId: string; username: string }) => {
removeOnlineUser(data.userId);
}
);
this.socket.on(
'rate_limit_exceeded',
(data: { message: string; remainingTime?: number }) => {
console.warn('Rate limit exceeded:', data.message);
toast.error(`Rate limit exceeded: ${data.message}`, {
duration: data.remainingTime ? data.remainingTime * 1000 : 5000,
position: 'top-right',
style: {
background: '#f8d7da',
color: '#721c24',
border: '1px solid #f5c6cb',
},
});
}
);
this.socket.on(
'user_typing',
(data: { roomId: string; username: string }) => {
addTypingUser(data.roomId, data.username);
}
);
this.socket.on(
'user_stopped_typing',
(data: { roomId: string; username: string }) => {
removeTypingUser(data.roomId, data.username);
}
);
this.socket.on(
'message_seen_update',
(data: { messageId: string; seenBy: string[] }) => {
Object.keys(useChatStore.getState().messages).forEach((roomId) => {
useChatStore.getState().messages[roomId].forEach((msg) => {
if (msg.id === data.messageId) {
msg.seenBy = data.seenBy;
}
});
});
}
);
this.socket.on('error', (data: { message: string }) => {
console.error('Socket error:', data.message);
toast.error(`Socket error: ${data.message}`, {
duration: 5000,
position: 'top-right',
});
});
}
}
export const socketService = new SocketService();