Ott 701 videos panel (#278)

* feat: 🎸 OTT-701-videos-panel

add side panel

* feat: 🎸 OTT-701

add some styles

* feat: 🎸 OTT-701

delete //

* feat: 🎸 #701

hook deleted

* fix(701): reset route state on mount

* feat: 🎸 OTT-701

fix comments

* feat: 🎸 OTT-701

fix names

* fix: renamed dropdown

* fix: renamed dropdown back

* feat: 🎸 OTT-701

fix height

* feat: 🎸 OTT-701

undo changes for isClickable

* feat: 🎸 OTT-701

fix comments

Co-authored-by: Zoia <zizi2405@yandex.ru>
Co-authored-by: Mirlan <m.maksitaliev@gmail.com>
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Zoia 5 years ago committed by GitHub
parent 537a38a79a
commit c167d2dd77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/features/MatchPage/helpers/buildPlaylists.tsx
  2. 9
      src/features/MatchPage/hooks/useMatchPage.tsx
  3. 10
      src/features/MatchPage/hooks/usePlaylistData.tsx
  4. 29
      src/features/MatchPage/hooks/usePlaylists.tsx
  5. 2
      src/features/MatchPage/hooks/useRouteState.tsx
  6. 10
      src/features/MatchPage/index.tsx
  7. 2
      src/features/MatchPage/styled.tsx
  8. 3
      src/features/MatchPage/types.tsx
  9. 53
      src/features/MatchSidePlaylists/components/DropdownSection/index.tsx
  10. 78
      src/features/MatchSidePlaylists/components/DropdownSection/styled.tsx
  11. 63
      src/features/MatchSidePlaylists/components/InterviewButton/index.tsx
  12. 45
      src/features/MatchSidePlaylists/components/MatchPlaylists/index.tsx
  13. 37
      src/features/MatchSidePlaylists/components/PlayButton/index.tsx
  14. 69
      src/features/MatchSidePlaylists/components/PlayersPlaylists/index.tsx
  15. 53
      src/features/MatchSidePlaylists/components/SideInterviews/index.tsx
  16. 85
      src/features/MatchSidePlaylists/index.tsx
  17. 62
      src/features/MatchSidePlaylists/styled.tsx
  18. 4
      src/requests/getMatchPlaylists.tsx

@ -45,6 +45,7 @@ const getPlayerPlaylists = (players?: Players): PlayerPlaylistOptions => (
export const buildPlaylists = (matchPlaylists: MatchPlaylists | null) => { export const buildPlaylists = (matchPlaylists: MatchPlaylists | null) => {
const playlists: Playlists = { const playlists: Playlists = {
interview: [], interview: [],
lexics: matchPlaylists?.lexics,
match: getMatchPlaylists(matchPlaylists), match: getMatchPlaylists(matchPlaylists),
players: { players: {
team1: getPlayerPlaylists(matchPlaylists?.players1), team1: getPlayerPlaylists(matchPlaylists?.players1),

@ -14,7 +14,7 @@ import { useRouteState } from './useRouteState'
export const useMatchPage = () => { export const useMatchPage = () => {
const { closePopup } = useMatchPopupStore() const { closePopup } = useMatchPopupStore()
const { initialSelectedPlaylist } = useRouteState() const { initialPlaylistData, initialSelectedPlaylist } = useRouteState()
const [isFinishedMatch, setFinishedMatch] = useState(Boolean(initialSelectedPlaylist)) const [isFinishedMatch, setFinishedMatch] = useState(Boolean(initialSelectedPlaylist))
const [liveVideos, setLiveVideos] = useState<LiveVideos>([]) const [liveVideos, setLiveVideos] = useState<LiveVideos>([])
const { sportType } = useSportNameParam() const { sportType } = useSportNameParam()
@ -25,7 +25,11 @@ export const useMatchPage = () => {
playlistData, playlistData,
playlists, playlists,
selectedPlaylist, selectedPlaylist,
} = usePlaylists(isFinishedMatch) } = usePlaylists({
initialPlaylistData,
initialSelectedPlaylist,
isFinishedMatch,
})
useEffect(() => { useEffect(() => {
if (!isFinishedMatch) { if (!isFinishedMatch) {
@ -45,7 +49,6 @@ export const useMatchPage = () => {
}, [closePopup]) }, [closePopup])
return { return {
isFinishedMatch,
onPlaylistSelect, onPlaylistSelect,
playlists, playlists,
selectedPlaylist, selectedPlaylist,

@ -4,16 +4,18 @@ import {
useEffect, useEffect,
} from 'react' } from 'react'
import { getPlayerPlaylists } from 'requests' import { getPlayerPlaylists, PlaylistData } from 'requests'
import { usePageId, useSportNameParam } from 'hooks' import { usePageId, useSportNameParam } from 'hooks'
import { PlaylistOption, PlaylistTypes } from 'features/MatchPage/types' import { PlaylistOption, PlaylistTypes } from 'features/MatchPage/types'
import { useRouteState } from './useRouteState' type Args = {
initialPlaylistData?: PlaylistData,
initialSelectedPlaylist?: PlaylistOption,
}
export const usePlaylistsData = () => { export const usePlaylistsData = ({ initialPlaylistData, initialSelectedPlaylist }: Args) => {
const { initialPlaylistData, initialSelectedPlaylist } = useRouteState()
const [playlistData, setPlaylistData] = useState(initialPlaylistData || []) const [playlistData, setPlaylistData] = useState(initialPlaylistData || [])
const { sportType } = useSportNameParam() const { sportType } = useSportNameParam()
const matchId = usePageId() const matchId = usePageId()

@ -2,14 +2,14 @@ import { useEffect, useState } from 'react'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import { getMatchPlaylists } from 'requests' import { getMatchPlaylists, PlaylistData } from 'requests'
import { usePageId, useSportNameParam } from 'hooks' import { usePageId, useSportNameParam } from 'hooks'
import type { PlaylistOption, Playlists } from 'features/MatchPage/types' import type { PlaylistOption, Playlists } from 'features/MatchPage/types'
import { usePlaylistLexics } from 'features/MatchPopup/store/hooks/usePlaylistLexics'
import { buildPlaylists } from '../helpers/buildPlaylists' import { buildPlaylists } from '../helpers/buildPlaylists'
import { useRouteState } from './useRouteState'
import { usePlaylistsData } from './usePlaylistData' import { usePlaylistsData } from './usePlaylistData'
const initialPlaylists: Playlists = { const initialPlaylists: Playlists = {
@ -21,28 +21,43 @@ const initialPlaylists: Playlists = {
}, },
} }
export const usePlaylists = (isFinishedMatch: boolean) => { type Args = {
const { initialSelectedPlaylist } = useRouteState() initialPlaylistData?: PlaylistData,
initialSelectedPlaylist?: PlaylistOption,
isFinishedMatch: boolean,
}
export const usePlaylists = ({
initialPlaylistData,
initialSelectedPlaylist,
isFinishedMatch,
}: Args) => {
const [playlists, setPlaylists] = useState<Playlists>(initialPlaylists) const [playlists, setPlaylists] = useState<Playlists>(initialPlaylists)
const [selectedPlaylist, setSelectedPlaylist] = useState(initialSelectedPlaylist) const [selectedPlaylist, setSelectedPlaylist] = useState(initialSelectedPlaylist)
const { sportType } = useSportNameParam() const { sportType } = useSportNameParam()
const { fetchLexics } = usePlaylistLexics()
const matchId = usePageId() const matchId = usePageId()
const { const {
fetchPlaylists, fetchPlaylists,
playlistData, playlistData,
} = usePlaylistsData() } = usePlaylistsData({
initialPlaylistData,
initialSelectedPlaylist,
})
useEffect(() => { useEffect(() => {
if (isFinishedMatch) { if (isFinishedMatch) {
getMatchPlaylists({ getMatchPlaylists({
matchId, matchId,
selectedActions: [1, 2, 3], selectedActions: [],
sportType, sportType,
}).then(buildPlaylists) }).then(fetchLexics)
.then(buildPlaylists)
.then(setPlaylists) .then(setPlaylists)
} }
}, [ }, [
fetchLexics,
isFinishedMatch, isFinishedMatch,
matchId, matchId,
sportType, sportType,

@ -25,7 +25,7 @@ export const useRouteState = () => {
const { state } = useLocation<RouteState | undefined>() const { state } = useLocation<RouteState | undefined>()
const history = useHistory() const history = useHistory()
useEffect(() => () => { useEffect(() => {
// сбрасываем роут стейт // сбрасываем роут стейт
history.replace(history.location.pathname) history.replace(history.location.pathname)
}, [history]) }, [history])

@ -24,6 +24,9 @@ export const MatchPage = () => {
chapters, chapters,
isLastPlayPositionFetching, isLastPlayPositionFetching,
lastPlayPosition, lastPlayPosition,
onPlaylistSelect,
playlists,
selectedPlaylist,
url, url,
} = useMatchPage() } = useMatchPage()
const { onPlayerProgressChange, onPlayingChange } = usePlayerProgressReporter() const { onPlayerProgressChange, onPlayingChange } = usePlayerProgressReporter()
@ -61,7 +64,12 @@ export const MatchPage = () => {
) )
} }
</Container> </Container>
<MatchSidePlaylists /> <MatchSidePlaylists
playlists={playlists}
selectedPlaylist={selectedPlaylist}
onSelect={onPlaylistSelect}
profile={profile}
/>
</Wrapper> </Wrapper>
</MainWrapper> </MainWrapper>
) )

@ -3,7 +3,7 @@ import styled from 'styled-components/macro'
import { devices } from 'config/devices' import { devices } from 'config/devices'
export const MainWrapper = styled.div` export const MainWrapper = styled.div`
margin: 63px 16px 0 16px; margin: 63px 0px 0 16px;
display: flex; display: flex;
@media ${devices.laptop} { @media ${devices.laptop} {

@ -1,4 +1,4 @@
import type { PlaylistData } from 'requests' import type { Lexics, PlaylistData } from 'requests'
export enum PlaylistTypes { export enum PlaylistTypes {
MATCH, MATCH,
@ -40,6 +40,7 @@ export type PlayerPlaylistOptions = Array<PlayerPlaylistOption>
export type Playlists = { export type Playlists = {
interview: InterviewPlaylistOptions, interview: InterviewPlaylistOptions,
lexics?: Lexics,
match: MatchPlaylistOptions, match: MatchPlaylistOptions,
players: { players: {
team1: PlayerPlaylistOptions, team1: PlayerPlaylistOptions,

@ -0,0 +1,53 @@
import { ReactNode } from 'react'
import { useToggle } from 'hooks'
import { T9n } from 'features/T9n'
import {
Amount,
Arrows,
DropdownButton,
Title,
SectionWrapper,
Wrapper,
} from './styled'
type Props = {
children: ReactNode,
itemsCount: number,
title?: string | number,
}
export const DropdownSection = (props: Props) => {
const {
isOpen,
toggle,
} = useToggle()
const {
children,
itemsCount,
title,
} = props
if (!title) return null
return (
<Wrapper>
<DropdownButton
active={isOpen}
onClick={toggle}
>
<Title>
<T9n t={title} />
</Title>
<Amount>
{itemsCount}
</Amount>
<Arrows active={isOpen} />
</DropdownButton>
<SectionWrapper active={isOpen}>
{children}
</SectionWrapper>
</Wrapper>
)
}

@ -0,0 +1,78 @@
import styled from 'styled-components/macro'
type Props = {
active?: boolean,
}
export const Wrapper = styled.div`
margin-bottom: 10px;
`
export const Item = styled.li`
margin-top: 15px;
width: 100%;
height: 50px;
`
export const SectionWrapper = styled.div<Props>`
overflow: hidden;
transition: .3s;
${({ active }) => (
active
? 'height: auto;'
: 'height: 0;'
)}
`
export const DropdownButton = styled.button<Props>`
position: relative;
height: 100%;
width: 100%;
padding-right: 40px;
padding-left: 0px;
outline: none;
border: none;
border-radius: 2px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: transparent;
font-weight: 600;
font-size: 18px;
cursor: pointer;
`
export const Arrows = styled.span<Props>`
position: absolute;
right: 19px;
display: inline-block;
width: 12px;
height: 12px;
background-repeat: no-repeat;
background-position: center;
${({ active }) => (
active
? 'background-image: url(/images/arrowUp.svg);'
: 'background-image: url(/images/arrowDown.svg);'
)}
`
export const Title = styled.span`
font-weight: 500;
font-size: 20px;
line-height: 45px;
letter-spacing: 0.03em;
text-align: start;
color: #ffffff;
text-transform: uppercase;
`
export const Amount = styled.span`
font-weight: 300;
font-size: 20px;
line-height: 50px;
letter-spacing: 0.03em;
color: #ffffff;
`

@ -0,0 +1,63 @@
import { ReactNode } from 'react'
import styled, { css } from 'styled-components/macro'
import { Button, Title } from '../../styled'
type InterviewProps = {
overlength?: boolean,
}
const InterviewTitle = styled(Title)<InterviewProps>`
white-space: normal;
text-align: start;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
${({ overlength }) => (
overlength
? css`
font-size: 18px;
line-height: 26px;
`
: css`
font-size: 20px;
line-height: 50px;
`
)}
`
const InterviewButton = styled(Button)<InterviewProps>`
${({ overlength }) => (
overlength
? 'padding: 16px 25px;'
: 'padding: 0px 25px;'
)}
`
type Props = {
active?: boolean,
children: ReactNode,
onClick: () => void,
overlength?: boolean,
}
export const InterviewPlayButton = ({
active,
children,
onClick,
overlength,
}: Props) => (
<InterviewButton
onClick={onClick}
active={active}
overlength={overlength}
>
<InterviewTitle
overlength={overlength}
>
{children}
</InterviewTitle>
</InterviewButton>
)

@ -0,0 +1,45 @@
import styled from 'styled-components/macro'
import map from 'lodash/map'
import type { MatchPlaylistOptions, PlaylistOption } from 'features/MatchPage/types'
import { T9n } from 'features/T9n'
import { isEqual } from 'features/MatchSidePlaylists'
import { PlayButton } from '../PlayButton'
import { Item } from '../../styled'
type Props = {
onSelect: (selectedMathPlaylist: PlaylistOption) => void,
playlists?: MatchPlaylistOptions,
selectedMathPlaylist?: PlaylistOption,
}
const List = styled.ul``
export const MatchPlaylists = (props: Props) => {
const {
onSelect,
playlists,
selectedMathPlaylist,
} = props
return (
<List>
{
map(playlists, (playlist) => (
<Item key={playlist.id}>
<PlayButton
duration={playlist.duration}
active={isEqual(playlist, selectedMathPlaylist)}
onClick={() => onSelect(playlist)}
>
<T9n t={playlist.lexic} />
</PlayButton>
</Item>
))
}
</List>
)
}

@ -0,0 +1,37 @@
import { ReactNode } from 'react'
import styled from 'styled-components/macro'
import { secondsToHms } from 'helpers'
import { Button, Title } from '../../styled'
type Props = {
active?: boolean,
children: ReactNode,
duration?: number,
onClick: () => void,
}
const Duration = styled(Title)`
font-weight: 300;
font-size: 16px;
letter-spacing: 0.05em;
`
export const PlayButton = ({
active,
children,
duration,
onClick,
}: Props) => (
<Button
onClick={onClick}
active={active}
>
<Title>
{children}
</Title>
{duration && <Duration>{secondsToHms(duration)}</Duration>}
</Button>
)

@ -0,0 +1,69 @@
import styled from 'styled-components/macro'
import map from 'lodash/map'
import type { Team } from 'requests'
import type { PlaylistOption } from 'features/MatchPage/types'
import { Name } from 'features/Name'
import { isEqual } from 'features/MatchSidePlaylists'
import { PlayButton } from '../PlayButton'
import { Item } from '../DropdownSection/styled'
type Props = {
onSelect: (selectedMathPlaylist: PlaylistOption) => void,
players: Array<PlaylistOption>,
selectedMathPlaylist?: PlaylistOption,
teamName?: Team,
}
const Wrapper = styled.div`
margin-bottom: 15px;
`
const List = styled.ul``
const PlayersItem = styled(Item)`
margin-top: 10px
`
const Title = styled.span`
font-weight: 300;
font-size: 16px;
line-height: 20px;
letter-spacing: 0.03em;
color: #ffffff;
`
export const PlayersPlaylists = (props: Props) => {
const {
onSelect,
players,
selectedMathPlaylist,
teamName,
} = props
if (!selectedMathPlaylist || !teamName) return null
return (
<Wrapper>
<Title>
<Name nameObj={teamName} />
</Title>
<List>
{
map(players, (player) => (
<PlayersItem key={player.id}>
<PlayButton
onClick={() => onSelect(player)}
active={isEqual(player, selectedMathPlaylist)}
>
<Name nameObj={player} />
</PlayButton>
</PlayersItem>
))
}
</List>
</Wrapper>
)
}

@ -0,0 +1,53 @@
import map from 'lodash/map'
import size from 'lodash/size'
import styled from 'styled-components/macro'
import type { InterviewPlaylistOptions, PlaylistOption } from 'features/MatchPage/types'
import { isEqual } from 'features/MatchSidePlaylists'
import { Name } from 'features/Name'
import { InterviewPlayButton } from '../InterviewButton'
type Props = {
interviews: InterviewPlaylistOptions,
onSelect: (selectedMathPlaylist: PlaylistOption) => void,
selectedMathPlaylist?: PlaylistOption,
}
const List = styled.ul``
const InterviewItem = styled.li`
min-height: 50px;
margin-bottom: 10px;
width: 100%;
`
export const SideInterviews = (props: Props) => {
const {
interviews,
onSelect,
selectedMathPlaylist,
} = props
return (
<List>
{
map(interviews, (interview) => {
const overLength = (size(interview.name_eng) > 16 || size(interview.name_rus) > 16)
return (
<InterviewItem key={interview.id}>
<InterviewPlayButton
active={isEqual(interview, selectedMathPlaylist)}
onClick={() => onSelect(interview)}
overlength={overLength}
>
<Name nameObj={interview} />
</InterviewPlayButton>
</InterviewItem>
)
})
}
</List>
)
}

@ -1,12 +1,79 @@
import React from 'react' import size from 'lodash/size'
import styled from 'styled-components/macro' import type { PlaylistOption, Playlists } from 'features/MatchPage/types'
import type { MatchInfo } from 'requests'
const Wrapper = styled.div` import { DropdownSection } from './components/DropdownSection'
width: 288px; import { MatchPlaylists } from './components/MatchPlaylists'
height: 100px; import { SideInterviews } from './components/SideInterviews'
margin-top: 42px; import { PlayersPlaylists } from './components/PlayersPlaylists'
margin-left: 14px;
`
export const MatchSidePlaylists = () => <Wrapper /> import {
Container,
DropdownWrapper,
List,
Wrapper,
} from './styled'
type Props = {
onSelect: (option: PlaylistOption) => void,
playlists: Playlists,
profile: MatchInfo,
selectedPlaylist?: PlaylistOption,
}
export const isEqual = (target: PlaylistOption, selected?: PlaylistOption) => (
target.id === selected?.id && target.type === selected.type
)
export const MatchSidePlaylists = ({
onSelect,
playlists,
profile,
selectedPlaylist,
}: Props) => {
const playersCount = size(playlists.players.team1) + size(playlists.players.team2)
return (
<Wrapper>
<Container>
<MatchPlaylists
playlists={playlists.match}
selectedMathPlaylist={selectedPlaylist}
onSelect={onSelect}
/>
<DropdownWrapper>
<DropdownSection
itemsCount={size(playlists.interview)}
title={playlists.lexics?.interview}
>
<SideInterviews
interviews={playlists.interview}
selectedMathPlaylist={selectedPlaylist}
onSelect={onSelect}
/>
</DropdownSection>
<DropdownSection
itemsCount={playersCount}
title={playlists.lexics?.players}
>
<List>
<PlayersPlaylists
teamName={profile?.team1}
players={playlists.players.team1}
selectedMathPlaylist={selectedPlaylist}
onSelect={onSelect}
/>
<PlayersPlaylists
teamName={profile?.team2}
players={playlists.players.team2}
selectedMathPlaylist={selectedPlaylist}
onSelect={onSelect}
/>
</List>
</DropdownSection>
</DropdownWrapper>
</Container>
</Wrapper>
)
}

@ -0,0 +1,62 @@
import styled, { css } from 'styled-components/macro'
import { customScrollbar } from 'features/Common'
import { buttonStyles } from 'features/MatchPopup/components/PlaylistButton'
export const Wrapper = styled.div`
margin-top: 42px;
max-height: 86vh;
overflow-y: scroll;
${customScrollbar}
`
export const Container = styled.div`
width: 288px;
margin-left: 14px;
margin-right: 14px;
`
export const DropdownWrapper = styled.div`
padding-top: 10px;
`
export const List = styled.div``
type ButtonProps = {
active?: boolean,
}
export const Button = styled.button<ButtonProps>`
${buttonStyles}
min-height: 50px;
outline: none;
${({ active }) => (
active
? css`
background-color: #294FC4;
&:hover {
background-color: #0c3ccc;
}
`
: ''
)}
`
export const Item = styled.li`
margin-bottom: 10px;
width: 100%;
height: 50px;
`
export const Title = styled.span`
font-weight: 500;
font-size: 20px;
line-height: 50px;
letter-spacing: 0.05em;
color: #ffffff;
overflow: hidden;
white-space: nowrap;
text-overflow:ellipsis;
`

@ -44,11 +44,13 @@ type Player = {
export type Players = Array<Player> export type Players = Array<Player>
type Lexics = { export type Lexics = {
ball_in_play: number, ball_in_play: number,
full_game: number, full_game: number,
goals: number, goals: number,
highlights: number, highlights: number,
interview: number,
players: number,
} }
export type MatchPlaylists = { export type MatchPlaylists = {

Loading…
Cancel
Save