import { Updater } from "../fun/domains/updater/state"; import { BasicFun, Fun } from "../fun/state"; export type LoadedAsyncState = { kind: "loaded"; value: a }; export type ReloadingAsyncState = { kind: "reloading"; value: a }; export type FailedReloadAsyncState = { kind: "failed-reload"; value: a; error: any; }; export type AsyncState = ( | (( | { kind: "unloaded" } | { kind: "loading" } | ReloadingAsyncState | { kind: "error"; error: any } ) & { failedLoadingAttempts: number }) | LoadedAsyncState | FailedReloadAsyncState ) & { map: (f: BasicFun) => AsyncState; getLoadingAttempts: (this: AsyncState) => number; }; export type HasValueAsyncState = AsyncState & { kind: "loaded" | "reloading" | "failed-reload"; }; export type IsLoadingAsyncState = AsyncState & { kind: "loading" | "reloading"; }; function map(this: AsyncState, f: BasicFun): AsyncState { return this.kind == "loaded" ? AsyncState.Default.loaded(f(this.value)) : this.kind == "unloaded" ? AsyncState.Default.unloaded() : this.kind == "error" ? AsyncState.Default.error() : this.kind == "loading" ? AsyncState.Default.loading() : AsyncState.Default.reloading(f(this.value)); } function getLoadingAttempts(this: AsyncState): number { return this.kind == "loaded" || this.kind == "failed-reload" ? 0 : this.failedLoadingAttempts; } export const AsyncState = { Default: { unloaded: (): AsyncState => ({ kind: "unloaded", map, getLoadingAttempts, failedLoadingAttempts: 0, }), loading: (): AsyncState => ({ kind: "loading", map, getLoadingAttempts, failedLoadingAttempts: 0, }), reloading: (value: a): AsyncState => ({ kind: "reloading", value, map, getLoadingAttempts, failedLoadingAttempts: 0, }), failedReload: (value: a, error?: any): AsyncState => ({ kind: "failed-reload", value, error, map, getLoadingAttempts, }), error: (value?: any): AsyncState => ({ kind: "error", map, error: value, getLoadingAttempts, failedLoadingAttempts: 0, }), loaded: (value: a): AsyncState => ({ kind: "loaded", value, map, getLoadingAttempts, }), }, Updaters: { failedLoadingAttempts: ( updateAttempts: Updater, ): Updater> => Updater((current) => current.kind == "loaded" || current.kind == "failed-reload" ? current : { ...current, failedLoadingAttempts: updateAttempts( current.failedLoadingAttempts, ), }, ), toUnloaded: (): Updater> => Updater((_) => ({ kind: "unloaded", map, getLoadingAttempts, failedLoadingAttempts: 0, })), toLoading: (): Updater> => Updater((current) => ({ kind: "loading", map, getLoadingAttempts, failedLoadingAttempts: current.getLoadingAttempts(), })), toReloading: (): Updater> => Updater((current) => current.kind == "loaded" || current.kind == "reloading" ? AsyncState.Default.reloading(current.value) : AsyncState.Updaters.toLoading()(current), ), toError: (error?: any): Updater> => Updater((current) => AsyncState.Operations.hasValue(current) ? { kind: "failed-reload", value: current.value, map, error, getLoadingAttempts, failedLoadingAttempts: current.getLoadingAttempts() + 1, } : { kind: "error", map, error, getLoadingAttempts, failedLoadingAttempts: current.getLoadingAttempts() + 1, }, ), toLoaded: (value: a): Updater> => Updater((_) => ({ kind: "loaded", value, map, getLoadingAttempts })), }, Operations: { map: (f: BasicFun): Fun, AsyncState> => Fun((_) => _.map(f)), status: (_: AsyncState): a | AsyncState["kind"] => _.kind == "loaded" || _.kind == "reloading" ? _.value : _.kind, hasValue: (_: AsyncState): _ is HasValueAsyncState => _.kind == "loaded" || _.kind == "reloading" || _.kind == "failed-reload", isLoading: (_: AsyncState): _ is IsLoadingAsyncState => _.kind == "loading" || _.kind == "reloading", errors: (_: AsyncState): Array => _.kind == "error" ? [_.error] : [], }, };