// eslint-disable-next-line @typescript-eslint/triple-slash-reference /// import { Adapter, AdapterCreator, AdapterEnhancer, AdapterOptions, AdapterState, ReadyState } from '../../types/AdapterTypes'; import Observable, { Observer } from 'core-js/features/observable'; import { IDirectLineActivity } from '../../types/DirectLineTypes'; import shareObservable from '../utils/shareObservable'; import { StateKey } from '../../models/ACSAdapterState'; import EventManager from '../../utils/EventManager'; import { Logger, LogLevel } from '../../log/Logger'; import { LogEvent } from '../../types/LogTypes'; export enum ConnectionStatus { Uninitialized = 0, Connecting = 1, Connected = 2, FailedToConnect = 4 } export interface IDirectLineJS { activity$: Observable; connectionStatus$: Observable; end: () => void; postActivity: (activity: IDirectLineActivity) => Observable; } function timeout(ms: number): Promise { return new Promise((resolve) => setTimeout(() => { resolve(); }, ms) ); } export default function exportDLJSInterface(): AdapterEnhancer< IDirectLineActivity, TAdapterState > { return (next: AdapterCreator) => (options: AdapterOptions): Adapter & IDirectLineJS => { const adapter = next(options); let connectionStatusObserver: Observer; adapter.setState(StateKey.WebChatStatus, ConnectionStatus.Uninitialized); adapter.addEventListener('open', async () => { const eventManager: EventManager = adapter.getState(StateKey.EventManager); Logger.logEvent(LogLevel.INFO, { Event: LogEvent.ADAPTER_STATE_UPDATE, Description: `Adapter state has been changed to open` }); if (!connectionStatusObserver) { let waitTime = 5; const connectionStatusObserverWaitTime = options?.webChatInitTimeout ? options?.webChatInitTimeout : 2000; while (!connectionStatusObserver && waitTime < connectionStatusObserverWaitTime) { await timeout(waitTime); waitTime = waitTime * 2; } if (!connectionStatusObserver) { Logger.logEvent(LogLevel.ERROR, { Event: LogEvent.WEBCHAT_SUBSCRIPTION_TIMEOUT, Description: `WebChat couldn't subscribe to connection status changes in ${connectionStatusObserverWaitTime} ms` }); eventManager.handleError(new Error('WebChat subscription timeout to connection status changes')); adapter.setState(StateKey.WebChatStatus, ConnectionStatus.FailedToConnect); } else { Logger.logEvent(LogLevel.INFO, { Event: LogEvent.WEBCHAT_SUBSCRIPTION_SUCCESS, Description: `WebChat subscribed to connection status changes in ${waitTime} ms` }); connectionStatusObserver.next(ConnectionStatus.Connected); adapter.setState(StateKey.WebChatStatus, ConnectionStatus.Connected); eventManager.raiseCustomEvent('webchat-status-connected', {}); } } else { Logger.logEvent(LogLevel.INFO, { Event: LogEvent.WEBCHAT_SUBSCRIPTION_SUCCESS, Description: `WebChat already connected` }); connectionStatusObserver.next(ConnectionStatus.Connected); adapter.setState(StateKey.WebChatStatus, ConnectionStatus.Connected); eventManager.raiseCustomEvent('webchat-status-connected', {}); } }); adapter.addEventListener('error', () => { Logger.logEvent(LogLevel.INFO, { Event: LogEvent.ADAPTER_STATE_UPDATE, Description: `Adapter state has been changed to error` }); const connectionStatus = adapter.getReadyState() === ReadyState.CLOSED ? ConnectionStatus.FailedToConnect : ConnectionStatus.Connecting; connectionStatusObserver.next(connectionStatus); adapter.setState(StateKey.WebChatStatus, connectionStatus); }); let firstConnect = true; return { ...adapter, activity$: shareObservable( new Observable((observer) => { const abortController = new AbortController(); (async function () { try { for await (const activity of adapter.activities({ signal: abortController.signal })) { observer.next(activity); } observer.complete(); } catch (error) { observer.error(error); } })(); if (!firstConnect) { adapter.setReadyState(ReadyState.CONNECTING); adapter.setReadyState(ReadyState.OPEN); adapter.setState(StateKey.Reconnect, true); } else { firstConnect = false; } return () => { abortController.abort(); }; }) ), connectionStatus$: shareObservable( new Observable((observer) => { observer.next(ConnectionStatus.Uninitialized); observer.next(ConnectionStatus.Connecting); connectionStatusObserver = observer; return () => { connectionStatusObserver = undefined; }; }) ), end: () => { adapter.close(); }, postActivity(activity: IDirectLineActivity) { return new Observable((observer) => { (async function () { await adapter.egress(activity, { progress: ({ id }: { id?: string }) => id && observer.next(id) }); //await adapter.ingress({...activity, id: uniqueId()}); //No need to call ingress as IC3 is providing echo back. If we need this for DL, a new class for IC3 should be created observer.complete(); })(); }); } }; }; }