Ott 462 play live match (#170)

* Ott 462 play live match part 1 (#168)

* feat(#462): added ProfileLogo component

* refactor(#462): changed images to ProfileLogo component

* refactor(#462): added live video request (#169)
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent 98fc901af0
commit c12897677f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 42
      src/features/Common/Image/index.tsx
  2. 1
      src/features/Common/index.tsx
  3. 17
      src/features/HeaderFilters/components/TournamentFilter/helpers.tsx
  4. 20
      src/features/HeaderFilters/components/TournamentList/index.tsx
  5. 12
      src/features/ItemsList/hooks.tsx
  6. 20
      src/features/ItemsList/index.tsx
  7. 3
      src/features/ItemsList/styled.tsx
  8. 102
      src/features/MatchCard/CardSoon/index.tsx
  9. 24
      src/features/MatchPage/hooks/useVideoData.tsx
  10. 10
      src/features/MatchPage/index.tsx
  11. 34
      src/features/Matches/helpers/prepareMatches.tsx
  12. 18
      src/features/ProfileCard/hooks.tsx
  13. 8
      src/features/ProfileCard/index.tsx
  14. 10
      src/features/ProfileCard/styled.tsx
  15. 50
      src/features/ProfileLogo/index.tsx
  16. 55
      src/features/Search/hooks/useNormalizedItems.tsx
  17. 8
      src/features/StreamPlayer/hooks/index.tsx
  18. 7
      src/features/UserFavorites/helpers.tsx
  19. 10
      src/features/UserFavorites/index.tsx
  20. 15
      src/features/UserFavorites/styled.tsx
  21. 23
      src/helpers/handleImg/index.tsx
  22. 1
      src/helpers/index.tsx
  23. 21
      src/requests/getLiveVideos.tsx
  24. 1
      src/requests/index.tsx

@ -0,0 +1,42 @@
import type { BaseSyntheticEvent } from 'react'
import React, { useCallback } from 'react'
import styled from 'styled-components/macro'
const ImageStyled = styled.img``
type Props = {
alt?: string,
className?: string,
dataSrc?: string,
fallbackSrc?: string,
src: string,
title?: string,
}
export const Image = ({
alt,
className,
dataSrc,
fallbackSrc,
src,
title,
}: Props) => {
const onError = useCallback((e: BaseSyntheticEvent) => {
// eslint-disable-next-line no-param-reassign
e.target.onError = ''
// eslint-disable-next-line no-param-reassign
e.target.src = fallbackSrc
}, [fallbackSrc])
return (
<ImageStyled
alt={alt}
src={src}
data-src={dataSrc}
className={className}
onError={onError}
title={title}
/>
)
}

@ -8,3 +8,4 @@ export * from './SportName'
export * from './StarIcon'
export * from './customScrollbar'
export * from './customStyles'
export * from './Image'

@ -3,10 +3,6 @@ import map from 'lodash/map'
import { Tournaments } from 'requests'
import { ProfileTypes } from 'config'
import {
getProfileLogo,
getProfileFallbackLogo,
} from 'helpers'
type Name = 'name_rus' | 'name_eng'
type ShortName = 'short_name_rus' | 'short_name_eng'
@ -18,22 +14,11 @@ export const normalizeTournaments = (tournaments: Tournaments, suffix: string) =
const name = tournament[`name_${suffix}` as Name]
const shortName = tournament[`short_name_${suffix}` as ShortName] || name
const country = tournament.country?.[`name_${suffix}` as Name]
const logo = getProfileLogo({
id,
profileType,
sportType,
})
const fallbackImage = getProfileFallbackLogo({
profileType,
sportType,
})
return {
country,
fallbackImage,
id,
logo,
name,
profileType,
shortName,
sportType,
}

@ -1,8 +1,8 @@
import React, { SyntheticEvent } from 'react'
import React from 'react'
import map from 'lodash/map'
import { SportTypes } from 'config'
import { SportTypes, ProfileTypes } from 'config'
import {
Logo,
@ -18,10 +18,9 @@ import { ListItem } from './styled'
type Tournament = {
country?: string,
fallbackImage: string,
id: number,
logo: string,
name: string,
profileType: ProfileTypes,
sportType: SportTypes,
}
@ -30,19 +29,13 @@ type TournamentListProps = {
tournaments: Array<Tournament>,
}
const onError = (fallbackSrc: string) => (e: SyntheticEvent<HTMLImageElement>) => {
// eslint-disable-next-line no-param-reassign
e.currentTarget.src = fallbackSrc
}
export const TournamentList = ({ onSelect, tournaments }: TournamentListProps) => (
<Wrapper role='listbox'>
{map(tournaments, ({
country,
fallbackImage,
id,
logo,
name,
profileType,
sportType,
}) => (
<ListItem
@ -52,8 +45,9 @@ export const TournamentList = ({ onSelect, tournaments }: TournamentListProps) =
>
<LogoWrapper>
<Logo
src={logo}
onError={onError(fallbackImage)}
id={id}
profileType={profileType}
sportType={sportType}
alt={name}
/>
</LogoWrapper>

@ -1,8 +1,6 @@
import {
SyntheticEvent,
useEffect,
useRef,
useCallback,
} from 'react'
import forEach from 'lodash/forEach'
@ -10,11 +8,6 @@ import forEach from 'lodash/forEach'
export const useItemsList = () => {
const ref = useRef<HTMLUListElement>(null)
const onError = useCallback((fallbackImage: string) => (e: SyntheticEvent<HTMLImageElement>) => {
// eslint-disable-next-line no-param-reassign
e.currentTarget.src = fallbackImage
}, [])
useEffect(() => {
if (ref.current) {
/**
@ -55,8 +48,5 @@ export const useItemsList = () => {
}
}, [])
return {
onError,
ref,
}
return { ref }
}

@ -2,7 +2,7 @@ import React from 'react'
import map from 'lodash/map'
import { SportTypes } from 'config'
import { SportTypes, ProfileTypes } from 'config'
import { Gender } from 'requests'
@ -24,12 +24,10 @@ import {
type SearchItemsListProps = {
close: () => void,
list: Array<{
color: string,
fallbackImage: string,
gender?: Gender,
id: number,
logo: string,
name: string,
profileType: ProfileTypes,
profileUrl: string,
sportType: SportTypes,
teamOrCountry?: string,
@ -37,19 +35,15 @@ type SearchItemsListProps = {
}
export const ItemsList = ({ close, list }: SearchItemsListProps) => {
const {
onError,
ref,
} = useItemsList()
const { ref } = useItemsList()
return (
<Wrapper ref={ref}>
{map(list, ({
fallbackImage,
gender,
id,
logo,
name,
profileType,
profileUrl,
sportType,
teamOrCountry,
@ -58,8 +52,10 @@ export const ItemsList = ({ close, list }: SearchItemsListProps) => {
<StyledLink to={profileUrl} onClick={close}>
<LogoWrapper>
<Logo
data-src={logo}
onError={onError(fallbackImage)}
lazy
id={id}
profileType={profileType}
sportType={sportType}
/>
</LogoWrapper>
<ItemInfo>

@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'
import styled from 'styled-components/macro'
import { GenderComponent as GenderBase } from 'features/Gender'
import { ProfileLogo } from 'features/ProfileLogo'
export const Wrapper = styled.ul`
margin: 0;
@ -58,7 +59,7 @@ export const LogoWrapper = styled.div`
width: 55px;
`
export const Logo = styled.img`
export const Logo = styled(ProfileLogo)`
width: 24px;
height: 24px;
`

@ -1,14 +1,12 @@
import type { BaseSyntheticEvent } from 'react'
import React, { useCallback } from 'react'
import React from 'react'
import styled from 'styled-components/macro'
import { ProfileTypes, devices } from 'config'
import { handleImageError } from 'helpers'
import type { Match } from 'features/Matches'
import { SportName } from 'features/Common'
import { ProfileLogo } from 'features/ProfileLogo'
import {
MatchDate,
@ -36,7 +34,7 @@ const TeamLogos = styled.div`
}
`
const TeamLogo = styled.img`
const TeamLogo = styled(ProfileLogo)`
width: 60px;
:last-child {
@ -68,53 +66,47 @@ export const CardSoon = ({
tournamentName,
},
showSportName,
}: CardSoonProps) => {
const onError = useCallback((e: BaseSyntheticEvent) => handleImageError({
e,
sport: sportType,
type: ProfileTypes.TEAMS,
}), [sportType])
return (
<CardWrapper>
<MatchDate>
{date}
<Time>
{time}
</Time>
</MatchDate>
<PreviewWrapper>
<TeamLogos>
<TeamLogo
src={team1.logo}
alt={team1.name}
title={team1.name}
onError={onError}
/>
<TeamLogo
src={team2.logo}
alt={team2.name}
title={team2.name}
onError={onError}
/>
</TeamLogos>
</PreviewWrapper>
<Info>
{showSportName && <SportName sport={sportType} />}
{tournamentName && (
<TournamentName title={tournamentName}>
{tournamentName}
</TournamentName>
)}
<Teams>
<Team>
<TeamName title={team1.name}>{team1.name}</TeamName>
</Team>
<Team>
<TeamName title={team2.name}>{team2.name}</TeamName>
</Team>
</Teams>
</Info>
</CardWrapper>
)
}
}: CardSoonProps) => (
<CardWrapper>
<MatchDate>
{date}
<Time>
{time}
</Time>
</MatchDate>
<PreviewWrapper>
<TeamLogos>
<TeamLogo
id={team1.id}
alt={team1.name}
title={team1.name}
sportType={sportType}
profileType={ProfileTypes.TEAMS}
/>
<TeamLogo
id={team2.id}
alt={team2.name}
title={team2.name}
sportType={sportType}
profileType={ProfileTypes.TEAMS}
/>
</TeamLogos>
</PreviewWrapper>
<Info>
{showSportName && <SportName sport={sportType} />}
{tournamentName && (
<TournamentName title={tournamentName}>
{tournamentName}
</TournamentName>
)}
<Teams>
<Team>
<TeamName title={team1.name}>{team1.name}</TeamName>
</Team>
<Team>
<TeamName title={team2.name}>{team2.name}</TeamName>
</Team>
</Teams>
</Info>
</CardWrapper>
)

@ -1,30 +1,36 @@
import { useEffect, useState } from 'react'
import type { Videos } from 'requests'
import { getVideos } from 'requests'
import { MatchStatuses } from 'features/HeaderFilters'
import type { LiveVideos, Videos } from 'requests'
import { getLiveVideos, getVideos } from 'requests'
import { useSportNameParam, usePageId } from 'hooks'
import { isNull } from 'lodash'
export const useVideoData = (matchStatus?: MatchStatuses) => {
export const useVideoData = () => {
const [videos, setVideos] = useState<Videos>([])
const [liveVideos, setLiveVideos] = useState<LiveVideos>([])
const { sportType } = useSportNameParam()
const matchId = usePageId()
useEffect(() => {
if (matchStatus === MatchStatuses.Finished) {
getVideos(sportType, matchId).then(setVideos)
const requestVideos = async () => {
const live = await getLiveVideos(sportType, matchId)
if (isNull(live)) {
const videosResponse = await getVideos(sportType, matchId)
setVideos(videosResponse)
} else {
setLiveVideos(live)
}
}
requestVideos()
},
[
matchStatus,
sportType,
matchId,
])
return {
url: '',
url: liveVideos[0] || '',
videos,
}
}

@ -3,7 +3,6 @@ import React from 'react'
import isEmpty from 'lodash/isEmpty'
import { StreamPlayer } from 'features/StreamPlayer'
import { MatchStatuses } from 'features/HeaderFilters'
import { MultiSourcePlayer } from 'features/MultiSourcePlayer'
import { MatchProfileCard } from './MatchProfileCard'
@ -14,14 +13,11 @@ import { MainWrapper, Container } from './styled'
export const MatchPage = () => {
const profile = useMatchProfile()
const { url, videos } = useVideoData(profile?.stream_status)
const { url, videos } = useVideoData()
const { onPlayerProgressChange, onPlayingChange } = usePlayerProgressReporter()
const isLiveMatch = profile?.stream_status === MatchStatuses.Live
const isFinishedMatch = (
profile?.stream_status === MatchStatuses.Finished
&& !isEmpty(videos)
)
const isLiveMatch = Boolean(url)
const isFinishedMatch = !isEmpty(videos)
return (
<MainWrapper>

@ -3,29 +3,13 @@ import map from 'lodash/map'
import format from 'date-fns/format'
import type { Match, Team } from 'requests'
import { ProfileTypes, SportTypes } from 'config'
import { getProfileLogo, getSportLexic } from 'helpers'
import { getSportLexic } from 'helpers'
type Name = 'name_rus' | 'name_eng'
type Args = {
sport: SportTypes,
suffix: string,
team: Team,
}
const prepareTeam = ({
sport,
suffix,
team,
}: Args) => ({
logo: getProfileLogo({
id: team.id,
profileType: ProfileTypes.TEAMS,
sportType: sport,
}),
const prepareTeam = (team: Team, suffix: string) => ({
...team,
name: team[`name_${suffix}` as Name],
score: team.score,
})
const prepareMatch = ({
@ -43,16 +27,8 @@ const prepareMatch = ({
sportName: getSportLexic(sport),
sportType: sport,
streamStatus: stream_status,
team1: prepareTeam({
sport,
suffix,
team: team1,
}),
team2: prepareTeam({
sport,
suffix,
team: team2,
}),
team1: prepareTeam(team1, suffix),
team2: prepareTeam(team2, suffix),
time: format(new Date(date), 'HH:mm'),
tournamentName: tournament?.[`name_${suffix}` as Name],
})

@ -1,10 +1,8 @@
import type { BaseSyntheticEvent } from 'react'
import { useCallback } from 'react'
import find from 'lodash/find'
import { FavoritesActions } from 'requests'
import { handleImageError, getProfileLogo } from 'helpers'
import { useSportNameParam, usePageId } from 'hooks'
@ -23,12 +21,6 @@ export const useProfileCard = ({ profileType }: ProfileCardProps) => {
type: profileType,
}))
const logo = getProfileLogo({
id: profileId,
profileType,
sportType,
})
const toggleFavorites = useCallback(() => {
const action = isFavorite
? FavoritesActions.REMOVE
@ -47,16 +39,10 @@ export const useProfileCard = ({ profileType }: ProfileCardProps) => {
sportType,
])
const onError = useCallback((e: BaseSyntheticEvent) => handleImageError({
e,
sport: sportType,
type: profileType,
}), [profileType, sportType])
return {
isFavorite,
logo,
onError,
profileId,
sportType,
toggleFavorites,
}
}

@ -28,18 +28,18 @@ export const ProfileCard = (props: ProfileCardProps) => {
const {
isFavorite,
logo,
onError,
profileId,
sportType,
toggleFavorites,
} = useProfileCard(props)
return (
<Wrapper>
<Logo
src={logo}
id={profileId}
alt={name}
onError={onError}
profileType={profileType}
sportType={sportType}
/>
<Details>
<Name>{name}</Name>

@ -1,8 +1,8 @@
import styled from 'styled-components/macro'
import { devices } from 'config/devices'
import { ProfileTypes, devices } from 'config'
import { ProfileTypes } from 'config'
import { ProfileLogo } from 'features/ProfileLogo'
export const Wrapper = styled.div`
display: flex;
@ -34,7 +34,7 @@ export const Name = styled.h1`
}
`
export const Logo = styled.img<{ profileType: number }>`
export const Logo = styled(ProfileLogo)`
width: 88px;
height: 88px;
background-color: #1C1C1C;
@ -96,10 +96,10 @@ export const InfoItems = styled.div`
margin-left: 20px;
display: flex;
align-items: center;
@media ${devices.laptop} {
flex-direction: column;
align-items: flex-start;
align-items: flex-start;
justify-content: center;
}

@ -0,0 +1,50 @@
import React from 'react'
import { ProfileTypes, SportTypes } from 'config'
import { getProfileFallbackLogo, getProfileLogo } from 'helpers'
import { Image } from 'features/Common'
type ProfileImageProps = {
alt?: string,
className?: string,
id: number,
lazy?: boolean,
profileType: ProfileTypes,
size?: number,
sportType: SportTypes,
title?: string,
}
export const ProfileLogo = ({
alt,
className,
id,
lazy = false,
profileType,
size,
sportType,
title,
}: ProfileImageProps) => {
const src = getProfileLogo({
id,
profileType,
size,
sportType,
})
const fallbackSrc = getProfileFallbackLogo({
profileType,
sportType,
})
return (
<Image
alt={alt}
src={lazy ? '' : src}
data-src={lazy ? src : ''}
fallbackSrc={fallbackSrc}
className={className}
title={title}
/>
)
}

@ -2,12 +2,7 @@ import map from 'lodash/map'
import { SearchItems } from 'requests'
import { ProfileTypes } from 'config'
import {
getProfileLogo,
getSportColor,
getProfileFallbackLogo,
getProfileUrl,
} from 'helpers'
import { getProfileUrl } from 'helpers'
import { useLexicsStore } from 'features/LexicsStore'
type Firstname = 'firstname_eng' | 'firstname_rus'
@ -27,26 +22,14 @@ export const useNormalizedItems = (searchItems: SearchItems) => {
const lastName = player[`lastname_${suffix}` as Lastname]
const teamName = player.team?.[`name_${suffix}` as Name]
const logo = getProfileLogo({
id,
profileType,
sportType,
})
const fallbackImage = getProfileFallbackLogo({
profileType,
sportType,
})
return {
color: getSportColor(sportType),
fallbackImage,
gender: player.gender,
id,
logo,
name: `${firstName} ${lastName}`,
profileType,
profileUrl: getProfileUrl({
id,
profileType: ProfileTypes.PLAYERS,
profileType,
sportType,
}),
sportType,
@ -59,28 +42,16 @@ export const useNormalizedItems = (searchItems: SearchItems) => {
const profileType = ProfileTypes.TEAMS
const name = team[`name_${suffix}` as Name]
const logo = getProfileLogo({
id,
profileType,
sportType,
})
const fallbackImage = getProfileFallbackLogo({
profileType,
sportType,
})
const country = team.country?.[`name_${suffix}` as Name]
return {
color: getSportColor(sportType),
fallbackImage,
gender: team.gender,
id,
logo,
name,
profileType,
profileUrl: getProfileUrl({
id,
profileType: ProfileTypes.TEAMS,
profileType,
sportType,
}),
sportType,
@ -93,28 +64,16 @@ export const useNormalizedItems = (searchItems: SearchItems) => {
const { id, sport: sportType } = tournament
const name = tournament[`name_${suffix}` as Name]
const logo = getProfileLogo({
id,
profileType,
sportType,
})
const fallbackImage = getProfileFallbackLogo({
profileType,
sportType,
})
const country = tournament.country?.[`name_${suffix}` as Name]
return {
color: getSportColor(sportType),
fallbackImage,
gender: tournament.gender,
id,
logo,
name,
profileType,
profileUrl: getProfileUrl({
id,
profileType: ProfileTypes.TOURNAMENTS,
profileType,
sportType,
}),
sportType,

@ -60,7 +60,7 @@ export const useVideoPlayer = ({
const setPlayerProgress = useCallback(
throttle((value: number) => {
playerRef.current?.seekTo(value, 'seconds')
playerRef.current?.seekTo(value, 'fraction')
}, 100),
[],
)
@ -70,9 +70,9 @@ export const useVideoPlayer = ({
}
const onProgressChange = useCallback((progress: number) => {
const progressSeconds = progress * duration
setPlayedProgress(toMilliSeconds(progressSeconds))
setPlayerProgress(progressSeconds)
const progressMs = progress * duration
setPlayedProgress(progressMs)
setPlayerProgress(progress)
}, [
duration,
setPlayedProgress,

@ -1,6 +1,6 @@
import map from 'lodash/map'
import { getProfileLogo, getProfileUrl } from 'helpers'
import { getProfileUrl } from 'helpers'
import type { UserFavorites } from 'requests'
type Names = 'name_eng' | 'name_rus'
@ -23,11 +23,6 @@ export const normalizeUserFavorites = (favorites: UserFavorites, suffix: string)
lastname: item.info[lastNameField],
name: item.info[nameField],
nickname: item.info[nickNameField],
profileLogo: getProfileLogo({
id: item.id,
profileType: item.type,
sportType: item.sport,
}),
profileUrl: getProfileUrl({
id: item.id,
profileType: item.type,

@ -3,7 +3,6 @@ import React, { useState } from 'react'
import map from 'lodash/map'
import { FavoritesActions } from 'requests'
import { handleImageError } from 'helpers'
import { Modal } from 'features/Modal'
@ -70,13 +69,10 @@ export const UserFavorites = () => {
/>
<StyledLink to={item.profileUrl} target='_blank'>
<UserSportFavImgWrapper
src={item.profileLogo}
id={item.id}
alt={item.name}
onError={(e) => handleImageError({
e,
sport: item.sport,
type: item.type,
})}
sportType={item.sport}
profileType={item.type}
/>
</StyledLink>
</UserSportFavItemLogoWrapper>

@ -7,6 +7,7 @@ import styled from 'styled-components/macro'
import { Logo } from 'features/Logo'
import { T9n } from 'features/T9n'
import { customScrollbar } from 'features/Common'
import { ProfileLogo } from 'features/ProfileLogo'
import { TooltipBlockWrapper } from './TooltipBlock/styled'
@ -66,26 +67,26 @@ export const UserSportFavItemLogoWrapper = styled.div`
${UserSportFavXWrapper} {
display: none;
}
${TooltipBlockWrapper} {
display: none;
}
&:hover {
background-color: rgba(255, 255, 255, 0.7);
cursor: pointer;
${UserSportFavXWrapper} {
display: block;
}
${TooltipBlockWrapper} {
display: block;
}
}
`
export const UserSportFavImgWrapper = styled.img`
export const UserSportFavImgWrapper = styled(ProfileLogo)`
width: 100%;
`
@ -103,14 +104,14 @@ export const FavoriteModal = styled.div`
height: 202px;
display: flex;
flex-direction: column;
justify-content: center;
justify-content: center;
align-items: center;
`
export const ExclamationSign = styled.div`
width: 77px;
height: 66px;
background: url(/images/exclamation.svg) no-repeat;
background: url(/images/exclamation.svg) no-repeat;
margin-bottom: 20px;
`

@ -1,23 +0,0 @@
import { BaseSyntheticEvent } from 'react'
import {
SportTypes,
ProfileTypes,
} from 'config'
import { getProfileFallbackLogo } from 'helpers/getProfileFallbackLogo'
type Args = {
e: BaseSyntheticEvent,
sport: SportTypes,
type: ProfileTypes,
}
export const handleImageError = (arg: Args): void => {
// eslint-disable-next-line no-param-reassign
arg.e.target.onError = ''
// eslint-disable-next-line no-param-reassign
arg.e.target.src = getProfileFallbackLogo({
profileType: arg.type,
sportType: arg.sport,
})
}

@ -6,6 +6,5 @@ export * from './getProfileFallbackLogo'
export * from './getProfileUrl'
export * from './getSportColor'
export * from './getSportLexic'
export * from './handleImg'
export * from './msToMinutesAndSeconds'
export * from './secondsToHms'

@ -0,0 +1,21 @@
import { API_ROOT, SportTypes } from 'config'
import { callApi } from 'helpers'
export type LiveVideos = Array<string>
export const getLiveVideos = (
sportType: SportTypes,
matchId: number,
): Promise<LiveVideos> => {
const config = {
body: {
match_id: matchId,
sport_id: sportType,
},
}
return callApi({
config,
url: `${API_ROOT}/video/stream`,
})
}

@ -17,3 +17,4 @@ export * from './reportPlayerProgress'
export * from './getVideos'
export * from './saveUserInfo'
export * from './getPlayerInfo'
export * from './getLiveVideos'

Loading…
Cancel
Save