import { Prism } from 'monocle-ts'
import { iso, Newtype, prism } from 'newtype-ts'
import { Predicate, unsafeCoerce } from 'fp-ts/lib/function'
import { isUndefined } from './typeGuards'
// TODO: Use `SimSpaceNewtype` instead of `Newtype` if we ever make use of this
export interface Optimistic
extends Newtype<
{
readonly Optimistic: unique symbol
phantom: A
},
string
> {}
export interface Request
extends Newtype<
{
readonly Request: unique symbol
phantom: A
},
string
> {}
export interface Failure
extends Newtype<
{
readonly Failure: unique symbol
phantom: A
},
string
> {}
export interface Success
extends Newtype<
{
readonly Success: unique symbol
phantom: A
},
string
> {}
export type ApiActionType =
| Optimistic
| Request
| Failure
| Success
// this is safe, because we know ApiActionType is a union of newtypes,
// and we know each of those newtypes is actually a string at runtime
export const coerceApiActionTypeToString = (
x: ApiActionType,
): string => unsafeCoerce, string>(x)
export type ApiActionTypes = {
types: {
optimistic: Optimistic
request: Request
failure: Failure
success: Success
}
guards: {
isOptimistic: (x: ApiActionType) => x is Optimistic
isRequest: (x: ApiActionType) => x is Request
isFailure: (x: ApiActionType) => x is Failure
isSuccess: (x: ApiActionType) => x is Success
}
prisms: {
optimistic: Prism>
request: Prism>
failure: Prism>
success: Prism>
}
}
export const mkApiActionTypes = (
pathPrefix: A,
): ApiActionTypes => {
const isoOptimistic = iso>()
const isoRequest = iso>()
const isoFailure = iso>()
const isoSuccess = iso>()
const pathOptimistic = `${pathPrefix}_OPTIMISTIC`
const optimistic = isoOptimistic.wrap(pathOptimistic)
const pathRequest = `${pathPrefix}_REQUEST`
const request = isoRequest.wrap(pathRequest)
const pathFailure = `${pathPrefix}_FAILURE`
const failure = isoFailure.wrap(pathFailure)
const pathSuccess = `${pathPrefix}_SUCCESS`
const success = isoSuccess.wrap(pathSuccess)
const isOptimistic = (x: ApiActionType): x is Optimistic =>
coerceApiActionTypeToString(x) === pathOptimistic
const isRequest = (x: ApiActionType): x is Request =>
coerceApiActionTypeToString(x) === pathRequest
const isFailure = (x: ApiActionType): x is Failure =>
coerceApiActionTypeToString(x) === pathFailure
const isSuccess = (x: ApiActionType): x is Success =>
coerceApiActionTypeToString(x) === pathSuccess
const isOptimistic_: Predicate = x => x === pathOptimistic
const isRequest_: Predicate = x => x === pathRequest
const isFailure_: Predicate = x => x === pathFailure
const isSuccess_: Predicate = x => x === pathSuccess
const prismOptimistic = prism>(isOptimistic_)
const prismRequest = prism>(isRequest_)
const prismFailure = prism>(isFailure_)
const prismSuccess = prism>(isSuccess_)
return {
types: {
optimistic,
request,
failure,
success,
},
guards: {
isOptimistic,
isRequest,
isFailure,
isSuccess,
},
prisms: {
optimistic: prismOptimistic,
request: prismRequest,
failure: prismFailure,
success: prismSuccess,
},
}
}
export type GeneratedApiAction = Payload extends undefined
? Meta extends undefined
? { type: Type }
: { type: Type; meta: Meta }
: Meta extends undefined
? { type: Type; payload: Payload }
: { type: Type; payload: Payload; meta: Meta }
export const mkApiAction = <
A extends string,
Type extends ApiActionType,
Payload = undefined,
Meta = undefined
>(
type: Type,
payload?: Payload,
meta?: Meta,
): GeneratedApiAction =>
(isUndefined(payload)
? isUndefined(meta)
? { type }
: { type, meta }
: isUndefined(meta)
? { type, payload }
: { type, payload, meta }) as GeneratedApiAction