import { StateUpdater, useContext, useEffect, useState } from 'preact/hooks' import { createContext } from 'preact' import { prependBaseUrl } from '../shared/utils' import { DatabaseConnection } from '../shared/database' import { ServerAsyncCallback } from '../shared/ssr' type Metadata = { title?: string description?: string } export const MetadataContext = createContext({}) export const ServerContext = createContext(false) export const ClientContext = createContext(false) export const DatabaseContext = createContext(null) type RefreshFunction = () => AbortController type HeuristicStateUpdater = StateUpdater type ResourceHookFunction = ( url: string | (() => string), initialValue: T, onValue?: (value: T) => void ) => [T, RefreshFunction, HeuristicStateUpdater] export const useResource: ResourceHookFunction = (url, initialValue, onValue) => { const [value, setValue] = useState(initialValue) function refresh() { const controller = new AbortController() const realUrl = typeof url === 'function' ? url() : url fetch(prependBaseUrl(realUrl), { signal: controller.signal }) .then(res => { if (res.ok) { return res.json() } else { return initialValue } }) .then(newValue => { onValue?.(newValue) setValue(newValue) }) return controller } useEffect(() => { const controller = refresh() return () => { controller.abort() } }, []) return [value, refresh, setValue] } type HeuristicListItemUpdater = (index: number, value: S | ((prevValue: S) => S)) => void export const useListResource = ( url: string | (() => string) ): [T[], RefreshFunction, HeuristicListItemUpdater, HeuristicStateUpdater] => { const [list, refreshList, setListHeuristic] = useResource(url, []) const setItemHeuristic: HeuristicListItemUpdater = (index, newValue) => { setListHeuristic(list => { const newList = [...list] // @ts-ignore newList[index] = typeof newValue === 'function' ? newValue(list[index]) : newValue return newList }) } return [list, refreshList, setItemHeuristic, setListHeuristic] } export const ServerAsyncCallbacksContext = createContext([]) export const useServerAsyncCallback = (fn: () => Promise) => { const registeredAsyncCallbacks = useContext(ServerAsyncCallbacksContext) registeredAsyncCallbacks.push(fn) }