import { useAtom } from "jotai"; import { currentUserAtom } from "@/features/user/atoms/current-user-atom"; import React, { useEffect } from "react"; import useCurrentUser from "@/features/user/hooks/use-current-user"; import { useTranslation } from "react-i18next"; import { socketAtom } from "@/features/websocket/atoms/socket-atom.ts"; import { io } from "socket.io-client"; import { SOCKET_URL } from "@/features/websocket/types"; import { useQuerySubscription } from "@/features/websocket/use-query-subscription.ts"; import { useTreeSocket } from "@/features/websocket/use-tree-socket.ts"; import { useNotificationSocket } from "@/features/notification/hooks/use-notification-socket.ts"; import { useCollabToken } from "@/features/auth/queries/auth-query.tsx"; import { Error404 } from "@/components/ui/error-404.tsx"; import { queryClient } from "@/main.tsx"; export function UserProvider({ children }: React.PropsWithChildren) { const [, setCurrentUser] = useAtom(currentUserAtom); const { data, isLoading, error, isError } = useCurrentUser(); const { i18n } = useTranslation(); const [, setSocket] = useAtom(socketAtom); // fetch collab token on load const { data: collab } = useCollabToken(); useEffect(() => { if (isLoading || isError) { return; } const newSocket = io(SOCKET_URL, { transports: ["websocket"], withCredentials: true, }); // @ts-ignore setSocket(newSocket); // Distinguish the first connect from a reconnect so we only resync after a gap. let firstConnect = true; newSocket.on("connect", () => { console.log("ws connected"); if (!firstConnect) { // On RECONNECT (not the first connect) refetch the sidebar tree through the // authorized API so the view re-converges after a gap where ws events were // missed (wifi blip, laptop sleep). Invalidate both the root level and the // nested-page levels of every space tree. queryClient.invalidateQueries({ queryKey: ["root-sidebar-pages"] }); queryClient.invalidateQueries({ queryKey: ["sidebar-pages"] }); } firstConnect = false; }); return () => { console.log("ws disconnected"); newSocket.disconnect(); }; }, [isError, isLoading]); useQuerySubscription(); useTreeSocket(); useNotificationSocket(); useEffect(() => { if (data && data.user && data.workspace) { setCurrentUser(data); i18n.changeLanguage( data.user.locale === "en" ? "en-US" : data.user.locale, ); } }, [data, isLoading]); useEffect(() => { document.documentElement.lang = i18n.resolvedLanguage || i18n.language || "en-US"; }, [i18n.language, i18n.resolvedLanguage]); if (isLoading) return <>; if (isError && error?.["response"]?.status === 404) { return ; } if (error) { return <>; } return <>{children}; }