add sockets and change subscription
This commit is contained in:
@@ -20,7 +20,9 @@ import gamehubRouter from './routers/gamehub'
|
|||||||
import escRouter from './routers/esc'
|
import escRouter from './routers/esc'
|
||||||
import connectmeRouter from './routers/connectme'
|
import connectmeRouter from './routers/connectme'
|
||||||
import questioneerRouter from './routers/questioneer'
|
import questioneerRouter from './routers/questioneer'
|
||||||
import { setIo } from './io'
|
import { setIo, getIo } from './io'
|
||||||
|
// Импорт обработчика чата
|
||||||
|
const { initializeChatSocket } = require('./routers/kfu-m-24-1/sber_mobile/socket-chat')
|
||||||
|
|
||||||
export const app = express()
|
export const app = express()
|
||||||
|
|
||||||
@@ -66,6 +68,15 @@ const initServer = async () => {
|
|||||||
|
|
||||||
const server = setIo(app)
|
const server = setIo(app)
|
||||||
|
|
||||||
|
// Инициализация Socket.IO для чата
|
||||||
|
const io = getIo()
|
||||||
|
if (io) {
|
||||||
|
const chatHandler = initializeChatSocket(io)
|
||||||
|
// Сохраняем ссылку на chat handler для доступа из эндпоинтов
|
||||||
|
io.chatHandler = chatHandler
|
||||||
|
console.log('✅ Socket.IO для чата инициализирован')
|
||||||
|
}
|
||||||
|
|
||||||
const sess = {
|
const sess = {
|
||||||
secret: "super-secret-key",
|
secret: "super-secret-key",
|
||||||
resave: true,
|
resave: true,
|
||||||
|
|||||||
@@ -5,7 +5,14 @@ let io = null
|
|||||||
|
|
||||||
export const setIo = (app) => {
|
export const setIo = (app) => {
|
||||||
const server = createServer(app)
|
const server = createServer(app)
|
||||||
io = new Server(server, {})
|
io = new Server(server, {
|
||||||
|
cors: {
|
||||||
|
origin: "*",
|
||||||
|
methods: ["GET", "POST"],
|
||||||
|
credentials: false
|
||||||
|
},
|
||||||
|
transports: ['websocket', 'polling']
|
||||||
|
})
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,31 +3,22 @@ const { getSupabaseClient } = require('./supabaseClient');
|
|||||||
|
|
||||||
// Получить все чаты по дому
|
// Получить все чаты по дому
|
||||||
router.get('/chats', async (req, res) => {
|
router.get('/chats', async (req, res) => {
|
||||||
console.log('🏠 [Server] GET /chats запрос получен');
|
|
||||||
console.log('🏠 [Server] Query параметры:', req.query);
|
|
||||||
|
|
||||||
const supabase = getSupabaseClient();
|
const supabase = getSupabaseClient();
|
||||||
const { building_id } = req.query;
|
const { building_id } = req.query;
|
||||||
|
|
||||||
if (!building_id) {
|
if (!building_id) {
|
||||||
console.log('❌ [Server] Ошибка: building_id обязателен');
|
|
||||||
return res.status(400).json({ error: 'building_id required' });
|
return res.status(400).json({ error: 'building_id required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('🔍 [Server] Выполняем запрос к Supabase для здания:', building_id);
|
|
||||||
|
|
||||||
const { data, error } = await supabase.from('chats').select('*').eq('building_id', building_id);
|
const { data, error } = await supabase.from('chats').select('*').eq('building_id', building_id);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log('❌ [Server] Ошибка Supabase:', error);
|
|
||||||
return res.status(400).json({ error: error.message });
|
return res.status(400).json({ error: error.message });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ [Server] Чаты получены:', data?.length || 0, 'шт.');
|
|
||||||
res.json(data || []);
|
res.json(data || []);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('❌ [Server] Неожиданная ошибка:', err);
|
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -184,15 +175,10 @@ router.get('/chats/:chat_id/stats', async (req, res) => {
|
|||||||
|
|
||||||
// Получить последнее сообщение в чате
|
// Получить последнее сообщение в чате
|
||||||
router.get('/chats/:chat_id/last-message', async (req, res) => {
|
router.get('/chats/:chat_id/last-message', async (req, res) => {
|
||||||
console.log('💬 [Server] GET /chats/:chat_id/last-message запрос получен');
|
|
||||||
console.log('💬 [Server] Chat ID:', req.params.chat_id);
|
|
||||||
|
|
||||||
const supabase = getSupabaseClient();
|
const supabase = getSupabaseClient();
|
||||||
const { chat_id } = req.params;
|
const { chat_id } = req.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('🔍 [Server] Выполняем запрос последнего сообщения для чата:', chat_id);
|
|
||||||
|
|
||||||
// Получаем последнее сообщение
|
// Получаем последнее сообщение
|
||||||
const { data: lastMessage, error } = await supabase
|
const { data: lastMessage, error } = await supabase
|
||||||
.from('messages')
|
.from('messages')
|
||||||
@@ -205,10 +191,8 @@ router.get('/chats/:chat_id/last-message', async (req, res) => {
|
|||||||
let data = null;
|
let data = null;
|
||||||
|
|
||||||
if (error && error.code === 'PGRST116') {
|
if (error && error.code === 'PGRST116') {
|
||||||
console.log('ℹ️ [Server] Сообщений в чате нет (PGRST116)');
|
|
||||||
data = null;
|
data = null;
|
||||||
} else if (error) {
|
} else if (error) {
|
||||||
console.log('❌ [Server] Ошибка Supabase при получении последнего сообщения:', error);
|
|
||||||
return res.status(400).json({ error: error.message });
|
return res.status(400).json({ error: error.message });
|
||||||
} else if (lastMessage) {
|
} else if (lastMessage) {
|
||||||
// Получаем профиль пользователя для сообщения
|
// Получаем профиль пользователя для сообщения
|
||||||
@@ -223,12 +207,10 @@ router.get('/chats/:chat_id/last-message', async (req, res) => {
|
|||||||
...lastMessage,
|
...lastMessage,
|
||||||
user_profiles: userProfile || null
|
user_profiles: userProfile || null
|
||||||
};
|
};
|
||||||
console.log('✅ [Server] Последнее сообщение получено для чата:', chat_id);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
res.json(data);
|
res.json(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('❌ [Server] Неожиданная ошибка при получении последнего сообщения:', err);
|
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const buildingsRouter = require('./buildings');
|
|||||||
const userApartmentsRouter = require('./user_apartments');
|
const userApartmentsRouter = require('./user_apartments');
|
||||||
const avatarRouter = require('./media');
|
const avatarRouter = require('./media');
|
||||||
const supportRouter = require('./supportApi');
|
const supportRouter = require('./supportApi');
|
||||||
|
const { getIo } = require('../../../io');
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +1,59 @@
|
|||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const { getSupabaseClient } = require('./supabaseClient');
|
const { getSupabaseClient } = require('./supabaseClient');
|
||||||
|
const { getIo } = require('../../../io'); // Импортируем Socket.IO
|
||||||
|
|
||||||
// Получить все сообщения в чате с информацией о пользователе
|
// Получить все сообщения в чате с информацией о пользователе
|
||||||
router.get('/messages', async (req, res) => {
|
router.get('/messages', async (req, res) => {
|
||||||
console.log('📬 [Server] GET /messages запрос получен');
|
|
||||||
console.log('📬 [Server] Query параметры:', req.query);
|
|
||||||
|
|
||||||
const supabase = getSupabaseClient();
|
|
||||||
const { chat_id, limit = 50, offset = 0 } = req.query;
|
|
||||||
|
|
||||||
if (!chat_id) {
|
|
||||||
console.log('❌ [Server] Ошибка: chat_id обязателен');
|
|
||||||
return res.status(400).json({ error: 'chat_id required' });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('🔍 [Server] Выполняем запрос к Supabase для чата:', chat_id);
|
const { chat_id, limit = 50, offset = 0 } = req.query;
|
||||||
|
|
||||||
// Получаем сообщения
|
if (!chat_id) {
|
||||||
const { data: messages, error } = await supabase
|
return res.status(400).json({ error: 'chat_id is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
|
||||||
|
const { data, error } = await supabase
|
||||||
.from('messages')
|
.from('messages')
|
||||||
.select('*')
|
.select(`
|
||||||
|
*,
|
||||||
|
user_profiles (
|
||||||
|
id,
|
||||||
|
full_name,
|
||||||
|
avatar_url
|
||||||
|
)
|
||||||
|
`)
|
||||||
.eq('chat_id', chat_id)
|
.eq('chat_id', chat_id)
|
||||||
.order('created_at', { ascending: false })
|
.order('created_at', { ascending: true })
|
||||||
.limit(limit)
|
|
||||||
.range(offset, offset + limit - 1);
|
.range(offset, offset + limit - 1);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log('❌ [Server] Ошибка получения сообщений:', error);
|
return res.status(500).json({ error: 'Failed to fetch messages' });
|
||||||
return res.status(400).json({ error: error.message });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем профили пользователей для всех уникальных user_id
|
// Получаем уникальные ID пользователей из сообщений, у которых нет профиля
|
||||||
let data = messages || [];
|
const messagesWithoutProfiles = data.filter(msg => !msg.user_profiles);
|
||||||
if (data.length > 0) {
|
const userIds = [...new Set(messagesWithoutProfiles.map(msg => msg.user_id))];
|
||||||
const userIds = [...new Set(data.map(msg => msg.user_id))];
|
|
||||||
console.log('👥 [Server] Получаем профили для пользователей:', userIds);
|
|
||||||
|
|
||||||
|
if (userIds.length > 0) {
|
||||||
const { data: profiles, error: profilesError } = await supabase
|
const { data: profiles, error: profilesError } = await supabase
|
||||||
.from('user_profiles')
|
.from('user_profiles')
|
||||||
.select('id, full_name, avatar_url')
|
.select('id, full_name, avatar_url')
|
||||||
.in('id', userIds);
|
.in('id', userIds);
|
||||||
|
|
||||||
if (!profilesError && profiles) {
|
if (!profilesError && profiles) {
|
||||||
// Объединяем сообщения с профилями
|
// Добавляем профили к сообщениям
|
||||||
data = data.map(msg => ({
|
data.forEach(message => {
|
||||||
...msg,
|
if (!message.user_profiles) {
|
||||||
user_profiles: profiles.find(profile => profile.id === msg.user_id) || null
|
message.user_profiles = profiles.find(profile => profile.id === message.user_id) || null;
|
||||||
}));
|
}
|
||||||
console.log('✅ [Server] Профили пользователей добавлены к сообщениям');
|
});
|
||||||
} else {
|
|
||||||
console.log('⚠️ [Server] Ошибка получения профилей пользователей:', profilesError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ [Server] Сообщения получены:', data?.length || 0, 'шт.');
|
res.json(data);
|
||||||
res.json(data?.reverse() || []); // Возвращаем в хронологическом порядке
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('❌ [Server] Неожиданная ошибка:', err);
|
res.status(500).json({ error: 'Unexpected error occurred' });
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,6 +90,9 @@ router.post('/messages', async (req, res) => {
|
|||||||
user_profiles: userProfile || null
|
user_profiles: userProfile || null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Отправка через Socket.IO теперь происходит автоматически через Supabase Real-time подписку
|
||||||
|
// Это предотвращает дублирование сообщений
|
||||||
|
|
||||||
res.json(data);
|
res.json(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,29 @@ class ChatSocketHandler {
|
|||||||
this.io = io;
|
this.io = io;
|
||||||
this.onlineUsers = new Map(); // Хранение онлайн пользователей: socket.id -> user info
|
this.onlineUsers = new Map(); // Хранение онлайн пользователей: socket.id -> user info
|
||||||
this.chatRooms = new Map(); // Хранение участников комнат: chat_id -> Set(socket.id)
|
this.chatRooms = new Map(); // Хранение участников комнат: chat_id -> Set(socket.id)
|
||||||
|
this.realtimeSubscription = null; // Ссылка на подписку для управления
|
||||||
|
|
||||||
this.setupSocketHandlers();
|
this.setupSocketHandlers();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.setupRealtimeSubscription(); // Добавляем Real-time подписки
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запускаем тестирование через 2 секунды после инициализации
|
||||||
|
setTimeout(() => {
|
||||||
|
this.testRealtimeConnection();
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
// Проверяем статус подписки через 5 секунд
|
||||||
|
setTimeout(() => {
|
||||||
|
this.checkSubscriptionStatus();
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupSocketHandlers() {
|
setupSocketHandlers() {
|
||||||
this.io.on('connection', (socket) => {
|
this.io.on('connection', (socket) => {
|
||||||
console.log(`User connected: ${socket.id}`);
|
|
||||||
|
|
||||||
// Аутентификация пользователя
|
// Аутентификация пользователя
|
||||||
socket.on('authenticate', async (data) => {
|
socket.on('authenticate', async (data) => {
|
||||||
await this.handleAuthentication(socket, data);
|
await this.handleAuthentication(socket, data);
|
||||||
@@ -84,10 +100,7 @@ class ChatSocketHandler {
|
|||||||
message: 'Successfully authenticated',
|
message: 'Successfully authenticated',
|
||||||
user: userProfile
|
user: userProfile
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`User ${user_id} authenticated with socket ${socket.id}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Authentication error:', error);
|
|
||||||
socket.emit('auth_error', { message: 'Authentication failed' });
|
socket.emit('auth_error', { message: 'Authentication failed' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +118,6 @@ class ChatSocketHandler {
|
|||||||
socket.emit('error', { message: 'chat_id is required' });
|
socket.emit('error', { message: 'chat_id is required' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, что чат существует и пользователь имеет доступ к нему
|
// Проверяем, что чат существует и пользователь имеет доступ к нему
|
||||||
const supabase = getSupabaseClient();
|
const supabase = getSupabaseClient();
|
||||||
const { data: chat, error } = await supabase
|
const { data: chat, error } = await supabase
|
||||||
@@ -140,7 +152,6 @@ class ChatSocketHandler {
|
|||||||
socket.emit('error', { message: 'Access denied to this chat' });
|
socket.emit('error', { message: 'Access denied to this chat' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Добавляем сокет в комнату
|
// Добавляем сокет в комнату
|
||||||
socket.join(chat_id);
|
socket.join(chat_id);
|
||||||
|
|
||||||
@@ -148,7 +159,10 @@ class ChatSocketHandler {
|
|||||||
if (!this.chatRooms.has(chat_id)) {
|
if (!this.chatRooms.has(chat_id)) {
|
||||||
this.chatRooms.set(chat_id, new Set());
|
this.chatRooms.set(chat_id, new Set());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const participantsBefore = this.chatRooms.get(chat_id).size;
|
||||||
this.chatRooms.get(chat_id).add(socket.id);
|
this.chatRooms.get(chat_id).add(socket.id);
|
||||||
|
const participantsAfter = this.chatRooms.get(chat_id).size;
|
||||||
|
|
||||||
socket.emit('joined_chat', {
|
socket.emit('joined_chat', {
|
||||||
chat_id,
|
chat_id,
|
||||||
@@ -158,15 +172,13 @@ class ChatSocketHandler {
|
|||||||
|
|
||||||
// Уведомляем других участников о подключении
|
// Уведомляем других участников о подключении
|
||||||
const userInfo = this.onlineUsers.get(socket.id);
|
const userInfo = this.onlineUsers.get(socket.id);
|
||||||
|
|
||||||
socket.to(chat_id).emit('user_joined', {
|
socket.to(chat_id).emit('user_joined', {
|
||||||
chat_id,
|
chat_id,
|
||||||
user: userInfo?.profile,
|
user: userInfo?.profile,
|
||||||
timestamp: new Date()
|
timestamp: new Date()
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`User ${socket.user_id} joined chat ${chat_id}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Join chat error:', error);
|
|
||||||
socket.emit('error', { message: 'Failed to join chat' });
|
socket.emit('error', { message: 'Failed to join chat' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +208,7 @@ class ChatSocketHandler {
|
|||||||
timestamp: new Date()
|
timestamp: new Date()
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`User ${socket.user_id} left chat ${chat_id}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleSendMessage(socket, data) {
|
async handleSendMessage(socket, data) {
|
||||||
@@ -243,9 +255,7 @@ class ChatSocketHandler {
|
|||||||
timestamp: new Date()
|
timestamp: new Date()
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Message sent to chat ${chat_id} by user ${socket.user_id}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Send message error:', error);
|
|
||||||
socket.emit('error', { message: 'Failed to send message' });
|
socket.emit('error', { message: 'Failed to send message' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,7 +287,6 @@ class ChatSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDisconnect(socket) {
|
handleDisconnect(socket) {
|
||||||
console.log(`User disconnected: ${socket.id}`);
|
|
||||||
|
|
||||||
// Удаляем пользователя из всех комнат
|
// Удаляем пользователя из всех комнат
|
||||||
this.chatRooms.forEach((participants, chat_id) => {
|
this.chatRooms.forEach((participants, chat_id) => {
|
||||||
@@ -326,11 +335,119 @@ class ChatSocketHandler {
|
|||||||
timestamp: new Date()
|
timestamp: new Date()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Тестирование Real-time подписки
|
||||||
|
async testRealtimeConnection() {
|
||||||
|
try {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
if (!supabase) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем тестовый канал для проверки подключения
|
||||||
|
const testChannel = supabase
|
||||||
|
.channel('test_connection')
|
||||||
|
.subscribe((status, error) => {
|
||||||
|
if (status === 'SUBSCRIBED') {
|
||||||
|
// Отписываемся от тестового канала
|
||||||
|
setTimeout(() => {
|
||||||
|
testChannel.unsubscribe();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка статуса подписки
|
||||||
|
checkSubscriptionStatus() {
|
||||||
|
if (this.realtimeSubscription) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRealtimeSubscription() {
|
||||||
|
// Добавляем небольшую задержку, чтобы убедиться, что Supabase клиент инициализирован
|
||||||
|
setTimeout(() => {
|
||||||
|
this._doSetupRealtimeSubscription();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
_doSetupRealtimeSubscription() {
|
||||||
|
try {
|
||||||
|
const supabase = getSupabaseClient();
|
||||||
|
|
||||||
|
if (!supabase) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подписываемся на изменения в таблице messages
|
||||||
|
const subscription = supabase
|
||||||
|
.channel('messages_changes')
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'INSERT',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'messages'
|
||||||
|
},
|
||||||
|
async (payload) => {
|
||||||
|
try {
|
||||||
|
const newMessage = payload.new;
|
||||||
|
if (!newMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newMessage.chat_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем профиль пользователя
|
||||||
|
const { data: userProfile, error: profileError } = await supabase
|
||||||
|
.from('user_profiles')
|
||||||
|
.select('id, full_name, avatar_url')
|
||||||
|
.eq('id', newMessage.user_id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
// Объединяем сообщение с профилем
|
||||||
|
const messageWithProfile = {
|
||||||
|
...newMessage,
|
||||||
|
user_profiles: userProfile || null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Проверяем, есть ли участники в чате
|
||||||
|
const chatRoomParticipants = this.chatRooms.get(newMessage.chat_id);
|
||||||
|
|
||||||
|
// Отправляем сообщение через Socket.IO всем участникам чата
|
||||||
|
this.io.to(newMessage.chat_id).emit('new_message', {
|
||||||
|
message: messageWithProfile,
|
||||||
|
timestamp: new Date()
|
||||||
|
});
|
||||||
|
} catch (callbackError) {
|
||||||
|
// Ignore error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
// Сохраняем ссылку на подписку для возможности отписки
|
||||||
|
this.realtimeSubscription = subscription;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция инициализации Socket.IO для чатов
|
// Функция инициализации Socket.IO для чатов
|
||||||
function initializeChatSocket(io) {
|
function initializeChatSocket(io) {
|
||||||
const chatHandler = new ChatSocketHandler(io);
|
const chatHandler = new ChatSocketHandler(io);
|
||||||
|
|
||||||
return chatHandler;
|
return chatHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user