{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "plugin-signalr", "title": "Real-time (SignalR)", "description": "SignalR HubConnection yönetimi, otomatik reconnect, MessageHubContext, useSignalR ve useMessageHub hook'ları.", "dependencies": [ "@microsoft/signalr@10.0.0" ], "files": [ { "path": "registry/tra-plugins/signalr/lib/signalr.ts", "content": "import {\n HubConnection,\n HubConnectionBuilder,\n HubConnectionState,\n LogLevel,\n} from \"@microsoft/signalr\";\n\nexport interface SignalRConfig {\n hubUrl: string;\n /** Bağlantı öncesi çağrılır; güncel token döndürür */\n getAccessToken?: () => string | null;\n onReconnecting?: (error?: Error) => void;\n onReconnected?: (connectionId?: string) => void;\n onClose?: (error?: Error) => void;\n}\n\n/**\n * Yeni bir SignalR HubConnection oluşturur.\n * Otomatik reconnect ve token desteğiyle gelir.\n */\nexport function createHubConnection(config: SignalRConfig): HubConnection {\n const builder = new HubConnectionBuilder()\n .withUrl(config.hubUrl, {\n accessTokenFactory: config.getAccessToken ? () => config.getAccessToken!() ?? \"\" : undefined,\n })\n .withAutomaticReconnect()\n .configureLogging(import.meta.env.DEV ? LogLevel.Information : LogLevel.Warning);\n\n const connection = builder.build();\n\n if (config.onReconnecting) connection.onreconnecting(config.onReconnecting);\n if (config.onReconnected) connection.onreconnected(config.onReconnected);\n if (config.onClose) connection.onclose(config.onClose);\n\n return connection;\n}\n\nexport async function startConnection(connection: HubConnection): Promise {\n if (connection.state === HubConnectionState.Disconnected) {\n await connection.start();\n }\n}\n\nexport async function stopConnection(connection: HubConnection): Promise {\n if (connection.state !== HubConnectionState.Disconnected) {\n await connection.stop();\n }\n}\n\nexport { HubConnectionState };\n", "type": "registry:file", "target": "src/lib/signalr.ts" }, { "path": "registry/tra-plugins/signalr/contexts/messageHub/MessageHubContext.tsx", "content": "import { createContext, useContext } from \"react\";\nimport type { HubConnection } from \"@microsoft/signalr\";\n\nexport interface MessageHubContextValue {\n connection: HubConnection | null;\n isConnected: boolean;\n connectionId: string | null;\n}\n\nexport const MessageHubContext = createContext({\n connection: null,\n isConnected: false,\n connectionId: null,\n});\n\nexport function useMessageHubContext() {\n return useContext(MessageHubContext);\n}\n", "type": "registry:file", "target": "src/contexts/messageHub/MessageHubContext.tsx" }, { "path": "registry/tra-plugins/signalr/contexts/messageHub/MessageHubProvider.tsx", "content": "import { useEffect, useRef, useState } from \"react\";\nimport { HubConnectionState } from \"@microsoft/signalr\";\nimport { MessageHubContext } from \"./MessageHubContext\";\nimport { createHubConnection, startConnection, stopConnection } from \"@/lib/signalr\";\n\ninterface MessageHubProviderProps {\n children: React.ReactNode;\n /** Hub URL — örn. import.meta.env.VITE_HUB_URL */\n hubUrl: string;\n /** Opsiyonel: güncel access token döndüren fonksiyon */\n getAccessToken?: () => string | null;\n /** false ise bağlantı başlatılmaz (kullanıcı giriş yapmamış vb.) */\n enabled?: boolean;\n}\n\nexport function MessageHubProvider({\n children,\n hubUrl,\n getAccessToken,\n enabled = true,\n}: MessageHubProviderProps) {\n const connectionRef = useRef(createHubConnection({ hubUrl, getAccessToken }));\n const [isConnected, setIsConnected] = useState(false);\n const [connectionId, setConnectionId] = useState(null);\n\n useEffect(() => {\n if (!enabled) return;\n\n const conn = connectionRef.current;\n\n conn.onreconnected((id) => {\n setIsConnected(true);\n setConnectionId(id ?? null);\n });\n\n conn.onreconnecting(() => {\n setIsConnected(false);\n });\n\n conn.onclose(() => {\n setIsConnected(false);\n setConnectionId(null);\n });\n\n startConnection(conn).then(() => {\n setIsConnected(conn.state === HubConnectionState.Connected);\n setConnectionId(conn.connectionId);\n });\n\n return () => {\n stopConnection(conn);\n };\n }, [enabled, hubUrl]);\n\n return (\n \n {children}\n \n );\n}\n", "type": "registry:file", "target": "src/contexts/messageHub/MessageHubProvider.tsx" }, { "path": "registry/tra-plugins/signalr/hooks/useSignalR.ts", "content": "import { useEffect, useCallback } from \"react\";\nimport { useMessageHubContext } from \"@/contexts/messageHub/MessageHubContext\";\n\n/**\n * SignalR hub'ına event listener bağlar.\n *\n * @example\n * useSignalR('ReceiveNotification', (data) => {\n * console.log('Yeni bildirim:', data);\n * });\n */\nexport function useSignalR(eventName: string, handler: (data: T) => void) {\n const { connection } = useMessageHubContext();\n\n const stableHandler = useCallback(handler, []);\n\n useEffect(() => {\n if (!connection) return;\n\n connection.on(eventName, stableHandler);\n\n return () => {\n connection.off(eventName, stableHandler);\n };\n }, [connection, eventName, stableHandler]);\n}\n", "type": "registry:file", "target": "src/hooks/useSignalR.ts" }, { "path": "registry/tra-plugins/signalr/hooks/useMessageHub.ts", "content": "import { useMessageHubContext } from \"@/contexts/messageHub/MessageHubContext\";\n\n/**\n * Hub bağlantı durumunu ve invoke metodunu döner.\n *\n * @example\n * const { isConnected, invoke } = useMessageHub();\n * await invoke('SendMessage', { text: 'Merhaba' });\n */\nexport function useMessageHub() {\n const { connection, isConnected, connectionId } = useMessageHubContext();\n\n const invoke = async (methodName: string, ...args: unknown[]): Promise => {\n if (!connection) throw new Error(\"SignalR bağlantısı mevcut değil.\");\n return connection.invoke(methodName, ...args);\n };\n\n return { isConnected, connectionId, invoke };\n}\n", "type": "registry:file", "target": "src/hooks/useMessageHub.ts" } ], "type": "registry:block" }