fix(#720): timeline mode

pull/295/head
Rakov 2 years ago committed by Gitea
parent af912dca10
commit c41acef4ce
  1. 1
      src/config/lexics/indexLexics.tsx
  2. 2
      src/features/BuyMatchPopup/types.tsx
  3. 1
      src/features/Common/Image/index.tsx
  4. 14
      src/features/HeaderFilters/components/DateFilter/helpers.tsx
  5. 10
      src/features/HeaderFilters/components/DateFilter/hooks/index.tsx
  6. 199
      src/features/HeaderFilters/components/DateFilter/index.tsx
  7. 117
      src/features/HeaderFilters/components/DateFilter/styled.tsx
  8. 193
      src/features/HeaderFilters/components/FacrDateFilter/index.tsx
  9. 1
      src/features/HeaderFilters/index.tsx
  10. 1
      src/features/HeaderFilters/store/config.tsx
  11. 16
      src/features/HeaderFilters/store/hooks/index.tsx
  12. 5
      src/features/HeaderMobile/index.tsx
  13. 1
      src/features/HeaderMobile/styled.tsx
  14. 5
      src/features/HomePage/components/Header/index.tsx
  15. 3
      src/features/HomePage/components/HeaderFilters/index.tsx
  16. 3
      src/features/HomePage/components/HeaderFilters/styled.tsx
  17. 8
      src/features/HomePage/index.tsx
  18. 3
      src/features/Icon/index.tsx
  19. 2
      src/features/MatchCard/CardFrontside/MatchCardMobile/index.tsx
  20. 8
      src/features/MatchCard/CardFrontside/index.tsx
  21. 4
      src/features/MatchCard/config.tsx
  22. 2
      src/features/MatchCard/hooks.tsx
  23. 2
      src/features/MatchCard/index.tsx
  24. 4
      src/features/MatchPage/store/hooks/useTournamentData.tsx
  25. 3
      src/features/MatchPage/types.tsx
  26. 2
      src/features/MatchPopup/types.tsx
  27. 2
      src/features/Matches/components/MatchesList/index.tsx
  28. 4
      src/features/Matches/helpers/addSportType.tsx
  29. 4
      src/features/Matches/helpers/getMatchClickAction/__tests__/index.tsx
  30. 4
      src/features/Matches/helpers/getMatchClickAction/index.tsx
  31. 4
      src/features/Matches/hooks.tsx
  32. 2
      src/features/Matches/index.tsx
  33. 7
      src/features/MatchesGrid/index.tsx
  34. 50
      src/features/MatchesSlider/hooks.tsx
  35. 100
      src/features/MatchesSlider/index.tsx
  36. 108
      src/features/MatchesSlider/styled.tsx
  37. 111
      src/features/MatchesTimeline/hooks.tsx
  38. 115
      src/features/MatchesTimeline/index.tsx
  39. 90
      src/features/MatchesTimeline/styled.tsx
  40. 5
      src/features/PageLayout/styled.tsx
  41. 1
      src/features/SportsFilter/components/SelectSport/styled.tsx
  42. 3
      src/features/TournamentList/components/TournamentMobile/index.tsx
  43. 7
      src/features/TournamentList/hooks.tsx
  44. 3
      src/features/TournamentList/index.tsx
  45. 3
      src/features/UserFavorites/styled.tsx
  46. 1
      src/helpers/index.tsx
  47. 29
      src/helpers/prepareMatches/index.tsx
  48. 4
      src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
  49. 4
      src/pages/HighlightsPage/storeHighlightsAtoms.tsx
  50. 35
      src/requests/getMatches/getTimelineMatches.tsx
  51. 1
      src/requests/getMatches/index.tsx
  52. 16
      src/requests/getMatches/types.tsx

@ -224,6 +224,7 @@ export const indexLexics = {
sport: 12993, sport: 12993,
team: 14973, team: 14973,
terms_and_conditions: 15738, terms_and_conditions: 15738,
timeline_title: 20266,
to_home: 13376, to_home: 13376,
total_likes: 20253, total_likes: 20253,
tournament: 14974, tournament: 14974,

@ -1,7 +1,7 @@
import type { SubscriptionResponse, Subscription } from 'requests/getSubscriptions' import type { SubscriptionResponse, Subscription } from 'requests/getSubscriptions'
import type { LexicsId, Values } from 'features/LexicsStore/types' import type { LexicsId, Values } from 'features/LexicsStore/types'
import type { Match as MatchBase } from 'features/Matches/hooks' import type { Match as MatchBase } from 'helpers/prepareMatches'
import { MatchAccess } from 'features/Matches/helpers/getMatchClickAction' import { MatchAccess } from 'features/Matches/helpers/getMatchClickAction'
export enum Steps { export enum Steps {

@ -37,6 +37,7 @@ export const Image = ({
onError={onError} onError={onError}
onLoad={onLoad} onLoad={onLoad}
title={title} title={title}
loading='lazy'
/> />
) )
} }

@ -3,6 +3,8 @@ import addDays from 'date-fns/addDays'
import addMonths from 'date-fns/addMonths' import addMonths from 'date-fns/addMonths'
import startOfYear from 'date-fns/startOfYear' import startOfYear from 'date-fns/startOfYear'
import { isMobileDevice } from 'config'
type Args = { type Args = {
date: Date, date: Date,
lang: string, lang: string,
@ -20,7 +22,7 @@ const getMonthName = ({
export const getDisplayDate = ({ export const getDisplayDate = ({
date, date,
lang, lang,
monthType = 'long', monthType = isMobileDevice ? 'long' : 'short',
}: Args) => ({ }: Args) => ({
day: date.getDate(), day: date.getDate(),
month: getMonthName({ month: getMonthName({
@ -48,6 +50,8 @@ type Week = {
name: string, name: string,
} }
type Month = Week
export const getWeeks = (date: Date, locale: string) => { export const getWeeks = (date: Date, locale: string) => {
const weekStart = startOfWeek(date, { weekStartsOn: 1 }) const weekStart = startOfWeek(date, { weekStartsOn: 1 })
const getWeekDay = createDayGetter(weekStart, locale) const getWeekDay = createDayGetter(weekStart, locale)
@ -64,17 +68,17 @@ export const getWeeks = (date: Date, locale: string) => {
} }
const createMonthGetter = (yearStart: Date, locale: string) => (month: number) => { const createMonthGetter = (yearStart: Date, locale: string) => (month: number) => {
const dayDate = addMonths(yearStart, month) const monthDate = addMonths(yearStart, month)
return { return {
date: dayDate, date: monthDate,
name: new Intl.DateTimeFormat(locale || 'en', { month: 'short' }).format(dayDate), name: new Intl.DateTimeFormat(locale || 'en', { month: 'short' }).format(monthDate),
} }
} }
export const getMonths = (locale: string, date: Date) => { export const getMonths = (locale: string, date: Date) => {
const yearStart = startOfYear(date) const yearStart = startOfYear(date)
const getMonth = createMonthGetter(yearStart, locale) const getMonth = createMonthGetter(yearStart, locale)
const months: Array<Week> = [ const months: Array<Month> = [
getMonth(0), getMonth(0),
getMonth(1), getMonth(1),
getMonth(2), getMonth(2),

@ -29,6 +29,8 @@ import {
export const useDateFilter = () => { export const useDateFilter = () => {
const { const {
isMonthMode, isMonthMode,
isTimelineMode,
isWeekMode,
selectedDate, selectedDate,
selectedMode, selectedMode,
selectedMonthModeDate, selectedMonthModeDate,
@ -62,8 +64,11 @@ export const useDateFilter = () => {
lang, lang,
}) })
const filters = localStorage.getItem('filters') const filters = localStorage.getItem('filters')
const dateMode = localStorage.getItem('dateMode')
const parseFilters = filters && JSON.parse(filters) const parseFilters = filters && JSON.parse(filters)
const parseMode = dateMode && JSON.parse(dateMode)
const lastDate = parseFilters?.selectedDate const lastDate = parseFilters?.selectedDate
const lastMonthDate = new Date(parseMode?.selectedMonthModeDate)
const weekName = getWeekName(selectedDate, 'en') const weekName = getWeekName(selectedDate, 'en')
const validator = (value: unknown) => Boolean(value) && isObject(value) const validator = (value: unknown) => Boolean(value) && isObject(value)
@ -83,6 +88,7 @@ export const useDateFilter = () => {
useEffect(() => { useEffect(() => {
if (lastDate === selectedDate.getDate() if (lastDate === selectedDate.getDate()
&& lastMonthDate === selectedMonthModeDate
&& parseFilters && parseFilters
&& parseFilters.selectedLeague[0] !== 'all_competitions') { && parseFilters.selectedLeague[0] !== 'all_competitions') {
setIsShowTournament(false) setIsShowTournament(false)
@ -94,7 +100,7 @@ export const useDateFilter = () => {
setSelectedLeague(['all_competitions']) setSelectedLeague(['all_competitions'])
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDate]) }, [selectedDate, selectedMonthModeDate])
const onPreviousClick = () => { const onPreviousClick = () => {
addAdsViews() addAdsViews()
@ -130,6 +136,8 @@ export const useDateFilter = () => {
date, date,
isMonthMode, isMonthMode,
isOpen, isOpen,
isTimelineMode,
isWeekMode,
months, months,
onDateChange, onDateChange,
onNextClick, onNextClick,

@ -2,40 +2,62 @@ import { Fragment } from 'react'
import map from 'lodash/map' import map from 'lodash/map'
import { T9n } from 'features/T9n'
import { Icon } from 'features/Icon'
import { OutsideClick } from 'features/OutsideClick' import { OutsideClick } from 'features/OutsideClick'
import { BodyBackdrop } from 'features/PageLayout' import { BodyBackdrop } from 'features/PageLayout'
import { Icon } from 'features/Icon' import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { isInSportsClient, isMobileDevice } from 'config'
import { useDateFilter } from './hooks' import { useDateFilter } from './hooks'
import { DatePicker } from '../DatePicker' import { DatePicker } from '../DatePicker'
import { Tabs } from '../../store/config'
import { import {
Wrapper, TabsList,
MonthWrapper, Tab,
WeekDaysWrapper, TabTitle,
YearWrapper,
ArrowButton, ArrowButton,
Arrow, Arrow,
DateButton, MonthModeYear,
MonthYear, MonthModeWrapper,
Week, Month,
MonthName,
CalendarWrapper,
DateWrapper,
MonthArrow,
WeekDaysWrapper,
WeekDay, WeekDay,
WeekName, WeekName,
WeekNumber, WeekNumber,
WeekModeButton,
WeekModeYear,
Week,
MonthButtonWrapper,
} from './styled' } from './styled'
import { useHeaderFiltersStore } from '../../store'
export const DateFilter = () => { export const DateFilter = () => {
const { const {
addAdsViews, addAdsViews,
close, close,
date, date,
isMonthMode,
isOpen, isOpen,
isWeekMode,
months,
onDateChange, onDateChange,
onNextClick, onNextClick,
onNextYearClick,
onPreviousClick, onPreviousClick,
onPrevYearClick,
onWeekDayClick, onWeekDayClick,
openDatePicker, openDatePicker,
selectedDate, selectedDate,
selectedMode,
selectedMonthModeDate,
setSelectedMode,
setSelectedMonthModeDate,
week, week,
} = useDateFilter() } = useDateFilter()
@ -44,55 +66,134 @@ export const DateFilter = () => {
} = useHeaderFiltersStore() } = useHeaderFiltersStore()
return ( return (
<Wrapper> <CalendarWrapper isMonthMode={isMonthMode}>
<MonthWrapper> <DateWrapper isMonthMode={isMonthMode}>
<MonthYear onClick={openDatePicker}>
{date.month} {' '} {date.year} {isWeekMode && (
</MonthYear> <MonthButtonWrapper>
<DateButton <WeekModeYear onClick={openDatePicker}>
isActive={isOpen} {date.month} {' '} {date.year}
onClick={openDatePicker} </WeekModeYear>
id='main_calendar' <WeekModeButton isActive={isOpen} onClick={openDatePicker}>
> <Icon refIcon='Calendar' color='#fff' />
<Icon refIcon='Calendar' color='#fff' /> </WeekModeButton>
</DateButton> </MonthButtonWrapper>
</MonthWrapper> )}
{isMonthMode && (
<YearWrapper>
<MonthArrow
aria-label='Previous year'
onClick={onPrevYearClick}
>
<Arrow direction='left' />
</MonthArrow>
<MonthModeYear>
{selectedMonthModeDate.getFullYear()}
</MonthModeYear>
<MonthArrow
aria-label='Next year'
onClick={onNextYearClick}
>
<Arrow direction='right' />
</MonthArrow>
</YearWrapper>
)}
<WeekDaysWrapper> <TabsList>
<ArrowButton {!isInSportsClient && (
aria-label='Previous week' <Tab
onClick={onPreviousClick} aria-pressed={selectedMode === Tabs.MONTH}
> onClick={() => setSelectedMode(Tabs.MONTH)}
<Arrow direction='left' /> >
</ArrowButton> <TabTitle>
<Week> <T9n t='month_title' />
</TabTitle>
</Tab>
)}
{!isMobileDevice && (
<Tab
aria-pressed={selectedMode === Tabs.TIMELINE}
onClick={() => setSelectedMode(Tabs.TIMELINE)}
>
<TabTitle>
<T9n t='timeline_title' />
</TabTitle>
</Tab>
)}
{(!isMobileDevice || !isInSportsClient) && (
<Tab
aria-pressed={selectedMode === Tabs.WEEK}
onClick={() => setSelectedMode(Tabs.WEEK)}
>
<TabTitle>
<T9n t='week_title' />
</TabTitle>
</Tab>
)}
</TabsList>
</DateWrapper>
{isMonthMode && (
<MonthModeWrapper>
{ {
map(week, (day) => ( map(months, (month) => (
<WeekDay <Month
key={day.name} key={month.name}
selected={day.date.getDate() === selectedDate.getDate()} selected={month.date.getMonth() === selectedMonthModeDate.getMonth()}
onClick={() => { onClick={() => {
if (day.date.getDate() !== selectedDate.getDate()) { if (month.date.getMonth() !== selectedMonthModeDate.getMonth()) {
addAdsViews() setSelectedMonthModeDate(month.date)
onWeekDayClick(day.date)
} else { } else {
resetFilters() resetFilters()
} }
}} }}
> >
<WeekName>{day.name.slice(0, 3)}</WeekName> <MonthName>{month.name}</MonthName>
<WeekNumber>{day.date.getDate()}</WeekNumber> </Month>
</WeekDay>
)) ))
} }
</Week> </MonthModeWrapper>
<ArrowButton )}
aria-label='Next week'
onClick={onNextClick} {isWeekMode && (
> <WeekDaysWrapper>
<Arrow direction='right' /> <ArrowButton
</ArrowButton> aria-label='Previous week'
</WeekDaysWrapper> onClick={onPreviousClick}
>
<Arrow direction='left' />
</ArrowButton>
<Week>
{
map(week, (day) => (
<WeekDay
key={day.name}
selected={day.date.getDate() === selectedDate.getDate()}
onClick={() => {
if (day.date.getDate() !== selectedDate.getDate()) {
addAdsViews()
onWeekDayClick(day.date)
} else {
resetFilters()
}
}}
>
<WeekName>{day.name.slice(0, 3)}</WeekName>
<WeekNumber>{day.date.getDate()}</WeekNumber>
</WeekDay>
))
}
</Week>
<ArrowButton
aria-label='Next week'
onClick={onNextClick}
>
<Arrow direction='right' />
</ArrowButton>
</WeekDaysWrapper>
)}
{ {
isOpen && ( isOpen && (
<Fragment> <Fragment>
@ -107,6 +208,6 @@ export const DateFilter = () => {
</Fragment> </Fragment>
) )
} }
</Wrapper> </CalendarWrapper>
) )
} }

@ -2,6 +2,7 @@ import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent' import { isMobileDevice } from 'config/userAgent'
import { devices } from 'config/devices' import { devices } from 'config/devices'
import { isInSportsClient } from 'config'
type Props = { type Props = {
isMonthMode: boolean, isMonthMode: boolean,
@ -17,7 +18,6 @@ export const BaseButton = styled.button`
export const Wrapper = styled.div` export const Wrapper = styled.div`
position: relative; position: relative;
/* width: 32.8rem; */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@ -29,8 +29,6 @@ export const Wrapper = styled.div`
${isMobileDevice ${isMobileDevice
? css` ? css`
/* padding-top: 4px; */
/* min-height: 84px; */
justify-content: space-between; justify-content: space-between;
@media (max-width: 450px){ @media (max-width: 450px){
@ -108,7 +106,6 @@ export const WeekDaysWrapper = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin-top: 0.567rem;
${isMobileDevice ${isMobileDevice
? css` ? css`
@ -119,7 +116,6 @@ export const WeekDaysWrapper = styled.div`
` `
export const Week = styled.div` export const Week = styled.div`
margin: 0 0.95rem;
display: flex; display: flex;
@media (max-width: 600px) { @media (max-width: 600px) {
@ -225,11 +221,8 @@ export const Arrow = styled.span<ArrowProps>`
: ''}; : ''};
` `
export const MonthModeWrapper = styled(WeekDaysWrapper)<Props>` export const MonthModeWrapper = styled.div`
display: flex; display: flex;
justify-content: center;
align-items: center;
margin-top: ${({ isMonthMode }) => (isMonthMode ? '0' : '0.2rem')};;
::-webkit-scrollbar { ::-webkit-scrollbar {
display: none; display: none;
@ -241,47 +234,64 @@ export const MonthModeWrapper = styled(WeekDaysWrapper)<Props>`
margin-top: 0; margin-top: 0;
overflow-y: auto; overflow-y: auto;
height: 100%; height: 100%;
& > :not(:last-child) {
margin-right: 25px;
}
` `
: ''}; : ''};
` `
export const FacrWrapper = styled(Wrapper)<Props>` export const CalendarWrapper = styled(Wrapper)<Props>`
justify-content: space-between; display: flex;
height: ${(isMobileDevice ? '100%' : 'fit-content')}; flex-direction: column;
width: ${({ isMonthMode }) => (isMonthMode ? '49.5%' : '26.3rem')}; height: fit-content;
padding-top: 2rem;
position: relative;
@media (max-width: 450px){ @media (max-width: 450px){
width: 100%; width: 100%;
justify-content: flex-start; justify-content: flex-start;
padding-top: 0;
}; };
` `
export const FacrDateButton = styled(DateButton)` export const WeekModeButton = styled(DateButton)`
left: 24rem; right: 4.5rem;
top: 0; top: -5px;
`
export const FacrMonthWrapper = styled(MonthWrapper)`
position: static;
width: auto;
align-self: auto;
${isMobileDevice ${isMobileDevice
? css` ? css`
position: absolute; position: static;
left: 50%;
transform: translateX(-50%);
` `
: ''}; : ''};
` `
export const MonthButtonWrapper = styled.div`
${() => {
if (isMobileDevice) {
if (isInSportsClient) {
return css`
position: static;
display: flex;
align-items: baseline;
justify-content: center;
`
}
return css`
display: flex;
position: absolute;
left: 50%;
transform: translateX(-50%);
`
}
return ''
}}
`
export const DateWrapper = styled.div<Props>` export const DateWrapper = styled.div<Props>`
position: relative; position: relative;
display: flex; margin-bottom: 0.7rem;
align-items: flex-end;
justify-content: space-between;
width: ${({ isMonthMode }) => (isMonthMode ? '25rem' : '16.9rem')};
margin: ${({ isMonthMode }) => (isMonthMode ? '3.7rem 0 0.7rem' : '3rem 0 0')};
${isMobileDevice ${isMobileDevice
? css` ? css`
@ -300,24 +310,18 @@ export const MonthArrow = styled(ArrowButton)`
height: auto; height: auto;
` `
export const Months = styled(Week)` export const Months = styled(Week)``
height: 100%;
width: 100%;
margin: 0;
justify-content: space-between;
`
export const FacrWeek = styled(Week)` export const FacrWeek = styled(Week)`
margin: 0 1.8rem; margin: 0 1.8rem;
` `
export const Month = styled(WeekDay)` export const Month = styled(WeekDay)`
width: auto; width: 3.5rem;
${isMobileDevice ${isMobileDevice
? css` ? css`
margin-top: 10px; margin-top: 10px;
margin-right: 25px;
min-width: fit-content; min-width: fit-content;
` `
: ''}; : ''};
@ -335,13 +339,36 @@ export const MonthModeYear = styled(MonthYear)`
: ''}; : ''};
` `
export const WeekModeYear = styled(MonthYear)`
line-height: normal;
font-size: 1rem;
position: absolute;
left: 2.5rem;
cursor: pointer;
display: flex;
align-items: center;
height: 100%;
${isMobileDevice
? css`
position: static;
font-size: 10px;
`
: ''};
`
export const TabsList = styled.div` export const TabsList = styled.div`
display: flex; display: flex;
justify-content: space-between; justify-content: center;
height: fit-content; margin: 0 auto 0;
& > :not(:last-child) {
margin-right: 2rem;
}
${isMobileDevice ${isMobileDevice
? css` ? css`
justify-content: flex-start;
top: 3px; top: 3px;
padding: 0; padding: 0;
` `
@ -349,6 +376,9 @@ export const TabsList = styled.div`
` `
export const YearWrapper = styled.div` export const YearWrapper = styled.div`
position: absolute;
right: 0;
top: -5px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -360,6 +390,7 @@ export const YearWrapper = styled.div`
position: absolute; position: absolute;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
top: -1px;
` `
: ''}; : ''};
` `
@ -387,17 +418,13 @@ export const Tab = styled.button`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-size: .75rem; font-size: 0.85rem;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
border: none; border: none;
background: none; background: none;
padding: 0; padding: 0;
:first-child {
margin-right: 2rem;
}
${isMobileDevice ${isMobileDevice
? css` ? css`
font-size: 10px; font-size: 10px;

@ -1,193 +0,0 @@
import map from 'lodash/map'
import { T9n } from 'features/T9n'
import { Icon } from 'features/Icon'
import { OutsideClick } from 'features/OutsideClick'
import { BodyBackdrop } from 'features/PageLayout'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { Fragment } from 'react'
import { useDateFilter } from '../DateFilter/hooks'
import { DatePicker } from '../DatePicker'
import { Tabs } from '../../store/config'
import {
TabsList,
Tab,
TabTitle,
MonthsMode,
YearWrapper,
ArrowButton,
Arrow,
MonthModeYear,
MonthModeWrapper,
Months,
Month,
MonthName,
FacrWrapper,
DateWrapper,
MonthArrow,
WeekDaysWrapper,
FacrMonthWrapper,
WeekDay,
WeekName,
WeekNumber,
FacrDateButton,
FacrWeek,
} from '../DateFilter/styled'
export const FacrDateFilter = () => {
const {
addAdsViews,
close,
date,
isMonthMode,
isOpen,
months,
onDateChange,
onNextClick,
onNextYearClick,
onPreviousClick,
onPrevYearClick,
onWeekDayClick,
openDatePicker,
selectedDate,
selectedMode,
selectedMonthModeDate,
setSelectedMode,
setSelectedMonthModeDate,
week,
} = useDateFilter()
const {
resetFilters,
} = useHeaderFiltersStore()
return (
<FacrWrapper isMonthMode={isMonthMode}>
<DateWrapper isMonthMode={isMonthMode}>
<TabsList>
<Tab
aria-pressed={selectedMode === Tabs.MONTH}
onClick={() => setSelectedMode(Tabs.MONTH)}
>
<TabTitle>
<T9n t='month_title' />
</TabTitle>
</Tab>
<Tab
aria-pressed={selectedMode === Tabs.WEEK}
onClick={() => setSelectedMode(Tabs.WEEK)}
>
<TabTitle>
<T9n t='week_title' />
</TabTitle>
</Tab>
</TabsList>
{isMonthMode
? (
<YearWrapper>
<MonthArrow
aria-label='Previous year'
onClick={onPrevYearClick}
>
<Arrow direction='left' />
</MonthArrow>
<MonthModeYear>
{selectedMonthModeDate.getFullYear()}
</MonthModeYear>
<MonthArrow
aria-label='Next year'
onClick={onNextYearClick}
>
<Arrow direction='right' />
</MonthArrow>
</YearWrapper>
)
: (
<FacrMonthWrapper>
<MonthModeYear onClick={openDatePicker}>
{date.month} {' '} {date.year}
</MonthModeYear>
<FacrDateButton isActive={isOpen} onClick={openDatePicker}>
<Icon refIcon='Calendar' color='#fff' />
</FacrDateButton>
</FacrMonthWrapper>
)}
</DateWrapper>
{isMonthMode
? (
<MonthsMode>
<MonthModeWrapper isMonthMode={isMonthMode}>
<Months>
{
map(months, (day) => (
<Month
key={day.name}
selected={day.date.getMonth() === selectedMonthModeDate.getMonth()}
onClick={() => setSelectedMonthModeDate(day.date)}
>
<MonthName>{day.name}</MonthName>
</Month>
))
}
</Months>
</MonthModeWrapper>
</MonthsMode>
)
: (
<WeekDaysWrapper>
<ArrowButton
aria-label='Previous week'
onClick={onPreviousClick}
>
<Arrow direction='left' />
</ArrowButton>
<FacrWeek>
{
map(week, (day) => (
<WeekDay
key={day.name}
selected={day.date.getDate() === selectedDate.getDate()}
onClick={() => {
if (day.date.getDate() !== selectedDate.getDate()) {
addAdsViews()
onWeekDayClick(day.date)
} else {
resetFilters()
}
}}
>
<WeekName>{day.name.slice(0, 3)}</WeekName>
<WeekNumber>{day.date.getDate()}</WeekNumber>
</WeekDay>
))
}
</FacrWeek>
<ArrowButton
aria-label='Next week'
onClick={onNextClick}
>
<Arrow direction='right' />
</ArrowButton>
</WeekDaysWrapper>
)}
{
isOpen && (
<Fragment>
<OutsideClick onClick={close}>
<DatePicker
open
selected={selectedDate}
onChange={onDateChange}
/>
</OutsideClick>
<BodyBackdrop />
</Fragment>
)
}
</FacrWrapper>
)
}

@ -1,3 +1,2 @@
export * from './components/DateFilter' export * from './components/DateFilter'
export * from './components/FacrDateFilter'
export * from './store' export * from './store'

@ -8,4 +8,5 @@ export const filterKeys = {
export enum Tabs { export enum Tabs {
WEEK, WEEK,
MONTH, MONTH,
TIMELINE
} }

@ -14,12 +14,12 @@ import { useQueryParamStore } from 'hooks'
import { getSportLexic } from 'helpers' import { getSportLexic } from 'helpers'
import { querieKeys } from 'config' import { isMobileDevice, querieKeys } from 'config'
import { getLocalStorageItem } from 'helpers/getLocalStorage' import { getLocalStorageItem } from 'helpers/getLocalStorage'
import type { Match } from 'helpers'
import { filterKeys, Tabs } from '../config' import { filterKeys, Tabs } from '../config'
import { isValidDate } from '../helpers/isValidDate' import { isValidDate } from '../helpers/isValidDate'
import type { Match } from '../../../Matches'
export const useFilters = () => { export const useFilters = () => {
const { search } = useLocation() const { search } = useLocation()
@ -31,9 +31,11 @@ export const useFilters = () => {
}) })
const sportList = getLocalStorageItem(querieKeys.sportsList) const sportList = getLocalStorageItem(querieKeys.sportsList)
const [selectedMode, setSelectedMode] = useState(0) const [selectedMode, setSelectedMode] = useState<Tabs>(isMobileDevice ? Tabs.WEEK : Tabs.TIMELINE)
const [selectedMonthModeDate, setSelectedMonthModeDate] = useState(startOfMonth(new Date())) const [selectedMonthModeDate, setSelectedMonthModeDate] = useState(startOfMonth(new Date()))
const isMonthMode = selectedMode === Tabs.MONTH const isMonthMode = selectedMode === Tabs.MONTH
const isTimelineMode = selectedMode === Tabs.TIMELINE && !isMobileDevice
const isWeekMode = selectedMode === Tabs.WEEK
const [selectedSport, setSelectedSport] = useState(['all_sports']) const [selectedSport, setSelectedSport] = useState(['all_sports'])
const [selectedLeague, setSelectedLeague] = useState<Array<string | number>>(['all_competitions']) const [selectedLeague, setSelectedLeague] = useState<Array<string | number>>(['all_competitions'])
@ -61,11 +63,11 @@ export const useFilters = () => {
) )
}, [selectedMode, selectedMonthModeDate]) }, [selectedMode, selectedMonthModeDate])
const compareSport = useCallback((match: Match, sportNames: Array<string>) => { const compareSport = useCallback((sportType: number, sportNames: Array<string>) => {
if (sportNames[0] === 'all_sports') { if (sportNames[0] === 'all_sports') {
return true return true
} }
const sport = getSportLexic(match.sportType) const sport = getSportLexic(sportType)
return (sportNames.indexOf(sport) >= 0 || sportNames.indexOf(`${sport}_popup`) >= 0) return (sportNames.indexOf(sport) >= 0 || sportNames.indexOf(`${sport}_popup`) >= 0)
}, []) }, [])
@ -117,7 +119,9 @@ export const useFilters = () => {
compareSport, compareSport,
isMonthMode, isMonthMode,
isShowTournament, isShowTournament,
isTimelineMode,
isTodaySelected, isTodaySelected,
isWeekMode,
resetFilters, resetFilters,
selectTournament, selectTournament,
selectedDate, selectedDate,
@ -163,6 +167,8 @@ export const useFilters = () => {
setSelectedMonthModeDate, setSelectedMonthModeDate,
selectedMonthModeDate, selectedMonthModeDate,
isMonthMode, isMonthMode,
isTimelineMode,
isWeekMode,
]) ])
return store return store

@ -3,12 +3,11 @@ import { useRecoilValue } from 'recoil'
import { isAndroid, isIOS } from 'config/userAgent' import { isAndroid, isIOS } from 'config/userAgent'
import { import {
client, client,
isFqtvClient,
isLffClient, isLffClient,
} from 'config/clients' } from 'config/clients'
import { HeaderMenu } from 'features/HeaderMenu' import { HeaderMenu } from 'features/HeaderMenu'
import { DateFilter, FacrDateFilter } from 'features/HeaderFilters' import { DateFilter } from 'features/HeaderFilters'
import { ScoreSwitch } from 'features/MatchSwitches' import { ScoreSwitch } from 'features/MatchSwitches'
import { SportsFilter } from 'features/SportsFilter' import { SportsFilter } from 'features/SportsFilter'
import { isSportFilterShownAtom } from 'features/HomePage/Atoms/HomePageAtoms' import { isSportFilterShownAtom } from 'features/HomePage/Atoms/HomePageAtoms'
@ -45,7 +44,7 @@ export const HeaderMobile = ({
} }
<HeaderStyled> <HeaderStyled>
<HeaderMenu /> <HeaderMenu />
{isFqtvClient ? <FacrDateFilter /> : <DateFilter />} <DateFilter />
<ScSportsWrapper> <ScSportsWrapper>
{!isLffClient && isSportFilterShown ? <SportsFilter /> : null} {!isLffClient && isSportFilterShown ? <SportsFilter /> : null}
<ScoreSwitchWrapper> <ScoreSwitchWrapper>

@ -74,6 +74,7 @@ export const HeaderStyled = styled.header<HeaderProps>`
? css` ? css`
padding: 8px; padding: 8px;
margin-bottom: 50px; margin-bottom: 50px;
justify-content: flex-start;
` `
: ''} : ''}
` `

@ -1,7 +1,7 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { PAGES } from 'config/pages' import { PAGES } from 'config/pages'
import { client, isFqtvClient } from 'config/clients' import { client } from 'config/clients'
import { Menu } from 'features/Menu' import { Menu } from 'features/Menu'
import { ScoreSwitch } from 'features/MatchSwitches' import { ScoreSwitch } from 'features/MatchSwitches'
@ -9,7 +9,6 @@ import { Search } from 'features/Search'
import { import {
DateFilter, DateFilter,
useHeaderFiltersStore, useHeaderFiltersStore,
FacrDateFilter,
} from 'features/HeaderFilters' } from 'features/HeaderFilters'
import { import {
@ -45,7 +44,7 @@ export const Header = () => {
<Search /> <Search />
</HeaderGroup> </HeaderGroup>
</Position> </Position>
{isFqtvClient ? <FacrDateFilter /> : <DateFilter />} <DateFilter />
<Position right={0.71}> <Position right={0.71}>
<HeaderGroup> <HeaderGroup>
<ScoreSwitch /> <ScoreSwitch />

@ -22,6 +22,7 @@ import { isSportFilterShownAtom } from '../../Atoms/HomePageAtoms'
export const HeaderFilters = () => { export const HeaderFilters = () => {
const { const {
isShowTournament, isShowTournament,
isTimelineMode,
selectedFilters, selectedFilters,
selectTournament, selectTournament,
setIsShowTournament, setIsShowTournament,
@ -71,7 +72,7 @@ export const HeaderFilters = () => {
)} )}
{!isLffClient && isShowTournament && isSportFilterShown && <SportsFilter />} {!isLffClient && isShowTournament && isSportFilterShown && <SportsFilter />}
{isShowTournament && ( {isShowTournament && !isTimelineMode && (
<ScFilterItemsWrap> <ScFilterItemsWrap>
<ScFilterItem <ScFilterItem
className={isActiveFilter('live') ? 'activeLive' : ''} className={isActiveFilter('live') ? 'activeLive' : ''}

@ -6,7 +6,6 @@ export const ScHeaderFilters = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
margin-bottom: 23px; margin-bottom: 23px;
align-items: center;
.activeLive { .activeLive {
color: #ffffff; color: #ffffff;
@ -34,7 +33,7 @@ type Props = {
export const ScFilterItem = styled.div<Props>` export const ScFilterItem = styled.div<Props>`
text-transform: uppercase; text-transform: uppercase;
font-weight: 700; font-weight: 700;
font-size: 18px; font-size: 0.75rem;
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
margin: 0 10px; margin: 0 10px;
cursor: pointer; cursor: pointer;

@ -6,6 +6,7 @@ import { ConfirmPopup } from 'features/AuthServiceApp/components/ConfirmPopup'
import { Matches } from 'features/Matches' import { Matches } from 'features/Matches'
import { import {
HeaderFiltersStore, HeaderFiltersStore,
useHeaderFiltersStore,
} from 'features/HeaderFilters' } from 'features/HeaderFilters'
import { import {
PageWrapper, PageWrapper,
@ -14,6 +15,7 @@ import {
} from 'features/PageLayout' } from 'features/PageLayout'
import { UserFavorites } from 'features/UserFavorites' import { UserFavorites } from 'features/UserFavorites'
import { BuyMatchPopup } from 'features/BuyMatchPopup' import { BuyMatchPopup } from 'features/BuyMatchPopup'
import { MatchesTimeline } from 'features/MatchesTimeline'
import { HEADER_MOBILE_ADS } from 'components/Ads/types' import { HEADER_MOBILE_ADS } from 'components/Ads/types'
import { HeaderAds } from 'components/Ads' import { HeaderAds } from 'components/Ads'
@ -35,6 +37,8 @@ const Home = () => {
userInfo, userInfo,
} = useHomePage() } = useHomePage()
const { isTimelineMode } = useHeaderFiltersStore()
return ( return (
<PageWrapper> <PageWrapper>
{isMobileDevice ? ( {isMobileDevice ? (
@ -60,7 +64,9 @@ const Home = () => {
} }
/> />
)} )}
<Matches fetch={fetchMatches} /> {isTimelineMode
? <MatchesTimeline />
: <Matches fetch={fetchMatches} />}
<ConfirmPopup <ConfirmPopup
isModalOpen={isShowConfirmPopup} isModalOpen={isShowConfirmPopup}
handleModalClose={handleCloseConfirmPopup} handleModalClose={handleCloseConfirmPopup}

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { CSSProperties } from 'react' import React, { CSSProperties } from 'react'
import * as icons from '../../libs/index' import * as icons from '../../libs/index'
@ -7,7 +6,7 @@ export type IconProps = {
color?: string, color?: string,
direction?: number, direction?: number,
onClick?: () => void, onClick?: () => void,
refIcon: any, refIcon: keyof typeof icons | string,
size?: number | string, size?: number | string,
styles?: CSSProperties, styles?: CSSProperties,
} }

@ -9,7 +9,7 @@ import {
client, client,
} from 'config' } from 'config'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { useMatchSwitchesStore } from 'features/MatchSwitches' import { useMatchSwitchesStore } from 'features/MatchSwitches'
import { useName } from 'features/Name' import { useName } from 'features/Name'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'

@ -8,7 +8,7 @@ import { client } from 'config/clients'
import type { LiveScore } from 'requests' import type { LiveScore } from 'requests'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { useMatchSwitchesStore } from 'features/MatchSwitches' import { useMatchSwitchesStore } from 'features/MatchSwitches'
import { useName } from 'features/Name' import { useName } from 'features/Name'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
@ -82,7 +82,7 @@ export const CardFrontside = ({
const tournamentName = useName(tournament) const tournamentName = useName(tournament)
const { isInFavorites } = useUserFavoritesStore() const { isInFavorites } = useUserFavoritesStore()
const { isScoreHidden } = useMatchSwitchesStore() const { isScoreHidden } = useMatchSwitchesStore()
const { isMonthMode } = useHeaderFiltersStore() const { isMonthMode, isTimelineMode } = useHeaderFiltersStore()
const { color } = tournament const { color } = tournament
const isInFuture = getUnixTime(date) > getUnixTime(new Date()) const isInFuture = getUnixTime(date) > getUnixTime(new Date())
const showScore = !( const showScore = !(
@ -179,9 +179,9 @@ export const CardFrontside = ({
<MatchDate <MatchDate
isHomePage={isHomePage} isHomePage={isHomePage}
isMatchPage={isMatchPage} isMatchPage={isMatchPage}
isMonthMode={isMonthMode} isMonthMode={isMonthMode || isTimelineMode}
> >
{(isHomePage && !isMonthMode) || isMatchPage ? null : prepareDate} {(isHomePage && !isMonthMode && !isTimelineMode) || isMatchPage ? null : prepareDate}
<Time>{prepareTime}</Time> <Time>{prepareTime}</Time>
</MatchDate> </MatchDate>
{live && ( {live && (

@ -1,3 +1,3 @@
export const MATCH_CARD_WIDTH = 12 export const MATCH_CARD_WIDTH = 22
export const MATCH_CARD_GAP = 20 export const MATCH_CARD_GAP = 40

@ -10,7 +10,7 @@ import {
ProfileTypes, ProfileTypes,
} from 'config' } from 'config'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { useMatchPopupStore } from 'features/MatchPopup' import { useMatchPopupStore } from 'features/MatchPopup'
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup' import { useBuyMatchPopupStore } from 'features/BuyMatchPopup'
import { useAuthStore } from 'features/AuthStore' import { useAuthStore } from 'features/AuthStore'

@ -1,4 +1,4 @@
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { isMobileDevice } from 'config/userAgent' import { isMobileDevice } from 'config/userAgent'

@ -9,8 +9,8 @@ import sortedUniq from 'lodash/sortedUniq'
import isNull from 'lodash/isNull' import isNull from 'lodash/isNull'
import sortBy from 'lodash/sortBy' import sortBy from 'lodash/sortBy'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { prepareMatches } from 'features/Matches/helpers/prepareMatches' import { prepareMatches } from 'helpers'
import { useAuthStore } from 'features/AuthStore' import { useAuthStore } from 'features/AuthStore'
import type { MatchInfo } from 'requests' import type { MatchInfo } from 'requests'

@ -1,6 +1,7 @@
import type { Lexics, Episodes } from 'requests' import type { Lexics, Episodes } from 'requests'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { Tabs } from 'features/MatchSidePlaylists/config' import { Tabs } from 'features/MatchSidePlaylists/config'
import type { MatchPlaylistIds } from './helpers/buildPlaylists' import type { MatchPlaylistIds } from './helpers/buildPlaylists'

@ -1,5 +1,5 @@
import type { Match } from 'features/Matches/hooks' import type { Match } from 'helpers'
export type MatchData = Pick<Match, ( export type MatchData = Pick<Match, (
'calc' 'calc'

@ -2,7 +2,7 @@ import { Fragment } from 'react'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import type { Match } from 'features/Matches/hooks' import type { Match } from 'helpers'
import { MatchesSlider } from 'features/MatchesSlider' import { MatchesSlider } from 'features/MatchesSlider'
import { MatchesGrid } from 'features/MatchesGrid' import { MatchesGrid } from 'features/MatchesGrid'
import { TournamentList } from 'features/TournamentList' import { TournamentList } from 'features/TournamentList'

@ -1,8 +1,8 @@
import map from 'lodash/map' import map from 'lodash/map'
import type { MatchesBySection, Matches } from 'requests' import type { MatchesBySection, MatchesDto } from 'requests'
const addSportTypeToMatches = (matches: Matches, sport: number) => ( const addSportTypeToMatches = (matches: MatchesDto, sport: number) => (
map(matches, (match) => ({ ...match, sport })) map(matches, (match) => ({ ...match, sport }))
) )

@ -1,4 +1,4 @@
import type { Match } from 'requests' import type { MatchDto } from 'requests'
import { getMatchAccess, MatchAccess } from '..' import { getMatchAccess, MatchAccess } from '..'
@ -14,7 +14,7 @@ type Args = {
const createMatch = (args: Args) => ({ const createMatch = (args: Args) => ({
...args, ...args,
} as Match) } as MatchDto)
const user = undefined const user = undefined

@ -1,4 +1,4 @@
import type { Match } from 'requests' import type { MatchDto } from 'requests'
import type { User } from 'oidc-client' import type { User } from 'oidc-client'
import { isFuture } from 'date-fns' import { isFuture } from 'date-fns'
@ -13,7 +13,7 @@ export enum MatchAccess {
ViewMatchPopupWithoutUser = 'ViewMatchPopupWithoutUser', ViewMatchPopupWithoutUser = 'ViewMatchPopupWithoutUser',
} }
export const getMatchAccess = (match: Match, user: User | undefined) => { export const getMatchAccess = (match: MatchDto, user: User | undefined) => {
const { const {
access, access,
date, date,

@ -13,12 +13,10 @@ import { useRequest } from 'hooks'
import { usePreferencesStore } from 'features/PreferencesPopup' import { usePreferencesStore } from 'features/PreferencesPopup'
import { isMobileDevice } from 'config/userAgent' import { isMobileDevice } from 'config/userAgent'
import { prepareMatches } from './helpers/prepareMatches' import { prepareMatches } from 'helpers'
import { useAuthStore } from '../AuthStore' import { useAuthStore } from '../AuthStore'
export type Match = ReturnType<typeof prepareMatches>[number]
export type Props = { export type Props = {
fetch: (limit: number, offset: number) => Promise<MatchesBySection>, fetch: (limit: number, offset: number) => Promise<MatchesBySection>,
} }

@ -14,8 +14,6 @@ import { useMatches } from './hooks'
import { MatchesList } from './components/MatchesList' import { MatchesList } from './components/MatchesList'
import { Loading } from './styled' import { Loading } from './styled'
export type { Match } from './hooks'
export const Matches = memo((props: Props) => { export const Matches = memo((props: Props) => {
const { const {
fetchMoreMatches, fetchMoreMatches,

@ -11,10 +11,10 @@ import { getLiveScores } from 'requests'
import { MatchCard } from 'features/MatchCard' import { MatchCard } from 'features/MatchCard'
import { TournamentList } from 'features/TournamentList' import { TournamentList } from 'features/TournamentList'
import type { Match } from 'features/Matches'
import { useHeaderFiltersStore } from 'features/HeaderFilters' import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { readToken } from 'helpers' import { readToken } from 'helpers'
import type { Match } from 'helpers'
import { useMatchSwitchesStore } from '../MatchSwitches' import { useMatchSwitchesStore } from '../MatchSwitches'
import { useHomePage } from '../HomePage/hooks' import { useHomePage } from '../HomePage/hooks'
@ -41,6 +41,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
selectedDate, selectedDate,
selectedFilters, selectedFilters,
selectedLeague, selectedLeague,
selectedMode,
selectedSport, selectedSport,
updateSportIds, updateSportIds,
} = useHeaderFiltersStore() } = useHeaderFiltersStore()
@ -60,7 +61,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
} }
if (isHomePage && selectedSport) { if (isHomePage && selectedSport) {
return matches.filter((match) => compareSport(match, selectedSport)) return matches.filter((match) => compareSport(match.sportType, selectedSport))
} }
return matches return matches
@ -83,7 +84,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
updateSportIds(matches) updateSportIds(matches)
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDate, matches]) }, [selectedDate, matches, selectedMode])
return ( return (
<Wrapper> <Wrapper>

@ -9,28 +9,33 @@ import {
import throttle from 'lodash/throttle' import throttle from 'lodash/throttle'
import type { Match } from 'features/Matches'
import { MATCH_CARD_WIDTH, MATCH_CARD_GAP } from 'features/MatchCard/config' import { MATCH_CARD_WIDTH, MATCH_CARD_GAP } from 'features/MatchCard/config'
const MATCHES_TO_SCROLL = 6 import type { Match } from 'helpers'
const SCROLLING_DELAY = 750 const MATCHES_TO_SCROLL = 12
const SCROLLING_DELAY = 350
export const useMatchesSlider = (matches: Array<Match>) => { export const useMatchesSlider = (matches: Array<Match>) => {
const slidesRef = useRef<HTMLUListElement>(null) const slidesRef = useRef<HTMLUListElement>(null)
const [showLeftArrow, setShowLeftArrow] = useState(false) const [showLeftArrow, setShowLeftArrow] = useState(false)
const [showRightArrow, setShowRigthArrow] = useState(false) const [showRightArrow, setShowRigthArrow] = useState(false)
const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false)
const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false)
useEffect(() => { useEffect(() => {
const { const {
clientWidth = 0, // clientWidth = 0,
scrollLeft = 0, scrollLeft = 0,
scrollWidth = 0, // scrollWidth = 0,
} = slidesRef.current || {} } = slidesRef.current || {}
const scrollRight = scrollWidth - (scrollLeft + clientWidth) // const scrollRight = scrollWidth - (scrollLeft + clientWidth)
setShowRigthArrow(scrollRight > 1) // setShowRigthArrow(scrollRight > 1)
// setShowLeftArrow(scrollRight > 1)
setIsLeftArrowDisabled(scrollLeft <= 0)
}, [matches, slidesRef]) }, [matches, slidesRef])
const onScroll = useCallback((e: SyntheticEvent<HTMLUListElement>) => { const onScroll = useCallback((e: SyntheticEvent<HTMLUListElement>) => {
@ -39,11 +44,32 @@ export const useMatchesSlider = (matches: Array<Match>) => {
scrollLeft: targetScrollLeft, scrollLeft: targetScrollLeft,
scrollWidth: targetScrollWidth, scrollWidth: targetScrollWidth,
} = e.currentTarget } = e.currentTarget
const targetScrollRight = targetScrollWidth - (targetScrollLeft + targetClientWidth)
setShowLeftArrow(targetScrollLeft > 1) // setShowLeftArrow(targetScrollLeft > 1)
setShowRigthArrow((targetScrollWidth - (targetScrollLeft + targetClientWidth)) > 1) // setShowRigthArrow(targetScrollRight > 1)
setIsLeftArrowDisabled(targetScrollLeft === 0)
setIsRightArrowDisabled(Math.round(targetScrollRight) <= 1)
}, []) }, [])
const onWrapperMouseEnter = () => {
const {
clientWidth = 0,
scrollLeft = 0,
scrollWidth = 0,
} = slidesRef.current || {}
const scrollRight = scrollWidth - (scrollLeft + clientWidth)
if (scrollRight > 1 || scrollLeft > 1) {
setShowLeftArrow(true)
setShowRigthArrow(true)
}
}
const onWrapperMouseLeave = () => {
setShowLeftArrow(false)
setShowRigthArrow(false)
}
const slideLeft = useMemo(() => throttle(() => { const slideLeft = useMemo(() => throttle(() => {
slidesRef.current!.scrollBy(-((MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL), 0) slidesRef.current!.scrollBy(-((MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL), 0)
}, SCROLLING_DELAY), []) }, SCROLLING_DELAY), [])
@ -53,7 +79,11 @@ export const useMatchesSlider = (matches: Array<Match>) => {
}, SCROLLING_DELAY), []) }, SCROLLING_DELAY), [])
return { return {
isLeftArrowDisabled,
isRightArrowDisabled,
onScroll, onScroll,
onWrapperMouseEnter,
onWrapperMouseLeave,
showLeftArrow, showLeftArrow,
showRightArrow, showRightArrow,
slideLeft, slideLeft,

@ -1,14 +1,25 @@
import { useEffect } from 'react'
import { useQuery } from 'react-query'
import map from 'lodash/map' import map from 'lodash/map'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { querieKeys } from 'config'
import { MatchCard } from 'features/MatchCard' import { MatchCard } from 'features/MatchCard'
import { useMatchSwitchesStore } from 'features/MatchSwitches'
import { LiveScore, getLiveScores } from 'requests'
import { useMatchesSlider } from './hooks' import { useMatchesSlider } from './hooks'
import { import {
Wrapper, Wrapper,
Arrow, Arrow,
Slides, Slides,
ArrowButton,
} from './styled' } from './styled'
type MatchesSliderProps = { type MatchesSliderProps = {
@ -17,7 +28,11 @@ type MatchesSliderProps = {
export const MatchesSlider = ({ matches }: MatchesSliderProps) => { export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
const { const {
isLeftArrowDisabled,
isRightArrowDisabled,
onScroll, onScroll,
onWrapperMouseEnter,
onWrapperMouseLeave,
showLeftArrow, showLeftArrow,
showRightArrow, showRightArrow,
slideLeft, slideLeft,
@ -25,26 +40,89 @@ export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
slidesRef, slidesRef,
} = useMatchesSlider(matches) } = useMatchesSlider(matches)
const { isScoreHidden } = useMatchSwitchesStore()
const { data: liveMatchScores } = useQuery({
queryFn: async () => {
if (!isScoreHidden && matches.filter(({ live }) => live)?.length > 0) {
const scores = await getLiveScores()
return scores
}
return []
},
queryKey: querieKeys.liveMatchScores,
refetchInterval: 5000,
})
const scrollToMatchByIndex = (index: number) => {
const PADDING_PARENT = 10
const offsetLeft = slidesRef.current!.querySelectorAll('li')[index].offsetLeft - PADDING_PARENT
slidesRef.current!.scrollBy(offsetLeft, 0)
}
// скролл к лайв матчам или сегодняшней дате
useEffect(() => {
const matchIndexLive = matches.findIndex(({ live }) => live)
if (matchIndexLive !== -1) {
scrollToMatchByIndex(matchIndexLive)
return
}
const matchIndex = matches.findIndex((item) => new Date() <= item.date)
if (matchIndex !== -1) {
scrollToMatchByIndex(matchIndex)
return
}
const slidesRefClientWidth = slidesRef.current!.clientWidth
const slidesRefScrollWidth = slidesRef.current!.scrollWidth
slidesRef.current!.scrollBy(slidesRefScrollWidth - slidesRefClientWidth, 0)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
if (isEmpty(matches)) return null if (isEmpty(matches)) return null
return ( return (
<Wrapper> <Wrapper
onMouseEnter={onWrapperMouseEnter}
onMouseLeave={onWrapperMouseLeave}
>
{showLeftArrow && ( {showLeftArrow && (
<Arrow <ArrowButton
type='arrowLeft' direction='left'
aria-label='Slide left'
onClick={slideLeft} onClick={slideLeft}
/> disabled={isLeftArrowDisabled}
>
<Arrow
disabled={isLeftArrowDisabled}
direction='left'
/>
</ArrowButton>
)} )}
<Slides ref={slidesRef} onScroll={onScroll}> <Slides ref={slidesRef} onScroll={onScroll}>
{map(matches, (match) => <MatchCard match={match} key={match.id} />)} {map(matches, (match) => (
<MatchCard
match={match}
key={match.id}
score={liveMatchScores?.find(
({ match_id, sport_id }: LiveScore) => match_id === match.id
&& sport_id === match.sportType,
)}
/>
))}
</Slides> </Slides>
{showRightArrow && ( {showRightArrow && (
<Arrow <ArrowButton
type='arrowRight' direction='right'
aria-label='Slide right'
onClick={slideRight} onClick={slideRight}
/> disabled={isRightArrowDisabled}
>
<Arrow
disabled={isRightArrowDisabled}
direction='right'
/>
</ArrowButton>
)} )}
</Wrapper> </Wrapper>
) )

@ -1,53 +1,111 @@
import styled from 'styled-components/macro' import styled, { css } from 'styled-components/macro'
import { devices } from 'config/devices' import { CardWrapper, CardWrapperOuter } from '../MatchCard/styled'
import { CardWrapper } from '../MatchCard/styled'
export const Wrapper = styled.div` export const Wrapper = styled.div`
position: relative; position: relative;
margin-bottom: 16px; overflow: hidden;
padding-right: 5px;
` `
export const Slides = styled.ul` export const Slides = styled.ul`
display: flex; display: flex;
scroll-behavior: smooth; scroll-behavior: smooth;
overflow-x: auto; overflow-x: auto;
gap: 0.9rem;
padding: 10px 10px 10px 7px;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
scrollbar-width: none;
${CardWrapperOuter} {
padding-top: 0;
}
${CardWrapper} { ${CardWrapper} {
width: 283px; position: relative;
height: 12.9rem;
width: 12.9rem;
&:hover {
scale: 1.04;
}
@media ${devices.laptop} { @media screen and (min-width: 1920px) {
min-width: auto; height: 13.8rem;
width: 279px; width: 13.8rem;
} }
}
@media ${devices.mobile} { @media screen and (max-width: 1920px) {
flex-direction: column; height: 13.4rem;
width: 13.4rem;
}
${CardWrapper} { @media screen and (max-width: 1440px) {
width: 100%; height: 12.8rem;
} width: 12.8rem;
}
@media screen and (max-width: 1280px) {
height: 12.6rem;
width: 12.6rem;
}
} }
` `
export const Arrow = styled.div<{ type: 'arrowLeft' | 'arrowRight' }>` type ArrowProps = {
direction: 'left' | 'right',
disabled?: boolean,
}
export const Arrow = styled.span<ArrowProps>`
width: 1rem;
height: 1rem;
position: absolute; position: absolute;
border-left: 0.25rem solid #fff;
border-bottom: 0.25rem solid #fff;
top: 50%; top: 50%;
left: ${({ type }) => (type === 'arrowLeft' ? '10px' : 'calc(100% - 10px)')}; left: 50%;
width: 40px; border-radius: 3px;
height: 40px;
background-position: center; ${({ direction }) => (
background-repeat: no-repeat; direction === 'left'
background-image: url(${({ type }) => (type === 'arrowLeft' ? 'transform: translate(-50%, -50%) rotate(45deg);'
? '/images/slideLeft.svg' : 'transform: translate(-50%, -50%) rotate(225deg);'
: '/images/slideRight.svg')}); )}
${({ disabled }) => (disabled ? css`
border-left: 0.25rem solid gray;
border-bottom: 0.25rem solid gray;
` : '')}
`
export const ArrowButton = styled.button<ArrowProps>`
border: none;
outline: none;
padding: 0;
background-color: transparent;
cursor: pointer; cursor: pointer;
position: absolute;
width: 2.28rem;
height: 2.28rem;
z-index: 3;
top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
z-index: 1; left: ${({ direction }) => (direction === 'left' ? '1.6rem' : 'calc(100% - 1.6rem)')};
&:hover {
${({ direction, disabled }) => (!disabled ? css`
width: 3rem;
height: 3rem;
left: ${direction === 'left' ? '2rem' : 'calc(100% - 2rem)'};
${Arrow} {
width: 1.5rem;
height: 1.5rem;
}
` : '')}
}
` `

@ -0,0 +1,111 @@
import {
useCallback,
useEffect,
useMemo,
useState,
} from 'react'
import { useRouteMatch } from 'react-router-dom'
import {
MatchDto,
TimelineTournamentDto,
getTimelineMatches,
} from 'requests'
import { Match, prepareMatches } from 'helpers'
import { useAuthStore } from 'features/AuthStore'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { PAGES } from 'config'
export type TimelineTournamentList = Array<Omit<TimelineTournamentDto, 'matches'> & {
matches: Array<Match>,
}>
export const useTimeline = () => {
const isHomePage = useRouteMatch(PAGES.home)?.isExact
const { user } = useAuthStore()
const {
compareSport,
selectedMode,
selectedSport,
setSportIds,
} = useHeaderFiltersStore()
const [isTimelineFetching, setIsTimelineFetching] = useState(true)
const [onlineUpcomingMatches, setOnlineUpcomingMatches] = useState<Array<Match>>([])
const [tournamentList, setTournamentList] = useState<TimelineTournamentList>([])
const prepareMatchesDto = useCallback((matches: Array<MatchDto>) => prepareMatches(
matches,
user,
false,
), [user])
useEffect(() => {
(async () => {
setIsTimelineFetching(true)
try {
const timeline = await getTimelineMatches()
const convertedMatches = timeline.online_upcoming[0].matches
const preparedMatches = prepareMatchesDto(convertedMatches)
setOnlineUpcomingMatches(preparedMatches)
setTournamentList([
...timeline.favorite.map((item) => ({
...item,
matches: prepareMatchesDto(item.matches),
})),
...timeline.promo.map((item) => ({
...item,
matches: prepareMatchesDto(item.matches),
})),
...timeline.others.map((item) => ({
...item,
matches: prepareMatchesDto(item.matches),
})),
])
} catch (error) { /* empty */ } finally {
setIsTimelineFetching(false)
}
})()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const filteredTournamentsBySport = useMemo(() => {
if (isHomePage && selectedSport) {
return tournamentList.filter((t) => compareSport(t.sport_id, selectedSport))
}
return tournamentList
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tournamentList, selectedSport])
const filteredOnlineUpcomingBySport = useMemo(() => {
if (isHomePage && selectedSport) {
return onlineUpcomingMatches.filter((m) => compareSport(m.sportType, selectedSport))
}
return onlineUpcomingMatches
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [onlineUpcomingMatches, selectedSport])
useEffect(() => {
if (!isHomePage) return
const qwe = Array.from(new Set(tournamentList.map((t) => t.sport_id)))
setSportIds(qwe)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tournamentList, selectedMode])
return {
isTimelineFetching,
onlineUpcomingMatches: filteredOnlineUpcomingBySport,
tournamentList: filteredTournamentsBySport,
}
}

@ -0,0 +1,115 @@
import {
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import { MatchesSlider } from 'features/MatchesSlider'
import { TimelineTournamentList, useTimeline } from 'features/MatchesTimeline/hooks'
import { InfiniteScroll } from 'features/InfiniteScroll'
import { T9n } from 'features/T9n'
import { TournamentSubtitle } from 'features/TournamentSubtitle'
import { ProfileTypes } from 'config'
import {
Content,
Tournament,
RowWrapper,
Wrapper,
TournamentLogo,
Loading,
TournamentSubtitleWrapper,
} from './styled'
const TOURNAMENT_LIMIT = 6
export const MatchesTimeline = () => {
const {
isTimelineFetching,
onlineUpcomingMatches,
tournamentList,
} = useTimeline()
const [tournaments, setTournaments] = useState<TimelineTournamentList>([])
const pageRef = useRef(0)
const isLastPageRef = useRef(false)
const getTournaments = useCallback(() => tournamentList.slice(
pageRef.current * TOURNAMENT_LIMIT,
pageRef.current * TOURNAMENT_LIMIT + TOURNAMENT_LIMIT,
), [tournamentList])
const getMoreTournaments = () => {
if (isLastPageRef.current) return
const res = getTournaments()
if (res.length) {
setTournaments((prev) => ([
...prev,
...res,
]))
pageRef.current++
} else {
isLastPageRef.current = true
}
}
useEffect(() => {
if (tournamentList.length) {
pageRef.current = 0
tournamentList.length && setTournaments(getTournaments())
pageRef.current = 1
}
}, [getTournaments, tournamentList])
if (isTimelineFetching) {
return <Loading><T9n t='loading' />...</Loading>
}
return (
<InfiniteScroll fullPageScroll onFetchMore={getMoreTournaments}>
<Wrapper>
{onlineUpcomingMatches.length > 0 && (
<RowWrapper>
<Content>
<Tournament isOnlineUpcoming>
LIVE & UPCOMING
</Tournament>
<MatchesSlider matches={onlineUpcomingMatches} />
</Content>
</RowWrapper>
)}
{tournaments.map(({
matches,
sport_id,
tournament,
tournament_id,
}) => (
<RowWrapper key={tournament_id}>
<TournamentSubtitleWrapper>
<TournamentSubtitle
sportInfo={matches[0].sportInfo}
countryId={matches[0].countryId}
sportType={sport_id}
tournament={tournament}
/>
</TournamentSubtitleWrapper>
<Content>
<Tournament gradientColor={tournament.color}>
<TournamentLogo
id={tournament_id}
profileType={ProfileTypes.TOURNAMENTS}
sportType={sport_id}
/>
</Tournament>
<MatchesSlider matches={matches} />
</Content>
</RowWrapper>
))}
</Wrapper>
</InfiniteScroll>
)
}

@ -0,0 +1,90 @@
import { ProfileLogo } from 'features/ProfileLogo'
import { StyledLink } from 'features/TournamentSubtitle/styled'
import styled, { css } from 'styled-components/macro'
export const Wrapper = styled.div`
& > * {
margin-bottom: 20px;
}
`
export const RowWrapper = styled.div``
export const Content = styled.div`
display: flex;
gap: 10px;
`
export const Tournament = styled.div<{
gradientColor?: string,
isOnlineUpcoming?: boolean,
}>`
${({ gradientColor }) => (gradientColor
? css`background: linear-gradient(187deg, ${gradientColor} -4.49%, #000000 68.29%), #000000;`
: css`background-color: ${({ theme }) => theme.colors.matchCardBackground};`)}
// в будущем от этого нужно будет избавиться
${({ isOnlineUpcoming }) => isOnlineUpcoming && css`
background: linear-gradient(270deg, #C00 0%, #6A2131 100%);
`}
position: relative;
height: 12.9rem;
width: 12.9rem;
flex-shrink: 0;
margin: 10px 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
font-size: 1.9rem;
font-weight: 700;
color: #FFFFFF;
text-align: start;
padding: 10px;
@media screen and (min-width: 1920px) {
height: 13.8rem;
width: 13.8rem;
}
@media screen and (max-width: 1920px) {
height: 13.4rem;
width: 13.4rem;
}
@media screen and (max-width: 1440px) {
height: 12.8rem;
width: 12.8rem;
}
@media screen and (max-width: 1280px) {
height: 12.6rem;
width: 12.6rem;
}
`
export const TournamentLogo = styled(ProfileLogo)`
position: absolute;
max-height: 100%;
padding: 20px;
`
export const Loading = styled.div`
height: 30px;
margin-top: 20px;
font-size: 24px;
color: #fff;
text-align: center;
`
export const TournamentSubtitleWrapper = styled.div`
margin-bottom: 10px;
${StyledLink} {
font-weight: 700;
color: #FFFFFF;
font-size: 1rem;
text-transform: uppercase;
}
`

@ -59,10 +59,11 @@ export const Content = styled.div`
export const BodyBackdrop = styled.div` export const BodyBackdrop = styled.div`
position: fixed; position: fixed;
top: 8.5rem; // высота хедера
top: ${isMobileDevice ? '124px' : '8.5rem'} ;
left: 0; left: 0;
background-color: rgba(0, 0, 0, 0.7); background-color: rgba(0, 0, 0, 0.7);
width: 100vw; width: 100vw;
height: calc(100vh - 8.5rem); height: 100vh;
z-index: 2; z-index: 2;
` `

@ -21,6 +21,7 @@ export const ScSportsFilter = styled.div`
justify-content: space-between; justify-content: space-between;
min-width: 15.4%; min-width: 15.4%;
padding-left: 5px; padding-left: 5px;
align-items: flex-start;
${isMobileDevice ${isMobileDevice
? css` ? css`

@ -10,9 +10,10 @@ import {
import { AdType } from 'requests' import { AdType } from 'requests'
import type { Match } from 'helpers'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Icon } from 'features/Icon' import { Icon } from 'features/Icon'
import type { Match } from 'features/Matches'
import { MatchCard } from 'features/MatchCard' import { MatchCard } from 'features/MatchCard'
import { import {
CountryFlag, CountryFlag,

@ -4,10 +4,11 @@ import orderBy from 'lodash/orderBy'
import { ProfileTypes } from 'config' import { ProfileTypes } from 'config'
import { TournamentListProps } from 'features/TournamentList' import { TournamentListProps } from 'features/TournamentList'
import type { Match } from 'features/Matches'
import { useHeaderFiltersStore } from 'features/HeaderFilters' import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { useUserFavoritesStore } from 'features/UserFavorites/store' import { useUserFavoritesStore } from 'features/UserFavorites/store'
import type { Match } from 'helpers'
interface TournamentsSortProps { interface TournamentsSortProps {
id: number, id: number,
isFavorite: boolean, isFavorite: boolean,
@ -45,7 +46,7 @@ export const useTournaments = (matches: Array<Match>) => {
match.tournament.is_super_tournament ? match.group.id : match.tournament.id, match.tournament.is_super_tournament ? match.group.id : match.tournament.id,
) )
if (!acc[`${match.sportType}_${match.tournament.id}`] && compareSport(match, selectedSport) if (!acc[`${match.sportType}_${match.tournament.id}`] && compareSport(match.sportType, selectedSport)
&& compareLeague(uniqTournamentId)) { && compareLeague(uniqTournamentId)) {
const tournament = { const tournament = {
...match.tournament, ...match.tournament,
@ -68,7 +69,7 @@ export const useTournaments = (matches: Array<Match>) => {
isSuperTournament: Boolean(match.tournament.is_super_tournament), isSuperTournament: Boolean(match.tournament.is_super_tournament),
sportType: match.sportType, sportType: match.sportType,
}) })
} else if (compareSport(match, selectedSport) && compareLeague(uniqTournamentId)) { } else if (compareSport(match.sportType, selectedSport) && compareLeague(uniqTournamentId)) {
acc[uniqTournamentId] = { acc[uniqTournamentId] = {
...acc[uniqTournamentId], ...acc[uniqTournamentId],
tournament: { tournament: {

@ -1,6 +1,7 @@
import { useRouteMatch } from 'react-router-dom' import { useRouteMatch } from 'react-router-dom'
import type { Match } from 'features/Matches' import type { Match } from 'helpers'
import { MatchCard } from 'features/MatchCard' import { MatchCard } from 'features/MatchCard'
import type { TournamentType } from 'requests/getMatches/types' import type { TournamentType } from 'requests/getMatches/types'

@ -14,7 +14,6 @@ export const UserSportFavWrapper = styled.aside`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-top: 1rem; margin-top: 1rem;
padding-bottom: 2.75rem;
left: 0; left: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
@ -29,7 +28,7 @@ export const UserSportFavWrapper = styled.aside`
export const ScrollWrapper = styled.div` export const ScrollWrapper = styled.div`
width: 4.35rem; width: 4.35rem;
padding: 0.472rem 0.30rem 0 0.15rem; padding: 0.472rem 0.30rem 0.472rem 0.15rem;
height: calc(100vh - 14rem); height: calc(100vh - 14rem);
display: flex; display: flex;
flex-direction: column; flex-direction: column;

@ -17,3 +17,4 @@ export * from './languageUrlParam'
export * from './bodyScrollLock' export * from './bodyScrollLock'
export * from './getLocalStorage' export * from './getLocalStorage'
export * from './checkPage' export * from './checkPage'
export * from './prepareMatches'

@ -1,16 +1,19 @@
import map from 'lodash/map' import map from 'lodash/map'
import orderBy from 'lodash/orderBy' import orderBy from 'lodash/orderBy'
import format from 'date-fns/format' import format from 'date-fns/format'
import type { User } from 'oidc-client' import type { User } from 'oidc-client'
import type { Match } from 'requests' import type { MatchDto } from 'requests'
import { parseDate } from 'helpers/parseDate' import { parseDate } from 'helpers/parseDate'
import { getMatchAccess } from './getMatchClickAction' import { getMatchAccess } from 'features/Matches/helpers/getMatchClickAction'
export type Match = ReturnType<typeof prepareMatch>
const prepareMatch = (match: Match, user?: User | undefined) => { const prepareMatch = (match: MatchDto, user?: User | undefined) => {
const { const {
calc, calc,
country, country,
@ -56,14 +59,22 @@ const prepareMatch = (match: Match, user?: User | undefined) => {
} }
} }
export const prepareMatches = (matches: Array<Match>, user?: User | undefined) => { export const prepareMatches = (
matches: Array<MatchDto>,
user?: User | undefined,
liveOrder: boolean = true,
): Array<Match> => {
const preparedMatches = map( const preparedMatches = map(
matches, matches,
(match) => prepareMatch(match, user), (match) => prepareMatch(match, user),
) )
return orderBy(
preparedMatches, if (liveOrder) {
['live'], return orderBy(
['desc'], preparedMatches,
) ['live'],
['desc'],
)
}
return preparedMatches
} }

@ -16,7 +16,7 @@ import {
import { useUserFavoritesStore } from 'features/UserFavorites/store' import { useUserFavoritesStore } from 'features/UserFavorites/store'
import type { Match } from 'requests/getMatches/types' import type { MatchDto } from 'requests'
import type { Sport } from 'requests/getSportList' import type { Sport } from 'requests/getSportList'
import { getSounds } from 'requests/getSounds' import { getSounds } from 'requests/getSounds'
@ -409,7 +409,7 @@ export const useHighlightsForm = () => {
sub_only: true, sub_only: true,
}) })
.then(({ broadcast }) => setPlayerMatches( .then(({ broadcast }) => setPlayerMatches(
broadcast.map((match: Match) => ({ ...match, isChecked: false })), broadcast.map((match: MatchDto) => ({ ...match, isChecked: false })),
)) ))
.finally(() => setIsFetching(false)) .finally(() => setIsFetching(false))

@ -1,8 +1,8 @@
import { atom, selector } from 'recoil' import { atom, selector } from 'recoil'
import type { AdResponse, Match } from 'requests' import type { AdResponse, MatchDto } from 'requests'
export type MatchType = Match & { export type MatchType = MatchDto & {
isChecked: boolean, isChecked: boolean,
} }

@ -0,0 +1,35 @@
import { API_ROOT } from 'config'
import { callApi } from 'helpers'
import type { MatchesDto, TournamentType } from 'requests'
export type TimelineTournamentDto = {
matches: MatchesDto,
sport_id: number,
tournament: TournamentType,
tournament_id: number,
}
export type MatchesTimeline = {
favorite: Array<TimelineTournamentDto>,
online_upcoming: Array<{matches: MatchesDto}>,
others: Array<TimelineTournamentDto>,
promo: Array<TimelineTournamentDto>,
}
export const getTimelineMatches = (sportId?: number): Promise<MatchesTimeline> => {
const url = new URL(`${API_ROOT}/v1/matches/timeline`)
if (sportId) {
url.searchParams.append('sport_id', `${sportId}`)
}
const config = {
method: 'GET',
}
return callApi({
config,
url: url.href,
})
}

@ -3,3 +3,4 @@ export * from './getHomeMatches'
export * from './getTeamMatches' export * from './getTeamMatches'
export * from './getPlayerMatches' export * from './getPlayerMatches'
export * from './getTournamentMatches' export * from './getTournamentMatches'
export * from './getTimelineMatches'

@ -32,7 +32,7 @@ export type SportInfo = {
lexic: number, lexic: number,
} }
export type Match = { export type MatchDto = {
access: boolean, access: boolean,
/** тип трансляции */ /** тип трансляции */
c_type_broadcast: number, c_type_broadcast: number,
@ -61,12 +61,12 @@ export type Match = {
tournament: TournamentType, tournament: TournamentType,
} }
export type Matches = Array<Match> export type MatchesDto = Array<MatchDto>
type VideoContent = { type VideoContent = {
broadcast: Matches, broadcast: MatchesDto,
features: Matches, features: MatchesDto,
highlights: Matches, highlights: MatchesDto,
} }
export type MatchesResponse = { export type MatchesResponse = {
@ -76,9 +76,9 @@ export type MatchesResponse = {
} }
export type MatchesBySection = { export type MatchesBySection = {
broadcast: Matches, broadcast: MatchesDto,
features: Matches, features: MatchesDto,
hasNextPage: boolean, hasNextPage: boolean,
highlights: Matches, highlights: MatchesDto,
isVideoSections: boolean, isVideoSections: boolean,
} }

Loading…
Cancel
Save