var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { jsx as _jsx } from "react/jsx-runtime";
/* eslint-disable react/no-unused-prop-types */
import * as React from 'react';
import { Await, useLoaderData, useMatches } from 'react-router';
import {} from 'recoil';
import { DefaultFallbackStatic } from '../components/fallback/default';
import { depsRegistry } from '../shared';
import {} from '../types';
import DynamicComponent from '../shared/dynamic-component/dynamic-component';
import { useExecuteAction } from '../shared/interactivity/use-action';
import { useWindowTitle } from '../shared/utils';
import { fetchRouteData, getFromPreloadCache } from './fetching';
/**
 * Determines whether to hold navigation (await promise) or let it suspend.
 * Currently holds navigation unless the page defines an explicit fallback which we can show immediately.
 */
function shouldHoldPromise(route) {
    return !route.fallback;
}
export function createRouteLoader(route, snapshot) {
    return function loader(_a) {
        return __awaiter(this, arguments, void 0, function* ({ request: loaderRequest, params }) {
            // Check preload cache first
            let result = getFromPreloadCache(route.id, params);
            // Not in cache, fetch fresh data
            // Note: loader-initiated requests are NOT cached
            if (!result) {
                const snapshotInstance = snapshot();
                const release = snapshotInstance.retain();
                result = fetchRouteData(route, params, snapshotInstance, loaderRequest.signal).finally(() => {
                    // ensure that after the fetcher completes, the snapshot is released
                    release();
                });
            }
            if (shouldHoldPromise(route)) {
                result = yield result;
            }
            return { data: result, fallback: route.fallback };
        });
    };
}
function Content({ route, on_load, template, py_components, derived_variables, }) {
    const executeAction = useExecuteAction();
    const [isLoading, setIsLoading] = React.useState(on_load.length > 0 || py_components.length > 0 || derived_variables.length > 0);
    React.useLayoutEffect(() => {
        if (on_load.length === 0 && py_components.length === 0 && derived_variables.length === 0) {
            return;
        }
        // put the pre-computed promises into the deps registry
        for (const resultHandle of [...py_components, ...derived_variables]) {
            depsRegistry.set(resultHandle.result.depsKey, {
                args: resultHandle.result.relevantValues,
                result: resultHandle.handle,
            });
        }
        let cancelled = false;
        const result = executeAction(on_load);
        // if result is a promise, handle async case
        if (result instanceof Promise) {
            result
                .then(() => {
                if (!cancelled) {
                    setIsLoading(false);
                }
            })
                .catch((error) => {
                if (!cancelled) {
                    // eslint-disable-next-line no-console
                    console.error('Error executing on_load actions:', error);
                    setIsLoading(false);
                }
            });
        }
        else {
            // synchronous case - immediately stop loading
            setIsLoading(false);
        }
        return () => {
            cancelled = true;
        };
    }, [route.id, on_load, executeAction, py_components, derived_variables]);
    // only sync title for the most exact route
    const matches = useMatches();
    const isMostExact = matches.at(-1).id === route.id;
    useWindowTitle(route.name, isMostExact);
    if (isLoading) {
        return _jsx(DefaultFallbackStatic, {});
    }
    return _jsx(DynamicComponent, { component: template });
}
function RouteContent(props) {
    const { data, fallback } = useLoaderData();
    const fallbackComponent = React.useMemo(() => (fallback ? _jsx(DynamicComponent, { component: fallback }) : _jsx(DefaultFallbackStatic, {})), [fallback]);
    if (data instanceof Promise) {
        return (_jsx(React.Suspense, { fallback: fallbackComponent, children: _jsx(Await, { resolve: data, children: (resolved) => _jsx(Content, Object.assign({}, resolved, { route: props.route })) }) }));
    }
    return _jsx(Content, Object.assign({}, data, { route: props.route }));
}
export default RouteContent;
//# sourceMappingURL=route-content.js.map