import { type IOmnichannelAgent, type OmichannelRoutingConfig, OmnichannelSortingMechanismSettingType, LivechatInquiryStatus, } from '@rocket.chat/core-typings'; import { useSafely } from '@rocket.chat/fuselage-hooks'; import { createComparatorFromSort } from '@rocket.chat/mongo-adapter'; import { useUser, useSetting, usePermission, useEndpoint, useStream, useCustomSound } from '@rocket.chat/ui-contexts'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import type { ReactNode } from 'react'; import { useState, useEffect, useMemo, memo, useRef } from 'react'; import { useShallow } from 'zustand/shallow'; import { getOmniChatSortQuery } from '../../app/livechat/lib/inquiries'; import { ClientLogger } from '../../lib/ClientLogger'; import type { OmnichannelContextValue } from '../contexts/OmnichannelContext'; import { OmnichannelContext } from '../contexts/OmnichannelContext'; import { useHasLicenseModule } from '../hooks/useHasLicenseModule'; import { useLivechatInquiryStore } from '../hooks/useLivechatInquiryStore'; import { useOmnichannelContinuousSoundNotification } from '../hooks/useOmnichannelContinuousSoundNotification'; import { useShouldPreventAction } from '../hooks/useShouldPreventAction'; import { initializeLivechatInquiryStream } from '../lib/omnichannel/queueManager'; const emptyContextValue: OmnichannelContextValue = { inquiries: { enabled: false }, enabled: false, isEnterprise: false, agentAvailable: false, showOmnichannelQueueLink: false, isOverMacLimit: false, livechatPriorities: { enabled: false, data: [], isLoading: false, isError: false, }, }; export type OmnichannelProviderProps = { children?: ReactNode; }; const OmnichannelProvider = ({ children }: OmnichannelProviderProps) => { const omniChannelEnabled = useSetting('Livechat_enabled', true); const omnichannelRouting = useSetting('Livechat_Routing_Method', 'Auto_Selection'); const showOmnichannelQueueLink = useSetting('Livechat_show_queue_list_link', false); const omnichannelPoolMaxIncoming = useSetting('Livechat_guest_pool_max_number_incoming_livechats_displayed', 0); const omnichannelSortingMechanism = useSetting( 'Omnichannel_sorting_mechanism', OmnichannelSortingMechanismSettingType.Timestamp, ); const lastQueueSize = useRef(0); const loggerRef = useRef(new ClientLogger('OmnichannelProvider')); const hasAccess = usePermission('view-l-room'); const canViewOmnichannelQueue = usePermission('view-livechat-queue'); const user = useUser() as IOmnichannelAgent; const agentAvailable = user?.statusLivechat === 'available'; const getRoutingConfig = useEndpoint('GET', '/v1/livechat/config/routing'); const [routeConfig, setRouteConfig] = useSafely(useState(undefined)); const accessible = hasAccess && omniChannelEnabled; const { data: isEnterprise = false } = useHasLicenseModule('livechat-enterprise'); const getPriorities = useEndpoint('GET', '/v1/livechat/priorities'); const subscribe = useStream('notify-logged'); const queryClient = useQueryClient(); const isPrioritiesEnabled = isEnterprise && accessible; const enabled = accessible && !!user && !!routeConfig; const { notificationSounds } = useCustomSound(); const { data: { priorities = [] } = {}, isLoading: isLoadingPriorities, isError: isErrorPriorities, } = useQuery({ queryKey: ['/v1/livechat/priorities'], queryFn: () => getPriorities({ sort: JSON.stringify({ sortItem: 1 }) }), staleTime: Infinity, enabled: isPrioritiesEnabled, }); const isOverMacLimit = useShouldPreventAction('monthlyActiveContacts'); useEffect(() => { if (!isPrioritiesEnabled) { return; } return subscribe('omnichannel.priority-changed', () => { queryClient.invalidateQueries({ queryKey: ['/v1/livechat/priorities'], }); }); }, [isPrioritiesEnabled, queryClient, subscribe]); useEffect(() => { if (!accessible) { return; } const update = async (): Promise => { try { const { config } = await getRoutingConfig(); setRouteConfig(config); } catch (error) { loggerRef.current.error(`update() error in routeConfig ${error}`); } }; if (omnichannelRouting || !omnichannelRouting) { update(); } }, [accessible, getRoutingConfig, omnichannelRouting, setRouteConfig]); const manuallySelected = enabled && canViewOmnichannelQueue && !!routeConfig && routeConfig.showQueue && !routeConfig.autoAssignAgent && agentAvailable; const streamNotifyUser = useStream('notify-user'); useEffect(() => { if (!manuallySelected) { return; } const handleDepartmentAgentData = (): void => { initializeLivechatInquiryStream(user?._id); }; initializeLivechatInquiryStream(user?._id); if (!user?._id) { return; } return streamNotifyUser(`${user._id}/departmentAgentData`, handleDepartmentAgentData); }, [manuallySelected, streamNotifyUser, user?._id]); const queue = useLivechatInquiryStore( useShallow((state) => { if (!manuallySelected) { return undefined; } return state.records .filter((inquiry) => inquiry.status === LivechatInquiryStatus.QUEUED) .sort(createComparatorFromSort(getOmniChatSortQuery(omnichannelSortingMechanism))) .slice(...(omnichannelPoolMaxIncoming > 0 ? [0, omnichannelPoolMaxIncoming] : [])); }), ); useEffect(() => { if (lastQueueSize.current < (queue?.length ?? 0)) { notificationSounds.playNewRoom(); } lastQueueSize.current = queue?.length ?? 0; return () => { notificationSounds.stopNewRoom(); }; }, [notificationSounds, queue?.length]); useOmnichannelContinuousSoundNotification(queue ?? []); const contextValue = useMemo(() => { if (!enabled) { return emptyContextValue; } const livechatPriorities = { enabled: isPrioritiesEnabled, data: priorities, isLoading: isLoadingPriorities, isError: isErrorPriorities, }; if (!manuallySelected) { return { ...emptyContextValue, enabled: true, isEnterprise, agentAvailable, routeConfig, livechatPriorities, isOverMacLimit, }; } return { ...emptyContextValue, enabled: true, isEnterprise, agentAvailable, routeConfig, inquiries: queue ? { enabled: true, queue, } : { enabled: false }, showOmnichannelQueueLink: showOmnichannelQueueLink && !!agentAvailable, livechatPriorities, isOverMacLimit, }; }, [ enabled, isPrioritiesEnabled, priorities, isLoadingPriorities, isErrorPriorities, manuallySelected, isEnterprise, agentAvailable, routeConfig, queue, showOmnichannelQueueLink, isOverMacLimit, ]); return {children}; }; export default memo(OmnichannelProvider);