Home Reference Source

src/actions.js

import angular from 'angular';

import {Annotation} from './annotation';
import {Annotations} from './annotations';

import {addStaticGetterObjectMember, addStaticGetter} from './utils';


export class ActionsAnnotation extends Annotation {
  get serviceName() {
    const name = this.name;
    return `${name[0].toUpperCase()}${name.slice(1)}Actions`;
  }

  getInjectionTokens() {
    return [
      'LuxyFlux',
      'LuxyFluxActionCreators',
      'ApplicationDispatcher'
    ].concat(super.getInjectionTokens());
  }

  get factoryFn() {
    const TargetCls = this.targetCls;
    const annotation = this;

    return function(LuxyFlux, LuxyFluxActionCreators, ApplicationDispatcher) {
      const injected = Array.from(arguments).slice(3);
      const instance = new TargetCls(...injected);

      annotation.applyInjectionBindings(instance, injected);
      annotation.applyDecorators(instance);

      return LuxyFlux.createActions({
        dispatcher: ApplicationDispatcher,
        serviceActions: TargetCls.serviceActions,
        decorate: instance
      }, LuxyFluxActionCreators);
    };
  }

  get module() {
    if (!this._module) {
      this._module = angular.module(
        `actions.${this.name}`,
        this.dependencies
      );

      this._module.factory(
        this.serviceName,
        this.getInjectionTokens().concat([this.factoryFn])
      );

      this.configure(this._module);
    }
    return this._module;
  }
}

// Decorators
export function Actions(config) {
  return cls => {
    let actionsName;
    const isConfigObject = angular.isObject(config);

    if (isConfigObject && config.name) {
      actionsName = config.name;
    } else if (angular.isString(config)) {
      actionsName = config;
    } else {
      const clsName = cls.name.replace(/actions$/i, '');
      actionsName = `${clsName[0].toLowerCase()}${clsName.slice(1)}`;
    }

    const namespace = isConfigObject && config.namespace !== undefined
      ? config.namespace
      : actionsName;

    cls.actionNamespace = angular.isString(namespace) && namespace.length
      ? namespace
        .replace(/([A-Z])/g, '_$1')
        .toUpperCase()
      : null;

    addStaticGetter(cls, 'annotation', () => Annotations.getActions(actionsName, cls));
  };
}

export function AsyncAction(actionName) {
  return (cls, methodName) => {
    addStaticGetterObjectMember(cls.constructor, 'serviceActions',
      () => prepareActionName(cls, actionName, methodName), methodName);
  };
}

export function Action(actionName) {
  return (cls, methodName, descriptor) => {
    const originalMethod = descriptor.value;
    descriptor.value = function(...payload) {
      const action = prepareActionName(cls, actionName, methodName);
      const originalReturn = Reflect.apply(originalMethod, this, payload);
      const dispatchPromise = this.dispatch(action, ...payload);
      return angular.isDefined(originalReturn) ? originalReturn : dispatchPromise;
    };
  };
}

export default ActionsAnnotation;

function prepareActionName(cls, actionName, methodName) {
  let preparedActionName = actionName;
  if (!preparedActionName) {
    preparedActionName = methodName.replace(/([A-Z])/g, '_$1');
  }
  const actionNamespace = cls.constructor.actionNamespace;
  if (actionNamespace) {
    preparedActionName = `${actionNamespace}_${preparedActionName}`;
  }
  return preparedActionName.toUpperCase();
}