containers/App/index.js

import React, { Component } from 'react';
import { connect } from 'react-redux';

import Constants, { FIXED_HEADER_HEIGHT, HEADER_HEIGHT, GAME_TYPE } from '../../constants';
import SVGProvider from '../../components/SVG/SVGProvider';
import Header from '../../components/Header';
import PushMenu from '../../components/PushMenu';
import MMenu from '../../components/MMenu';
import TopBar from '../TopBar';
import MyAccountWrap from '../../components/Account/MyAccountWrap';
import Router from '../../Router';
import Modals from '../Modals';
import {
  initRequest, logoutRequest, getBalanceAndBonusRequest, togglePM, changePage,
  toggleBetslip, setSelectedLeague, toggleAccount, goToLogin, goToMyAccountMenu,
  setNeedReCountBet, liveFinished, reCount, setBlockMultipleRecount,
  setNeedTurboReCountBet,
  setNeedTurboKickOffReCountBet,
  wakeUpApp, setAppOutOfFocus,
  closeModal, setInitialiseBet,
  setPlacebetAfterLogin, showHideKickOff, removeTicketsFromCount, setSelectedBetType,
} from '../../reducers';
import {
  getIsPMOpen, getCurrentPage, getUser, getBonus, getIsAccountOpen, getIsLoading,
  getDestinations, getCnt, getlastReqBalanceDateTime,
  getIsBalanceLoading,
  getConfigs,
  getSelectedBetType,
  getFreeBets,
} from '../../store/common';
import {
  getNumberOfBets,
  getNumberOfBetsTurbo,
  getPlacedBetsRecord,
  getBlockMultipleRecount,
  getTotalBetCount,
  getBetCountSchedule,
} from '../../store/tickets';
import {
  getHaveLiveMatches, getLeagues, getSelectedLeague, getGameType, getRoundEnd,
  getLiveMatchDuration,
  getSelectedLeagueIndex,
  getRoundLoading,
  getRoundId,
  getLiveId,
} from '../../store/bets';
import {
  getNumberOfSelections,
  getNeedReCountBet,
  getNeedTurboReCountBet,
  getTurboRoundDuration,
  getNeedTurboKickOffReCountBet,
  getSelectedOdds,
  getPlacedSelectionsTurbo,
} from '../../store/placebet';

const mapToProps = (state) => ({
  isPMOpen: getIsPMOpen(state),
  currentPage: getCurrentPage(state),
  numberOfBets: getNumberOfBets(state),
  numberOfBetsTurbo: getNumberOfBetsTurbo(state),
  haveLiveMatches: getHaveLiveMatches(state),
  user: getUser(state),
  bonus: getBonus(state),
  isAccountOpen: getIsAccountOpen(state),
  isLoading: getIsLoading(state),
  destinations: getDestinations(state),
  leagues: getLeagues(state),
  numberOfSelections: getNumberOfSelections(state),
  selectedLeague: getSelectedLeague(state),
  cnt: getCnt(state),
  gameType: getGameType(state),
  lastReqBalanceDateTime: getlastReqBalanceDateTime(state),
  isBalanceLoading: getIsBalanceLoading(state),
  roundEnd: getRoundEnd(state),
  liveMatchDuration: getLiveMatchDuration(state),
  placedBetsRecord: getPlacedBetsRecord(state),
  needReCountBet: getNeedReCountBet(state),
  blockMultipleRecount: getBlockMultipleRecount(state),
  needTurboReCountBet: getNeedTurboReCountBet(state),
  turboRoundDuration: getTurboRoundDuration(state),
  needTurboKickOffReCountBet: getNeedTurboKickOffReCountBet(state),
  config: getConfigs(state),
  selectedLeagueIndex: getSelectedLeagueIndex(state),
  selectedOdds: getSelectedOdds(state),
  totalBetCount: getTotalBetCount(state),
  betCountSchedule: getBetCountSchedule(state),
  selectedBetType: getSelectedBetType(state),
  placedSelectionsTurbo: getPlacedSelectionsTurbo(state),
  freeBets: getFreeBets(state),
  roundLoading: getRoundLoading(state),
  roundId: getRoundId(state),
  liveId: getLiveId(state),
});

const actionsToProps = (dispatch) => ({
  init: (payload) => dispatch(initRequest(payload)),
  logout: (payload) => dispatch(logoutRequest(payload)),
  getBalanceAndBonus: (payload) => dispatch(getBalanceAndBonusRequest(payload)),
  goToMyAccountMenu: (payload) => dispatch(goToMyAccountMenu(payload)),
  togglePM: (payload) => dispatch(togglePM(payload)),
  changePage: (payload) => dispatch(changePage(payload)),
  toggleBetslip: (payload) => dispatch(toggleBetslip(payload)),
  setSelectedLeague: (payload) => dispatch(setSelectedLeague(payload)),
  toggleAccount: (payload) => dispatch(toggleAccount(payload)),
  goToLogin: (payload) => dispatch(goToLogin(payload)),
  setNeedReCountBet: (payload) => dispatch(setNeedReCountBet(payload)),
  liveFinished: (payload) => dispatch(liveFinished(payload)),
  reCount: (payload) => dispatch(reCount(payload)),
  setBlockMultipleRecount: (payload) => dispatch(setBlockMultipleRecount(payload)),
  setNeedTurboReCountBet: (payload) => dispatch(setNeedTurboReCountBet(payload)),
  setNeedTurboKickOffReCountBet: (payload) => dispatch(setNeedTurboKickOffReCountBet(payload)),
  setAppOutOfFocus: (payload) => dispatch(setAppOutOfFocus(payload)),
  wakeUpApp: (payload) => dispatch(wakeUpApp(payload)),
  closeModal: (payload) => dispatch(closeModal(payload)),
  setInitialiseBet: (payload) => dispatch(setInitialiseBet(payload)),
  setPlacebetAfterLogin: (payload) => dispatch(setPlacebetAfterLogin(payload)),
  showHideKickOff: (payload) => dispatch(showHideKickOff(payload)),
  removeTicketsFromCount: (payload) => dispatch(removeTicketsFromCount(payload)),
  setSelectedBetType: (payload) => dispatch(setSelectedBetType(payload)),
});

let turboTimeout;
let normalLeagueTimeout;
let betcountScheduleInterval;
/**
 * @class
 * @property {object} props
 * @property {boolean} props.isPMOpen Indicate is Push Menue on left side is open
 * @property {string} props.currentPage Indicate current page
 * @property {number} props.numberOfBets Number of placed bets showed in bottom menu
 * @property {boolean} props.haveLiveMatches Indicate is LIVE round is in progress
 * @property {object} props.user User informations
 * @property {object} props.bonus Bonus informations
 * @property {boolean} props.isAccountOpen Indicate is account from Header is open
 * @property {boolean} props.isLoading Indicate is loader active
 * @property {object} props.destinations Destination informations
 * @property {object} props.leagues Leagues informations
 * @property {number} props.numberOfSelections Number of selected odds
 * @property {object} props.selectedLeague Selected league informations
 * @property {string} props.cnt CNT path for SVG icons
 * @property {string} props.gameType Classic/Turbo
 *
 * @property {Function} props.init Get inital data from API
 * @property {Function} props.logout Logout user
 * @property {Function} props.getBalanceAndBonus API call to refresh balance
 * @property {Function} props.togglePM Toogle push menu on left side
 * @property {Function} props.changePage Action to change page
 * @property {Function} props.toggleBetslip Toggle betslip modal
 * @property {Function} props.setSelectedLeague Change selected league
 * @property {Function} props.toggleAccount Toggle account menu from the header
 * @property {Function} props.goToLogin Change page to Login page
 * @property {Function} props.goToMyAccountMenu Change page to My account menu section
 */
class App extends Component {
  constructor() {
    super();
    /**
     * @member {object}
     * @property {boolean} fixedTopBar
     */
    this.state = {
      fixedTopBar: false,
    };
    /**
     * @member {number}
     */
    this.startPos = 0;
    /**
     * @member {Element}
     */
    this.handleScrollDiv = null;
  }

  /**
   * Call init API
   *
   * @returns {void}
   */
  componentDidMount() {
    this.props.init();

    window.addEventListener('blur', this.setAppOutOfFocus.bind(this));
    window.addEventListener('focus', this.wakeUpApp.bind(this));

    betcountScheduleInterval = setInterval(this.checkBetCountSchedule.bind(this), 1000);
  }

  /**
   * Add handler for scroll Event Listener
   *
   * @param {object} prevProps
   * @returns {void}
   */
  componentDidUpdate(prevProps) {
    const scrollArea = document.getElementsByClassName('scroll-area')[0];
    if (scrollArea && !scrollArea.classList.contains('listener')) {
      this.startPos = this.state.fixedTopBar !== 1
        ? scrollArea.getBoundingClientRect().top
        : scrollArea.getBoundingClientRect().top + FIXED_HEADER_HEIGHT;
      this.startPos = parseInt(this.startPos);
      if (scrollArea.childNodes[0].offsetHeight + this.startPos + FIXED_HEADER_HEIGHT
        > window.innerHeight) {
        this.handleScrollDiv = scrollArea.childNodes[0];
        scrollArea.classList.add('listener');
        scrollArea.addEventListener('scroll', this.handleFixTopBar);
      }
    }
    if (this.props.currentPage !== prevProps.currentPage) {
      // eslint-disable-next-line react/no-did-update-set-state
      this.setState({ fixedTopBar: 0 });
    }
    if (this.props.gameType === GAME_TYPE.TURBO
      && (this.props.needTurboKickOffReCountBet || this.props.needTurboReCountBet)) {
      clearTimeout(turboTimeout);
    }
    const { selectedOdds, freeBets } = this.props;

    if (selectedOdds.length !== prevProps.selectedOdds.length
      || (freeBets.length === 0 && prevProps.freeBets.length === 1)) {
      const matches = {};
      for (let i = 0; i < selectedOdds.length; i += 1) {
        const sel = selectedOdds[i];
        matches[`${sel.league}-${sel.matchId}`] = true;
      }
      if (selectedOdds.length === 0) {
        this.props.setSelectedBetType({ selectedBetType: Constants.BET_TYPE.NO_BET });
      } else if (freeBets.length > 0 && Object.keys(matches).length === selectedOdds.length) {
        this.props.setSelectedBetType({ selectedBetType: Constants.BET_TYPE.FREEBET });
      } else if (selectedOdds.length === 1 || Object.keys(matches).length === 1) {
        this.props.setSelectedBetType({ selectedBetType: Constants.BET_TYPE.SINGLE });
      } else if (Object.keys(matches).length !== selectedOdds.length) {
        this.props.setSelectedBetType({ selectedBetType: Constants.BET_TYPE.SPLIT_COLUMN });
      } else if (this.props.selectedBetType !== Constants.BET_TYPE.COMBINATION) {
        this.props.setSelectedBetType({ selectedBetType: Constants.BET_TYPE.MULTIPLE });
      }
    }
  }

  /**
   * Remove handler for scroll Event Listener
   *
   * @returns {void}
   */
  componentWillUnmount() {
    document.removeEventListener('scroll', this.handleFixTopBar);
    window.removeEventListener('blur', this.setAppOutOfFocus.bind(this));
    window.removeEventListener('focus', this.wakeUpApp.bind(this));

    clearTimeout(turboTimeout);
    clearTimeout(normalLeagueTimeout);
    clearInterval(betcountScheduleInterval);
  }

  /**
   * Set app is not in focus
   *
   * @param {object} e
   */
  setAppOutOfFocus(e) {
    e.stopPropagation();
    this.props.setAppOutOfFocus();
  }

  /**
   * Handler on scroll Event Listener on TopBar
   *
   * @function
   * @returns {void}
   */
  handleFixTopBar = () => {
    let { top } = this.handleScrollDiv.getBoundingClientRect();
    top = parseInt(top);
    if (top === this.startPos || this.props.isAccountOpen || top === 0) {
      this.setState({ fixedTopBar: 0 });
    } else if (top < this.startPos - HEADER_HEIGHT && this.state.fixedTopBar !== 1) {
      this.setState({ fixedTopBar: 1 });
    } else if (top >= this.startPos - HEADER_HEIGHT && this.state.fixedTopBar !== 0) {
      this.setState({ fixedTopBar: 0 });
    }
  }

  /**
   * Check for betcount schedule
   *
   */
  checkBetCountSchedule() {
    const schedule = this.props.betCountSchedule;
    const rounds = Object.keys(schedule);
    const endedRounds = rounds.filter((r) => new Date(schedule[r].time) < new Date());
    if (endedRounds.length > 0) {
      this.props.removeTicketsFromCount({
        roundId: endedRounds[0],
        gameType: schedule[endedRounds[0]].gameType,
      });
    }
  }

  /**
   * Wake up app when gets focus back
   *
   * @param {object} e
   */
  wakeUpApp(e) {
    e.stopPropagation();
    this.props.wakeUpApp();
  }

  /**
   * Render
   *
   * @see module:SVG/SVGProvider
   * @see module:PushMenu
   * @see module:Header
   * @see TopBar
   * @see Router
   * @see module:MMenu
   * @see module:Account/MyAccountWrap
   * @see Modals
   * @returns {view}
   */
  render() {
    const props = this.props;
    const isLobby = Constants.PAGES.LEAGUE_SELECTION === props.currentPage
      || Constants.PAGES.LOGIN === props.currentPage;
    const isBetslipOpen = Constants.PAGES.BETSLIP === props.currentPage;
    return (
      <SVGProvider
        isPMOpen={props.isPMOpen}
        isAccountOpen={props.isAccountOpen}
        cnt={props.cnt}
        isBetslipOpen={isBetslipOpen}
        wrapperClass={this.state.fixedTopBar === 1 ? 'scroll-down' : this.state.fixedTopBar === -1 ? 'scroll-up' : ''}
      >
        <PushMenu
          isOpen={props.isPMOpen}
          togglePM={props.togglePM}
          changePage={props.changePage}
          haveLiveMatches={props.haveLiveMatches}
          toggleBetslip={props.toggleBetslip}
          numberOfSelections={props.selectedOdds.length}
          destinations={props.destinations}
          leagues={props.leagues}
          setSelectedLeague={props.setSelectedLeague}
          goToMyAccountMenu={props.goToMyAccountMenu}
          user={props.user}
          numberOfBets={props.numberOfBets}
          gameType={props.gameType}
          currencySymbol={props.config?.currencySymbol}
          selectedLeagueIndex={props.selectedLeagueIndex}
        />

        <Header
          currentPage={props.currentPage}
          changePage={props.changePage}
          user={props.user}
          toggleAccount={props.toggleAccount}
          isAccountOpen={props.isAccountOpen}
          goToLogin={props.goToLogin}
          isLoading={props.isLoading}
          destinations={props.destinations}
          gameType={props.gameType}
          currencySymbol={props.config?.currencySymbol}
          setInitialiseBet={props.setInitialiseBet}
          setPlacebetAfterLogin={props.setPlacebetAfterLogin}
        />
        <main
          className={`flex-grow-1 ${props.isLoading ? 'loading' : ''} ${isLobby ? 'lobby' : 'markets'}`}
          id="market"
        >
          {!props.isLoading && props.currentPage !== Constants.PAGES.LEAGUE_SELECTION && (
            <TopBar />
          )}
          {/* CHILDREN */}
          <Router
            currentPage={props.currentPage}
          />
        </main>
        {
          !isLobby && (
            <MMenu
              isOpen={props.isPMOpen}
              currentPage={props.currentPage}
              togglePM={props.togglePM}
              changePage={props.changePage}
              leagues={props.leagues}
              setSelectedLeague={props.setSelectedLeague}
              numberOfBets={props.numberOfBets + props.numberOfBetsTurbo}
              totalBetCount={props.totalBetCount}
              haveLiveMatches={props.haveLiveMatches}
              toggleBetslip={props.toggleBetslip}
              numberOfSelections={props.selectedOdds.length}
              selectedOdds={props.selectedOdds}
              user={props.user}
              closeModal={props.closeModal}
              placedSelectionsLiveTurbo={props.placedSelectionsTurbo[this.props.liveId]}
              roundLoading={props.roundLoading}
            />
          )
        }
        <MyAccountWrap
          user={props.user}
          bonus={props.bonus}
          totalBetCount={props.totalBetCount}
          isAccountOpen={props.isAccountOpen}
          changePage={props.changePage}
          toggleAccount={props.toggleAccount}
          logout={props.logout}
          getBalanceAndBonus={props.getBalanceAndBonus}
          goToMyAccountMenu={props.goToMyAccountMenu}
          gameType={this.props.gameType}
          lastReqBalanceDateTime={this.props.lastReqBalanceDateTime}
          isBalanceLoading={this.props.isBalanceLoading}
          currencySymbol={this.props.config?.currencySymbol}
        />

        <div className="push-menu-mask" onClick={this.props.togglePM} />

        <Modals />
      </SVGProvider>
    );
  }
}
export default connect(
  mapToProps,
  actionsToProps,
)(App);