diff --git a/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx index 0e36cb4a1..8c1aaa381 100644 --- a/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx +++ b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx @@ -21,6 +21,13 @@ import type { AppRouter } from "@homarr/api"; import { clientApi } from "@homarr/api/client"; import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared"; import { env } from "@homarr/common/env"; +import { atom, useSetAtom } from "jotai"; + +interface WebSocketConnection { + state: 'connecting' | 'connected' | 'error' | 'closed' +} + +export const webSocketConnectionAtom = atom({ state: 'connecting' }); const getWebSocketProtocol = () => { // window is not defined on server side @@ -44,9 +51,27 @@ const constructWebsocketUrl = () => { return `${getWebSocketProtocol()}://${window.location.hostname}:${window.location.port}/websockets`; }; -const wsClient = createWSClient({ - url: constructWebsocketUrl(), -}); +let wsClient: WsClient | null; + +const createWsClientOrDefault = (setState: (connection: WebSocketConnection) => void) => { + if (wsClient) { + return wsClient; + } + + wsClient = createWSClient({ + url: constructWebsocketUrl(), + onOpen() { + setState({ state: "connected" }); + }, + onClose() { + setState({ state: "closed" }); + }, + onError() { + setState({ state: "error" }); + } + }); + return wsClient; +} export function TRPCReactProvider(props: PropsWithChildren) { const [queryClient] = useState( @@ -60,6 +85,8 @@ export function TRPCReactProvider(props: PropsWithChildren) { }), ); + const setWebSocketConnection = useSetAtom(webSocketConnectionAtom); + const [trpcClient] = useState(() => { return clientApi.createClient({ links: [ @@ -70,7 +97,7 @@ export function TRPCReactProvider(props: PropsWithChildren) { splitLink({ condition: ({ type }) => type === "subscription", true: wsLink({ - client: wsClient, + client: createWsClientOrDefault((state) => setWebSocketConnection(state)), transformer: superjson, }), false: splitLink({ @@ -106,7 +133,7 @@ export function TRPCReactProvider(props: PropsWithChildren) { {props.children} - + ); diff --git a/apps/nextjs/src/components/layout/banners/websocket-connection.tsx b/apps/nextjs/src/components/layout/banners/websocket-connection.tsx new file mode 100644 index 000000000..40ca5b1c8 --- /dev/null +++ b/apps/nextjs/src/components/layout/banners/websocket-connection.tsx @@ -0,0 +1,11 @@ +"use client"; + +import { useAtomValue } from "jotai"; +import { webSocketConnectionAtom } from "~/app/[locale]/_client-providers/trpc"; + +export const WebSocketConnectionBanner = () => { + const value = useAtomValue(webSocketConnectionAtom); + + console.log('socket connection', value) + return
dfheuihjfieufheuhfuefhuehfu
+}; \ No newline at end of file diff --git a/apps/nextjs/src/components/layout/shell.tsx b/apps/nextjs/src/components/layout/shell.tsx index b51cf3220..3f3095bfd 100644 --- a/apps/nextjs/src/components/layout/shell.tsx +++ b/apps/nextjs/src/components/layout/shell.tsx @@ -6,6 +6,7 @@ import { useAtomValue } from "jotai"; import { useOptionalBackgroundProps } from "./background"; import { navigationCollapsedAtom } from "./header/burger"; +import { webSocketConnectionAtom } from "~/app/[locale]/_client-providers/trpc"; interface ClientShellProps { hasHeader?: boolean; @@ -18,12 +19,14 @@ export const ClientShell = ({ children, }: PropsWithChildren) => { const collapsed = useAtomValue(navigationCollapsedAtom); - const backgroundProps = useOptionalBackgroundProps(); + const backgroundProps = useOptionalBackgroundProps() + + const value = useAtomValue(webSocketConnectionAtom); return (