import type { Loadable, ReadableFamilyToken, ReadableToken, ViewOf, } from "atom.io" import type { Canonical } from "atom.io/foundations/canonical" import { findInStore, type ReadableState, withdraw } from "atom.io/internal" import { useContext } from "solid-js" import { StoreContext } from "./store-context.ts" import { useO } from "./use-o.ts" export function useLoadable( token: ReadableToken, any, E>, ): () => `LOADING` | { loading: boolean; value: ViewOf } export function useLoadable( token: ReadableFamilyToken, K, E>, key: NoInfer, ): () => `LOADING` | { loading: boolean; value: ViewOf } export function useLoadable( token: ReadableToken, any, E>, fallback: F, ): () => { loading: boolean; value: ViewOf; error?: E } export function useLoadable( token: ReadableFamilyToken, K, E>, key: NoInfer, fallback: F, ): () => { loading: boolean; value: ViewOf; error?: E } export function useLoadable( ...params: | readonly [ReadableFamilyToken, Canonical, unknown] | readonly [ReadableFamilyToken, Canonical] | readonly [ReadableToken, unknown] | readonly [ReadableToken] ): () => `LOADING` | { loading: boolean; value: unknown; error?: unknown } { const store = useContext(StoreContext) let readValue: () => unknown let state: ReadableState let fallback: unknown const [token] = params let key: Canonical switch (token.type) { case `atom`: case `mutable_atom`: case `readonly_held_selector`: case `readonly_pure_selector`: case `writable_held_selector`: case `writable_pure_selector`: readValue = useO(token) state = withdraw(store, token) fallback = params[1] break case `atom_family`: case `mutable_atom_family`: case `readonly_held_selector_family`: case `readonly_pure_selector_family`: case `writable_held_selector_family`: case `writable_pure_selector_family`: key = params[1] as Canonical readValue = useO(token, key) state = withdraw(store, findInStore(store, token, key)) fallback = params[2] } const hasFallback = fallback !== undefined let wrapper: { loading: boolean value: unknown error?: unknown } = { loading: false, value: null } let lastLoaded = fallback return () => { const value = readValue() const isErr = `catch` in state && state.catch.some((E) => value instanceof E) lastLoaded ??= value instanceof Promise ? `LOADING` : value const wasErr = `catch` in state && state.catch.some((E) => lastLoaded instanceof E) if (value instanceof Promise) { if (lastLoaded === `LOADING`) { return `LOADING` } if (wasErr && hasFallback) { if ( wrapper.loading === true && wrapper.value === fallback && wrapper.error === lastLoaded ) { return wrapper } wrapper = { loading: true, value: fallback, error: lastLoaded, } } else { if ( wrapper.loading === true && wrapper.value === lastLoaded && wrapper.error === undefined ) { return wrapper } wrapper = { loading: true, value: lastLoaded } } return wrapper } lastLoaded = value if (wrapper.loading === true) { if (isErr && hasFallback) { wrapper = { loading: false, value: fallback, error: value, } } else { wrapper = { loading: false, value } } } else if (isErr && hasFallback) { wrapper.loading = false wrapper.value = fallback wrapper.error = value } else { wrapper.loading = false wrapper.value = value delete wrapper.error } return wrapper } }