import React, { useEffect, useState } from "react"; import ErrorBoundary, { ErrorBoundaryContext } from "@redash/viz/lib/components/ErrorBoundary"; import { Auth } from "@/services/auth"; import { policy } from "@/services/policy"; import { CurrentRoute } from "@/services/routes"; import organizationStatus from "@/services/organizationStatus"; import DynamicComponent from "@/components/DynamicComponent"; import ApplicationLayout from "./ApplicationLayout"; import ErrorMessage from "./ErrorMessage"; export type UserSessionWrapperRenderChildrenProps

= { pageTitle?: string; onError: (error: Error) => void; } & P; export interface UserSessionWrapperProps

{ render: (props: UserSessionWrapperRenderChildrenProps

) => React.ReactNode; currentRoute: CurrentRoute

; bodyClass?: string; } // This wrapper modifies `route.render` function and instead of passing `currentRoute` passes an object // that contains: // - `currentRoute.routeParams` // - `pageTitle` field which is equal to `currentRoute.title` // - `onError` field which is a `handleError` method of nearest error boundary export function UserSessionWrapper

({ bodyClass, currentRoute, render }: UserSessionWrapperProps

) { const [isAuthenticated, setIsAuthenticated] = useState(!!Auth.isAuthenticated()); useEffect(() => { let isCancelled = false; Promise.all([Auth.requireSession(), organizationStatus.refresh(), policy.refresh()]) .then(() => { if (!isCancelled) { setIsAuthenticated(!!Auth.isAuthenticated()); } }) .catch(() => { if (!isCancelled) { setIsAuthenticated(false); } }); return () => { isCancelled = true; }; }, []); useEffect(() => { if (bodyClass) { document.body.classList.toggle(bodyClass, true); return () => { document.body.classList.toggle(bodyClass, false); }; } }, [bodyClass]); if (!isAuthenticated) { return null; } return ( {/* @ts-expect-error FIXME */} }> {({ handleError } /* : { handleError: UserSessionWrapperRenderChildrenProps

["onError"] } FIXME bring back type */) => render({ ...currentRoute.routeParams, pageTitle: currentRoute.title, onError: handleError }) } ); } export type RouteWithUserSessionOptions

= { render: (props: UserSessionWrapperRenderChildrenProps

) => React.ReactNode; bodyClass?: string; title: string; path: string; }; export const UserSessionWrapperDynamicComponentName = "UserSessionWrapper"; export default function routeWithUserSession

({ render: originalRender, bodyClass, ...rest }: RouteWithUserSessionOptions

) { return { ...rest, render: (currentRoute: CurrentRoute

) => { const props = { render: originalRender, bodyClass, currentRoute, }; return ( } /> ); }, }; }