hexlet-codebattle/codebattle

View on GitHub
services/app/apps/codebattle/assets/js/widgets/middlewares/Tournament.js

Summary

Maintainability
F
4 days
Test Coverage
import axios from 'axios';
import Gon from 'gon';
import { camelizeKeys } from 'humps';
import compact from 'lodash/compact';

import socket, { channelMethods } from '../../socket';
import TournamentTypes from '../config/tournamentTypes';
import { actions } from '../slices';

import { addWaitingRoomListeners } from './WaitingRoom';

const tournamentId = Gon.getAsset('tournament_id');
const channelName = `tournament:${tournamentId}`;
let channel = socket.channel(channelName);

export const updateTournamentChannel = newTournamentId => {
  const newChannelName = `tournament:${newTournamentId}`;
  channel = socket.channel(newChannelName);
};

const initTournamentChannel = waitingRoomMachine => dispatch => {
  const onJoinFailure = () => {
    window.location.reload();
  };

  const onJoinSuccess = response => {
    dispatch(
      actions.setTournamentData({
        ...response.tournament,
        topPlayerIds: response.topPlayerIds || [],
        matches: {},
        ranking: response.ranking || { entries: [] },
        clans: response.clans || {},
        players: {},
        showBots: response.tournament.type !== TournamentTypes.show,
        channel: { online: true },
        playersPageNumber: 1,
        playersPageSize: 20,
      }),
    );

    if (response.tournament.waitingRoomName && response.currentPlayer) {
      waitingRoomMachine.send('LOAD_WAITING_ROOM', {
        payload: { currentPlayer: response.currentPlayer },
      });
      dispatch(actions.setActiveTournamentPlayer(response.currentPlayer));
    } else {
      waitingRoomMachine.send('REJECT_LOADING', {});
    }

    dispatch(actions.updateTournamentPlayers(compact(response.players)));
    dispatch(actions.updateTournamentMatches(compact(response.matches)));
    dispatch(actions.setTournamentTaskList(compact(response.tasksInfo)));
  };

  channel.onMessage = (_event, payload) => camelizeKeys(payload);

  channel.join().receive('ok', onJoinSuccess).receive('error', onJoinFailure);

  channel.onError(() => {
    dispatch(actions.updateTournamentChannelState(false));
  });
};

// export const soundNotification = notification();

export const connectToTournament = waitingRoomMachine => dispatch => {
  initTournamentChannel(waitingRoomMachine)(dispatch);

  const oldChannel = channel;

  const handleUpdate = response => {
    dispatch(actions.updateTournamentData(response.tournament));
    dispatch(actions.updateTournamentPlayers(compact(response.players || [])));
    dispatch(actions.updateTournamentMatches(compact(response.matches || [])));
    if (response.tasksInfo) {
      dispatch(actions.setTournamentTaskList(compact(response.tasksInfo)));
    }
  };

  const handleMatchesUpdate = response => {
    dispatch(actions.updateTournamentMatches(compact(response.matches)));
  };

  const handlePlayersUpdate = response => {
    dispatch(actions.updateTournamentPlayers(compact(response.players)));
  };

  const handleTournamentRoundCreated = response => {
    dispatch(actions.updateTournamentData(response.tournament));
  };

  const handleRoundFinished = response => {
    dispatch(
      actions.updateTournamentData({
        ...response.tournament,
        topPlayerIds: response.topPlayerIds,
        playersPageNumber: 1,
        playersPageSize: 20,
      }),
    );

    dispatch(actions.updateTournamentPlayers(compact(response.players)));
    dispatch(actions.updateTopPlayers(compact(response.players)));
  };

  const handleTournamentRestarted = response => {
    dispatch(
      actions.setTournamentData({
        ...response.tournament,
        channel: { online: true },
        playersPageNumber: 1,
        playersPageSize: 20,
        matches: {},
        players: {},
      }),
    );
  };

  const handlePlayerJoined = response => {
    dispatch(actions.addTournamentPlayer(response));
    dispatch(actions.updateTournamentData(response.tournament));
  };

  const handlePlayerLeft = response => {
    dispatch(actions.removeTournamentPlayer(response));
    dispatch(actions.updateTournamentData(response.tournament));
  };

  const handleMatchUpserted = response => {
    dispatch(actions.updateTournamentMatches(compact([response.match])));
    dispatch(actions.updateTournamentPlayers(compact(response.players)));
  };

  const handleTournamentFinished = response => {
    dispatch(actions.updateTournamentData(response.tournament));
  };

  const handleTournamentRankingUpdate = response => {
    dispatch(actions.updateTournamentData({ ranking: response.ranking, clans: response.clans }));
  };

  const clearWaitingRoomListeners = addWaitingRoomListeners(
    oldChannel,
    waitingRoomMachine,
    { cancelRedirect: true },
  )(dispatch);

  const refs = [
    oldChannel.on('tournament:update', handleUpdate),
    oldChannel.on('tournament:matches:update', handleMatchesUpdate),
    oldChannel.on('tournament:players:update', handlePlayersUpdate),
    oldChannel.on('tournament:round_created', handleTournamentRoundCreated),
    oldChannel.on('tournament:round_finished', handleRoundFinished),
    oldChannel.on('tournament:player:joined', handlePlayerJoined),
    oldChannel.on('tournament:player:left', handlePlayerLeft),
    oldChannel.on('tournament:match:upserted', handleMatchUpserted),
    oldChannel.on('tournament:restarted', handleTournamentRestarted),
    oldChannel.on('tournament:finished', handleTournamentFinished),
    oldChannel.on('tournament:ranking_update', handleTournamentRankingUpdate),
  ];

  const clearTournamentChannel = () => {
    oldChannel.off('tournament:update', refs[0]);
    oldChannel.off('tournament:matches:update', refs[1]);
    oldChannel.off('tournament:players:update', refs[2]);
    oldChannel.off('tournament:round_created', refs[3]);
    oldChannel.off('tournament:round_finished', refs[4]);
    oldChannel.off('tournament:player:joined', refs[5]);
    oldChannel.off('tournament:player:left', refs[6]);
    oldChannel.off('tournament:match:upserted', refs[7]);
    oldChannel.off('tournament:restarted', refs[8]);
    oldChannel.off('tournament:finished', refs[9]);
    oldChannel.off('tournament:ranking_update', refs[10]);

    clearWaitingRoomListeners();
  };

  return clearTournamentChannel;
};

// TODO (tournaments): request matches by searched player id
export const uploadPlayers = playerIds => (dispatch, getState) => {
  const state = getState();

  const { isLive, id } = state.tournament;

  if (isLive) {
    channel
      .push('tournament:players:request', { player_ids: playerIds })
      .receive('ok', response => {
        dispatch(actions.updateTournamentPlayers(response.players));
      })
      .receive('error', error => console.error(error));
  } else {
    const playerIdsStr = playerIds.join(',');

    axios
      .get(`/api/v1/tournaments/${id}/players?player_ids=${playerIdsStr}`, {
        headers: {
          'Content-Type': 'application/json',
          'x-csrf-token': window.csrf_token,
        },
      })
      .then(response => {
        dispatch(actions.updateTournamentPlayers(response.players));
      })
      .catch(error => console.error(error));
  }
};

export const requestMatchesByPlayerId = userId => dispatch => {
  channel
    .push('tournament:matches:request', { player_id: userId })
    .receive('ok', data => {
      dispatch(actions.updateTournamentMatches(data.matches));
      dispatch(actions.updateTournamentPlayers(data.players));
    })
    .receive('error', error => console.error(error));
};

export const uploadPlayersMatches = playerId => (dispatch, getState) => {
  const state = getState();

  const { isLive, id } = state.tournament;

  if (isLive) {
    requestMatchesByPlayerId(playerId)(dispatch);
  } else {
    axios
      // TODO: add BE api for fetching games with tournaemnt_id+player_id and mapt to touanemtnMatch
      .get(`/api/v1/tournaments/${id}/matches?player_id=${playerId}`, {
        headers: {
          'Content-Type': 'application/json',
          'x-csrf-token': window.csrf_token,
        },
      })
      .then(response => {
        dispatch(actions.updateTournamentMatches(response.matches));
      })
      .catch(error => console.error(error));
  }
};

export const joinTournament = teamId => {
  const params = teamId !== undefined ? { team_id: teamId } : {};
  channel
    .push('tournament:join', params)
    .receive('error', error => console.error(error));
};

export const leaveTournament = teamId => {
  const params = teamId !== undefined ? { team_id: teamId } : {};
  channel
    .push('tournament:leave', params)
    .receive('error', error => console.error(error));
};

export const pauseWaitingRoomMatchmaking = () => {
  channel
    .push('waiting_room:player:matchmaking_pause', {})
    .receive('error', error => console.error(error));
};

export const startWaitingRoomMatchmaking = () => {
  channel
    .push('waiting_room:player:matchmaking_start', {})
    .receive('error', error => console.error(error));
};

export const sendTournamentWaitingRoomPaused = () => {
  channel
    .push(channelMethods.matchmakingResume, {})
    .receive('error', error => console.error(error));
};
export const sendTournamentWaitingRoomResumed = () => {
  channel
    .push(channelMethods.matchmakingPause, {})
    .receive('error', error => console.error(error));
};