Files
kupshop/admin/static/sections-autocomplete/utils.ts
2025-08-02 16:30:27 +02:00

97 lines
2.7 KiB
TypeScript

import { createContext, useContext, useEffect, useState } from 'react';
import { useCache } from './cache';
import type { WidgetProps } from './index';
type UseQueryOptions = {
cache: boolean;
cacheKey?: string;
};
export function useQuery<Result>(endpoint: string | undefined, options: UseQueryOptions = { cache: true }) {
const [cache, setCache] = useCache();
const [result, setResult] = useState<Result | undefined>(undefined);
const [fetching, setFetching] = useState(false);
const [error, setError] = useState<Error | undefined>(undefined);
useEffect(() => {
let abortController: AbortController | undefined = undefined;
async function handleQuery() {
if (typeof endpoint === 'undefined') {
return;
}
const cacheKey = options.cacheKey ?? endpoint;
if (options.cache && cacheKey in cache._query) {
setResult(cache._query[cacheKey] as Result);
return;
}
abortController = new AbortController();
setFetching(true);
try {
const url = new URL(endpoint, `${window.location.protocol}//${window.location.host}`);
const response = await fetch(url.href, { signal: abortController.signal });
if (!response.ok || response.status !== 200) {
throw new Error('Failed to fetch sections');
}
const data = await response.json();
setResult(data);
setError(undefined);
if (cache) {
setCache(prev => ({
...prev,
_query: {
...prev._query,
[cacheKey]: data,
},
}));
}
} catch (e: unknown) {
if (!(e instanceof Error)) {
e = new Error(String(e));
}
if ((e as Error).name === 'AbortError') {
return;
}
setError(e as Error);
console.error(e);
} finally {
setFetching(false);
}
}
void handleQuery();
return () => {
if (abortController) {
abortController.abort();
}
};
}, [endpoint]);
function clear() {
setResult(undefined);
setFetching(false);
setError(undefined);
}
return { data: result, isFetching: fetching, error, clear };
}
type WidgetPropsContextValue = WidgetProps & {
hide: () => void;
show: () => void;
};
const WidgetPropsContext = createContext<WidgetPropsContextValue | undefined>(undefined);
export const WidgetPropsProvider = WidgetPropsContext.Provider;
export function useWidgetProps() {
return useContext(WidgetPropsContext) as WidgetPropsContextValue;
}