src/domain/task/store/reducer/task.reducer.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { OrderedSet, OrderedMap } from 'immutable'
import { combineReducers } from 'redux'
import type { Task } from 'domain/task/entity/task'
import type { AcknowledgeTaskActionTypes } from 'domain/task/usecase/acknowledge-task/actionCreators'
import type { RemoveTaskActionTypes } from 'domain/task/usecase/remove-task/actionCreators'
import type { RegisterTaskActionTypes } from 'domain/task/usecase/register-task/actionCreators'
import type { AmendTaskStatusActionTypes } from 'domain/task/usecase/amend-task-status/actionCreators'
import type { DeepReadonly } from 'superTypes'
import type { TaskState } from '../appState'
import type { TaskRegisteredPayload } from 'domain/task/event/taskRegistered'
import type { TaskStatusAmendedPayload } from 'domain/task/event/taskStatusAmended'
import type { TaskRemovedPayload } from 'domain/task/event/taskRemoved'
import type { ConfigureTaskProgressBoundsActionTypes } from 'domain/task/usecase/configure-task-progress-bounds/actionCreators'
import type { TaskProgressBoundsConfiguredPayload } from 'domain/task/event/taskProgressBoundsConfigured'
import type { Progress } from 'domain/task/event/type'
import type { SetTaskProgressValueActionTypes } from 'domain/task/usecase/set-task-progress-value/actionCreators'
import type { TaskProgressValueSetPayload } from 'domain/task/event/taskProgressValueSet'

const initialTaskState: TaskState<string, string> = {
  byId: OrderedMap<string, Task>(),
  byType: OrderedMap<string, OrderedSet<string>>()
}

const registerTask = (
  state: DeepReadonly<TaskState>,
  payload: DeepReadonly<TaskRegisteredPayload>
): TaskState => {
  const { id, type, timestamp, initiator, status }: TaskRegisteredPayload = payload
  const foundList = state.byType.get(type)
  const task: Task = {
    id,
    creationDate: timestamp,
    lastUpdateDate: timestamp,
    initiator,
    status,
    type
  }
  return {
    ...state,
    byId: state.byId.set(id, task),
    byType: state.byType.set(type, foundList?.size ? foundList.add(id) : OrderedSet([id]))
  }
}

const amendTaskStatus = (
  state: DeepReadonly<TaskState>,
  payload: DeepReadonly<TaskStatusAmendedPayload>
): TaskState => {
  const { id, timestamp, status }: TaskStatusAmendedPayload = payload
  const foundTaskById = state.byId.get(id)
  return {
    ...state,
    ...(foundTaskById && {
      byId: state.byId.set(id, { ...foundTaskById, status, lastUpdateDate: timestamp })
    })
  }
}

const removeTask = (
  state: DeepReadonly<TaskState>,
  payload: DeepReadonly<TaskRemovedPayload>
): TaskState => {
  const { id }: TaskRemovedPayload = payload
  const foundTaskById = state.byId.get(id)
  return {
    ...state,
    ...(foundTaskById && {
      byId: state.byId.remove(id),
      byType: state.byType
        .map((value: Readonly<OrderedSet<string>>) => value.delete(id))
        .filter((value: Readonly<OrderedSet<string>>) => !value.isEmpty())
    })
  }
}

const configureTaskProgressBounds = (
  state: DeepReadonly<TaskState>,
  payload: DeepReadonly<TaskProgressBoundsConfiguredPayload>
): TaskState => {
  const { id, timestamp, progressBounds }: TaskProgressBoundsConfiguredPayload = payload
  const foundTaskById = state.byId.get(id)
  const { max, min }: Progress = progressBounds
  const bounds = {
    ...(max !== undefined && { max }),
    ...(min !== undefined && { min })
  }
  return {
    ...state,
    ...(foundTaskById && {
      byId: state.byId.set(id, {
        ...foundTaskById,
        lastUpdateDate: timestamp,
        progress: { ...foundTaskById.progress, ...bounds }
      })
    })
  }
}

const setTaskProgressValue = (
  state: DeepReadonly<TaskState>,
  payload: DeepReadonly<TaskProgressValueSetPayload>
): TaskState => {
  const { id, timestamp, progressValue }: TaskProgressValueSetPayload = payload
  const foundTaskById = state.byId.get(id)
  return {
    ...state,
    ...(foundTaskById && {
      byId: state.byId.set(id, {
        ...foundTaskById,
        lastUpdateDate: timestamp,
        progress: { ...foundTaskById.progress, current: progressValue }
      })
    })
  }
}

const task = (
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
  state: TaskState = initialTaskState,
  action: DeepReadonly<
    | RegisterTaskActionTypes
    | RemoveTaskActionTypes
    | AmendTaskStatusActionTypes
    | ConfigureTaskProgressBoundsActionTypes
    | SetTaskProgressValueActionTypes
  >
): TaskState => {
  switch (action.type) {
    case 'task/taskRegistered':
      return registerTask(state, action.payload)

    case 'task/taskStatusAmended':
      return amendTaskStatus(state, action.payload)

    case 'task/taskRemoved':
      return removeTask(state, action.payload)

    case 'task/taskProgressBoundsConfigured':
      return configureTaskProgressBounds(state, action.payload)

    case 'task/taskProgressValueSet':
      return setTaskProgressValue(state, action.payload)

    default:
      return state
  }
}

const displayedTaskIds = (
  state: Readonly<OrderedSet<string>> = OrderedSet(),
  action: DeepReadonly<
    | RegisterTaskActionTypes
    | RemoveTaskActionTypes
    | AmendTaskStatusActionTypes
    | AcknowledgeTaskActionTypes
    | ConfigureTaskProgressBoundsActionTypes
    | SetTaskProgressValueActionTypes
  >
): OrderedSet<string> => {
  switch (action.type) {
    case 'task/taskRegistered':
      return state.add(action.payload.id)

    case 'task/taskRemoved':
      return state.delete(action.payload.id)

    case 'task/taskStatusAmended':
      return state.add(action.payload.id)

    case 'task/taskAcknowledged':
      return state.remove(action.payload.id)

    case 'task/taskProgressBoundsConfigured':
      return state.add(action.payload.id)

    case 'task/taskProgressValueSet':
      return state.add(action.payload.id)

    default:
      return state
  }
}

const rootReducer = combineReducers({ task, displayedTaskIds })

export default rootReducer