feat(in-380): prevent requests for unanalysed matches #100

Merged
andrey.dekterev merged 1 commits from in-380 into in-142 3 years ago
  1. 65
      src/features/MatchPage/components/LiveMatch/helpers.tsx
  2. 11
      src/features/MatchPage/components/LiveMatch/hooks/useChapters.tsx
  3. 2
      src/features/MatchPage/components/MatchDescription/index.tsx
  4. 11
      src/features/MatchPage/helpers/fullMatchDuration.tsx
  5. 2
      src/features/MatchPage/store/hooks/index.tsx
  6. 9
      src/features/MatchPage/store/hooks/useMatchData.tsx
  7. 4
      src/features/MatchPage/store/hooks/useMatchPlaylists.tsx
  8. 34
      src/features/MatchPage/store/hooks/usePlayersStats.tsx
  9. 13
      src/features/MatchPage/store/hooks/useSelectedPlaylist.tsx
  10. 25
      src/features/MatchPage/store/hooks/useTeamsStats.tsx
  11. 14
      src/features/MatchPage/store/hooks/useTournamentData.tsx
  12. 19
      src/features/MatchSidePlaylists/components/PlayersTable/hooks/useTable.tsx
  13. 19
      src/features/MatchSidePlaylists/components/TeamsStatsTable/Cell.tsx
  14. 2
      src/features/MatchSidePlaylists/hooks.tsx
  15. 9
      src/requests/getMatchInfo.tsx
  16. 11
      src/requests/getMatchScore.tsx

@ -3,8 +3,11 @@ import find from 'lodash/find'
import reduce from 'lodash/reduce'
import concat from 'lodash/concat'
import type { Episodes } from 'requests/getMatchPlaylists'
import type { MatchInfo } from 'requests/getMatchInfo'
import type {
MatchInfo,
MatchScore,
Episodes,
} from 'requests'
import type { Chapters, Chapter } from 'features/StreamPlayer/types'
@ -13,16 +16,25 @@ import { FULL_GAME_KEY } from '../../helpers/buildPlaylists'
export const FULL_MATCH_BOUNDARY = '0'
type GetFullMatchChaptersArgs = {
matchScore?: MatchScore,
playlist: MatchPlaylistOption,
profile: MatchInfo,
url: string,
}
/**
* Формирует эпизоды плейлиста Полный матч
* API не выдает полный матч как плейлист, формируем на фронте
* */
const getFullMatchChapters = (
profile: MatchInfo,
url: string,
playlist: MatchPlaylistOption,
) => {
const bound = find(profile?.video_bounds, { h: FULL_MATCH_BOUNDARY })
const getFullMatchChapters = ({
matchScore,
playlist,
profile,
url,
}: GetFullMatchChaptersArgs) => {
const videoBounds = matchScore?.video_bounds || profile?.video_bounds
const bound = find(videoBounds, { h: FULL_MATCH_BOUNDARY })
const durationMs = (bound && !profile?.live)
? ((playlist.duration ?? 0) - Number(bound.s)) * 1000
@ -42,14 +54,22 @@ const getFullMatchChapters = (
]
}
type GetPlaylistChaptersArgs = {
episodes: Episodes,
matchScore?: MatchScore,
profile: MatchInfo,
url: string,
}
/**
* Формирует эпизоды плейлистов матча и игроков
* */
const getPlaylistChapters = (
profile: MatchInfo,
url: string,
episodes: Episodes,
) => reduce(
const getPlaylistChapters = ({
episodes,
matchScore,
profile,
url,
}: GetPlaylistChaptersArgs) => reduce(
episodes,
(
acc: Chapters,
@ -58,7 +78,8 @@ const getPlaylistChapters = (
) => {
if (episode.s >= episode.e) return acc
const bound = find(profile?.video_bounds, { h: String(episode.h) })
const videoBounds = matchScore?.video_bounds || profile?.video_bounds
const bound = find(videoBounds, { h: String(episode.h) })
const boundStart = bound ? Number(bound.s) : 0
const episodeDuration = (episode.e - episode.s) * 1000
@ -79,6 +100,7 @@ const getPlaylistChapters = (
)
type Args = {
matchScore?: MatchScore,
profile: MatchInfo,
selectedPlaylist?: PlaylistOption,
url: string,
@ -88,21 +110,24 @@ type Args = {
* Формирует список эпизодов из выбранного плейлиста для плеера
*/
export const buildChapters = ({
matchScore,
profile,
selectedPlaylist,
url,
}: Args): Chapters => {
if (!selectedPlaylist) return []
if (selectedPlaylist.id === FULL_GAME_KEY) {
return getFullMatchChapters(
return getFullMatchChapters({
matchScore,
playlist: selectedPlaylist,
profile,
url,
selectedPlaylist,
)
})
}
return getPlaylistChapters(
return getPlaylistChapters({
episodes: selectedPlaylist.episodes,
matchScore,
profile,
url,
selectedPlaylist.episodes,
)
})
}

@ -1,7 +1,10 @@
import { useMemo } from 'react'
import { useQueryClient } from 'react-query'
import { querieKeys } from 'config'
import type { PlaylistOption } from 'features/MatchPage/types'
import type { MatchInfo } from 'requests/getMatchInfo'
import type { MatchInfo, MatchScore } from 'requests'
import { buildChapters } from '../helpers'
@ -16,13 +19,19 @@ export const useChapters = ({
selectedPlaylist,
url,
}: Args) => {
const client = useQueryClient()
const matchScore = client.getQueryData<MatchScore>(querieKeys.matchScore)
const chapters = useMemo(
() => buildChapters({
matchScore,
profile,
selectedPlaylist,
url,
}),
[
matchScore,
profile,
selectedPlaylist,
url,

@ -57,7 +57,7 @@ export const MatchDescription = () => {
const { data: queryScore } = useQuery({
queryFn: async () => {
if (profile?.live && !isScoreHidden) {
if (profile?.live) {
const score = await getMatchScore({ profileId, sportType })
return score
}

@ -1,11 +0,0 @@
import find from 'lodash/find'
import type { MatchInfo } from 'requests/getMatchInfo'
import { FULL_MATCH_BOUNDARY } from 'features/MatchPage/components/LiveMatch/helpers'
export const calculateDuration = (profile: MatchInfo) => {
const bound = find(profile?.video_bounds, { h: FULL_MATCH_BOUNDARY })
if (!bound) return 0
return Number(bound.e) - Number(bound.s)
}

@ -240,7 +240,7 @@ export const useMatchPage = () => {
: true
), [profile?.date])
const { tournamentData } = useTournamentData(matchProfile?.tournament.id ?? null)
const { tournamentData } = useTournamentData(matchProfile)
const filteredEvents = useMemo(() => {
switch (true) {

@ -17,6 +17,7 @@ import { useMatchPopupStore } from 'features/MatchPopup'
import { useMatchPlaylists } from './useMatchPlaylists'
import { useEvents } from './useEvents'
import { initialPlaylist } from './useSelectedPlaylist'
const MATCH_DATA_POLL_INTERVAL = 60000
const MATCH_PLAYLISTS_DELAY = 5000
@ -43,7 +44,7 @@ export const useMatchData = (profile: MatchInfo) => {
const chaptersDuration = useDuration(chapters) / 1000
const fullMatchDuration = matchDuration
useEffect(() => {
if (!profile) return
if (!profile || (profile.live && Number(profile.c_match_calc_status) <= 1)) return
fetchMatchPlaylists({
fullMatchDuration,
id: matchId,
@ -75,12 +76,12 @@ export const useMatchData = (profile: MatchInfo) => {
})
useEffect(() => {
if (profile?.live) {
if (profile?.live && Number(profile.c_match_calc_status) > 1) {
start()
} else {
stop()
}
}, [profile?.live, start, stop])
}, [profile?.live, profile?.c_match_calc_status, start, stop])
useEffect(() => {
selectedPlaylist?.id === FULL_GAME_KEY && setMatchDuration(chaptersDuration)
@ -88,7 +89,7 @@ export const useMatchData = (profile: MatchInfo) => {
}, [profile, chaptersDuration])
useEffect(() => {
setSelectedPlaylist(matchPlaylists.match[0])
setSelectedPlaylist(matchPlaylists.match[0] || initialPlaylist)
// eslint-disable-next-line
}, [matchId])

@ -17,7 +17,7 @@ import type { Playlists } from 'features/MatchPage/types'
import { buildPlaylists, FULL_GAME_KEY } from 'features/MatchPage/helpers/buildPlaylists'
import { usePlaylistLexics } from './usePlaylistLexics'
import { useSelectedPlaylist } from './useSelectedPlaylist'
import { initialPlaylist, useSelectedPlaylist } from './useSelectedPlaylist'
type ArgsFetchMatchPlaylists = {
fullMatchDuration: number,
@ -71,7 +71,7 @@ export const useMatchPlaylists = (profile: MatchInfo) => {
useEffect(() => {
if (selectedPlaylist?.id !== FULL_GAME_KEY) return
setSelectedPlaylist(matchPlaylists?.match[0])
setSelectedPlaylist(matchPlaylists?.match[0] || initialPlaylist)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
matchPlaylists?.match,

@ -4,6 +4,7 @@ import {
useEffect,
useState,
} from 'react'
import { useQueryClient } from 'react-query'
import throttle from 'lodash/throttle'
import isEmpty from 'lodash/isEmpty'
@ -11,12 +12,16 @@ import every from 'lodash/every'
import find from 'lodash/find'
import isUndefined from 'lodash/isUndefined'
import type {
import { querieKeys } from 'config'
import type { MatchScore } from 'requests'
import {
MatchInfo,
PlayersStats,
Player,
getPlayersStats,
getMatchParticipants,
} from 'requests'
import { getPlayersStats, getMatchParticipants } from 'requests'
import { useObjectState, usePageParams } from 'hooks'
@ -57,6 +62,10 @@ export const usePlayersStats = ({
sportType,
} = usePageParams()
const client = useQueryClient()
const matchScore = client.getQueryData<MatchScore>(querieKeys.matchScore)
const isCurrentStats = statsType === StatsType.CURRENT_STATS
const isEmptyPlayersStats = (teamId: number) => (
@ -66,17 +75,19 @@ export const usePlayersStats = ({
)
const fetchPlayers = useMemo(() => throttle(async (second?: number) => {
const videoBounds = matchScore?.video_bounds || matchProfile?.video_bounds
if (
!matchProfile?.team1.id
|| !matchProfile?.team2.id
|| !matchProfile?.video_bounds
|| !videoBounds
) return null
try {
return getMatchParticipants({
matchId,
sportType,
...(!isUndefined(second) && getHalfTime(matchProfile.video_bounds, second)),
...(!isUndefined(second) && getHalfTime(videoBounds, second)),
})
} catch (e) {
return Promise.reject(e)
@ -86,18 +97,21 @@ export const usePlayersStats = ({
matchProfile?.team1.id,
matchProfile?.team2.id,
matchProfile?.video_bounds,
matchScore?.video_bounds,
sportType,
])
const fetchPlayersStats = useMemo(() => (async (team: 'team1' | 'team2', second?: number) => {
if (!sportName || !matchProfile?.[team].id || !matchProfile?.video_bounds) return null
const videoBounds = matchScore?.video_bounds || matchProfile?.video_bounds
if (!sportName || !matchProfile?.[team].id || !videoBounds) return null
try {
return getPlayersStats({
matchId,
sportName,
teamId: matchProfile[team].id,
...(!isUndefined(second) && getHalfTime(matchProfile.video_bounds, second)),
...(!isUndefined(second) && getHalfTime(videoBounds, second)),
})
} catch (e) {
return Promise.reject(e)
@ -111,7 +125,10 @@ export const usePlayersStats = ({
])
const fetchData = useMemo(() => throttle(async (second?: number) => {
if (selectedPlaylist?.id !== FULL_GAME_KEY || !matchProfile?.video_bounds) return
if (
selectedPlaylist?.id !== FULL_GAME_KEY
|| (matchProfile?.live && Number(matchProfile.c_match_calc_status) <= 1)
) return
const [res1, res2, res3] = await Promise.all([
fetchPlayers(second),
@ -140,7 +157,8 @@ export const usePlayersStats = ({
setPlayersStats,
matchProfile?.team1.id,
matchProfile?.team2.id,
matchProfile?.video_bounds,
matchProfile?.live,
matchProfile?.c_match_calc_status,
setIsPlayersStatsFetching,
])

@ -1,6 +1,8 @@
import type { MouseEvent } from 'react'
import { useState, useCallback } from 'react'
import { indexLexics } from 'config/lexics/indexLexics'
import { getPlayerPlaylists } from 'requests/getPlayerPlaylists'
import { usePageParams } from 'hooks/usePageParams'
@ -11,10 +13,19 @@ import {
PlaylistTypes,
} from 'features/MatchPage/types'
import { defaultSettings } from 'features/MatchPopup/types'
import { FULL_GAME_KEY } from 'features/MatchPage/helpers/buildPlaylists'
export const initialPlaylist = {
duration: 0,
episodes: [],
id: FULL_GAME_KEY,
lexic: indexLexics.full_game,
type: 0,
}
export const useSelectedPlaylist = () => {
const { profileId: matchId, sportType } = usePageParams()
const [selectedPlaylist, setSelectedPlaylist] = useState<PlaylistOption>()
const [selectedPlaylist, setSelectedPlaylist] = useState<PlaylistOption>(initialPlaylist)
const fetchPlayerEpisodes = useCallback((playlistOption: PlayerPlaylistOption) => (
getPlayerPlaylists({

@ -4,14 +4,17 @@ import {
useState,
useMemo,
} from 'react'
import { useQueryClient } from 'react-query'
import throttle from 'lodash/throttle'
import isUndefined from 'lodash/isUndefined'
import type { MatchInfo } from 'requests'
import { querieKeys } from 'config'
import type { MatchInfo, MatchScore } from 'requests'
import { getTeamsStats, TeamStatItem } from 'requests'
import { usePageParams } from 'hooks/usePageParams'
import { usePageParams } from 'hooks'
import type { PlaylistOption } from 'features/MatchPage/types'
import { StatsType } from 'features/MatchSidePlaylists/components/TabStats/config'
@ -42,16 +45,27 @@ export const useTeamsStats = ({
const { profileId: matchId, sportName } = usePageParams()
const client = useQueryClient()
const matchScore = client.getQueryData<MatchScore>(querieKeys.matchScore)
const isCurrentStats = statsType === StatsType.CURRENT_STATS
const fetchTeamsStats = useMemo(() => throttle(async (second?: number) => {
if (!sportName || selectedPlaylist?.id !== FULL_GAME_KEY || !matchProfile?.video_bounds) return
const videoBounds = matchScore?.video_bounds || matchProfile?.video_bounds
if (
!sportName
|| selectedPlaylist?.id !== FULL_GAME_KEY
|| !videoBounds
|| (matchProfile?.live && Number(matchProfile.c_match_calc_status) <= 1)
) return
try {
const data = await getTeamsStats({
matchId,
sportName,
...(!isUndefined(second) && getHalfTime(matchProfile.video_bounds, second)),
...(!isUndefined(second) && getHalfTime(videoBounds, second)),
})
setTeamsStats(data)
@ -61,6 +75,9 @@ export const useTeamsStats = ({
} catch (e) {}
}, REQUEST_DELAY), [
matchProfile?.video_bounds,
matchProfile?.c_match_calc_status,
matchProfile?.live,
matchScore?.video_bounds,
selectedPlaylist?.id,
matchId,
setIsTeamsStatsFetching,

@ -12,6 +12,7 @@ import sortBy from 'lodash/sortBy'
import type { Match } from 'features/Matches'
import { prepareMatches } from 'features/Matches/helpers/prepareMatches'
import type { MatchInfo } from 'requests'
import { getTournamentMatches } from 'requests'
import { parseDate } from 'helpers/parseDate'
@ -20,14 +21,18 @@ import { usePageParams } from 'hooks/usePageParams'
import { TournamentData } from '../../types'
export const useTournamentData = (tournamentId: number | null) => {
export const useTournamentData = (matchProfile: MatchInfo) => {
const { sportType } = usePageParams()
const [tournamentMatches, setTournamentMatches] = useState<Array<Match>>([])
const [matchDates, setMatchDates] = useState<Array<string>>([])
const tournamentId = matchProfile?.tournament.id ?? null
useEffect(() => {
if (!isNull(tournamentId)) {
if (matchProfile?.live && Number(matchProfile.c_match_calc_status) <= 1) return
(async () => {
const matchesBySection = await getTournamentMatches({
limit: 1000,
@ -44,7 +49,12 @@ export const useTournamentData = (tournamentId: number | null) => {
setTournamentMatches(sortBy(prepareMatches(matchesBySection.broadcast), ['date']))
})()
}
}, [tournamentId, sportType])
}, [
tournamentId,
sportType,
matchProfile?.live,
matchProfile?.c_match_calc_status,
])
const tournamentData: TournamentData = useMemo(() => ({
matchDates,

@ -11,6 +11,7 @@ import {
useLayoutEffect,
useMemo,
} from 'react'
import { useQueryClient } from 'react-query'
import size from 'lodash/size'
import isNil from 'lodash/isNil'
@ -19,9 +20,13 @@ import forEach from 'lodash/forEach'
import values from 'lodash/values'
import map from 'lodash/map'
import { isMobileDevice } from 'config'
import { isMobileDevice, querieKeys } from 'config'
import type { PlayerParam, PlayersStats } from 'requests'
import type {
PlayerParam,
PlayersStats,
MatchScore,
} from 'requests'
import { getStatsEvents } from 'requests'
import { usePageParams, useToggle } from 'hooks'
@ -74,6 +79,10 @@ export const useTable = ({
} = useMatchPageStore()
const { profileId, sportType } = usePageParams()
const client = useQueryClient()
const matchScore = client.getQueryData<MatchScore>(querieKeys.matchScore)
const params = useMemo(() => (
reduce<PlayersStats, Record<string, HeaderParam>>(
playersStats[teamId],
@ -181,6 +190,8 @@ export const useTable = ({
setWatchAllEpisodesTimer(false)
setIsPlayingFiltersEpisodes(false)
const videoBounds = matchScore?.video_bounds || profile?.video_bounds
setPlayingData({
player: {
id: playerId,
@ -199,8 +210,8 @@ export const useTable = ({
playerId,
sportType,
teamId,
...(statsType === StatsType.CURRENT_STATS && profile?.video_bounds && (
getHalfTime(profile.video_bounds, playingProgress)
...(statsType === StatsType.CURRENT_STATS && videoBounds && (
getHalfTime(videoBounds, playingProgress)
)),
})

@ -1,10 +1,15 @@
import { Fragment, useRef } from 'react'
import { useQueryClient } from 'react-query'
import isNumber from 'lodash/isNumber'
import { KEYBOARD_KEYS } from 'config'
import { KEYBOARD_KEYS, querieKeys } from 'config'
import type { Param, TeamStatItem } from 'requests'
import type {
Param,
TeamStatItem,
MatchScore,
} from 'requests'
import { getStatsEvents } from 'requests'
import { usePageParams, useEventListener } from 'hooks'
@ -47,6 +52,10 @@ export const Cell = ({
watchAllEpisodesTimer,
} = useMatchPageStore()
const client = useQueryClient()
const matchScore = client.getQueryData<MatchScore>(querieKeys.matchScore)
const isClickable = (param: Param) => (
Boolean(param.val) && param.clickable
)
@ -58,6 +67,8 @@ export const Cell = ({
const onParamClick = async (param: Param) => {
if (!isClickable(param)) return
const videoBounds = matchScore?.video_bounds || profile?.video_bounds
setWatchAllEpisodesTimer(false)
setIsPlayingFiltersEpisodes(false)
@ -78,8 +89,8 @@ export const Cell = ({
paramId: param.id,
sportType,
teamId,
...(statsType === StatsType.CURRENT_STATS && profile?.video_bounds && (
getHalfTime(profile.video_bounds, playingProgress)
...(statsType === StatsType.CURRENT_STATS && videoBounds && (
getHalfTime(videoBounds, playingProgress)
)),
})

@ -19,7 +19,7 @@ export const useMatchSidePlaylists = () => {
teamsStats,
} = useMatchPageStore()
const isWatchTabVisible = true
const isWatchTabVisible = !matchProfile?.live || Number(matchProfile.c_match_calc_status) > 1
const isEventTabVisible = useMemo(() => (
events.length > 0

@ -35,8 +35,17 @@ export type VideoBound = {
export type VideoBounds = Array<VideoBound>
export enum MatchStatuses {
Upcoming = 1,
Active,
Timeout,
Finished,
Parsed,
}
export type MatchInfo = {
access?: boolean,
c_match_calc_status: MatchStatuses | null,
calc: boolean,
country: TournamentType,
country_id: number,

@ -2,14 +2,18 @@ import { callApi } from 'helpers'
import { API_ROOT } from 'config'
import type { Team, MatchTournament } from 'requests/getMatchInfo'
import type {
Team,
MatchTournament,
VideoBounds,
} from 'requests/getMatchInfo'
type Params = {
profileId: number,
sportType: number,
}
type Response = {
export type MatchScore = {
match_date: string,
match_date_utc: string,
match_id: number,
@ -17,9 +21,10 @@ type Response = {
team1: Team,
team2: Team,
tournament: MatchTournament,
video_bounds: VideoBounds | null,
}
export const getMatchScore = ({ profileId, sportType }: Params): Promise<Response> => {
export const getMatchScore = ({ profileId, sportType }: Params): Promise<MatchScore> => {
const url = `${API_ROOT}/v1/matches/${sportType}/${profileId}/scores`
const config = {

Loading…
Cancel
Save