fix(887): reuse same playlist data in match page (#320)

keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent a2b2299492
commit 3b10015c9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      src/features/MatchCard/hooks.tsx
  2. 65
      src/features/MatchPage/hooks/useEpisodes.tsx
  3. 65
      src/features/MatchPage/hooks/useMatchPage.tsx
  4. 94
      src/features/MatchPage/hooks/usePlaylists.tsx
  5. 37
      src/features/MatchPage/hooks/useRouteState.tsx
  6. 18
      src/features/MatchPage/index.tsx
  7. 4
      src/features/MatchPopup/components/PlayerSettingsPage/index.tsx
  8. 37
      src/features/MatchPopup/components/PlaylistButton/index.tsx
  9. 22
      src/features/MatchPopup/components/SettingsDesktop/index.tsx
  10. 40
      src/features/MatchPopup/store/hooks/index.tsx
  11. 60
      src/features/MatchPopup/store/hooks/usePlayerClickHandler.tsx

@ -2,14 +2,19 @@ import type { KeyboardEvent } from 'react'
import { useCallback } from 'react'
import { useHistory } from 'react-router-dom'
import { ProfileTypes } from 'config'
import type { Match } from 'features/Matches'
import { useMatchPopupStore } from 'features/MatchPopup'
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup'
import { getProfileUrl } from '../ProfileLink/helpers'
import { ProfileTypes } from '../../config'
import { getProfileUrl } from 'features/ProfileLink/helpers'
export const useCard = (match: Match) => {
const { openPopup, setMatch } = useMatchPopupStore()
const {
fetchMatchPlaylists,
openPopup,
setMatch,
} = useMatchPopupStore()
const { open: openBuyMatchPopup } = useBuyMatchPopupStore()
const history = useHistory()
const redirectToMatchPage = useCallback(() => {
@ -28,6 +33,7 @@ export const useCard = (match: Match) => {
if (match.isClickable && match.calc && match.hasVideo) {
setMatch(match)
openPopup()
fetchMatchPlaylists(match)
} else if (match.calc && !match.hasVideo) {
redirectToMatchPage()
} else if (!match.accessibleBySubscription) {
@ -39,6 +45,7 @@ export const useCard = (match: Match) => {
openBuyMatchPopup,
setMatch,
redirectToMatchPage,
fetchMatchPlaylists,
])
const onKeyPress = useCallback((e: KeyboardEvent<HTMLLIElement>) => {

@ -1,50 +1,69 @@
import {
useCallback,
useState,
useEffect,
useState,
} from 'react'
import type { Settings } from 'features/MatchPopup'
import isEmpty from 'lodash/isEmpty'
import { getPlayerPlaylists, Episodes } from 'requests'
import type { Episodes } from 'requests'
import { getPlayerPlaylists } from 'requests'
import { usePageId, useSportNameParam } from 'hooks'
import { PlaylistOption, PlaylistTypes } from 'features/MatchPage/types'
import {
defaultSettings,
Settings,
useMatchPopupStore,
} from 'features/MatchPopup'
type Args = {
initialEpisodes?: Episodes,
initialSelectedPlaylist?: PlaylistOption,
}
export const useEpisodes = ({ initialEpisodes, initialSelectedPlaylist }: Args) => {
const [episodes, setEpisodes] = useState(initialEpisodes || [])
export const useEpisodes = () => {
const {
handlePlaylistClick,
matchPlaylists: playlists,
selectedPlaylist,
settings,
} = useMatchPopupStore()
const [episodes, setEpisodes] = useState<Episodes>([])
const { sportType } = useSportNameParam()
const matchId = usePageId()
const fetchEpisodes = useCallback((
selectedPlaylist: PlaylistOption,
settings?: Settings,
playlistOption: PlaylistOption,
popupSettings: Settings = defaultSettings,
) => {
if (!selectedPlaylist) return
if (selectedPlaylist.type === PlaylistTypes.PLAYER) {
if (playlistOption.type === PlaylistTypes.PLAYER) {
getPlayerPlaylists({
matchId,
playerId: selectedPlaylist.id,
settings,
playerId: playlistOption.id,
settings: popupSettings,
sportType,
}).then(setEpisodes)
} else if (selectedPlaylist.type === PlaylistTypes.MATCH) {
setEpisodes(selectedPlaylist.data)
} else if (playlistOption.type === PlaylistTypes.MATCH) {
setEpisodes(playlistOption.data)
}
}, [matchId, sportType])
useEffect(() => {
if (initialSelectedPlaylist?.type === PlaylistTypes.MATCH) {
fetchEpisodes(initialSelectedPlaylist)
if (!selectedPlaylist && playlists && !isEmpty(playlists.match)) {
handlePlaylistClick(playlists.match[0])
}
}, [
selectedPlaylist,
playlists,
handlePlaylistClick,
])
useEffect(() => {
if (selectedPlaylist) {
fetchEpisodes(selectedPlaylist, settings)
}
}, [initialSelectedPlaylist, fetchEpisodes])
}, [
settings,
selectedPlaylist,
fetchEpisodes,
])
return { episodes, fetchEpisodes }
return { episodes }
}

@ -12,25 +12,27 @@ import {
import { Settings, useMatchPopupStore } from 'features/MatchPopup'
import { useLastPlayPosition } from './useLastPlayPosition'
import { usePlaylists } from './usePlaylists'
import { useEpisodes } from './useEpisodes'
import { useChapters } from './useChapters'
import { useRouteState } from './useRouteState'
import { useMatchProfile } from './useMatchProfile'
export const useMatchPage = () => {
const { initialEpisodes, initialSelectedPlaylist } = useRouteState()
const [isFinishedMatch, setFinishedMatch] = useState(Boolean(initialSelectedPlaylist))
const [liveVideos, setLiveVideos] = useState<LiveVideos>([])
const profile = useMatchProfile()
const { sportType } = useSportNameParam()
const matchId = usePageId()
const {
actions,
closePopup,
fetchMatchPlaylists,
handlePlaylistClick,
matchPlaylists,
selectedPlaylist,
setMatch,
setSettings,
settings,
} = useMatchPopupStore()
const [isFinishedMatch, setFinishedMatch] = useState(Boolean(selectedPlaylist))
const [liveVideos, setLiveVideos] = useState<LiveVideos>([])
const profile = useMatchProfile()
const { sportType } = useSportNameParam()
const matchId = usePageId()
const {
close,
@ -38,8 +40,23 @@ export const useMatchPage = () => {
open,
} = useToggle()
const { episodes } = useEpisodes()
useEffect(() => {
if (!isFinishedMatch) {
getLiveVideos(sportType, matchId)
.then(setLiveVideos)
.catch(() => setFinishedMatch(true))
}
},
[
isFinishedMatch,
sportType,
matchId,
])
useEffect(() => {
if (profile) {
if (profile && isFinishedMatch) {
const match = {
calc: false,
id: matchId,
@ -49,24 +66,16 @@ export const useMatchPage = () => {
team2: profile.team2,
}
setMatch(match)
fetchMatchPlaylists(match)
}
}, [
matchId,
profile,
setMatch,
sportType,
])
const {
episodes,
onPlaylistSelect,
playlists,
selectedPlaylist,
} = usePlaylists({
initialEpisodes,
initialSelectedPlaylist,
isFinishedMatch,
})
fetchMatchPlaylists,
])
const setEpisodesSettings = (values: Settings) => {
const isSettingsChanged = !isEqual(values, settings)
@ -79,18 +88,6 @@ export const useMatchPage = () => {
}
close()
}
useEffect(() => {
if (!isFinishedMatch) {
getLiveVideos(sportType, matchId)
.then(setLiveVideos)
.catch(() => setFinishedMatch(true))
}
},
[
isFinishedMatch,
sportType,
matchId,
])
useEffect(() => {
closePopup()
@ -100,9 +97,9 @@ export const useMatchPage = () => {
actions,
closeSettingsPopup: close,
isOpen,
onPlaylistSelect,
onPlaylistSelect: handlePlaylistClick,
openSettingsPopup: open,
playlists,
playlists: matchPlaylists,
profile,
selectedPlaylist,
setEpisodesSettings,

@ -1,94 +0,0 @@
import { useEffect, useState } from 'react'
import isEmpty from 'lodash/isEmpty'
import { getMatchPlaylists, Episodes } from 'requests'
import { usePageId, useSportNameParam } from 'hooks'
import type { PlaylistOption, Playlists } from 'features/MatchPage/types'
import { usePlaylistLexics } from 'features/MatchPopup/store/hooks/usePlaylistLexics'
import { useMatchPopupStore } from 'features/MatchPopup'
import { buildPlaylists } from '../helpers/buildPlaylists'
import { useEpisodes } from './useEpisodes'
const initialPlaylists: Playlists = {
interview: [],
match: [],
players: {
team1: [],
team2: [],
},
}
type Args = {
initialEpisodes?: Episodes,
initialSelectedPlaylist?: PlaylistOption,
isFinishedMatch: boolean,
}
export const usePlaylists = ({
initialEpisodes,
initialSelectedPlaylist,
isFinishedMatch,
}: Args) => {
const [playlists, setPlaylists] = useState<Playlists>(initialPlaylists)
const [selectedPlaylist, setSelectedPlaylist] = useState(initialSelectedPlaylist)
const { sportType } = useSportNameParam()
const { fetchLexics } = usePlaylistLexics()
const matchId = usePageId()
const { settings } = useMatchPopupStore()
const {
episodes,
fetchEpisodes,
} = useEpisodes({
initialEpisodes,
initialSelectedPlaylist,
})
useEffect(() => {
if (selectedPlaylist) {
fetchEpisodes(selectedPlaylist, settings)
}
}, [
fetchEpisodes,
selectedPlaylist,
settings,
])
useEffect(() => {
if (isFinishedMatch) {
getMatchPlaylists({
matchId,
selectedActions: [],
sportType,
}).then(fetchLexics)
.then(buildPlaylists)
.then(setPlaylists)
}
}, [
fetchLexics,
isFinishedMatch,
matchId,
sportType,
])
useEffect(() => {
if (!selectedPlaylist && !isEmpty(playlists.match)) {
setSelectedPlaylist(playlists.match[0])
}
}, [selectedPlaylist, playlists])
const onPlaylistSelect = (option: PlaylistOption) => {
if (option === selectedPlaylist) return
setSelectedPlaylist(option)
}
return {
episodes,
onPlaylistSelect,
playlists,
selectedPlaylist,
}
}

@ -1,37 +0,0 @@
import { useEffect } from 'react'
import { useHistory, useLocation } from 'react-router'
import type { Episodes } from 'requests'
import type { PlaylistOption } from 'features/MatchPage/types'
export type RouteState = {
/**
* Данные плейлиста если был выбран игрок
*/
episodes?: Episodes,
/**
* Выбранный плейлист на попапе матчей
*/
selectedPlaylist: PlaylistOption,
}
export const useRouteState = () => {
// считываем стейт из роутера
// если есть стейт то на этот матч мы переходили
// из попапа матчей и статус матча Завершенный
// и запрос на получение ссылки лив матча можно пропустить
const { state } = useLocation<RouteState | undefined>()
const history = useHistory()
useEffect(() => {
// сбрасываем роут стейт
history.replace(history.location.pathname)
}, [history])
return {
initialEpisodes: state?.episodes,
initialSelectedPlaylist: state?.selectedPlaylist,
}
}

@ -87,13 +87,17 @@ const MatchPage = () => {
)
}
</Container>
<MatchSidePlaylists
playlists={playlists}
selectedPlaylist={selectedPlaylist}
onSelect={onPlaylistSelect}
profile={profile}
openPopup={openSettingsPopup}
/>
{
playlists && (
<MatchSidePlaylists
playlists={playlists}
selectedPlaylist={selectedPlaylist}
onSelect={onPlaylistSelect}
profile={profile}
openPopup={openSettingsPopup}
/>
)
}
</Wrapper>
</MainWrapper>
)

@ -21,7 +21,7 @@ export const PlayerSettingsPage = () => {
const {
closePopup,
match,
selectedPlayer,
selectedPlaylist,
} = useMatchPopupStore()
if (!match) return null
@ -44,7 +44,7 @@ export const PlayerSettingsPage = () => {
<CloseButton onClick={closePopup} />
</HeaderActions>
<TitleWrapper>
{selectedPlayer && <Name nameObj={selectedPlayer} />}
{selectedPlaylist && <Name nameObj={selectedPlaylist} />}
<TeamNames>
<Name nameObj={match.team1} />
{` ${MDASH} `}

@ -1,4 +1,3 @@
import type { MouseEvent } from 'react'
import { Link } from 'react-router-dom'
import styled, { css } from 'styled-components/macro'
@ -9,6 +8,8 @@ import { secondsToHms } from 'helpers'
import { T9n } from 'features/T9n'
import { MatchPlaylistOption } from 'features/MatchPage/types'
import { useMatchPopupStore } from '../../store'
type ButtonsStypesProps = {
disabled?: boolean,
}
@ -91,8 +92,6 @@ export const Duration = styled(Title)`
}
`
const stopPropagation = (e: MouseEvent<HTMLAnchorElement>) => e.stopPropagation()
type Props = {
playlist: MatchPlaylistOption,
to: string,
@ -101,19 +100,19 @@ type Props = {
export const PlaylistButton = ({
playlist,
to,
}: Props) => (
<StyledLink
to={{
pathname: to,
state: { selectedPlaylist: playlist },
}}
onClick={stopPropagation}
disabled={!playlist.duration}
tabIndex={!playlist.duration ? -1 : 0}
>
<Title>
<T9n t={playlist.lexic} />
</Title>
{playlist.duration && <Duration>{secondsToHms(playlist.duration)}</Duration>}
</StyledLink>
)
}: Props) => {
const { handlePlaylistClick } = useMatchPopupStore()
return (
<StyledLink
to={to}
onClick={(e) => handlePlaylistClick(playlist, e)}
disabled={!playlist.duration}
tabIndex={!playlist.duration ? -1 : 0}
>
<Title>
<T9n t={playlist.lexic} />
</Title>
{playlist.duration && <Duration>{secondsToHms(playlist.duration)}</Duration>}
</StyledLink>
)
}

@ -2,8 +2,11 @@ import { Fragment } from 'react'
import styled from 'styled-components/macro'
import { ProfileTypes } from 'config'
import { useMatchPopupStore } from 'features/MatchPopup'
import { ButtonSolid } from 'features/Common'
import { solidButtonStyles } from 'features/Common'
import { ProfileLink } from 'features/ProfileLink'
import { T9n } from 'features/T9n'
import { PlaylistFormats } from '../PlaylistFormats'
@ -17,22 +20,29 @@ const ButtonsWrapper = styled.div`
margin: 45px 0 15px 0;
`
const WatchButton = styled(ButtonSolid)`
const WatchButton = styled(ProfileLink)`
${solidButtonStyles}
width: auto;
padding: 0 20px;
display: flex;
align-items: center;
`
export const SettingsDesktop = () => {
const {
actions,
episodeDuration,
handleWatchEpisodesClick,
match,
onActionClick,
onDurationChange,
onFormatSelect,
selectedActions,
selectedPlaylistFormat,
} = useMatchPopupStore()
if (!match) return null
return (
<Fragment>
<PlaylistFormats
@ -52,7 +62,11 @@ export const SettingsDesktop = () => {
)}
<ButtonsWrapper>
<WatchButton onClick={handleWatchEpisodesClick}>
<WatchButton
id={match.id}
sportType={match.sportType}
profileType={ProfileTypes.MATCHES}
>
<T9n t='watch_players_episodes' />
</WatchButton>
</ButtonsWrapper>

@ -1,6 +1,7 @@
import {
useState,
useEffect,
useCallback,
} from 'react'
import isEmpty from 'lodash/isEmpty'
@ -14,7 +15,7 @@ import { useSettingsState } from './useSettingsState'
import { useSportActions } from './useSportActions'
import { usePopupNavigation } from './usePopupNavigation'
import type { MatchData } from '../../types'
import type { MatchData, SelectedActions } from '../../types'
import { PopupPages, PlayerPlaylistFormats } from '../../types'
import { usePlayerClickHandler } from './usePlayerClickHandler'
import { usePlaylistLexics } from './usePlaylistLexics'
@ -50,56 +51,51 @@ export const useMatchPopup = () => {
const { fetchLexics } = usePlaylistLexics()
const isFormatSelectedActions = selectedPlaylistFormat === PlayerPlaylistFormats.SELECTED_ACTIONS
useEffect(() => {
if (selectedPlaylistFormat === PlayerPlaylistFormats.SELECTED_ACTIONS) {
if (isFormatSelectedActions) {
fetchSportActions()
} else {
setSelectedActions([])
}
}, [
selectedPlaylistFormat,
isFormatSelectedActions,
fetchSportActions,
setSelectedActions,
])
useEffect(() => {
if (!isOpen) {
setMatch(null)
setMatchPlaylists(null)
}
}, [isOpen])
useEffect(() => {
const isPlaylistPage = page === PopupPages.PLAYLIST
const actionsFormatSelected = (
selectedPlaylistFormat === PlayerPlaylistFormats.SELECTED_ACTIONS
)
if (isPlaylistPage && actionsFormatSelected && isEmpty(selectedActions)) {
if (isPlaylistPage && isFormatSelectedActions && isEmpty(selectedActions)) {
setSelectedPlaylistFormat(PlayerPlaylistFormats.ALL_MATCH_TIME)
}
}, [
selectedActions,
selectedPlaylistFormat,
isFormatSelectedActions,
page,
setSelectedPlaylistFormat,
])
useEffect(() => {
if (!match) return
const fetchMatchPlaylists = useCallback((
matchData: MatchData,
selected: SelectedActions = [],
) => {
if (!matchData) return
getMatchPlaylists({
matchId: match.id,
selectedActions: [],
sportType: match.sportType,
matchId: matchData.id,
selectedActions: selected,
sportType: matchData.sportType,
}).then(fetchLexics)
.then(buildPlaylists)
.then(setMatchPlaylists)
}, [match, fetchLexics])
}, [fetchLexics])
return {
actions,
closePopup,
episodeDuration,
fetchMatchPlaylists,
goBack,
goTo,
isOpen,
@ -117,8 +113,6 @@ export const useMatchPopup = () => {
settings,
...usePlayerClickHandler({
goTo,
match,
settings,
}),
}
}

@ -1,65 +1,31 @@
import type { MouseEvent } from 'react'
import { useCallback, useState } from 'react'
import { useHistory } from 'react-router'
import { useState } from 'react'
import { ProfileTypes } from 'config'
import { getPlayerPlaylists } from 'requests'
import type { PlayerPlaylistOption } from 'features/MatchPage/types'
import type { MatchData, Settings } from 'features/MatchPopup/types'
import type { RouteState } from 'features/MatchPage/hooks/useRouteState'
import type { PlaylistOption, PlayerPlaylistOption } from 'features/MatchPage/types'
import { PopupPages } from 'features/MatchPopup/types'
import { getProfileUrl } from 'features/ProfileLink/helpers'
type Args = {
goTo: (page: PopupPages) => void,
match: MatchData,
settings: Settings,
}
export const usePlayerClickHandler = ({
goTo,
match,
settings,
}: Args) => {
const history = useHistory()
const [selectedPlayer, setSelectedPlayer] = useState<PlayerPlaylistOption | null>(null)
const handlePlayerClick = (player: PlayerPlaylistOption, e: MouseEvent) => {
e.stopPropagation()
setSelectedPlayer(player)
const [selectedPlaylist, setSelectedPlaylist] = useState<PlaylistOption>()
const handlePlaylistClick = (playlist: PlaylistOption, e?: MouseEvent) => {
e?.stopPropagation()
if (playlist !== selectedPlaylist) {
setSelectedPlaylist(playlist)
}
}
const handlePlayerClick = (player: PlayerPlaylistOption, e?: MouseEvent) => {
handlePlaylistClick(player, e)
goTo(PopupPages.PLAYER_SETTINGS)
}
const handleWatchEpisodesClick = useCallback(async () => {
if (!match || !selectedPlayer) return
const episodes = await getPlayerPlaylists({
matchId: match.id,
playerId: selectedPlayer.id,
settings,
sportType: match.sportType,
})
const matchLink = getProfileUrl({
id: match.id,
profileType: ProfileTypes.MATCHES,
sportType: match.sportType,
})
const routeState: RouteState = {
episodes,
selectedPlaylist: selectedPlayer,
}
history.push(matchLink, routeState)
}, [
match,
selectedPlayer,
settings,
history,
])
return {
handlePlayerClick,
handleWatchEpisodesClick,
selectedPlayer,
handlePlaylistClick,
selectedPlaylist,
}
}

Loading…
Cancel
Save