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] : [],
},
};