sagas/live.js

import {
  takeEvery, put, call, select,
} from 'redux-saga/effects';
/**
 * @namespace sagas/live
 */
import Constants, { MATCH_STATUS } from '../constants';

import {
  REMOVE_BLINK, SET_BLINK,
  SET_LIVE_DATA, GET_SCORES, SETUP_LIVE, GO_TO_MY_LIVE, SHOW_ERROR_MODAL, SETUP_TURBO_LIVE,
} from '../reducers';
import { delay, calulatePlacedSelection } from '../utils/common';
import { parseTimelineResults } from '../utils/parser';
import {
  getBlink, getPreviousSecond, getLiveDecrement, getLiveCountdown, getScores,
} from '../store/live';
import { getCurrentPage, getUser } from '../store/common';
import {
  getMatches, getTimeline, getHalftimeResults, getResults, getLiveId,
} from '../store/bets';
import { getPlacedSelections, getPlacedSelectionsTurbo } from '../store/placebet';
/**
 * Manage sores, track timeline and handle blink goal animations
 *
 * @memberof sagas/live
 * @async
 * @param {object} action
 * @property {object} action
 * @property {object} action.payload
 * @property {number} action.payload.endMinute
 * @property {number} action.payload.startMinutes
 * @property {number} [action.payload.skipBlink] Called after setup live to indicate
 *   skip blink animation
 * @property {object} action.payload.league
 * @property {Array} action.payload.leagues
 *
 *
 * @yields {select} getCurrentPage
 * @yields {select} getPreviousSecond
 * @yields {select} getPlacedSelections
 * @yields {select} getBlink
 * @yields {select} getScores
 * @yields {select} getLiveDecrement
 * @yields {select} getLiveCountdown
 * @yields {select} getTimeline
 * @yields {put} SET_LIVE_DATA
 * @yields {put} REMOVE_BLINK
 * @yields {put} SHOW_ERROR_MODAL
 * @returns {void}
 */
function* getScoresSaga(action) {
  try {
    let minuteString;
    const selectedLeague = action.payload.league;
    const leagues = action.payload.leagues;
    const startMinutes = action.payload.startMinutes + 1;
    const endMinute = action.payload.endMinute;
    const currentPage = yield select(getCurrentPage);
    const showBlink = Constants.PAGES.ALL_LIVE === currentPage
      || Constants.PAGES.MY_LIVE === currentPage;
    let previousSecond = yield select(getPreviousSecond);
    let placedSelectionsLive = yield select(getPlacedSelections);
    const liveId = yield select(getLiveId);
    placedSelectionsLive = placedSelectionsLive[liveId];
    const blink = yield select(getBlink);
    const scores = yield select(getScores);
    const liveDecrement = yield select(getLiveDecrement);
    let liveCountdown = yield select(getLiveCountdown);
    const timeline = yield select(getTimeline);
    let res = {
      blink,
      scores,
      placedSelectionsLive,
      blinksForRemove: [],
    };

    if (liveCountdown > 0) {
      liveCountdown -= liveDecrement;
      liveCountdown = liveCountdown < 0 ? 0 : liveCountdown;
      for (let minute = parseInt(startMinutes);
        minute <= parseInt(endMinute); minute += 1) {
        for (let i = 0; i < leagues.length; i += 1) {
          if (timeline[leagues[i].id]) {
            minuteString = timeline[leagues[i].id][minute];

            res = parseTimelineResults(
              minuteString,
              leagues[i].id,
              !action.payload.skipBlink
              && (selectedLeague.id === parseInt(leagues[i].id) || showBlink),
              res.placedSelectionsLive,
              res.blink,
              res.scores,
              res.blinksForRemove,
            );
          }
        }
        previousSecond = minute;
      }

      yield put({
        type: SET_LIVE_DATA,
        payload: {
          blink: res.blink,
          scores: res.scores,
          previousSecond,
          liveCountdown,
          setupLive: true,
          liveDecrement,
          placedSelections: res.placedSelectionsLive,
          roundId: liveId,
        },
      });
      if (res.blinksForRemove.length > 0) {
        yield put({
          type: REMOVE_BLINK,
          payload: {
            blinksForRemove: res.blinksForRemove,
          },
        });
      }
    }
  } catch (error) {
    yield put({
      type: SHOW_ERROR_MODAL,
      payload: {
        activeModal: Constants.MODALS.GENERAL_ERROR,
        message: error.message,
      },
    });
  }
}

/**
 * Calculate coundown and start interval, setup beggining sores and call fun to setup
 * placed selections. Also handle showing Live finished modal
 *
 * @memberof sagas/live
 * @async
 * @param {object} action
 * @property {object} action
 * @property {object} action.payload
 * @property {number} action.payload.liveMatchDuration
 * @property {number} action.payload.liveStart
 * @property {Array} action.payload.leagues
 * @property {object} action.payload.league
 *
 * @yields {select} getMatches
 * @yields {select} getPlacedSelections
 * @yields {put} GO_TO_MY_LIVE
 * @yields {put} SET_LIVE_DATA
 * @yields {put} GET_SCORES
 * @yields {put} SHOW_ERROR_MODAL
 * @returns {void}
 */
function* setupLive(action) {
  try {
    const matches = yield select(getMatches);
    const previousSecond = 0;
    const liveMatchDuration = action.payload.liveMatchDuration;
    const liveDecrement = Constants.REAL_MATCH_DURATION / liveMatchDuration;
    const liveStart = action.payload.liveStart;
    let liveCountdown = (Math.ceil((liveStart.getTime()
      + ((liveMatchDuration * 1000) - new Date().getTime()))
      / 1000) * liveDecrement) + liveDecrement;
    liveCountdown = liveCountdown < 0 ? 0 : liveCountdown;

    const scores = {};
    const blink = {};
    const leagues = action.payload.leagues;
    for (let i = 0; i < leagues.length; i += 1) {
      if (leagues[i].enabled) {
        scores[leagues[i].id] = [];
        blink[leagues[i].id] = [];
        if (matches[leagues[i].id]) {
          for (let j = 0; j < matches[leagues[i].id].length * 2; j += 1) {
            scores[leagues[i].id].push(0);
            blink[leagues[i].id].push(false);
          }
        }
      }
    }
    /**
     *   Calculate coundown and start interval, setup beggining sores and call fun to setup
     *   placed selections. Also handle showing Live finished modal
     */
    let placedSelectionsLive = yield select(getPlacedSelections);
    const liveId = yield select(getLiveId);
    placedSelectionsLive = placedSelectionsLive[liveId];
    const user = yield select(getUser);
    if (placedSelectionsLive && user) {
      const keys = Object.keys(placedSelectionsLive);
      keys.map((league) => {
        Object.keys(placedSelectionsLive[league]).map((match) => {
          placedSelectionsLive = calulatePlacedSelection(placedSelectionsLive, league, match, '0:0');
        });
      });
      if (keys.length > 0) {
        yield put({
          type: GO_TO_MY_LIVE,
        });
      }
    }

    yield put({
      type: SET_LIVE_DATA,
      payload: {
        blink,
        scores,
        liveCountdown,
        liveDecrement,
        previousSecond,
        setupLive: true,
        placedSelectionsLive,
        roundId: liveId,
      },
    });
    yield put({
      type: GET_SCORES,
      payload: {
        startMinutes: -1,
        endMinute: (Constants.REAL_MATCH_DURATION - liveCountdown) + liveDecrement,
        league: action.payload.league,
        leagues,
        skipBlink: true,
      },
    });
  } catch (error) {
    yield put({
      type: SHOW_ERROR_MODAL,
      payload: {
        activeModal: Constants.MODALS.GENERAL_ERROR,
        message: error.message,
      },
    });
  }
}

/**
 * Calculate coundown and start interval, setup beggining sores and call fun to setup
 * placed selections. Also handle showing Live finished modal
 *
 * @memberof sagas/live
 * @async
 * @param {object} action
 * @property {object} action
 * @property {object} action.payload
 * @property {number} action.payload.liveMatchDuration
 * @property {number} action.payload.liveStart
 * @property {Array} action.payload.leagues
 * @property {object} action.payload.league
 *
 * @yields {select} getMatches
 * @yields {select} getPlacedSelections
 * @yields {put} GO_TO_MY_LIVE
 * @yields {put} SET_LIVE_DATA
 * @yields {put} GET_SCORES
 * @yields {put} SHOW_ERROR_MODAL
 * @returns {void}
 */
function* setupLiveTurbo(action) {
  try {
    const matches = yield select(getMatches);
    const liveMatchDuration = action.payload.liveMatchDuration;
    const liveStart = action.payload.liveStart;
    const liveCountdown = parseInt((new Date().getTime() - liveStart.getTime()) / 1000)
      > parseInt(liveMatchDuration / 2) ? MATCH_STATUS.FULLTIME : MATCH_STATUS.HALFTIME;

    const halftimeResults = yield select(getHalftimeResults);
    const results = yield select(getResults);
    const scores = {};
    const leagues = action.payload.leagues;
    let placedSelectionsLiveTurbo = yield select(getPlacedSelectionsTurbo);
    const liveId = yield select(getLiveId);
    placedSelectionsLiveTurbo = placedSelectionsLiveTurbo[liveId];
    let placedBetLeagueIds = [];

    if (placedSelectionsLiveTurbo) {
      placedBetLeagueIds = Object.keys(placedSelectionsLiveTurbo);
    }

    const leaguesIds = leagues.map((l) => l.id);

    // Fill leagues with missing ids.
    // Needed when we had bets placed and league is disabled in mean while, before kick off
    placedBetLeagueIds.map((leagueId) => {
      if (leaguesIds.indexOf(parseInt(leagueId)) === -1) {
        leagues.push({ id: parseInt(leagueId), enabled: true });
      }
    });

    for (let i = 0; i < leagues.length; i += 1) {
      if (leagues[i].enabled) {
        scores[leagues[i].id] = [];
        if (matches[leagues[i].id]) {
          for (let j = 0; j < matches[leagues[i].id].length * 2; j += 1) {
            if (liveCountdown === MATCH_STATUS.HALFTIME) {
              scores[leagues[i].id].push(halftimeResults[leagues[i].id][j]);
            } else {
              scores[leagues[i].id].push(results[leagues[i].id][j]);
            }
          }
        }
      }
    }
    /**
     *   Calculate coundown and start interval, setup beggining sores and call fun to setup
     *   placed selections. Also handle showing Live finished modal
     */
    if (placedSelectionsLiveTurbo) {
      // eslint-disable-next-line sonarjs/no-identical-functions
      placedBetLeagueIds.map((league) => {
        Object.keys(placedSelectionsLiveTurbo[league]).map((match) => {
          const teamHome = match * 2;
          const teamAway = (match * 2) + 1;
          placedSelectionsLiveTurbo = calulatePlacedSelection(
            placedSelectionsLiveTurbo, league, match,
            `${scores[league][teamHome]}:${scores[league][teamAway]}`,
          );
        });
      });
      if (placedBetLeagueIds.length > 0 && liveCountdown === MATCH_STATUS.HALFTIME) {
        yield put({
          type: GO_TO_MY_LIVE,
        });
      }
    }

    yield put({
      type: SET_LIVE_DATA,
      payload: {
        scores,
        liveCountdown,
        setupLive: true,
        placedSelectionsLiveTurbo,
        roundId: liveId,
      },
    });
  } catch (error) {
    yield put({
      type: SHOW_ERROR_MODAL,
      payload: {
        activeModal: Constants.MODALS.GENERAL_ERROR,
        message: error.message,
      },
    });
  }
}

/**
 * Remove all blinks animations from array in 2 seconds
 *
 * @memberof sagas/live
 * @async
 * @param {object} action
 * @property {object} action
 * @property {object} action.payload
 * @property {Array} action.payload.blinksForRemove
 *
 * @yields {call} delay 2000
 * @yields {select} getBlink
 * @yields {put} SET_BLINK
 * @yields {put} SHOW_ERROR_MODAL
 * @returns {void}
 */
function* removeBlinks(action) {
  try {
    yield call(delay, 2000);
    const blinksForRemove = action.payload.blinksForRemove;
    const blink = yield select(getBlink);
    blinksForRemove.map((b) => {
      if (blink[b.l]) blink[b.l][b.t] = false;
    });
    yield put({
      type: SET_BLINK,
      payload: {
        blink,
      },
    });
  } catch (error) {
    yield put({
      type: SHOW_ERROR_MODAL,
      payload: {
        activeModal: Constants.MODALS.GENERAL_ERROR,
        message: error.message,
      },
    });
  }
}

/**
 * @memberof sagas/live
 * @async
 * @returns {void}
 */
export default function* liveSaga() {
  yield takeEvery(GET_SCORES, getScoresSaga);
  yield takeEvery(SETUP_LIVE, setupLive);
  yield takeEvery(SETUP_TURBO_LIVE, setupLiveTurbo);
  yield takeEvery(REMOVE_BLINK, removeBlinks);
}