teamdigitale/italia-app

View on GitHub
ts/features/messages/store/reducers/messagePrecondition.ts

Summary

Maintainability
F
3 days
Test Coverage
import {
  constFalse,
  constTrue,
  constUndefined,
  pipe
} from "fp-ts/lib/function";
import * as B from "fp-ts/lib/boolean";
import { getType } from "typesafe-actions";
import { ThirdPartyMessagePrecondition } from "../../../../../definitions/backend/ThirdPartyMessagePrecondition";
import { Action } from "../../../../store/actions/types";
import { UIMessageId } from "../../types";
import {
  errorPreconditionStatusAction,
  idlePreconditionStatusAction,
  loadingContentPreconditionStatusAction,
  retrievingDataPreconditionStatusAction,
  scheduledPreconditionStatusAction,
  shownPreconditionStatusAction,
  updateRequiredPreconditionStatusAction
} from "../actions/preconditions";
import { GlobalState } from "../../../../store/reducers/types";
import { isPnAppVersionSupportedSelector } from "../../../../store/reducers/backendStatus";
import { TagEnum as SENDTagEnum } from "../../../../../definitions/backend/MessageCategoryPN";
import { MessageCategory } from "../../../../../definitions/backend/MessageCategory";

// MPS stands for Message Precondition Status
type MPSError = {
  state: "error";
  messageId: UIMessageId;
  categoryTag: MessageCategory["tag"];
  reason: string;
};
type MPSIdle = {
  state: "idle";
};
type MPSLoadingContent = {
  state: "loadingContent";
  messageId: UIMessageId;
  categoryTag: MessageCategory["tag"];
  content: ThirdPartyMessagePrecondition;
};
type MPSRetrievingData = {
  state: "retrievingData";
  messageId: UIMessageId;
  categoryTag: MessageCategory["tag"];
};
type MPSScheduled = {
  state: "scheduled";
  messageId: UIMessageId;
  categoryTag: MessageCategory["tag"];
};
type MPSShown = {
  state: "shown";
  messageId: UIMessageId;
  categoryTag: MessageCategory["tag"];
  content: ThirdPartyMessagePrecondition;
};
type MPSUpdateRequired = {
  state: "updateRequired";
};

export type MessagePreconditionStatus =
  | MPSError
  | MPSIdle
  | MPSLoadingContent
  | MPSRetrievingData
  | MPSScheduled
  | MPSShown
  | MPSUpdateRequired;

const INITIAL_STATE: MPSIdle = {
  state: "idle"
};

export const preconditionReducer = (
  state: MessagePreconditionStatus = INITIAL_STATE,
  action: Action
): MessagePreconditionStatus => {
  switch (action.type) {
    case getType(errorPreconditionStatusAction):
      return foldPreconditionStatus(
        () => state,
        () => state,
        loadingContentStatus =>
          toErrorMPS(
            loadingContentStatus.messageId,
            loadingContentStatus.categoryTag,
            action.payload.reason
          ), // From Loading Content to Error
        retrievingDataStatus =>
          toErrorMPS(
            retrievingDataStatus.messageId,
            retrievingDataStatus.categoryTag,
            action.payload.reason
          ), // From Retrieving Data to Error
        () => state,
        () => state,
        () => state
      )(state);
    case getType(idlePreconditionStatusAction):
      return foldPreconditionStatus(
        () => toIdleMPS(), // From Error to Idle
        () => state,
        () => toIdleMPS(), // From Loading Content to Idle,
        () => toIdleMPS(), // From Retrieving Data to Idle,
        () => state,
        () => toIdleMPS(), // From Shown to Idle
        () => toIdleMPS() // From Update Required to Idle
      )(state);
    case getType(loadingContentPreconditionStatusAction):
      return foldPreconditionStatus(
        () => state,
        () => state,
        () => state,
        retrievingDataStatus =>
          toLoadingContentMPS(
            retrievingDataStatus.messageId,
            retrievingDataStatus.categoryTag,
            action.payload.content
          ), // From Retrieving Data to Loading Content
        () => state,
        () => state,
        () => state
      )(state);
    case getType(retrievingDataPreconditionStatusAction):
      return foldPreconditionStatus(
        errorStatus =>
          toRetrievingDataMPS(errorStatus.messageId, errorStatus.categoryTag), // From Error to Retrieving Data
        () => state,
        () => state,
        () => state,
        scheduledStatus =>
          toRetrievingDataMPS(
            scheduledStatus.messageId,
            scheduledStatus.categoryTag
          ), // From Scheduled to Retrieving Data
        () => state,
        () => state
      )(state);
    case getType(scheduledPreconditionStatusAction):
      return foldPreconditionStatus(
        () => state,
        () =>
          toScheduledMPS(action.payload.messageId, action.payload.categoryTag), // From Idle to Scheduled
        () => state,
        () => state,
        () => state,
        () => state,
        () => state
      )(state);
    case getType(shownPreconditionStatusAction):
      return foldPreconditionStatus(
        () => state,
        () => state,
        loadingContentStatus =>
          toShownMPS(
            loadingContentStatus.messageId,
            loadingContentStatus.categoryTag,
            loadingContentStatus.content
          ), // From Loading Content to Shown
        () => state,
        () => state,
        () => state,
        () => state
      )(state);
    case getType(updateRequiredPreconditionStatusAction):
      return foldPreconditionStatus(
        () => state,
        () => state,
        () => state,
        () => state,
        () => toUpdateRequiredMPS(), // From Scheduled to Update Required,
        () => state,
        () => state
      )(state);
  }
  return state;
};

export const toErrorMPS = (
  messageId: UIMessageId,
  categoryTag: MessageCategory["tag"],
  reason: string
): MPSError => ({
  state: "error",
  messageId,
  categoryTag,
  reason
});
export const toIdleMPS = (): MPSIdle => ({
  state: "idle"
});
export const toLoadingContentMPS = (
  messageId: UIMessageId,
  categoryTag: MessageCategory["tag"],
  content: ThirdPartyMessagePrecondition
): MPSLoadingContent => ({
  state: "loadingContent",
  messageId,
  categoryTag,
  content
});
export const toRetrievingDataMPS = (
  messageId: UIMessageId,
  categoryTag: MessageCategory["tag"]
): MPSRetrievingData => ({
  state: "retrievingData",
  messageId,
  categoryTag
});
export const toScheduledMPS = (
  messageId: UIMessageId,
  categoryTag: MessageCategory["tag"]
): MPSScheduled => ({
  state: "scheduled",
  messageId,
  categoryTag
});
export const toShownMPS = (
  messageId: UIMessageId,
  categoryTag: MessageCategory["tag"],
  content: ThirdPartyMessagePrecondition
): MPSShown => ({
  state: "shown",
  messageId,
  categoryTag,
  content
});
export const toUpdateRequiredMPS = (): MPSUpdateRequired => ({
  state: "updateRequired"
});

export const foldPreconditionStatus =
  <A>(
    onError: (status: MPSError) => A,
    onIdle: (status: MPSIdle) => A,
    onLoadingContent: (status: MPSLoadingContent) => A,
    onRetrievingData: (status: MPSRetrievingData) => A,
    onScheduled: (status: MPSScheduled) => A,
    onShown: (status: MPSShown) => A,
    onUpdateRequired: (status: MPSUpdateRequired) => A
  ) =>
  (status: MessagePreconditionStatus) => {
    switch (status.state) {
      case "error":
        return onError(status);
      case "loadingContent":
        return onLoadingContent(status);
      case "retrievingData":
        return onRetrievingData(status);
      case "scheduled":
        return onScheduled(status);
      case "shown":
        return onShown(status);
      case "updateRequired":
        return onUpdateRequired(status);
    }
    return onIdle(status);
  };

export const shouldPresentPreconditionsBottomSheetSelector = (
  state: GlobalState
) => state.entities.messages.precondition.state === "scheduled";

export const preconditionsRequireAppUpdateSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      constFalse,
      constFalse,
      constFalse,
      constFalse,
      scheduled =>
        pipe(
          scheduled.categoryTag === SENDTagEnum.PN,
          B.fold(constFalse, () =>
            pipe(
              state,
              isPnAppVersionSupportedSelector,
              appVersionSupported => !appVersionSupported
            )
          )
        ),
      constFalse,
      constTrue
    )
  );

export const preconditionsTitleContentSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      () => "empty" as const,
      constUndefined,
      () => "header" as const,
      () => "loading" as const,
      constUndefined,
      () => "header" as const,
      () => "empty" as const
    )
  );

export const preconditionsTitleSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      constUndefined,
      constUndefined,
      loadingContentStatus => loadingContentStatus.content.title,
      constUndefined,
      constUndefined,
      shownStatus => shownStatus.content.title,
      constUndefined
    )
  );

export const preconditionsContentSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      _ => "error" as const,
      constUndefined,
      _ => "content" as const,
      _ => "loading" as const,
      constUndefined,
      _ => "content" as const,
      _ => "update" as const
    )
  );

export const preconditionsContentMarkdownSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      constUndefined,
      constUndefined,
      loadingContentStatus => loadingContentStatus.content.markdown,
      constUndefined,
      constUndefined,
      shownStatus => shownStatus.content.markdown,
      constUndefined
    )
  );

export const preconditionsFooterSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      _ => "view" as const,
      constUndefined,
      _ => "view" as const,
      _ => "view" as const,
      constUndefined,
      _ => "content" as const,
      _ => "update" as const
    )
  );

export const preconditionsCategoryTagSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      errorStatus => errorStatus.categoryTag,
      constUndefined,
      loadingContentStatus => loadingContentStatus.categoryTag,
      retrievingDataStatus => retrievingDataStatus.categoryTag,
      scheduledStatus => scheduledStatus.categoryTag,
      shownStatus => shownStatus.categoryTag,
      constUndefined
    )
  );

export const preconditionsMessageIdSelector = (state: GlobalState) =>
  pipe(
    state.entities.messages.precondition,
    foldPreconditionStatus(
      errorStatus => errorStatus.messageId,
      constUndefined,
      loadingContentStatus => loadingContentStatus.messageId,
      retrievingDataStatus => retrievingDataStatus.messageId,
      scheduledStatus => scheduledStatus.messageId,
      shownStatus => shownStatus.messageId,
      constUndefined
    )
  );