import { onMounted, onUnmounted } from 'vue'; import { HubConnection, HubConnectionBuilder, HubConnectionState, IHttpConnectionOptions, LogLevel, } from '@microsoft/signalr'; import { useUserStoreWithOut } from '/@/store/modules/user'; import mitt from '/@/utils/mitt'; interface SignalROptions { serverUrl: string; autoStart?: boolean; useAccessToken?: boolean; automaticReconnect?: boolean; nextRetryDelayInMilliseconds?: number; } interface UseSignalR { lazyInit?: boolean; } export function useSignalR(options: UseSignalR & SignalROptions) { const emitter = mitt(); let connection: HubConnection | null = null; onMounted(() => { !options.lazyInit && init(options); }); onUnmounted(() => { if (connection !== null && connection.state === HubConnectionState.Connected) { stop(); } }); function init({ serverUrl, autoStart = false, useAccessToken = true, automaticReconnect = true, nextRetryDelayInMilliseconds = 60000, }: SignalROptions) { const httpOptions: IHttpConnectionOptions = {}; if (useAccessToken) { const userStore = useUserStoreWithOut(); const token = userStore.getToken; httpOptions.accessTokenFactory = () => token.startsWith('Bearer ') ? token.substring(7) : token; } const connectionBuilder = new HubConnectionBuilder() .withUrl(serverUrl, httpOptions) .configureLogging(LogLevel.Warning); if (automaticReconnect && nextRetryDelayInMilliseconds) { connectionBuilder.withAutomaticReconnect({ nextRetryDelayInMilliseconds: () => nextRetryDelayInMilliseconds, }); } connection = connectionBuilder.build(); if (autoStart) { start(); } } async function start(): Promise { if (connection == null) { return Promise.reject('unable to start, connection not initialized!'); } emitter.emit('signalR:beforeStart'); try { await connection.start(); emitter.emit('signalR:onStart'); } catch (error) { emitter.emit('signalR:onError', error); } } async function stop(): Promise { if (connection == null) { return Promise.reject('unable to stop, connection not initialized!'); } emitter.emit('signalR:beforeStop'); try { await connection.stop(); emitter.emit('signalR:onStop'); } catch (error) { emitter.emit('signalR:onError', error); } } function beforeStart(callback: (event?: T) => void) { emitter.on('signalR:beforeStart', callback); } function onStart(callback: (event?: T) => void) { emitter.on('signalR:onStart', callback); } function beforeStop(callback: (event?: T) => void) { emitter.on('signalR:beforeStop', callback); } function onStop(callback: (event?: T) => void) { emitter.on('signalR:onStop', callback); } function onError(callback: (error?: Error) => void) { emitter.on('signalR:onError', callback); } function on(methodName: string, newMethod: (...args: any[]) => void): void { connection?.on(methodName, newMethod); } function off(methodName: string, method: (...args: any[]) => void): void { connection?.off(methodName, method); } function onClose(callback: (error?: Error) => void): void { connection?.onclose(callback); } function send(methodName: string, ...args: any[]): Promise { if (connection == null) { return Promise.reject('unable to send message, connection not initialized!'); } return connection.send(methodName, ...args); } function invoke(methodName: string, ...args: any[]): Promise { if (connection == null) { return Promise.reject('unable to send message, connection not initialized!'); } return connection.invoke(methodName, ...args); } return { on, off, init, onError, onClose, beforeStart, onStart, beforeStop, onStop, send, invoke, start, stop, }; }