From e202aa1d3043f0208bdc80c4636a0e5ccd80d745 Mon Sep 17 00:00:00 2001 From: Ruslan Khayrullin Date: Thu, 18 May 2023 19:53:18 +0500 Subject: [PATCH] feat(in-603): send requests only for open tabs --- src/features/MatchPage/store/hooks/index.tsx | 8 +- .../MatchPage/store/hooks/useMatchData.tsx | 18 ++- .../MatchPage/store/hooks/usePlayersStats.tsx | 54 ++++--- .../MatchPage/store/hooks/useTeamsStats.tsx | 18 ++- .../components/TabStats/hooks.tsx | 43 +----- .../components/TabStats/index.tsx | 141 ++++++++---------- src/features/MatchSidePlaylists/hooks.tsx | 76 ++-------- src/features/MatchSidePlaylists/index.tsx | 118 +++++++-------- src/features/MatchSidePlaylists/styled.tsx | 20 +-- src/hooks/useInterval.tsx | 4 +- src/requests/getMatchParticipants.tsx | 2 +- src/requests/getMatchScore.tsx | 4 +- 12 files changed, 210 insertions(+), 296 deletions(-) diff --git a/src/features/MatchPage/store/hooks/index.tsx b/src/features/MatchPage/store/hooks/index.tsx index 832a480e..1d7ee57a 100644 --- a/src/features/MatchPage/store/hooks/index.tsx +++ b/src/features/MatchPage/store/hooks/index.tsx @@ -111,7 +111,7 @@ export const useMatchPage = () => { matchPlaylists, selectedPlaylist, setFullMatchPlaylistDuration, - } = useMatchData({ matchProfile }) + } = useMatchData({ matchProfile, selectedTab }) const profile = matchProfile @@ -297,6 +297,8 @@ export const useMatchPage = () => { matchProfile, playingProgress, selectedPlaylist, + selectedStatsTable, + selectedTab, setIsTeamsStatsFetching, statsType, }) @@ -304,13 +306,14 @@ export const useMatchPage = () => { const { beforeCloseTourCallback: beforeCloseTourCallbackPlayers, getParams, - isEmptyPlayersStats, playersData, playersStats, } = usePlayersStats({ matchProfile, playingProgress, selectedPlaylist, + selectedStatsTable, + selectedTab, setIsPlayersStatsFetching, statsType, }) @@ -413,7 +416,6 @@ export const useMatchPage = () => { isAllActionsChecked, isClickable, isEmptyFilters, - isEmptyPlayersStats, isExpanded, isFirstTeamPlayersChecked, isLiveMatch, diff --git a/src/features/MatchPage/store/hooks/useMatchData.tsx b/src/features/MatchPage/store/hooks/useMatchData.tsx index 4e12e383..5d6d9302 100644 --- a/src/features/MatchPage/store/hooks/useMatchData.tsx +++ b/src/features/MatchPage/store/hooks/useMatchData.tsx @@ -4,6 +4,8 @@ import { useCallback, } from 'react' +import isEmpty from 'lodash/isEmpty' + import type { MatchInfo } from 'requests/getMatchInfo' import { @@ -14,6 +16,7 @@ import { import { FULL_GAME_KEY } from 'features/MatchPage/helpers/buildPlaylists' import { useMatchPopupStore } from 'features/MatchPopup' +import { Tabs } from 'features/MatchSidePlaylists/config' import { useMatchPlaylists } from './useMatchPlaylists' import { useEvents } from './useEvents' @@ -23,9 +26,10 @@ const MATCH_DATA_POLL_INTERVAL = 5000 type UseMatchDataArgs = { matchProfile: MatchInfo, + selectedTab: Tabs, } -export const useMatchData = ({ matchProfile: profile }: UseMatchDataArgs) => { +export const useMatchData = ({ matchProfile: profile, selectedTab }: UseMatchDataArgs) => { const { profileId: matchId, sportType } = usePageParams() const { chapters } = useMatchPopupStore() const [matchDuration, setMatchDuration] = useState(0) @@ -42,41 +46,43 @@ export const useMatchData = ({ matchProfile: profile }: UseMatchDataArgs) => { const chaptersDuration = useDuration(chapters) / 1000 const fullMatchDuration = matchDuration + useEffect(() => { if (!profile || profile.live) return - fetchMatchPlaylists({ + isEmpty(matchPlaylists.match) && selectedTab === Tabs.WATCH && fetchMatchPlaylists({ fullMatchDuration, id: matchId, sportType, }) - fetchMatchEvents() + isEmpty(events) && selectedTab === Tabs.EVENTS && fetchMatchEvents() // eslint-disable-next-line react-hooks/exhaustive-deps }, [ profile?.live, fullMatchDuration, matchId, sportType, + selectedTab, ]) const intervalCallback = useCallback(() => { - fetchMatchPlaylists({ + (selectedTab === Tabs.WATCH || selectedTab === Tabs.PLAYERS) && fetchMatchPlaylists({ fullMatchDuration, id: matchId, sportType, }) - fetchMatchEvents() + selectedTab === Tabs.EVENTS && fetchMatchEvents() // eslint-disable-next-line react-hooks/exhaustive-deps }, [ fullMatchDuration, matchId, sportType, + selectedTab, ]) const { start, stop } = useInterval({ callback: intervalCallback, intervalDuration: MATCH_DATA_POLL_INTERVAL, - startImmediate: false, }) useEffect(() => { diff --git a/src/features/MatchPage/store/hooks/usePlayersStats.tsx b/src/features/MatchPage/store/hooks/usePlayersStats.tsx index c0c75771..f46f1c69 100644 --- a/src/features/MatchPage/store/hooks/usePlayersStats.tsx +++ b/src/features/MatchPage/store/hooks/usePlayersStats.tsx @@ -9,7 +9,7 @@ import { useQueryClient } from 'react-query' import throttle from 'lodash/throttle' import isEmpty from 'lodash/isEmpty' -import every from 'lodash/every' +import includes from 'lodash/includes' import find from 'lodash/find' import isUndefined from 'lodash/isUndefined' import flatMapDepth from 'lodash/flatMapDepth' @@ -19,7 +19,11 @@ import size from 'lodash/size' import { querieKeys } from 'config' -import type { MatchScore, PlayerParam } from 'requests' +import type { + DataItem, + MatchScore, + PlayerParam, +} from 'requests' import { MatchInfo, PlayersStats, @@ -33,21 +37,24 @@ import { getLocalStorageItem } from 'helpers/getLocalStorage' import { useObjectState, usePageParams } from 'hooks' import type{ PlaylistOption } from 'features/MatchPage/types' -import { StatsType } from 'features/MatchSidePlaylists/components/TabStats/config' +import { StatsType, Tabs as StatsTabs } from 'features/MatchSidePlaylists/components/TabStats/config' import { FULL_GAME_KEY } from 'features/MatchPage/helpers/buildPlaylists' import { getHalfTime } from 'features/MatchPage/helpers/getHalfTime' import { TOUR_COMPLETED_STORAGE_KEY } from 'features/MatchTour' +import { Tabs } from 'features/MatchSidePlaylists/config' import { DISPLAYED_PARAMS_COLUMNS } from 'features/MatchSidePlaylists/components/PlayersTable/config' import { useFakeData } from './useFakeData' -const REQUEST_DELAY = 3000 +const REQUEST_DELAY = 5000 const STATS_POLL_INTERVAL = 30000 type UsePlayersStatsArgs = { matchProfile: MatchInfo, playingProgress: number, selectedPlaylist?: PlaylistOption, + selectedStatsTable: StatsTabs, + selectedTab: Tabs, setIsPlayersStatsFetching: Dispatch>, statsType: StatsType, } @@ -61,6 +68,8 @@ export const usePlayersStats = ({ matchProfile, playingProgress, selectedPlaylist, + selectedStatsTable, + selectedTab, setIsPlayersStatsFetching, statsType, }: UsePlayersStatsArgs) => { @@ -81,12 +90,6 @@ export const usePlayersStats = ({ const isCurrentStats = statsType === StatsType.CURRENT_STATS - const isEmptyPlayersStats = (teamId: number) => ( - isEmpty(playersStats[teamId]) - || every(playersStats[teamId], isEmpty) - || isEmpty(playersData[matchProfile?.team1.id === teamId ? 'team1' : 'team2']) - ) - const getParams = useCallback((stats: PlayersStats) => ( uniqBy(flatMapDepth(stats, values), 'id') as Array ), []) @@ -141,16 +144,25 @@ export const usePlayersStats = ({ ]) const fetchData = useMemo(() => throttle(async (second?: number) => { + const isTeam1Selected = selectedStatsTable === StatsTabs.TEAM1 + if ( selectedPlaylist?.id !== FULL_GAME_KEY - || (matchProfile?.live && Number(matchProfile.c_match_calc_status) <= 1) + || selectedTab !== Tabs.STATS + || !includes([StatsTabs.TEAM1, StatsTabs.TEAM2], selectedStatsTable) + || !matchProfile?.team1.id + || !matchProfile?.team2.id + || (!matchProfile?.live && !isCurrentStats && !isEmpty(playersStats[isTeam1Selected + ? matchProfile.team1.id + : matchProfile.team2.id])) ) return - const [res1, res2, res3] = await Promise.all([ + const [res1, res2] = await Promise.all([ fetchPlayers(second), - fetchPlayersStats('team1', second), - fetchPlayersStats('team2', second), - ]) + isTeam1Selected + ? fetchPlayersStats('team1', second) + : fetchPlayersStats('team2', second), + ]) as [Array | null, PlayersStats | null] const team1Players = find(res1, { team_id: matchProfile?.team1.id })?.players || [] const team2Players = find(res1, { team_id: matchProfile?.team2.id })?.players || [] @@ -168,19 +180,20 @@ export const usePlayersStats = ({ }) setPlayersStats({ - ...(matchProfile?.team1.id && res2 && { + ...(isTeam1Selected && res2 && { [matchProfile.team1.id]: needUseFakeData ? fakeData.playersStats as unknown as PlayersStats : res2, }), - ...(matchProfile?.team2.id && res3 && { + ...(!isTeam1Selected && res2 && { [matchProfile.team2.id]: needUseFakeData ? fakeData.playersStats as unknown as PlayersStats - : res3, + : res2, }), }) setIsPlayersStatsFetching(false) + // eslint-disable-next-line react-hooks/exhaustive-deps }, REQUEST_DELAY), [ selectedPlaylist?.id, fetchPlayers, @@ -189,10 +202,12 @@ export const usePlayersStats = ({ matchProfile?.team1.id, matchProfile?.team2.id, matchProfile?.live, - matchProfile?.c_match_calc_status, setIsPlayersStatsFetching, getParams, fakeData, + selectedTab, + selectedStatsTable, + isCurrentStats, ]) const beforeCloseTourCallback = () => { @@ -233,7 +248,6 @@ export const usePlayersStats = ({ return { beforeCloseTourCallback, getParams, - isEmptyPlayersStats, playersData, playersStats, } diff --git a/src/features/MatchPage/store/hooks/useTeamsStats.tsx b/src/features/MatchPage/store/hooks/useTeamsStats.tsx index 8017b99d..b2c0c862 100644 --- a/src/features/MatchPage/store/hooks/useTeamsStats.tsx +++ b/src/features/MatchPage/store/hooks/useTeamsStats.tsx @@ -26,20 +26,23 @@ import { usePageParams } from 'hooks' import { getLocalStorageItem } from 'helpers/getLocalStorage' import type { PlaylistOption } from 'features/MatchPage/types' -import { StatsType } from 'features/MatchSidePlaylists/components/TabStats/config' +import { StatsType, Tabs as StatsTab } from 'features/MatchSidePlaylists/components/TabStats/config' import { FULL_GAME_KEY } from 'features/MatchPage/helpers/buildPlaylists' import { getHalfTime } from 'features/MatchPage/helpers/getHalfTime' import { TOUR_COMPLETED_STORAGE_KEY } from 'features/MatchTour' +import { Tabs } from 'features/MatchSidePlaylists/config' import { useFakeData } from './useFakeData' -const REQUEST_DELAY = 3000 +const REQUEST_DELAY = 5000 const STATS_POLL_INTERVAL = 30000 type UseTeamsStatsArgs = { matchProfile: MatchInfo, playingProgress: number, selectedPlaylist?: PlaylistOption, + selectedStatsTable: StatsTab, + selectedTab: Tabs, setIsTeamsStatsFetching: Dispatch>, statsType: StatsType, } @@ -52,6 +55,8 @@ export const useTeamsStats = ({ matchProfile, playingProgress, selectedPlaylist, + selectedStatsTable, + selectedTab, setIsTeamsStatsFetching, statsType, }: UseTeamsStatsArgs) => { @@ -98,7 +103,9 @@ export const useTeamsStats = ({ !sportName || selectedPlaylist?.id !== FULL_GAME_KEY || !videoBounds - || (matchProfile?.live && Number(matchProfile.c_match_calc_status) <= 1) + || selectedTab !== Tabs.STATS + || selectedStatsTable !== StatsTab.TEAMS + || (!matchProfile?.live && !isCurrentStats && !isEmpty(teamsStats)) ) return try { @@ -117,9 +124,9 @@ export const useTeamsStats = ({ // eslint-disable-next-line no-empty } catch (e) {} + // eslint-disable-next-line react-hooks/exhaustive-deps }, REQUEST_DELAY), [ matchProfile?.video_bounds, - matchProfile?.c_match_calc_status, matchProfile?.live, matchScore?.video_bounds, selectedPlaylist?.id, @@ -128,6 +135,9 @@ export const useTeamsStats = ({ sportName, fakeData, getFirstClickableParam, + selectedTab, + selectedStatsTable, + isCurrentStats, ]) const beforeCloseTourCallback = () => { diff --git a/src/features/MatchSidePlaylists/components/TabStats/hooks.tsx b/src/features/MatchSidePlaylists/components/TabStats/hooks.tsx index 79f8c957..0ad509df 100644 --- a/src/features/MatchSidePlaylists/components/TabStats/hooks.tsx +++ b/src/features/MatchSidePlaylists/components/TabStats/hooks.tsx @@ -1,21 +1,14 @@ -import { useEffect } from 'react' - -import isEmpty from 'lodash/isEmpty' - import { useTooltip } from 'hooks' import { useMatchPageStore } from 'features/MatchPage/store' -import { StatsType, Tabs } from './config' +import { StatsType } from './config' export const useTabStats = () => { const { - isEmptyPlayersStats, - profile: matchProfile, selectedStatsTable: selectedTab, setSelectedStatsTable: setSelectedTab, statsType, - teamsStats, toggleStatsType, } = useMatchPageStore() @@ -32,43 +25,9 @@ export const useTabStats = () => { const switchTitleLexic = isFinalStatsType ? 'final_stats' : 'current_stats' const switchButtonTooltipLexic = isFinalStatsType ? 'display_all_stats' : 'display_stats_according_to_video' - const isVisibleTeamsTab = !isEmpty(teamsStats) - const isVisibleTeam1PlayersTab = Boolean( - matchProfile && !isEmptyPlayersStats(matchProfile.team1.id), - ) - const isVisibleTeam2PlayersTab = Boolean( - matchProfile && !isEmptyPlayersStats(matchProfile.team2.id), - ) - - useEffect(() => { - switch (true) { - case isVisibleTeamsTab: - setSelectedTab(Tabs.TEAMS) - break - - case isVisibleTeam1PlayersTab: - setSelectedTab(Tabs.TEAM1) - break - - case isVisibleTeam2PlayersTab: - setSelectedTab(Tabs.TEAM2) - break - - default: - } - }, [ - isVisibleTeam1PlayersTab, - isVisibleTeam2PlayersTab, - isVisibleTeamsTab, - setSelectedTab, - ]) - return { isFinalStatsType, isTooltipShown, - isVisibleTeam1PlayersTab, - isVisibleTeam2PlayersTab, - isVisibleTeamsTab, onMouseLeave, onMouseOver, selectedTab, diff --git a/src/features/MatchSidePlaylists/components/TabStats/index.tsx b/src/features/MatchSidePlaylists/components/TabStats/index.tsx index a9f343b0..d6cedeed 100644 --- a/src/features/MatchSidePlaylists/components/TabStats/index.tsx +++ b/src/features/MatchSidePlaylists/components/TabStats/index.tsx @@ -46,9 +46,6 @@ export const TabStats = () => { const { isFinalStatsType, isTooltipShown, - isVisibleTeam1PlayersTab, - isVisibleTeam2PlayersTab, - isVisibleTeamsTab, onMouseLeave, onMouseOver, selectedTab, @@ -76,80 +73,74 @@ export const TabStats = () => {
- {isVisibleTeamsTab && ( - setSelectedTab(Tabs.TEAMS)} - data-step={Steps.TeamsTab} + setSelectedTab(Tabs.TEAMS)} + data-step={Steps.TeamsTab} + > + + + + {Boolean(currentStep === Steps.TeamsTab && isOpen) && ( + + )} + + setSelectedTab(Tabs.TEAM1)} + data-step={Steps.PlayersTab} + id='match_stats_team1' + > + - - - - {Boolean(currentStep === Steps.TeamsTab && isOpen) && ( - - )} - - )} - {isVisibleTeam1PlayersTab && ( - setSelectedTab(Tabs.TEAM1)} - data-step={Steps.PlayersTab} - id='match_stats_team1' - > - - - - {Boolean(currentStep === Steps.PlayersTab && isOpen) && ( - - )} - - )} - {isVisibleTeam2PlayersTab && ( - setSelectedTab(Tabs.TEAM2)} - id='match_stats_team2' + + + {Boolean(currentStep === Steps.PlayersTab && isOpen) && ( + + )} + + setSelectedTab(Tabs.TEAM2)} + id='match_stats_team2' + > + - - - - - )} + + + { const { closePopup, - events, - isEmptyPlayersStats, - matchPlaylists: playlists, profile: matchProfile, selectedTab, setSelectedTab, - teamsStats, } = useMatchPageStore() + const client = useQueryClient() - const isWatchTabVisible = !matchProfile?.live || Number(matchProfile.c_match_calc_status) > 1 - - const isEventTabVisible = useMemo(() => ( - events.length > 0 - ), [events]) - - const isPlayersTabVisible = useMemo(() => ( - !isEmpty(playlists.players.team1) - ), [playlists.players.team1]) + const matchScore = client.getQueryData(querieKeys.matchScore) - const isStatsTabVisible = useMemo(() => ( - !isEmpty(teamsStats) - || (matchProfile?.team1.id && !isEmptyPlayersStats(matchProfile.team1.id)) - || (matchProfile?.team2.id && !isEmptyPlayersStats(matchProfile.team2.id)) - ), [ - isEmptyPlayersStats, - matchProfile?.team1.id, - matchProfile?.team2.id, - teamsStats, - ]) + const videoBounds = matchScore?.video_bounds || matchProfile?.video_bounds + const matchStatus = matchScore?.c_match_calc_status || matchProfile?.c_match_calc_status - const hasLessThanFourTabs = compact([ - isWatchTabVisible, - isEventTabVisible, - isPlayersTabVisible, - isStatsTabVisible, - ]).length < 4 - - useEffect(() => { - switch (true) { - case isWatchTabVisible: - setSelectedTab(Tabs.WATCH) - break - case isEventTabVisible: - setSelectedTab(Tabs.EVENTS) - break - case isPlayersTabVisible: - setSelectedTab(Tabs.PLAYERS) - break - case isStatsTabVisible: - setSelectedTab(Tabs.STATS) - break - } - }, [ - isEventTabVisible, - isPlayersTabVisible, - isStatsTabVisible, - isWatchTabVisible, - setSelectedTab, - ]) + const showTabs = Number(matchStatus) > MatchStatuses.Upcoming + && findIndex(videoBounds, ({ h, s }) => h === '1' && !isNil(s)) !== -1 useEffect(() => { if (selectedTab !== Tabs.EVENTS) closePopup() }, [selectedTab, closePopup]) return { - hasLessThanFourTabs, - isEventTabVisible, - isPlayersTabVisible, - isStatsTabVisible, - isWatchTabVisible, onTabClick: setSelectedTab, selectedTab, + showTabs, } } diff --git a/src/features/MatchSidePlaylists/index.tsx b/src/features/MatchSidePlaylists/index.tsx index 81cb11da..4af4749a 100644 --- a/src/features/MatchSidePlaylists/index.tsx +++ b/src/features/MatchSidePlaylists/index.tsx @@ -67,12 +67,8 @@ export const MatchSidePlaylists = ({ } = useMatchPageStore() const { - hasLessThanFourTabs, - isEventTabVisible, - isPlayersTabVisible, - isStatsTabVisible, - isWatchTabVisible, onTabClick, + showTabs, } = useMatchSidePlaylists() const { @@ -108,14 +104,14 @@ export const MatchSidePlaylists = ({ if ( getLocalStorageItem(TOUR_COMPLETED_STORAGE_KEY) === 'true' || isOpen - || !isStatsTabVisible + || !showTabs || Number(profile?.c_match_calc_status) < 2 ) return undefined const timer = setTimeout(() => setIsOpen(true), 1500) return () => clearTimeout(timer) - }, [isStatsTabVisible, setIsOpen, profile?.c_match_calc_status, isOpen]) + }, [showTabs, setIsOpen, profile?.c_match_calc_status, isOpen]) useEventListener({ callback: () => { @@ -140,63 +136,57 @@ export const MatchSidePlaylists = ({ isTourOpen={Boolean(isOpen)} isHidden={!profileCardShown} > - - - {isWatchTabVisible ? ( - onTabClick(Tabs.WATCH)} - id='match_watch' - > - - - - - - ) : null} - {isEventTabVisible ? ( - onTabClick(Tabs.EVENTS)} - id='match_plays' - > - - - - - - ) : null} - {isPlayersTabVisible ? ( - onTabClick(Tabs.PLAYERS)} - id='match_players' - > - - - - - - ) : null} - {isStatsTabVisible ? ( - onTabClick(Tabs.STATS)} - data-step={Steps.Start} - id='match_stats' - > - {Boolean(currentStep === Steps.Start && isOpen) && ( - - )} - - - - - - ) : null} - - - + {showTabs + && ( + + + onTabClick(Tabs.WATCH)} + id='match_watch' + > + + + + + + onTabClick(Tabs.EVENTS)} + id='match_plays' + > + + + + + + onTabClick(Tabs.PLAYERS)} + id='match_players' + > + + + + + + onTabClick(Tabs.STATS)} + data-step={Steps.Start} + id='match_stats' + > + {Boolean(currentStep === Steps.Start && isOpen) && ( + + )} + + + + + + + + )} ` export const TabsWrapper = styled.div`` -type TabsGroupProps = { - hasLessThanFourTabs?: boolean, -} - -export const TabsGroup = styled.div.attrs({ role: 'tablist' })` +export const TabsGroup = styled.div.attrs({ role: 'tablist' })` display: flex; justify-content: center; gap: ${isMobileDevice ? 30 : 20}px; - - ${({ hasLessThanFourTabs }) => (hasLessThanFourTabs - ? css` - ${Tab} { - justify-content: center; - flex-direction: row; - gap: 5px; - } - - ${TabIcon} { - margin-bottom: 0; - } - ` - : '')} ` export const TabTitle = styled(T9n)` diff --git a/src/hooks/useInterval.tsx b/src/hooks/useInterval.tsx index eaf9cd9c..468c721a 100644 --- a/src/hooks/useInterval.tsx +++ b/src/hooks/useInterval.tsx @@ -29,9 +29,11 @@ export const useInterval = ({ useEffect(() => { if (!isRunning) return undefined + if (startImmediate) savedCallback.current() + const id = setInterval(savedCallback.current, intervalDuration) return () => clearInterval(id) - }, [isRunning, intervalDuration, callback]) + }, [isRunning, intervalDuration, callback, startImmediate]) return { start, stop } } diff --git a/src/requests/getMatchParticipants.tsx b/src/requests/getMatchParticipants.tsx index 1be5bd60..0f44029c 100644 --- a/src/requests/getMatchParticipants.tsx +++ b/src/requests/getMatchParticipants.tsx @@ -28,7 +28,7 @@ export type Player = { weight: number | null, } -type DataItem = { +export type DataItem = { players: Array, team_id: number, } diff --git a/src/requests/getMatchScore.tsx b/src/requests/getMatchScore.tsx index 523e965a..de21d2a0 100644 --- a/src/requests/getMatchScore.tsx +++ b/src/requests/getMatchScore.tsx @@ -6,7 +6,8 @@ import type { Team, MatchTournament, VideoBounds, -} from 'requests/getMatchInfo' +} from './getMatchInfo' +import { MatchStatuses } from './getMatchInfo' type Params = { profileId: number, @@ -14,6 +15,7 @@ type Params = { } export type MatchScore = { + c_match_calc_status: MatchStatuses | null, match_date: string, match_date_utc: string, match_id: number,