Files
Stirling-PDF/frontend/src/proprietary/contexts/LicenseContext.tsx
T
James BruntonandGitHub 8ab060a4be Prettier 2: Electric Boogaloo (#6113)
# Description of Changes
When I added Prettier formatting in #6052, my aim was to use just the
default settings in Prettier. Turns out, Prettier looks _really hard_
for any config files if it's not explicitly given one, which means that
if a developer has some sort of Prettier config file lying around on
their system, Prettier might find it and use it. Also, Prettier changes
its defaults based on stuff in `.editorconfig` without any good way of
disabling that behaviour explicitly in its config file.

To solve both of these issues, I've introduced a `.prettierrc` file
which sets Prettier's defaults explicitly, and then reformatted all our
code _again_ in Prettier's actual default settings. This should achieve
the aim of #6052 and remove the possibility for it breaking on different
dev computers.
2026-04-17 09:50:16 +00:00

151 lines
4.2 KiB
TypeScript

import React, {
createContext,
useContext,
useState,
useCallback,
useEffect,
useMemo,
useRef,
ReactNode,
} from "react";
import { useLocation } from "react-router-dom";
import licenseService, { LicenseInfo } from "@app/services/licenseService";
import { useAppConfig } from "@app/contexts/AppConfigContext";
import { getSimulatedLicenseInfo } from "@app/testing/serverExperienceSimulations";
interface LicenseContextValue {
licenseInfo: LicenseInfo | null;
loading: boolean;
error: string | null;
refetchLicense: () => Promise<void>;
}
const LicenseContext = createContext<LicenseContextValue | undefined>(
undefined,
);
interface LicenseProviderProps {
children: ReactNode;
}
export const LicenseProvider: React.FC<LicenseProviderProps> = ({
children,
}) => {
const { config } = useAppConfig();
const location = useLocation();
const configRef = useRef(config);
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
// Keep ref updated with latest config
useEffect(() => {
configRef.current = config;
}, [config]);
const refetchLicense = useCallback(async () => {
// Wait for config to load if it's not available yet
let currentConfig = configRef.current;
if (!currentConfig) {
console.log("[LicenseContext] Config not loaded yet, waiting...");
// OPTIMIZATION: Reduced from 5s to 1s - config should load quickly
const maxWait = 1000;
const startTime = Date.now();
while (!configRef.current && Date.now() - startTime < maxWait) {
await new Promise((resolve) => setTimeout(resolve, 100));
currentConfig = configRef.current;
}
if (!currentConfig) {
console.error("[LicenseContext] Config failed to load after waiting");
setLoading(false);
return;
}
}
// Only fetch license info if user is an admin
if (!currentConfig.isAdmin) {
console.log(
"[LicenseContext] User is not an admin, skipping license fetch",
);
setLoading(false);
return;
}
console.log("[LicenseContext] Fetching license info");
try {
const testInfo = getSimulatedLicenseInfo();
if (testInfo) {
setLicenseInfo(testInfo);
setLoading(false);
setError(null);
return;
}
setLoading(true);
setError(null);
const info = await licenseService.getLicenseInfo();
setLicenseInfo(info);
} catch (err) {
const errorMessage =
err instanceof Error ? err.message : "Failed to fetch license info";
console.error("Error fetching license info:", errorMessage);
setError(errorMessage);
setLicenseInfo(null);
} finally {
setLoading(false);
}
}, []);
// Fetch license info when config changes (only if user is admin)
useEffect(() => {
// CRITICAL FIX: Skip license fetch on auth routes to prevent race conditions
// during SAML/OAuth callback processing. License isn't needed until user
// is authenticated and navigates to main app.
const isAuthRoute =
location.pathname === "/login" ||
location.pathname === "/signup" ||
location.pathname === "/auth/callback" ||
location.pathname.startsWith("/invite/");
if (isAuthRoute) {
console.log("[LicenseContext] On auth route, skipping license fetch");
setLoading(false);
return;
}
if (config) {
refetchLicense();
}
}, [config, refetchLicense, location.pathname]);
const contextValue: LicenseContextValue = useMemo(
() => ({
licenseInfo,
loading,
error,
refetchLicense,
}),
[licenseInfo, loading, error, refetchLicense],
);
return (
<LicenseContext.Provider value={contextValue}>
{children}
</LicenseContext.Provider>
);
};
export const useOptionalLicense = (): LicenseContextValue | undefined => {
return useContext(LicenseContext);
};
export const useLicense = (): LicenseContextValue => {
const context = useContext(LicenseContext);
if (!context) {
throw new Error("useLicense must be used within LicenseProvider");
}
return context;
};