fix(#2556): display of tabs on match or event page

keep-around/1e28a34fbd392900c7a7d1e084705d08070e1034
Rakov Roman 3 years ago
parent 4f8e7ffa08
commit c9029648d0
  1. 1
      src/config/lexics/indexLexics.tsx
  2. 22
      src/features/Common/Tabs/index.tsx
  3. 6
      src/features/MatchCard/CardFrontside/MatchCardMobile/index.tsx
  4. 6
      src/features/MatchCard/CardFrontside/index.tsx
  5. 9
      src/features/MatchPage/components/FinishedMatch/index.tsx
  6. 9
      src/features/MatchPage/components/LiveMatch/index.tsx
  7. 4
      src/features/MatchPage/hooks/useMatchProfile.tsx
  8. 55
      src/features/MatchPage/hooks/useTournamentData.tsx
  9. 3
      src/features/MatchPage/index.tsx
  10. 2
      src/features/MatchPage/styled.tsx
  11. 7
      src/features/MatchPage/types.tsx
  12. 45
      src/features/MatchSidePlaylists/components/MatchPlaylists/index.tsx
  13. 73
      src/features/MatchSidePlaylists/components/TabVideo/components/VideoDate/VideoDate.tsx
  14. 42
      src/features/MatchSidePlaylists/components/TabVideo/components/VideoDate/styled.tsx
  15. 59
      src/features/MatchSidePlaylists/components/TabVideo/index.tsx
  16. 7
      src/features/MatchSidePlaylists/components/TabVideo/styled.tsx
  17. 1
      src/features/MatchSidePlaylists/config.tsx
  18. 74
      src/features/MatchSidePlaylists/hooks.tsx
  19. 58
      src/features/MatchSidePlaylists/index.tsx
  20. 2
      src/features/Search/index.tsx
  21. 2
      src/requests/getMatches/types.tsx

@ -29,6 +29,7 @@ const matchPopupLexics = {
selected_player_actions: 13413,
started_streaming_at: 16042,
streamed_live_on: 16043,
video: 1017,
views: 13440,
watch: 818,
watch_from: 13022,

@ -15,6 +15,7 @@ export const Tab = styled.button.attrs(({ selected }: TabProps) => ({
border: none;
outline: none;
height: 100%;
width: 100%;
padding-top: 0.38rem;
padding-bottom: 0.27rem;
font-weight: 600;
@ -73,25 +74,28 @@ export const Tab = styled.button.attrs(({ selected }: TabProps) => ({
}
`
: ''};
`
type TabGroupProps = {
buttons: number,
}
&:only-child {
background: transparent;
color: #FFFFFF;
cursor: default;
}
`
export const TabsGroup = styled.div.attrs({
role: 'group',
})<TabGroupProps>`
})`
width: 100%;
height: 100%;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3);
overflow: hidden;
border: 1px solid #FFFFFF;
border-radius: 2px;
display: flex;
${Tab} {
width: ${({ buttons }) => 100 / buttons}%;
> :not(:only-child) {
border: 1px solid #FFFFFF;
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3);
}
${isMobileDevice
? css`
height: 28px;

@ -1,5 +1,5 @@
import type { KeyboardEvent } from 'react'
import { useRouteMatch } from 'react-router'
import { useLocation, useRouteMatch } from 'react-router'
import getUnixTime from 'date-fns/getUnixTime'
@ -49,6 +49,7 @@ export const CardFrontsideMobile = ({
onClick,
onKeyPress,
}: Props) => {
const location = useLocation()
const {
access,
date,
@ -63,6 +64,7 @@ export const CardFrontsideMobile = ({
tournament,
} = match
const isHomePage = useRouteMatch(PAGES.home)?.isExact
const isMatchPage = location.pathname.includes(PAGES.match)
const tournamentName = useName(tournament)
const { isInFavorites } = useUserFavoritesStore()
const { isScoreHidden } = useMatchSwitchesStore()
@ -106,7 +108,7 @@ export const CardFrontsideMobile = ({
)}
<MatchTimeInfo>
<MatchDate isHomePage={isHomePage}>
{isHomePage ? null : formattedDate}
{isHomePage || isMatchPage ? null : formattedDate}
{live && (
<LiveSign>
<T9n t='live' />

@ -1,5 +1,5 @@
import type { KeyboardEvent } from 'react'
import { useRouteMatch } from 'react-router'
import { useLocation, useRouteMatch } from 'react-router'
import getUnixTime from 'date-fns/getUnixTime'
@ -51,6 +51,7 @@ export const CardFrontside = ({
onClick,
onKeyPress,
}: Props) => {
const location = useLocation()
const {
access,
date,
@ -65,6 +66,7 @@ export const CardFrontside = ({
tournament,
} = match
const isHomePage = useRouteMatch(PAGES.home)?.isExact
const isMatchPage = location.pathname.includes(PAGES.match)
const tournamentName = useName(tournament)
const { isInFavorites } = useUserFavoritesStore()
const { isScoreHidden } = useMatchSwitchesStore()
@ -119,7 +121,7 @@ export const CardFrontside = ({
{access === MatchAccess.CanBuyMatch && <BuyMatchBadge />}
<MatchTimeInfo>
<MatchDate isHomePage={isHomePage}>
{isHomePage ? null : formattedDate}
{isHomePage || isMatchPage ? null : formattedDate}
<Time>{time}</Time>
</MatchDate>
{live && (

@ -13,13 +13,19 @@ import { SettingsPopup } from '../SettingsPopup'
import { useFinishedMatch } from './hooks'
import { Container } from '../../styled'
import { Modal } from './styled'
import { TournamentData } from '../../types'
type Props = {
events: Events,
profile: MatchInfo,
tournamentData: TournamentData,
}
export const FinishedMatch = ({ events, profile }: Props) => {
export const FinishedMatch = ({
events,
profile,
tournamentData,
}: Props) => {
const {
chapters,
closeSettingsPopup,
@ -61,6 +67,7 @@ export const FinishedMatch = ({ events, profile }: Props) => {
selectedPlaylist={selectedPlaylist}
onSelect={onPlaylistSelect}
profile={profile}
tournamentData={tournamentData}
/>
</Fragment>
)

@ -10,13 +10,19 @@ import { MatchSidePlaylists } from 'features/MatchSidePlaylists'
import { Container } from '../../styled'
import { useLiveMatch } from './hooks'
import { TournamentData } from '../../types'
type Props = {
events: Events,
profile: MatchInfo,
tournamentData: TournamentData,
}
export const LiveMatch = ({ events, profile }: Props) => {
export const LiveMatch = ({
events,
profile,
tournamentData,
}: Props) => {
const {
matchPlaylists,
onPlayerProgressChange,
@ -42,6 +48,7 @@ export const LiveMatch = ({ events, profile }: Props) => {
</Container>
<MatchSidePlaylists
tournamentData={tournamentData}
events={events}
onSelect={onPlaylistSelect}
playlists={matchPlaylists}

@ -13,6 +13,7 @@ import { parseDate } from 'helpers/parseDate'
import type { Playlists } from '../types'
import { useMatchData } from './useMatchData'
import { useTournamentData } from './useTournamentData'
const addScoresFromPlaylists = (
profile: MatchInfo,
@ -58,6 +59,8 @@ export const useMatchProfile = () => {
[matchProfile, matchPlaylists],
)
const { tournamentData } = useTournamentData(matchProfile?.tournament.id ?? null)
const isStarted = useMemo(() => (
profile?.date
? parseDate(profile.date) < new Date()
@ -68,5 +71,6 @@ export const useMatchProfile = () => {
events,
isStarted,
profile,
tournamentData,
}
}

@ -0,0 +1,55 @@
import {
useEffect,
useMemo,
useState,
} from 'react'
import { format } from 'date-fns'
import sortedUniq from 'lodash/sortedUniq'
import isNull from 'lodash/isNull'
import sortBy from 'lodash/sortBy'
import type { Match } from 'features/Matches'
import { prepareMatches } from 'features/Matches/helpers/prepareMatches'
import { getTournamentMatches } from 'requests'
import { parseDate } from 'helpers/parseDate'
import { usePageParams } from 'hooks/usePageParams'
import { TournamentData } from '../types'
export const useTournamentData = (tournamentId: number | null) => {
const { sportType } = usePageParams()
const [tournamentMatches, setTournamentMatches] = useState<Array<Match>>([])
const [matchDates, setMatchDates] = useState<Array<string>>([])
useEffect(() => {
if (!isNull(tournamentId)) {
(async () => {
const matchesBySection = await getTournamentMatches({
limit: 1000,
offset: 0,
sportType,
tournamentId,
})
const matchDateList = matchesBySection.broadcast.map((match) => (
parseDate(match.date)
)).sort((a, b) => a.getTime() - b.getTime())
setMatchDates(sortedUniq(matchDateList.map((date) => format(date, 'yyyy-MM-dd'))))
setTournamentMatches(sortBy(prepareMatches(matchesBySection.broadcast), ['date']))
})()
}
}, [tournamentId, sportType])
const tournamentData: TournamentData = useMemo(() => ({
matchDates,
matches: tournamentMatches,
}), [matchDates, tournamentMatches])
return { tournamentData }
}

@ -31,6 +31,7 @@ const MatchPage = () => {
events,
isStarted,
profile,
tournamentData,
} = useMatchProfile()
const {
@ -81,12 +82,14 @@ const MatchPage = () => {
<LiveMatch
events={events}
profile={profile}
tournamentData={tournamentData}
/>
)}
{playFromScout && (
<FinishedMatch
events={events}
profile={profile}
tournamentData={tournamentData}
/>
)}
</Wrapper>

@ -50,7 +50,7 @@ export const Container = styled.div`
margin-right: 0;
padding: 0;
margin-bottom: 15px;
min-height: 32vh;
min-height: min-content;
@media screen and (orientation: landscape){
display: block;

@ -1,5 +1,7 @@
import type { Lexics, Episodes } from 'requests'
import type { Match } from 'features/Matches'
import type { MatchPlaylistIds } from './helpers/buildPlaylists'
export enum PlaylistTypes {
@ -64,3 +66,8 @@ export type Playlists = {
score2: number,
state?: string,
}
export type TournamentData = {
matchDates: Array<string>,
matches: Array<Match>,
}

@ -1,5 +1,8 @@
import { useMemo } from 'react'
import styled, { css } from 'styled-components/macro'
import filter from 'lodash/filter'
import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map'
@ -36,21 +39,27 @@ export const MatchPlaylists = ({
onSelect,
playlists,
selectedMathPlaylist,
}: Props) => (
<List>
{
map(playlists, (playlist) => (
<Item key={playlist.id}>
<PlayButton
duration={playlist.duration}
active={isEqual(playlist, selectedMathPlaylist)}
disabled={playlist.id !== FULL_GAME_KEY && isEmpty(playlist.data)}
onClick={() => onSelect?.(playlist)}
>
<T9n t={playlist.lexic} />
</PlayButton>
</Item>
))
}
</List>
)
}: Props) => {
const filteredPlayListByDuration = useMemo(() => (
filter(playlists, (playlist) => !!playlist.duration)
), [playlists])
return (
<List>
{
map(filteredPlayListByDuration, (playlist) => (
<Item key={playlist.id}>
<PlayButton
duration={playlist.duration}
active={isEqual(playlist, selectedMathPlaylist)}
disabled={playlist.id !== FULL_GAME_KEY && isEmpty(playlist.data)}
onClick={() => onSelect?.(playlist)}
>
<T9n t={playlist.lexic} />
</PlayButton>
</Item>
))
}
</List>
)
}

@ -0,0 +1,73 @@
import {
useCallback,
useMemo,
} from 'react'
import { format } from 'date-fns'
import { WeekDay, Wrapper } from './styled'
export type Props = {
matchDates: Array<string>,
onDateClick: (date: string) => void,
selectedDate: string,
}
export const VideoDate = (props: Props) => {
const {
matchDates,
onDateClick,
selectedDate,
} = props
const selectedDateIndex = useMemo(() => (
matchDates.findIndex((date) => date === selectedDate)
), [matchDates, selectedDate])
const currentDay = useMemo(() => (
matchDates.length ? matchDates[selectedDateIndex] : null
), [matchDates, selectedDateIndex])
const previousDay = useMemo(() => {
if (selectedDateIndex !== 0) {
return matchDates[selectedDateIndex - 1]
}
return null
}, [matchDates, selectedDateIndex])
const nextDay = useMemo(() => {
if (selectedDateIndex !== matchDates.length - 1) {
return matchDates[selectedDateIndex + 1]
}
return null
}, [matchDates, selectedDateIndex])
const onDayClick = (date: string) => {
onDateClick?.(date)
}
const formatDate = useCallback((date: string) => format(Date.parse(date), 'MMM dd, EE'), [])
return (
<Wrapper>
{previousDay && (
<WeekDay
onClick={() => onDayClick(previousDay)}
>{formatDate(previousDay)}
</WeekDay>
)}
{currentDay && (
<WeekDay
isActive
>{formatDate(currentDay)}
</WeekDay>
)}
{nextDay && (
<WeekDay
onClick={() => onDayClick(nextDay)}
>{formatDate(nextDay)}
</WeekDay>
)}
</Wrapper>
)
}

@ -0,0 +1,42 @@
import styled, { css } from 'styled-components'
export const Wrapper = styled.div`
color: #FFFFFF;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
> :not(:last-child) {
margin-right: 20px;
}
`
export const WeekDay = styled.div.attrs(() => ({
'aria-hidden': true,
}))<{isActive?: boolean}>`
position: relative;
color: rgba(255, 255, 255, 0.5);
font-size: 12px;
white-space: nowrap;
padding: 5px;
cursor: pointer;
${({ isActive }) => (
isActive
? css`
color: #FFFFFF;
cursor: default;
:after {
position: absolute;
bottom: 0;
left: 0;
content: '';
width: 100%;
height: 2px;
background-color: #FFFFFF;
}
`
: '')}
`

@ -0,0 +1,59 @@
import {
Fragment,
useMemo,
useState,
} from 'react'
import { format } from 'date-fns'
import map from 'lodash/map'
import { MatchCard } from 'features/MatchCard'
import { TournamentData } from 'features/MatchPage/types'
import { MatchInfo } from 'requests'
import { parseDate } from 'helpers/parseDate'
import { usePageParams } from 'hooks/usePageParams'
import { VideoDate } from './components/VideoDate/VideoDate'
import { MatchesWrapper } from './styled'
type Props = {
profile: MatchInfo,
tournamentData: TournamentData,
}
export const TabVideo = ({
profile,
tournamentData,
}: Props) => {
const { profileId } = usePageParams()
const [selectedDate, setSelectedDate] = useState(format(parseDate(profile?.date!), 'yyyy-MM-dd'))
const matches = useMemo(() => (
tournamentData.matches.filter((match) => (
format(match.date, 'yyyy-MM-dd') === selectedDate && match.id !== profileId
))
), [profileId, selectedDate, tournamentData.matches])
return (
<Fragment>
<VideoDate
matchDates={tournamentData.matchDates}
selectedDate={selectedDate}
onDateClick={setSelectedDate}
/>
<MatchesWrapper>
{
map(matches, (match) => (
<MatchCard
key={match.id}
match={match}
/>
))
}
</MatchesWrapper>
</Fragment>
)
}

@ -0,0 +1,7 @@
import styled from 'styled-components'
export const MatchesWrapper = styled.div`
> :not(:last-child) {
margin-bottom: 15px;
}
`

@ -1,4 +1,5 @@
export enum Tabs {
WATCH,
EVENTS,
VIDEO
}

@ -1,11 +1,79 @@
import { useState } from 'react'
import {
useEffect,
useMemo,
useState,
} from 'react'
import reduce from 'lodash/reduce'
import { Playlists, TournamentData } from 'features/MatchPage/types'
import type { Events } from 'requests'
import { Tabs } from './config'
export const useMatchSidePlaylists = () => {
const [selectedTab, setSelectedTab] = useState<Tabs>(Tabs.WATCH)
export type Props = {
events: Events,
playlists: Playlists,
tournamentData: TournamentData,
}
export const useMatchSidePlaylists = (props: Props) => {
const {
events,
playlists,
tournamentData,
} = props
const [selectedTab, setSelectedTab] = useState<Tabs>(Tabs.VIDEO)
const isWatchTabVisible = useMemo(() => {
const playListFilter = reduce(
playlists.match,
(acc, item) => {
let result = acc
if (item.duration) result++
return result
},
0,
)
return playListFilter > 1
}, [playlists])
const isEventTabVisible = useMemo(() => {
if (events.length > 0) {
setSelectedTab(Tabs.EVENTS)
return true
}
return false
}, [events])
const isVideoTabVisible = useMemo(() => {
if (tournamentData.matches.length > 0) {
setSelectedTab(Tabs.EVENTS)
return true
}
return false
}, [tournamentData])
useEffect(() => {
switch (true) {
case isWatchTabVisible:
setSelectedTab(Tabs.WATCH)
break
case isEventTabVisible:
setSelectedTab(Tabs.EVENTS)
break
case isVideoTabVisible:
setSelectedTab(Tabs.VIDEO)
break
}
}, [isEventTabVisible, isVideoTabVisible, isWatchTabVisible])
return {
isEventTabVisible,
isVideoTabVisible,
isWatchTabVisible,
onTabClick: setSelectedTab,
selectedTab,
}

@ -1,12 +1,17 @@
import type { Events, MatchInfo } from 'requests'
import type { PlaylistOption, Playlists } from 'features/MatchPage/types'
import type {
PlaylistOption,
Playlists,
TournamentData,
} from 'features/MatchPage/types'
import { Tab, TabsGroup } from 'features/Common'
import { T9n } from 'features/T9n'
import { Tabs } from './config'
import { TabEvents } from './components/TabEvents'
import { TabWatch } from './components/TabWatch'
import { TabVideo } from './components/TabVideo'
import { useMatchSidePlaylists } from './hooks'
import {
Wrapper,
@ -17,6 +22,7 @@ import {
const tabPanes = {
[Tabs.WATCH]: TabWatch,
[Tabs.EVENTS]: TabEvents,
[Tabs.VIDEO]: TabVideo,
}
type Props = {
@ -25,6 +31,7 @@ type Props = {
playlists: Playlists,
profile: MatchInfo,
selectedPlaylist?: PlaylistOption,
tournamentData: TournamentData,
}
export const MatchSidePlaylists = ({
@ -33,34 +40,55 @@ export const MatchSidePlaylists = ({
playlists,
profile,
selectedPlaylist,
tournamentData,
}: Props) => {
const {
isEventTabVisible,
isVideoTabVisible,
isWatchTabVisible,
onTabClick,
selectedTab,
} = useMatchSidePlaylists()
} = useMatchSidePlaylists({
events,
playlists,
tournamentData,
})
const TabPane = tabPanes[selectedTab]
return (
<Wrapper>
<TabsWrapper>
<TabsGroup buttons={2}>
<Tab
selected={selectedTab === Tabs.WATCH}
onClick={() => onTabClick(Tabs.WATCH)}
>
<T9n t='watch' />
</Tab>
<Tab
selected={selectedTab === Tabs.EVENTS}
onClick={() => onTabClick(Tabs.EVENTS)}
>
<T9n t='actions' />
</Tab>
<TabsGroup>
{isWatchTabVisible ? (
<Tab
selected={selectedTab === Tabs.WATCH}
onClick={() => onTabClick(Tabs.WATCH)}
>
<T9n t='watch' />
</Tab>
) : null}
{isEventTabVisible ? (
<Tab
selected={selectedTab === Tabs.EVENTS}
onClick={() => onTabClick(Tabs.EVENTS)}
>
<T9n t='actions' />
</Tab>
) : null}
{isVideoTabVisible ? (
<Tab
selected={selectedTab === Tabs.VIDEO}
onClick={() => onTabClick(Tabs.VIDEO)}
>
<T9n t='video' />
</Tab>
) : null}
</TabsGroup>
</TabsWrapper>
<Container>
<TabPane
tournamentData={tournamentData}
events={events}
onSelect={onSelect}
playlists={playlists}

@ -64,7 +64,7 @@ export const Search = () => {
</Form>
{showResults && (
<Results>
<TabsGroup buttons={3}>
<TabsGroup>
<Tab
disabled={isEmpty(searchItems[ProfileTypes.TOURNAMENTS])}
selected={selectedTab === ProfileTypes.TOURNAMENTS}

@ -15,6 +15,8 @@ type Team = {
export type Match = {
access: boolean,
/** тип трансляции */
c_type_broadcast: number,
calc: boolean,
country_id: number,
date: string,

Loading…
Cancel
Save