All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m18s
172 lines
4.6 KiB
TypeScript
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();
|