You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

87 lines
2.7 KiB
TypeScript

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<Metadata>({})
export const ServerContext = createContext<boolean>(false)
export const ClientContext = createContext<boolean>(false)
export const DatabaseContext = createContext<DatabaseConnection | null>(null)
type RefreshFunction = () => AbortController
type HeuristicStateUpdater<S> = StateUpdater<S>
type ResourceHookFunction = <T>(
url: string | (() => string),
initialValue: T,
onValue?: (value: T) => void
) => [T, RefreshFunction, HeuristicStateUpdater<T>]
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<S> = (index: number, value: S | ((prevValue: S) => S)) => void
export const useListResource = <T,>(
url: string | (() => string)
): [T[], RefreshFunction, HeuristicListItemUpdater<T>, HeuristicStateUpdater<T[]>] => {
const [list, refreshList, setListHeuristic] = useResource<T[]>(url, [])
const setItemHeuristic: HeuristicListItemUpdater<T> = (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<ServerAsyncCallback[]>([])
export const useServerAsyncCallback = (fn: () => Promise<void>) => {
const registeredAsyncCallbacks = useContext(ServerAsyncCallbacksContext)
registeredAsyncCallbacks.push(fn)
}