diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index 16b79274..ab2522f1 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -15,4 +15,5 @@ export const PROCEDURES = { lst_c_country: 'lst_c_country', param_lexical: 'param_lexical', save_user_favorite: 'save_user_favorite', + save_user_match_second: 'save_user_match_second', } diff --git a/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx b/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx index 5b43980b..cc0be53d 100644 --- a/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx +++ b/src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx @@ -35,6 +35,7 @@ export const useSportTypeFilter = () => { const onResetSelectedSport = (e: MouseEvent) => { e.stopPropagation() setSelectedSportTypeId(null) + setSelectedTournamentId(null) } const selectedSportType = find(sportList, (sport) => sport.id === selectedSportTypeId) diff --git a/src/features/MatchPage/MatchProfileCard/hooks.tsx b/src/features/MatchPage/MatchProfileCard/hooks.tsx new file mode 100644 index 00000000..2cdbb102 --- /dev/null +++ b/src/features/MatchPage/MatchProfileCard/hooks.tsx @@ -0,0 +1,37 @@ +import { useEffect, useState } from 'react' + +import type { MatchInfo } from 'requests' +import { getMatchInfo } from 'requests' + +import { useLexicsStore } from 'features/LexicsStore' + +import { useSportNameParam, usePageId } from 'hooks' + +type Name = 'name_rus' | 'name_eng' + +export const useMatchProfileCard = () => { + const [matchProfile, setMatchProfile] = useState(null) + const { sportType } = useSportNameParam() + const pageId = usePageId() + const { suffix } = useLexicsStore() + + const matchProfileNames = { + team1Name: matchProfile?.team1[`name_${suffix}` as Name], + team2Name: matchProfile?.team2[`name_${suffix}` as Name], + tournament: matchProfile?.tournament[`name_${suffix}` as Name], + } + + useEffect(() => { + getMatchInfo(sportType, pageId) + .then(setMatchProfile) + }, + [ + sportType, + pageId, + ]) + + return { + matchProfile, + matchProfileNames, + } +} diff --git a/src/features/MatchPage/MatchProfileCard/index.tsx b/src/features/MatchPage/MatchProfileCard/index.tsx index fc6e4dea..943e43f1 100644 --- a/src/features/MatchPage/MatchProfileCard/index.tsx +++ b/src/features/MatchPage/MatchProfileCard/index.tsx @@ -8,7 +8,7 @@ import { SportName } from 'features/Common/SportName' import { useScoreStore } from 'features/ToggleScore' import { useSportNameParam } from 'hooks/useSportNameParam' -import { useMatchPage } from '../hooks' +import { useMatchProfileCard } from './hooks' import { Wrapper, @@ -26,7 +26,7 @@ export const MatchProfileCard = () => { team2Name, tournament, }, - } = useMatchPage() + } = useMatchProfileCard() const { sportName, sportType } = useSportNameParam() const { isHidden } = useScoreStore() diff --git a/src/features/MatchPage/hooks.tsx b/src/features/MatchPage/hooks.tsx index cbd18b56..e721993b 100644 --- a/src/features/MatchPage/hooks.tsx +++ b/src/features/MatchPage/hooks.tsx @@ -1,37 +1,43 @@ -import { useEffect, useState } from 'react' +import { useCallback, useRef } from 'react' -import type { MatchInfo } from 'requests' -import { getMatchInfo } from 'requests' +import { reportPlayerProgress } from 'requests' -import { useLexicsStore } from 'features/LexicsStore' +import { + useSportNameParam, + usePageId, + useInterval, +} from 'hooks' -import { useSportNameParam, usePageId } from 'hooks' +const reportRequestInterval = 30000 -type Name = 'name_rus' | 'name_eng' - -export const useMatchPage = () => { - const [matchProfile, setMatchProfile] = useState(null) +export const usePlayerProgressReporter = () => { const { sportType } = useSportNameParam() - const pageId = usePageId() - const { suffix } = useLexicsStore() - - const matchProfileNames = { - team1Name: matchProfile?.team1[`name_${suffix}` as Name], - team2Name: matchProfile?.team2[`name_${suffix}` as Name], - tournament: matchProfile?.tournament[`name_${suffix}` as Name], - } - - useEffect(() => { - getMatchInfo(sportType, pageId) - .then(setMatchProfile) - }, - [ - sportType, - pageId, - ]) - - return { - matchProfile, - matchProfileNames, + const matchId = usePageId() + const secondsRef = useRef(0) + + const intervalCallback = () => { + reportPlayerProgress({ + matchId, + seconds: secondsRef.current, + sport: sportType, + }) } + const { start, stop } = useInterval({ + callback: intervalCallback, + intervalDuration: reportRequestInterval, + }) + + const onPlayingChange = useCallback((playing: boolean) => { + if (playing) { + start() + } else { + stop() + } + }, [start, stop]) + + const onPlayerProgressChange = useCallback((seconds: number) => { + secondsRef.current = seconds + }, []) + + return { onPlayerProgressChange, onPlayingChange } } diff --git a/src/features/MatchPage/index.tsx b/src/features/MatchPage/index.tsx index 971edfc6..770972a5 100644 --- a/src/features/MatchPage/index.tsx +++ b/src/features/MatchPage/index.tsx @@ -3,15 +3,24 @@ import React from 'react' import { VideoPlayer } from 'features/VideoPlayer' import { MatchProfileCard } from './MatchProfileCard' +import { usePlayerProgressReporter } from './hooks' import { MainWrapper, Container } from './styled' const url = 'https://bserv.instatfootball.tv/common/outhls.m3u8' -export const MatchPage = () => ( - - - - - - -) +export const MatchPage = () => { + const { onPlayerProgressChange, onPlayingChange } = usePlayerProgressReporter() + + return ( + + + + + + + ) +} diff --git a/src/features/QueryParamsStorage/index.tsx b/src/features/QueryParamsStorage/index.tsx index 84568cb4..285eed52 100644 --- a/src/features/QueryParamsStorage/index.tsx +++ b/src/features/QueryParamsStorage/index.tsx @@ -6,10 +6,9 @@ import type { History } from 'history' import { history } from 'config/history' /** - * Не явно наследует от Storage (https://developer.mozilla.org/ru/docs/Web/API/Storage) - * прямое наследование выдает ошибку: TypeError: Illegal constructor + * Имплементит Storage API (https://developer.mozilla.org/ru/docs/Web/API/Storage) */ -class QueryParamStorage { +class QueryParamStorage implements Storage { /** * через history обновляем url строку */ diff --git a/src/features/VideoPlayer/hooks/index.tsx b/src/features/VideoPlayer/hooks/index.tsx index 1ae88352..ffcb7489 100644 --- a/src/features/VideoPlayer/hooks/index.tsx +++ b/src/features/VideoPlayer/hooks/index.tsx @@ -6,6 +6,7 @@ import { } from 'react' import ReactPlayer from 'react-player' +import once from 'lodash/once' import throttle from 'lodash/throttle' import { useFullscreen } from './useFullscreen' @@ -18,7 +19,16 @@ type ProgressState = { playedSeconds: number, } -export const useVideoPlayer = () => { +export type Props = { + onPlayingChange: (playing: boolean) => void, + onProgressChange: (seconds: number) => void, + url: string, +} + +export const useVideoPlayer = ({ + onPlayingChange, + onProgressChange: progressChangeCallback, +}: Props) => { const [ready, setReady] = useState(false) const [playing, setPlaying] = useState(false) const [duration, setDuration] = useState(0) @@ -28,14 +38,16 @@ export const useVideoPlayer = () => { const wrapperRef = useRef(null) const playerRef = useRef(null) - const startPlaying = () => { + const startPlaying = useCallback(once(() => { setReady(true) setPlaying(true) - } + onPlayingChange(true) + }), []) const togglePlaying = () => { if (ready) { setPlaying(!playing) + onPlayingChange(!playing) } } @@ -68,6 +80,8 @@ export const useVideoPlayer = () => { setLoadedProgress(loaded * 100) setPlayedProgress(played * 100) setPlayedSeconds(seconds) + + progressChangeCallback(seconds) } return { diff --git a/src/features/VideoPlayer/index.tsx b/src/features/VideoPlayer/index.tsx index d35f8636..a90e2ca0 100644 --- a/src/features/VideoPlayer/index.tsx +++ b/src/features/VideoPlayer/index.tsx @@ -1,6 +1,7 @@ import React from 'react' import { playerConfig, progressCallbackInterval } from './config' +import type { Props } from './hooks' import { useVideoPlayer } from './hooks' import { ProgressBar } from './components/ProgressBar' import { VolumeBar } from './components/VolumeBar' @@ -12,11 +13,8 @@ import { Fullscreen, } from './styled' -type Props = { - url: string, -} - -export const VideoPlayer = ({ url }: Props) => { +export const VideoPlayer = (props: Props) => { + const { url } = props const { isFullscreen, loadedProgress, @@ -36,7 +34,7 @@ export const VideoPlayer = ({ url }: Props) => { togglePlaying, volume, wrapperRef, - } = useVideoPlayer() + } = useVideoPlayer(props) const volumeInPercent = volume * 100 diff --git a/src/hooks/index.tsx b/src/hooks/index.tsx index ab9755cb..379dcba7 100644 --- a/src/hooks/index.tsx +++ b/src/hooks/index.tsx @@ -3,3 +3,4 @@ export * from './useToggle' export * from './useRequest' export * from './useSportNameParam' export * from './useStorage' +export * from './useInterval' diff --git a/src/hooks/useInterval.tsx b/src/hooks/useInterval.tsx new file mode 100644 index 00000000..01fa4ee0 --- /dev/null +++ b/src/hooks/useInterval.tsx @@ -0,0 +1,37 @@ +import { useEffect, useRef } from 'react' + +import noop from 'lodash/noop' + +import { useToggle } from 'hooks' + +type Args = { + callback: () => void, + intervalDuration: number, + startImmediate?: boolean, +} + +export const useInterval = ({ + callback = noop, + intervalDuration, + startImmediate = true, +}: Args) => { + const { + close: stop, + isOpen: isRunning, + open: start, + } = useToggle(startImmediate) + const savedCallback = useRef(callback) + + useEffect(() => { + savedCallback.current = callback + }, [callback]) + + useEffect(() => { + if (!isRunning) return undefined + + const id = setInterval(savedCallback.current, intervalDuration) + return () => clearInterval(id) + }, [isRunning, intervalDuration]) + + return { start, stop } +} diff --git a/src/requests/index.tsx b/src/requests/index.tsx index a9cf5472..8e94e7ec 100644 --- a/src/requests/index.tsx +++ b/src/requests/index.tsx @@ -12,3 +12,4 @@ export * from './getSportTournaments' export * from './getTournamentInfo' export * from './getTeamInfo' export * from './getMatchInfo' +export * from './reportPlayerProgress' diff --git a/src/requests/reportPlayerProgress.tsx b/src/requests/reportPlayerProgress.tsx new file mode 100644 index 00000000..65e6f8f9 --- /dev/null +++ b/src/requests/reportPlayerProgress.tsx @@ -0,0 +1,39 @@ +import { + DATA_URL, + PROCEDURES, + SportTypes, +} from 'config' +import { callApi } from 'helpers' + +const proc = PROCEDURES.save_user_match_second + +type Args = { + half?: number, + matchId: number, + seconds: number, + sport: SportTypes, +} + +export const reportPlayerProgress = ({ + half, + matchId, + seconds, + sport, +}: Args) => { + const config = { + body: { + params: { + _p_half: half || null, + _p_match_id: matchId, + _p_second: seconds, + _p_sport: sport, + }, + proc, + }, + } + + callApi({ + config, + url: DATA_URL, + }) +}