jelhan/croodle

View on GitHub
app/routes/poll/participation.ts

Summary

Maintainability
A
0 mins
Test Coverage
import Route from '@ember/routing/route';
import { tracked } from '@glimmer/tracking';
import { TrackedArray } from 'tracked-built-ins';
import IntlMessage from 'croodle/utils/intl-message';
import type Poll from 'croodle/models/poll';
import type Option from 'croodle/models/option';

class FormDataSelections {
  @tracked value = null;
  valueIsRequired;

  get valueValidation() {
    const { value, valueIsRequired } = this;

    if (!value && valueIsRequired) {
      return new IntlMessage('poll.error.selection.valueMissing');
    }

    return null;
  }

  get isValid() {
    return this.valueValidation === null;
  }

  constructor(valueIsRequired: boolean) {
    this.valueIsRequired = valueIsRequired;
  }
}

class FormData {
  @tracked name: null | string = null;
  nameIsRequired;
  namesTaken;
  selections;

  get nameValidation() {
    const { name, nameIsRequired, namesTaken } = this;

    if (!name && nameIsRequired) {
      return new IntlMessage('poll.error.name.valueMissing');
    }

    if (name && namesTaken.includes(name)) {
      return new IntlMessage('poll.error.name.duplicate');
    }

    return null;
  }

  get selectionsValidation() {
    const isValid = this.selections.every((selection) => selection.isValid);

    if (!isValid) {
      return new IntlMessage('poll.error.newUser.everyOptionIsAnswered');
    }

    return null;
  }

  constructor(
    options: Option[],
    {
      nameIsRequired,
      namesTaken,
      selectionIsRequired,
    }: {
      nameIsRequired: boolean;
      namesTaken: string[];
      selectionIsRequired: boolean;
    },
  ) {
    this.nameIsRequired = nameIsRequired;
    this.namesTaken = namesTaken;
    this.selections = new TrackedArray(
      options.map(() => new FormDataSelections(selectionIsRequired)),
    );
  }
}

export default class PollParticipationRoute extends Route {
  model() {
    const poll = this.modelFor('poll') as Poll;
    const { anonymousUser, forceAnswer, options, users } = poll;
    const formData = new FormData(options, {
      nameIsRequired: !anonymousUser,
      namesTaken: users
        .map(({ name }) => name)
        .filter((_) => _ !== null) as string[],
      selectionIsRequired: forceAnswer,
    });

    return {
      formData,
      poll,
    };
  }
}

type Resolved<P> = P extends Promise<infer T> ? T : P;
export type PollParticipationRouteModel = Resolved<
  ReturnType<PollParticipationRoute['model']>
>;