import * as R from 'ramda'; import { arr2obj, type } from '../js'; import { Type, Obj, Action } from '../models/models'; export interface MyAction { type: string; payload: T; }; // export type ActionCtor = Type> export interface ActionCtor { new(payload: T): MyAction; } export type ActionClass = Type; // TODO: swap this out for MyAction if I can get the type info -- requires heterogeneous map() export type ActionInfo = { actions: Obj, types: Obj }; export type ActionPair = { type: string, action: ActionCtor }; export let toPayload = (action: MyAction): T => action.payload; // create an action type string export let actionTp = R.curry((typeName: string, action: string): string => `[${typeName}] ${action}`); // type() // create the Complete string for an action let derivedAction: Obj<(action: string) => string> = R.map((tp: string) => (action: string) => `${action}_${tp}` )({ complete: 'Complete', fail: 'Failed', // wait, should these get a different payload type? }); // create an action class export function makeAction(typeName: string, actionName: string): ActionCtor { let tn = actionTp(typeName, actionName); return class implements Action { type = tn; constructor(public payload: T) {} }; } export let make = { actions: (name: string, actions: string[]) => >>arr2obj((action: string) => makeAction(name, action))(actions), // ^ where to get this T?: http://orizens.com/wp/topics/simple-action-creators-for-ngrxstore-in-angular-2/ types: (name: string, actions: string[]): Obj => R.fromPairs(actions.map((act: string): [string, string] => [act.toUpperCase(), actionTp(name, act)])), }; // create actions for given types and a name export let actionFactory = (name: string, actions: string[]) => R.map((fn: (name: string, actions: string[]) => any) => fn(name, actions))(make); // create actions for given types and a name export function effectfulAction(types: string[]): string[] { let reducer = (arr: string[], fn: (action: string) => string) => arr.concat(types.map(fn, types)); return R.values(derivedAction).reduce(reducer, []); } // create actions in batch export let mapSyncActions: (actionGroups: Obj) => Obj = R.mapObjIndexed(R.flip(actionFactory)); export let mapAsyncActions: (actionGroups: Obj) => Obj = R.mapObjIndexed((v: string[], k: string): ActionInfo => R.pipe(effectfulAction, R.curry(actionFactory)(k))(v)); // attempt to flip the structure to put actions and their types together // make an action pair (type string / Action ctor) // export let makeBoth = R.curry((name: string, action: string): ActionPair => ({ export let makeBoth = (name: string) => (action: string): ActionPair => ({ type: actionTp(name, action), action: makeAction(name, action), }); // make pairs in batch. missing types! export let pairFactory = (name: string, actions: string[]): ActionPair[] => actions.map(a => makeBoth(name)(a)); export let pairObjFactory = (name: string, actions: string[]) => >> arr2obj((a: string) => makeBoth(name)(a))(actions); /* let baseline = class { type = 'shoe'; constructor(public payload: string) {} }; let improved = makeAction('shoe', 'make'); // attempt to preserve typing through data structures: export interface Tp { tp: T; } let c = (): Type> => class implements Tp { tp: T; toString() { return this.tp.toString(); }; } let tpCls = c(); // v like makeAction with type moved to param for gen by het map let makeTypedAction = (typeName: string, actionName: string, cls: Type>): ActionCtor => makeAction(typeName, actionName); // usage: let typedAction = makeTypedAction('shoe', 'make', c()); // INFO LOSS: ?, ActionInfo with ActionClass rather than heterogeneous object of MyAction // create actions for given types and a name export let typedActionFactory = (name: string, actions: Obj>>): ActionInfo => ({ actions: R.mapObjIndexed((cls: Type>, action: string) => makeTypedAction(name, action, cls))(actions), types: R.fromPairs(R.keys(actions).map((act: string): [string, string] => [act.toUpperCase(), actionTp(name, act)])), }); // typedActionFactory('shoe', { make: c() }); // INFO LOSS: no way TS can follow key mutations export let mapKeys = R.curry((fn: (str: string) => string, obj: Object) => R.zipObj(R.map(fn, R.keys(obj)), R.values(obj))); // create actions for given types and a name // INFO LOSS: reduce, mapKeys, probably merge export function typedEffectfulAction(types: Obj>>): Obj>> { let reducer = (obj: Obj>>, fn: (action: string) => string) => R.merge(mapKeys(fn)(obj))(obj); return R.values(derivedAction).reduce(reducer, types); } // create actions in batch // INFO LOSS: heterogeneous map export let mapTypedSyncActions: (actionGroups: Obj>>>) => Obj = R.mapObjIndexed(R.flip(typedActionFactory)); export let mapTypedAsyncActions: (actionGroups: Obj>>>) => Obj = R.mapObjIndexed((v: Obj>>, k: string): ActionInfo => R.pipe(typedEffectfulAction, R.curry(typedActionFactory)(k))(v)); */