Ott 338 player progress report (#128)

* feat(#338): added useInterval hook

* feat(#338): added progress reporter request

* refactor(#338): renamed hook

* feat(#338): added match player progress reporting

* fix(#338): fixed bug when player paused while buffering

* fix(#338): reset tournament on sport reset

* fix(#338): review fix

Co-authored-by: mirlan.maksitaliev <mirlan.maksitaliev@instatsport.com>
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent 988a0142a3
commit a312a9b47a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/config/procedures.tsx
  2. 1
      src/features/HeaderFilters/components/SportTypeFilter/hooks.tsx
  3. 37
      src/features/MatchPage/MatchProfileCard/hooks.tsx
  4. 4
      src/features/MatchPage/MatchProfileCard/index.tsx
  5. 66
      src/features/MatchPage/hooks.tsx
  6. 25
      src/features/MatchPage/index.tsx
  7. 5
      src/features/QueryParamsStorage/index.tsx
  8. 20
      src/features/VideoPlayer/hooks/index.tsx
  9. 10
      src/features/VideoPlayer/index.tsx
  10. 1
      src/hooks/index.tsx
  11. 37
      src/hooks/useInterval.tsx
  12. 1
      src/requests/index.tsx
  13. 39
      src/requests/reportPlayerProgress.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',
}

@ -35,6 +35,7 @@ export const useSportTypeFilter = () => {
const onResetSelectedSport = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
setSelectedSportTypeId(null)
setSelectedTournamentId(null)
}
const selectedSportType = find(sportList, (sport) => sport.id === selectedSportTypeId)

@ -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<MatchInfo>(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,
}
}

@ -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()

@ -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<MatchInfo>(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 }
}

@ -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 = () => (
<MainWrapper>
<MatchProfileCard />
<Container>
<VideoPlayer url={url} />
</Container>
</MainWrapper>
)
export const MatchPage = () => {
const { onPlayerProgressChange, onPlayingChange } = usePlayerProgressReporter()
return (
<MainWrapper>
<MatchProfileCard />
<Container>
<VideoPlayer
url={url}
onPlayingChange={onPlayingChange}
onProgressChange={onPlayerProgressChange}
/>
</Container>
</MainWrapper>
)
}

@ -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 строку
*/

@ -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<HTMLDivElement>(null)
const playerRef = useRef<ReactPlayer>(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 {

@ -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

@ -3,3 +3,4 @@ export * from './useToggle'
export * from './useRequest'
export * from './useSportNameParam'
export * from './useStorage'
export * from './useInterval'

@ -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 }
}

@ -12,3 +12,4 @@ export * from './getSportTournaments'
export * from './getTournamentInfo'
export * from './getTeamInfo'
export * from './getMatchInfo'
export * from './reportPlayerProgress'

@ -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,
})
}
Loading…
Cancel
Save