148 lines
4.6 KiB
JavaScript
Vendored
148 lines
4.6 KiB
JavaScript
Vendored
import { ref } from 'vue';
|
|
import { getConfig } from '../utils/config';
|
|
|
|
export const REALTIME_KEY = Symbol('commRealtime');
|
|
|
|
function isRealtimeEnabled() {
|
|
const app = window.APP || {};
|
|
return typeof window.Echo !== 'undefined'
|
|
&& (app.PUSHER_ENABLED === true || app.PUSHER_ENABLED === '1' || app.PUSHER_ENABLED === 1);
|
|
}
|
|
|
|
/**
|
|
* Shared Laravel Echo service: multi-room subscriptions, whispers, user channel.
|
|
*/
|
|
export function useRealtime() {
|
|
const connected = ref(false);
|
|
const config = getConfig();
|
|
const businessId = config.businessId;
|
|
const userId = config.userId;
|
|
/** @type {Map<number, { name: string, channel: object }>} */
|
|
const subscribedRooms = new Map();
|
|
let userChannelName = null;
|
|
let connectionBound = false;
|
|
const handlers = new Set();
|
|
|
|
function register(handler) {
|
|
handlers.add(handler);
|
|
return () => handlers.delete(handler);
|
|
}
|
|
|
|
function notify(method, ...args) {
|
|
handlers.forEach((handler) => handler[method]?.(...args));
|
|
}
|
|
|
|
function roomChannel(roomId) {
|
|
return `business.${businessId}.chat.${roomId}`;
|
|
}
|
|
|
|
function userChannel() {
|
|
return `business.${businessId}.user.${userId}`;
|
|
}
|
|
|
|
function subscribeRoom(roomId) {
|
|
if (!isRealtimeEnabled() || !roomId) return;
|
|
const id = parseInt(roomId, 10);
|
|
if (subscribedRooms.has(id)) return;
|
|
|
|
const channelName = roomChannel(id);
|
|
const channel = window.Echo.private(channelName);
|
|
|
|
channel
|
|
.listen('.message.sent', (payload) => notify('onMessage', payload, id))
|
|
.listen('.user.typing', (payload) => notify('onTyping', payload, id))
|
|
.listenForWhisper('typing', (payload) => {
|
|
notify('onTyping', { ...payload, room_id: payload.room_id ?? id }, id);
|
|
});
|
|
|
|
subscribedRooms.set(id, { name: channelName, channel });
|
|
}
|
|
|
|
function unsubscribeRoom(roomId) {
|
|
const id = parseInt(roomId, 10);
|
|
const entry = subscribedRooms.get(id);
|
|
if (!entry || typeof window.Echo === 'undefined') return;
|
|
window.Echo.leave(entry.name);
|
|
subscribedRooms.delete(id);
|
|
}
|
|
|
|
function syncRoomSubscriptions(roomIds) {
|
|
if (!isRealtimeEnabled()) return;
|
|
const next = new Set((roomIds || []).map((id) => parseInt(id, 10)));
|
|
subscribedRooms.forEach((_, id) => {
|
|
if (!next.has(id)) unsubscribeRoom(id);
|
|
});
|
|
next.forEach((id) => subscribeRoom(id));
|
|
}
|
|
|
|
function subscribeUserChannel() {
|
|
if (!isRealtimeEnabled() || userChannelName) return;
|
|
userChannelName = userChannel();
|
|
window.Echo.private(userChannelName)
|
|
.listen('.meeting.invite', (payload) => notify('onMeetingInvite', payload));
|
|
}
|
|
|
|
/** Send typing via WebSocket whisper (no HTTP). */
|
|
function whisperTyping(roomId, extra = {}) {
|
|
if (!isRealtimeEnabled()) return false;
|
|
const id = parseInt(roomId, 10);
|
|
const entry = subscribedRooms.get(id);
|
|
if (!entry?.channel?.whisper) return false;
|
|
|
|
entry.channel.whisper('typing', {
|
|
room_id: id,
|
|
user_id: userId,
|
|
user_name: config.userName || extra.user_name || 'User',
|
|
...extra,
|
|
});
|
|
return true;
|
|
}
|
|
|
|
function bindConnection() {
|
|
if (!isRealtimeEnabled() || connectionBound || !window.Echo?.connector?.pusher) return;
|
|
connectionBound = true;
|
|
const conn = window.Echo.connector.pusher.connection;
|
|
|
|
const onConnected = () => {
|
|
connected.value = true;
|
|
notify('onConnected');
|
|
};
|
|
const onDisconnected = () => {
|
|
connected.value = false;
|
|
notify('onDisconnected');
|
|
};
|
|
|
|
conn.bind('connected', onConnected);
|
|
conn.bind('disconnected', onDisconnected);
|
|
conn.bind('unavailable', onDisconnected);
|
|
conn.bind('failed', onDisconnected);
|
|
|
|
if (conn.state === 'connected') {
|
|
onConnected();
|
|
}
|
|
}
|
|
|
|
function cleanup() {
|
|
if (typeof window.Echo === 'undefined') return;
|
|
subscribedRooms.forEach((entry) => window.Echo.leave(entry.name));
|
|
subscribedRooms.clear();
|
|
if (userChannelName) {
|
|
window.Echo.leave(userChannelName);
|
|
userChannelName = null;
|
|
}
|
|
handlers.clear();
|
|
connectionBound = false;
|
|
}
|
|
|
|
return {
|
|
connected,
|
|
register,
|
|
isRealtimeEnabled,
|
|
bindConnection,
|
|
syncRoomSubscriptions,
|
|
subscribeUserChannel,
|
|
whisperTyping,
|
|
cleanup,
|
|
};
|
|
}
|