diff --git a/src/config/lexics/indexLexics.tsx b/src/config/lexics/indexLexics.tsx
index 03a8224b..3a3442cd 100644
--- a/src/config/lexics/indexLexics.tsx
+++ b/src/config/lexics/indexLexics.tsx
@@ -224,6 +224,7 @@ export const indexLexics = {
sport: 12993,
team: 14973,
terms_and_conditions: 15738,
+ timeline_title: 20266,
to_home: 13376,
total_likes: 20253,
tournament: 14974,
diff --git a/src/features/BuyMatchPopup/types.tsx b/src/features/BuyMatchPopup/types.tsx
index e8145b99..eef6d7d0 100644
--- a/src/features/BuyMatchPopup/types.tsx
+++ b/src/features/BuyMatchPopup/types.tsx
@@ -1,7 +1,7 @@
import type { SubscriptionResponse, Subscription } from 'requests/getSubscriptions'
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'
export enum Steps {
diff --git a/src/features/Common/Image/index.tsx b/src/features/Common/Image/index.tsx
index 310aa9a4..a9d3591c 100644
--- a/src/features/Common/Image/index.tsx
+++ b/src/features/Common/Image/index.tsx
@@ -37,6 +37,7 @@ export const Image = ({
onError={onError}
onLoad={onLoad}
title={title}
+ loading='lazy'
/>
)
}
diff --git a/src/features/HeaderFilters/components/DateFilter/helpers.tsx b/src/features/HeaderFilters/components/DateFilter/helpers.tsx
index 20e53193..ac78ccf0 100644
--- a/src/features/HeaderFilters/components/DateFilter/helpers.tsx
+++ b/src/features/HeaderFilters/components/DateFilter/helpers.tsx
@@ -3,6 +3,8 @@ import addDays from 'date-fns/addDays'
import addMonths from 'date-fns/addMonths'
import startOfYear from 'date-fns/startOfYear'
+import { isMobileDevice } from 'config'
+
type Args = {
date: Date,
lang: string,
@@ -20,7 +22,7 @@ const getMonthName = ({
export const getDisplayDate = ({
date,
lang,
- monthType = 'long',
+ monthType = isMobileDevice ? 'long' : 'short',
}: Args) => ({
day: date.getDate(),
month: getMonthName({
@@ -48,6 +50,8 @@ type Week = {
name: string,
}
+type Month = Week
+
export const getWeeks = (date: Date, locale: string) => {
const weekStart = startOfWeek(date, { weekStartsOn: 1 })
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 dayDate = addMonths(yearStart, month)
+ const monthDate = addMonths(yearStart, month)
return {
- date: dayDate,
- name: new Intl.DateTimeFormat(locale || 'en', { month: 'short' }).format(dayDate),
+ date: monthDate,
+ name: new Intl.DateTimeFormat(locale || 'en', { month: 'short' }).format(monthDate),
}
}
export const getMonths = (locale: string, date: Date) => {
const yearStart = startOfYear(date)
const getMonth = createMonthGetter(yearStart, locale)
- const months: Array = [
+ const months: Array = [
getMonth(0),
getMonth(1),
getMonth(2),
diff --git a/src/features/HeaderFilters/components/DateFilter/hooks/index.tsx b/src/features/HeaderFilters/components/DateFilter/hooks/index.tsx
index ac6bc17f..46707a5a 100644
--- a/src/features/HeaderFilters/components/DateFilter/hooks/index.tsx
+++ b/src/features/HeaderFilters/components/DateFilter/hooks/index.tsx
@@ -29,6 +29,8 @@ import {
export const useDateFilter = () => {
const {
isMonthMode,
+ isTimelineMode,
+ isWeekMode,
selectedDate,
selectedMode,
selectedMonthModeDate,
@@ -62,8 +64,11 @@ export const useDateFilter = () => {
lang,
})
const filters = localStorage.getItem('filters')
+ const dateMode = localStorage.getItem('dateMode')
const parseFilters = filters && JSON.parse(filters)
+ const parseMode = dateMode && JSON.parse(dateMode)
const lastDate = parseFilters?.selectedDate
+ const lastMonthDate = new Date(parseMode?.selectedMonthModeDate)
const weekName = getWeekName(selectedDate, 'en')
const validator = (value: unknown) => Boolean(value) && isObject(value)
@@ -83,6 +88,7 @@ export const useDateFilter = () => {
useEffect(() => {
if (lastDate === selectedDate.getDate()
+ && lastMonthDate === selectedMonthModeDate
&& parseFilters
&& parseFilters.selectedLeague[0] !== 'all_competitions') {
setIsShowTournament(false)
@@ -94,7 +100,7 @@ export const useDateFilter = () => {
setSelectedLeague(['all_competitions'])
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [selectedDate])
+ }, [selectedDate, selectedMonthModeDate])
const onPreviousClick = () => {
addAdsViews()
@@ -130,6 +136,8 @@ export const useDateFilter = () => {
date,
isMonthMode,
isOpen,
+ isTimelineMode,
+ isWeekMode,
months,
onDateChange,
onNextClick,
diff --git a/src/features/HeaderFilters/components/DateFilter/index.tsx b/src/features/HeaderFilters/components/DateFilter/index.tsx
index 4f65973b..7155c731 100644
--- a/src/features/HeaderFilters/components/DateFilter/index.tsx
+++ b/src/features/HeaderFilters/components/DateFilter/index.tsx
@@ -2,40 +2,62 @@ import { Fragment } from 'react'
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 { Icon } from 'features/Icon'
+import { useHeaderFiltersStore } from 'features/HeaderFilters'
+import { isInSportsClient, isMobileDevice } from 'config'
import { useDateFilter } from './hooks'
import { DatePicker } from '../DatePicker'
+import { Tabs } from '../../store/config'
+
import {
- Wrapper,
- MonthWrapper,
- WeekDaysWrapper,
+ TabsList,
+ Tab,
+ TabTitle,
+ YearWrapper,
ArrowButton,
Arrow,
- DateButton,
- MonthYear,
- Week,
+ MonthModeYear,
+ MonthModeWrapper,
+ Month,
+ MonthName,
+ CalendarWrapper,
+ DateWrapper,
+ MonthArrow,
+ WeekDaysWrapper,
WeekDay,
WeekName,
WeekNumber,
+ WeekModeButton,
+ WeekModeYear,
+ Week,
+ MonthButtonWrapper,
} from './styled'
-import { useHeaderFiltersStore } from '../../store'
-
export const DateFilter = () => {
const {
addAdsViews,
close,
date,
+ isMonthMode,
isOpen,
+ isWeekMode,
+ months,
onDateChange,
onNextClick,
+ onNextYearClick,
onPreviousClick,
+ onPrevYearClick,
onWeekDayClick,
openDatePicker,
selectedDate,
+ selectedMode,
+ selectedMonthModeDate,
+ setSelectedMode,
+ setSelectedMonthModeDate,
week,
} = useDateFilter()
@@ -44,55 +66,134 @@ export const DateFilter = () => {
} = useHeaderFiltersStore()
return (
-
-
-
- {date.month} {' '} {date.year}
-
-
-
-
-
+
+
+
+ {isWeekMode && (
+
+
+ {date.month} {' '} {date.year}
+
+
+
+
+
+ )}
+
+ {isMonthMode && (
+
+
+
+
+
+ {selectedMonthModeDate.getFullYear()}
+
+
+
+
+
+ )}
-
-
-
-
-
+
+ {!isInSportsClient && (
+ setSelectedMode(Tabs.MONTH)}
+ >
+
+
+
+
+ )}
+ {!isMobileDevice && (
+ setSelectedMode(Tabs.TIMELINE)}
+ >
+
+
+
+
+ )}
+ {(!isMobileDevice || !isInSportsClient) && (
+ setSelectedMode(Tabs.WEEK)}
+ >
+
+
+
+
+ )}
+
+
+
+ {isMonthMode && (
+
{
- map(week, (day) => (
- (
+ {
- if (day.date.getDate() !== selectedDate.getDate()) {
- addAdsViews()
- onWeekDayClick(day.date)
+ if (month.date.getMonth() !== selectedMonthModeDate.getMonth()) {
+ setSelectedMonthModeDate(month.date)
} else {
resetFilters()
}
}}
>
- {day.name.slice(0, 3)}
- {day.date.getDate()}
-
+ {month.name}
+
))
}
-
-
-
-
-
+
+ )}
+
+ {isWeekMode && (
+
+
+
+
+
+ {
+ map(week, (day) => (
+ {
+ if (day.date.getDate() !== selectedDate.getDate()) {
+ addAdsViews()
+ onWeekDayClick(day.date)
+ } else {
+ resetFilters()
+ }
+ }}
+ >
+ {day.name.slice(0, 3)}
+ {day.date.getDate()}
+
+ ))
+ }
+
+
+
+
+
+ )}
+
{
isOpen && (
@@ -107,6 +208,6 @@ export const DateFilter = () => {
)
}
-
+
)
}
diff --git a/src/features/HeaderFilters/components/DateFilter/styled.tsx b/src/features/HeaderFilters/components/DateFilter/styled.tsx
index 528ef42d..3deda5b8 100644
--- a/src/features/HeaderFilters/components/DateFilter/styled.tsx
+++ b/src/features/HeaderFilters/components/DateFilter/styled.tsx
@@ -2,6 +2,7 @@ import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent'
import { devices } from 'config/devices'
+import { isInSportsClient } from 'config'
type Props = {
isMonthMode: boolean,
@@ -17,7 +18,6 @@ export const BaseButton = styled.button`
export const Wrapper = styled.div`
position: relative;
- /* width: 32.8rem; */
display: flex;
flex-direction: column;
justify-content: center;
@@ -29,8 +29,6 @@ export const Wrapper = styled.div`
${isMobileDevice
? css`
- /* padding-top: 4px; */
- /* min-height: 84px; */
justify-content: space-between;
@media (max-width: 450px){
@@ -108,7 +106,6 @@ export const WeekDaysWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
- margin-top: 0.567rem;
${isMobileDevice
? css`
@@ -119,7 +116,6 @@ export const WeekDaysWrapper = styled.div`
`
export const Week = styled.div`
- margin: 0 0.95rem;
display: flex;
@media (max-width: 600px) {
@@ -225,11 +221,8 @@ export const Arrow = styled.span`
: ''};
`
-export const MonthModeWrapper = styled(WeekDaysWrapper)`
+export const MonthModeWrapper = styled.div`
display: flex;
- justify-content: center;
- align-items: center;
- margin-top: ${({ isMonthMode }) => (isMonthMode ? '0' : '0.2rem')};;
::-webkit-scrollbar {
display: none;
@@ -241,47 +234,64 @@ export const MonthModeWrapper = styled(WeekDaysWrapper)`
margin-top: 0;
overflow-y: auto;
height: 100%;
+
+ & > :not(:last-child) {
+ margin-right: 25px;
+ }
`
: ''};
`
-export const FacrWrapper = styled(Wrapper)`
- justify-content: space-between;
- height: ${(isMobileDevice ? '100%' : 'fit-content')};
- width: ${({ isMonthMode }) => (isMonthMode ? '49.5%' : '26.3rem')};
+export const CalendarWrapper = styled(Wrapper)`
+ display: flex;
+ flex-direction: column;
+ height: fit-content;
+ padding-top: 2rem;
+ position: relative;
@media (max-width: 450px){
width: 100%;
justify-content: flex-start;
+ padding-top: 0;
};
`
-export const FacrDateButton = styled(DateButton)`
- left: 24rem;
- top: 0;
-`
-
-export const FacrMonthWrapper = styled(MonthWrapper)`
- position: static;
- width: auto;
- align-self: auto;
+export const WeekModeButton = styled(DateButton)`
+ right: 4.5rem;
+ top: -5px;
${isMobileDevice
? css`
- position: absolute;
- left: 50%;
- transform: translateX(-50%);
+ position: static;
`
: ''};
`
+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`
position: relative;
- display: flex;
- 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')};
+ margin-bottom: 0.7rem;
${isMobileDevice
? css`
@@ -300,24 +310,18 @@ export const MonthArrow = styled(ArrowButton)`
height: auto;
`
-export const Months = styled(Week)`
- height: 100%;
- width: 100%;
- margin: 0;
- justify-content: space-between;
-`
+export const Months = styled(Week)``
export const FacrWeek = styled(Week)`
margin: 0 1.8rem;
`
export const Month = styled(WeekDay)`
- width: auto;
+ width: 3.5rem;
${isMobileDevice
? css`
margin-top: 10px;
- margin-right: 25px;
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`
display: flex;
- justify-content: space-between;
- height: fit-content;
+ justify-content: center;
+ margin: 0 auto 0;
+
+ & > :not(:last-child) {
+ margin-right: 2rem;
+ }
${isMobileDevice
? css`
+ justify-content: flex-start;
top: 3px;
padding: 0;
`
@@ -349,6 +376,9 @@ export const TabsList = styled.div`
`
export const YearWrapper = styled.div`
+ position: absolute;
+ right: 0;
+ top: -5px;
display: flex;
align-items: center;
justify-content: center;
@@ -360,6 +390,7 @@ export const YearWrapper = styled.div`
position: absolute;
left: 50%;
transform: translateX(-50%);
+ top: -1px;
`
: ''};
`
@@ -387,17 +418,13 @@ export const Tab = styled.button`
display: flex;
justify-content: space-between;
align-items: center;
- font-size: .75rem;
+ font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
border: none;
background: none;
padding: 0;
- :first-child {
- margin-right: 2rem;
- }
-
${isMobileDevice
? css`
font-size: 10px;
diff --git a/src/features/HeaderFilters/components/FacrDateFilter/index.tsx b/src/features/HeaderFilters/components/FacrDateFilter/index.tsx
deleted file mode 100644
index a6ccd5e7..00000000
--- a/src/features/HeaderFilters/components/FacrDateFilter/index.tsx
+++ /dev/null
@@ -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 (
-
-
-
- setSelectedMode(Tabs.MONTH)}
- >
-
-
-
-
- setSelectedMode(Tabs.WEEK)}
- >
-
-
-
-
-
- {isMonthMode
- ? (
-
-
-
-
-
- {selectedMonthModeDate.getFullYear()}
-
-
-
-
-
- )
- : (
-
-
- {date.month} {' '} {date.year}
-
-
-
-
-
- )}
-
-
- {isMonthMode
- ? (
-
-
-
- {
- map(months, (day) => (
- setSelectedMonthModeDate(day.date)}
- >
- {day.name}
-
- ))
- }
-
-
-
- )
- : (
-
-
-
-
-
- {
- map(week, (day) => (
- {
- if (day.date.getDate() !== selectedDate.getDate()) {
- addAdsViews()
- onWeekDayClick(day.date)
- } else {
- resetFilters()
- }
- }}
- >
- {day.name.slice(0, 3)}
- {day.date.getDate()}
-
- ))
- }
-
-
-
-
-
-
- )}
- {
- isOpen && (
-
-
-
-
-
-
- )
- }
-
- )
-}
diff --git a/src/features/HeaderFilters/index.tsx b/src/features/HeaderFilters/index.tsx
index 80de6b06..08cf91a0 100644
--- a/src/features/HeaderFilters/index.tsx
+++ b/src/features/HeaderFilters/index.tsx
@@ -1,3 +1,2 @@
export * from './components/DateFilter'
-export * from './components/FacrDateFilter'
export * from './store'
diff --git a/src/features/HeaderFilters/store/config.tsx b/src/features/HeaderFilters/store/config.tsx
index 1ba0f9c6..a631586a 100644
--- a/src/features/HeaderFilters/store/config.tsx
+++ b/src/features/HeaderFilters/store/config.tsx
@@ -8,4 +8,5 @@ export const filterKeys = {
export enum Tabs {
WEEK,
MONTH,
+ TIMELINE
}
diff --git a/src/features/HeaderFilters/store/hooks/index.tsx b/src/features/HeaderFilters/store/hooks/index.tsx
index 0035a0f7..be3255a2 100644
--- a/src/features/HeaderFilters/store/hooks/index.tsx
+++ b/src/features/HeaderFilters/store/hooks/index.tsx
@@ -14,12 +14,12 @@ import { useQueryParamStore } from 'hooks'
import { getSportLexic } from 'helpers'
-import { querieKeys } from 'config'
+import { isMobileDevice, querieKeys } from 'config'
import { getLocalStorageItem } from 'helpers/getLocalStorage'
+import type { Match } from 'helpers'
import { filterKeys, Tabs } from '../config'
import { isValidDate } from '../helpers/isValidDate'
-import type { Match } from '../../../Matches'
export const useFilters = () => {
const { search } = useLocation()
@@ -31,9 +31,11 @@ export const useFilters = () => {
})
const sportList = getLocalStorageItem(querieKeys.sportsList)
- const [selectedMode, setSelectedMode] = useState(0)
+ const [selectedMode, setSelectedMode] = useState(isMobileDevice ? Tabs.WEEK : Tabs.TIMELINE)
const [selectedMonthModeDate, setSelectedMonthModeDate] = useState(startOfMonth(new Date()))
const isMonthMode = selectedMode === Tabs.MONTH
+ const isTimelineMode = selectedMode === Tabs.TIMELINE && !isMobileDevice
+ const isWeekMode = selectedMode === Tabs.WEEK
const [selectedSport, setSelectedSport] = useState(['all_sports'])
const [selectedLeague, setSelectedLeague] = useState>(['all_competitions'])
@@ -61,11 +63,11 @@ export const useFilters = () => {
)
}, [selectedMode, selectedMonthModeDate])
- const compareSport = useCallback((match: Match, sportNames: Array) => {
+ const compareSport = useCallback((sportType: number, sportNames: Array) => {
if (sportNames[0] === 'all_sports') {
return true
}
- const sport = getSportLexic(match.sportType)
+ const sport = getSportLexic(sportType)
return (sportNames.indexOf(sport) >= 0 || sportNames.indexOf(`${sport}_popup`) >= 0)
}, [])
@@ -117,7 +119,9 @@ export const useFilters = () => {
compareSport,
isMonthMode,
isShowTournament,
+ isTimelineMode,
isTodaySelected,
+ isWeekMode,
resetFilters,
selectTournament,
selectedDate,
@@ -163,6 +167,8 @@ export const useFilters = () => {
setSelectedMonthModeDate,
selectedMonthModeDate,
isMonthMode,
+ isTimelineMode,
+ isWeekMode,
])
return store
diff --git a/src/features/HeaderMobile/index.tsx b/src/features/HeaderMobile/index.tsx
index feffe855..939f6beb 100644
--- a/src/features/HeaderMobile/index.tsx
+++ b/src/features/HeaderMobile/index.tsx
@@ -3,12 +3,11 @@ import { useRecoilValue } from 'recoil'
import { isAndroid, isIOS } from 'config/userAgent'
import {
client,
- isFqtvClient,
isLffClient,
} from 'config/clients'
import { HeaderMenu } from 'features/HeaderMenu'
-import { DateFilter, FacrDateFilter } from 'features/HeaderFilters'
+import { DateFilter } from 'features/HeaderFilters'
import { ScoreSwitch } from 'features/MatchSwitches'
import { SportsFilter } from 'features/SportsFilter'
import { isSportFilterShownAtom } from 'features/HomePage/Atoms/HomePageAtoms'
@@ -45,7 +44,7 @@ export const HeaderMobile = ({
}
- {isFqtvClient ? : }
+
{!isLffClient && isSportFilterShown ? : null}
diff --git a/src/features/HeaderMobile/styled.tsx b/src/features/HeaderMobile/styled.tsx
index 4709bd59..926d12bd 100644
--- a/src/features/HeaderMobile/styled.tsx
+++ b/src/features/HeaderMobile/styled.tsx
@@ -74,6 +74,7 @@ export const HeaderStyled = styled.header`
? css`
padding: 8px;
margin-bottom: 50px;
+ justify-content: flex-start;
`
: ''}
`
diff --git a/src/features/HomePage/components/Header/index.tsx b/src/features/HomePage/components/Header/index.tsx
index 58df0145..c01950cb 100644
--- a/src/features/HomePage/components/Header/index.tsx
+++ b/src/features/HomePage/components/Header/index.tsx
@@ -1,7 +1,7 @@
import { Link } from 'react-router-dom'
import { PAGES } from 'config/pages'
-import { client, isFqtvClient } from 'config/clients'
+import { client } from 'config/clients'
import { Menu } from 'features/Menu'
import { ScoreSwitch } from 'features/MatchSwitches'
@@ -9,7 +9,6 @@ import { Search } from 'features/Search'
import {
DateFilter,
useHeaderFiltersStore,
- FacrDateFilter,
} from 'features/HeaderFilters'
import {
@@ -45,7 +44,7 @@ export const Header = () => {
- {isFqtvClient ? : }
+
diff --git a/src/features/HomePage/components/HeaderFilters/index.tsx b/src/features/HomePage/components/HeaderFilters/index.tsx
index 5d7e4c3d..b20d88c9 100644
--- a/src/features/HomePage/components/HeaderFilters/index.tsx
+++ b/src/features/HomePage/components/HeaderFilters/index.tsx
@@ -22,6 +22,7 @@ import { isSportFilterShownAtom } from '../../Atoms/HomePageAtoms'
export const HeaderFilters = () => {
const {
isShowTournament,
+ isTimelineMode,
selectedFilters,
selectTournament,
setIsShowTournament,
@@ -71,7 +72,7 @@ export const HeaderFilters = () => {
)}
{!isLffClient && isShowTournament && isSportFilterShown && }
- {isShowTournament && (
+ {isShowTournament && !isTimelineMode && (
`
text-transform: uppercase;
font-weight: 700;
- font-size: 18px;
+ font-size: 0.75rem;
color: rgba(255, 255, 255, 0.5);
margin: 0 10px;
cursor: pointer;
diff --git a/src/features/HomePage/index.tsx b/src/features/HomePage/index.tsx
index 2b90ebc0..41eba88f 100644
--- a/src/features/HomePage/index.tsx
+++ b/src/features/HomePage/index.tsx
@@ -6,6 +6,7 @@ import { ConfirmPopup } from 'features/AuthServiceApp/components/ConfirmPopup'
import { Matches } from 'features/Matches'
import {
HeaderFiltersStore,
+ useHeaderFiltersStore,
} from 'features/HeaderFilters'
import {
PageWrapper,
@@ -14,6 +15,7 @@ import {
} from 'features/PageLayout'
import { UserFavorites } from 'features/UserFavorites'
import { BuyMatchPopup } from 'features/BuyMatchPopup'
+import { MatchesTimeline } from 'features/MatchesTimeline'
import { HEADER_MOBILE_ADS } from 'components/Ads/types'
import { HeaderAds } from 'components/Ads'
@@ -35,6 +37,8 @@ const Home = () => {
userInfo,
} = useHomePage()
+ const { isTimelineMode } = useHeaderFiltersStore()
+
return (
{isMobileDevice ? (
@@ -60,7 +64,9 @@ const Home = () => {
}
/>
)}
-
+ {isTimelineMode
+ ?
+ : }
void,
- refIcon: any,
+ refIcon: keyof typeof icons | string,
size?: number | string,
styles?: CSSProperties,
}
diff --git a/src/features/MatchCard/CardFrontside/MatchCardMobile/index.tsx b/src/features/MatchCard/CardFrontside/MatchCardMobile/index.tsx
index f7850103..1ff45cae 100644
--- a/src/features/MatchCard/CardFrontside/MatchCardMobile/index.tsx
+++ b/src/features/MatchCard/CardFrontside/MatchCardMobile/index.tsx
@@ -9,7 +9,7 @@ import {
client,
} from 'config'
-import type { Match } from 'features/Matches'
+import type { Match } from 'helpers'
import { useMatchSwitchesStore } from 'features/MatchSwitches'
import { useName } from 'features/Name'
import { T9n } from 'features/T9n'
diff --git a/src/features/MatchCard/CardFrontside/index.tsx b/src/features/MatchCard/CardFrontside/index.tsx
index 0c1b047a..6606eb94 100644
--- a/src/features/MatchCard/CardFrontside/index.tsx
+++ b/src/features/MatchCard/CardFrontside/index.tsx
@@ -8,7 +8,7 @@ import { client } from 'config/clients'
import type { LiveScore } from 'requests'
-import type { Match } from 'features/Matches'
+import type { Match } from 'helpers'
import { useMatchSwitchesStore } from 'features/MatchSwitches'
import { useName } from 'features/Name'
import { T9n } from 'features/T9n'
@@ -82,7 +82,7 @@ export const CardFrontside = ({
const tournamentName = useName(tournament)
const { isInFavorites } = useUserFavoritesStore()
const { isScoreHidden } = useMatchSwitchesStore()
- const { isMonthMode } = useHeaderFiltersStore()
+ const { isMonthMode, isTimelineMode } = useHeaderFiltersStore()
const { color } = tournament
const isInFuture = getUnixTime(date) > getUnixTime(new Date())
const showScore = !(
@@ -179,9 +179,9 @@ export const CardFrontside = ({
- {(isHomePage && !isMonthMode) || isMatchPage ? null : prepareDate}
+ {(isHomePage && !isMonthMode && !isTimelineMode) || isMatchPage ? null : prepareDate}
{live && (
diff --git a/src/features/MatchCard/config.tsx b/src/features/MatchCard/config.tsx
index 9930e6b5..56d89051 100644
--- a/src/features/MatchCard/config.tsx
+++ b/src/features/MatchCard/config.tsx
@@ -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
diff --git a/src/features/MatchCard/hooks.tsx b/src/features/MatchCard/hooks.tsx
index 696d3272..0986c8f6 100644
--- a/src/features/MatchCard/hooks.tsx
+++ b/src/features/MatchCard/hooks.tsx
@@ -10,7 +10,7 @@ import {
ProfileTypes,
} from 'config'
-import type { Match } from 'features/Matches'
+import type { Match } from 'helpers'
import { useMatchPopupStore } from 'features/MatchPopup'
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup'
import { useAuthStore } from 'features/AuthStore'
diff --git a/src/features/MatchCard/index.tsx b/src/features/MatchCard/index.tsx
index db334df0..90630ef4 100644
--- a/src/features/MatchCard/index.tsx
+++ b/src/features/MatchCard/index.tsx
@@ -1,4 +1,4 @@
-import type { Match } from 'features/Matches'
+import type { Match } from 'helpers'
import { isMobileDevice } from 'config/userAgent'
diff --git a/src/features/MatchPage/store/hooks/useTournamentData.tsx b/src/features/MatchPage/store/hooks/useTournamentData.tsx
index e2450632..bab7afe3 100644
--- a/src/features/MatchPage/store/hooks/useTournamentData.tsx
+++ b/src/features/MatchPage/store/hooks/useTournamentData.tsx
@@ -9,8 +9,8 @@ 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 type { Match } from 'helpers'
+import { prepareMatches } from 'helpers'
import { useAuthStore } from 'features/AuthStore'
import type { MatchInfo } from 'requests'
diff --git a/src/features/MatchPage/types.tsx b/src/features/MatchPage/types.tsx
index d39d9805..82308e11 100644
--- a/src/features/MatchPage/types.tsx
+++ b/src/features/MatchPage/types.tsx
@@ -1,6 +1,7 @@
import type { Lexics, Episodes } from 'requests'
-import type { Match } from 'features/Matches'
+import type { Match } from 'helpers'
+
import { Tabs } from 'features/MatchSidePlaylists/config'
import type { MatchPlaylistIds } from './helpers/buildPlaylists'
diff --git a/src/features/MatchPopup/types.tsx b/src/features/MatchPopup/types.tsx
index 00e05f3a..b444a732 100644
--- a/src/features/MatchPopup/types.tsx
+++ b/src/features/MatchPopup/types.tsx
@@ -1,5 +1,5 @@
-import type { Match } from 'features/Matches/hooks'
+import type { Match } from 'helpers'
export type MatchData = Pick (
+const addSportTypeToMatches = (matches: MatchesDto, sport: number) => (
map(matches, (match) => ({ ...match, sport }))
)
diff --git a/src/features/Matches/helpers/getMatchClickAction/__tests__/index.tsx b/src/features/Matches/helpers/getMatchClickAction/__tests__/index.tsx
index eff2f50c..4d96154d 100644
--- a/src/features/Matches/helpers/getMatchClickAction/__tests__/index.tsx
+++ b/src/features/Matches/helpers/getMatchClickAction/__tests__/index.tsx
@@ -1,4 +1,4 @@
-import type { Match } from 'requests'
+import type { MatchDto } from 'requests'
import { getMatchAccess, MatchAccess } from '..'
@@ -14,7 +14,7 @@ type Args = {
const createMatch = (args: Args) => ({
...args,
-} as Match)
+} as MatchDto)
const user = undefined
diff --git a/src/features/Matches/helpers/getMatchClickAction/index.tsx b/src/features/Matches/helpers/getMatchClickAction/index.tsx
index 9a799281..b2639c41 100644
--- a/src/features/Matches/helpers/getMatchClickAction/index.tsx
+++ b/src/features/Matches/helpers/getMatchClickAction/index.tsx
@@ -1,4 +1,4 @@
-import type { Match } from 'requests'
+import type { MatchDto } from 'requests'
import type { User } from 'oidc-client'
import { isFuture } from 'date-fns'
@@ -13,7 +13,7 @@ export enum MatchAccess {
ViewMatchPopupWithoutUser = 'ViewMatchPopupWithoutUser',
}
-export const getMatchAccess = (match: Match, user: User | undefined) => {
+export const getMatchAccess = (match: MatchDto, user: User | undefined) => {
const {
access,
date,
diff --git a/src/features/Matches/hooks.tsx b/src/features/Matches/hooks.tsx
index ec59b014..a6a99fff 100644
--- a/src/features/Matches/hooks.tsx
+++ b/src/features/Matches/hooks.tsx
@@ -13,12 +13,10 @@ import { useRequest } from 'hooks'
import { usePreferencesStore } from 'features/PreferencesPopup'
import { isMobileDevice } from 'config/userAgent'
-import { prepareMatches } from './helpers/prepareMatches'
+import { prepareMatches } from 'helpers'
import { useAuthStore } from '../AuthStore'
-export type Match = ReturnType[number]
-
export type Props = {
fetch: (limit: number, offset: number) => Promise,
}
diff --git a/src/features/Matches/index.tsx b/src/features/Matches/index.tsx
index 4bf7a1c5..bf446e65 100644
--- a/src/features/Matches/index.tsx
+++ b/src/features/Matches/index.tsx
@@ -14,8 +14,6 @@ import { useMatches } from './hooks'
import { MatchesList } from './components/MatchesList'
import { Loading } from './styled'
-export type { Match } from './hooks'
-
export const Matches = memo((props: Props) => {
const {
fetchMoreMatches,
diff --git a/src/features/MatchesGrid/index.tsx b/src/features/MatchesGrid/index.tsx
index e8f6c451..5c78e3df 100644
--- a/src/features/MatchesGrid/index.tsx
+++ b/src/features/MatchesGrid/index.tsx
@@ -11,10 +11,10 @@ import { getLiveScores } from 'requests'
import { MatchCard } from 'features/MatchCard'
import { TournamentList } from 'features/TournamentList'
-import type { Match } from 'features/Matches'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { readToken } from 'helpers'
+import type { Match } from 'helpers'
import { useMatchSwitchesStore } from '../MatchSwitches'
import { useHomePage } from '../HomePage/hooks'
@@ -41,6 +41,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
selectedDate,
selectedFilters,
selectedLeague,
+ selectedMode,
selectedSport,
updateSportIds,
} = useHeaderFiltersStore()
@@ -60,7 +61,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
}
if (isHomePage && selectedSport) {
- return matches.filter((match) => compareSport(match, selectedSport))
+ return matches.filter((match) => compareSport(match.sportType, selectedSport))
}
return matches
@@ -83,7 +84,7 @@ export const MatchesGrid = memo(({ matches }: MatchesGridProps) => {
updateSportIds(matches)
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [selectedDate, matches])
+ }, [selectedDate, matches, selectedMode])
return (
diff --git a/src/features/MatchesSlider/hooks.tsx b/src/features/MatchesSlider/hooks.tsx
index 320149f3..dfd2ce02 100644
--- a/src/features/MatchesSlider/hooks.tsx
+++ b/src/features/MatchesSlider/hooks.tsx
@@ -9,28 +9,33 @@ import {
import throttle from 'lodash/throttle'
-import type { Match } from 'features/Matches'
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) => {
const slidesRef = useRef(null)
const [showLeftArrow, setShowLeftArrow] = useState(false)
const [showRightArrow, setShowRigthArrow] = useState(false)
+ const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false)
+ const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false)
useEffect(() => {
const {
- clientWidth = 0,
+ // clientWidth = 0,
scrollLeft = 0,
- scrollWidth = 0,
+ // scrollWidth = 0,
} = 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])
const onScroll = useCallback((e: SyntheticEvent) => {
@@ -39,11 +44,32 @@ export const useMatchesSlider = (matches: Array) => {
scrollLeft: targetScrollLeft,
scrollWidth: targetScrollWidth,
} = e.currentTarget
-
- setShowLeftArrow(targetScrollLeft > 1)
- setShowRigthArrow((targetScrollWidth - (targetScrollLeft + targetClientWidth)) > 1)
+ const targetScrollRight = targetScrollWidth - (targetScrollLeft + targetClientWidth)
+ // setShowLeftArrow(targetScrollLeft > 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(() => {
slidesRef.current!.scrollBy(-((MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL), 0)
}, SCROLLING_DELAY), [])
@@ -53,7 +79,11 @@ export const useMatchesSlider = (matches: Array) => {
}, SCROLLING_DELAY), [])
return {
+ isLeftArrowDisabled,
+ isRightArrowDisabled,
onScroll,
+ onWrapperMouseEnter,
+ onWrapperMouseLeave,
showLeftArrow,
showRightArrow,
slideLeft,
diff --git a/src/features/MatchesSlider/index.tsx b/src/features/MatchesSlider/index.tsx
index b5b27988..f1b546bf 100644
--- a/src/features/MatchesSlider/index.tsx
+++ b/src/features/MatchesSlider/index.tsx
@@ -1,14 +1,25 @@
+import { useEffect } from 'react'
+
+import { useQuery } from 'react-query'
+
import map from 'lodash/map'
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 { useMatchSwitchesStore } from 'features/MatchSwitches'
+
+import { LiveScore, getLiveScores } from 'requests'
import { useMatchesSlider } from './hooks'
import {
Wrapper,
Arrow,
Slides,
+ ArrowButton,
} from './styled'
type MatchesSliderProps = {
@@ -17,7 +28,11 @@ type MatchesSliderProps = {
export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
const {
+ isLeftArrowDisabled,
+ isRightArrowDisabled,
onScroll,
+ onWrapperMouseEnter,
+ onWrapperMouseLeave,
showLeftArrow,
showRightArrow,
slideLeft,
@@ -25,26 +40,89 @@ export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
slidesRef,
} = 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
return (
-
+
{showLeftArrow && (
-
+ disabled={isLeftArrowDisabled}
+ >
+
+
)}
- {map(matches, (match) => )}
+ {map(matches, (match) => (
+ match_id === match.id
+ && sport_id === match.sportType,
+ )}
+ />
+ ))}
{showRightArrow && (
-
+ disabled={isRightArrowDisabled}
+ >
+
+
)}
)
diff --git a/src/features/MatchesSlider/styled.tsx b/src/features/MatchesSlider/styled.tsx
index 99fab09d..eb341941 100644
--- a/src/features/MatchesSlider/styled.tsx
+++ b/src/features/MatchesSlider/styled.tsx
@@ -1,53 +1,111 @@
-import styled from 'styled-components/macro'
+import styled, { css } from 'styled-components/macro'
-import { devices } from 'config/devices'
-
-import { CardWrapper } from '../MatchCard/styled'
+import { CardWrapper, CardWrapperOuter } from '../MatchCard/styled'
export const Wrapper = styled.div`
position: relative;
- margin-bottom: 16px;
+ overflow: hidden;
+ padding-right: 5px;
`
export const Slides = styled.ul`
display: flex;
scroll-behavior: smooth;
overflow-x: auto;
+ gap: 0.9rem;
+ padding: 10px 10px 10px 7px;
&::-webkit-scrollbar {
display: none;
}
+ scrollbar-width: none;
+
+ ${CardWrapperOuter} {
+ padding-top: 0;
+ }
${CardWrapper} {
- width: 283px;
+ position: relative;
+ height: 12.9rem;
+ width: 12.9rem;
+
+ &:hover {
+ scale: 1.04;
+ }
- @media ${devices.laptop} {
- min-width: auto;
- width: 279px;
+ @media screen and (min-width: 1920px) {
+ height: 13.8rem;
+ width: 13.8rem;
}
- }
- @media ${devices.mobile} {
- flex-direction: column;
+ @media screen and (max-width: 1920px) {
+ height: 13.4rem;
+ width: 13.4rem;
+ }
- ${CardWrapper} {
- width: 100%;
- }
+ @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 Arrow = styled.div<{ type: 'arrowLeft' | 'arrowRight' }>`
+type ArrowProps = {
+ direction: 'left' | 'right',
+ disabled?: boolean,
+}
+
+export const Arrow = styled.span`
+ width: 1rem;
+ height: 1rem;
position: absolute;
+ border-left: 0.25rem solid #fff;
+ border-bottom: 0.25rem solid #fff;
top: 50%;
- left: ${({ type }) => (type === 'arrowLeft' ? '10px' : 'calc(100% - 10px)')};
- width: 40px;
- height: 40px;
- background-position: center;
- background-repeat: no-repeat;
- background-image: url(${({ type }) => (type === 'arrowLeft'
- ? '/images/slideLeft.svg'
- : '/images/slideRight.svg')});
+ left: 50%;
+ border-radius: 3px;
+
+ ${({ direction }) => (
+ direction === 'left'
+ ? 'transform: translate(-50%, -50%) rotate(45deg);'
+ : 'transform: translate(-50%, -50%) rotate(225deg);'
+ )}
+
+ ${({ disabled }) => (disabled ? css`
+ border-left: 0.25rem solid gray;
+ border-bottom: 0.25rem solid gray;
+ ` : '')}
+`
+
+export const ArrowButton = styled.button`
+ border: none;
+ outline: none;
+ padding: 0;
+ background-color: transparent;
cursor: pointer;
+ position: absolute;
+ width: 2.28rem;
+ height: 2.28rem;
+ z-index: 3;
+ top: 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;
+ }
+ ` : '')}
+ }
`
diff --git a/src/features/MatchesTimeline/hooks.tsx b/src/features/MatchesTimeline/hooks.tsx
new file mode 100644
index 00000000..d2cd0818
--- /dev/null
+++ b/src/features/MatchesTimeline/hooks.tsx
@@ -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 & {
+ matches: Array,
+}>
+
+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>([])
+ const [tournamentList, setTournamentList] = useState([])
+
+ const prepareMatchesDto = useCallback((matches: Array) => 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,
+ }
+}
+
diff --git a/src/features/MatchesTimeline/index.tsx b/src/features/MatchesTimeline/index.tsx
new file mode 100644
index 00000000..a1ec701f
--- /dev/null
+++ b/src/features/MatchesTimeline/index.tsx
@@ -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([])
+
+ 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 ...
+ }
+
+ return (
+
+
+ {onlineUpcomingMatches.length > 0 && (
+
+
+
+ LIVE & UPCOMING
+
+
+
+
+ )}
+ {tournaments.map(({
+ matches,
+ sport_id,
+ tournament,
+ tournament_id,
+ }) => (
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/src/features/MatchesTimeline/styled.tsx b/src/features/MatchesTimeline/styled.tsx
new file mode 100644
index 00000000..0a8696cd
--- /dev/null
+++ b/src/features/MatchesTimeline/styled.tsx
@@ -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;
+ }
+`
diff --git a/src/features/PageLayout/styled.tsx b/src/features/PageLayout/styled.tsx
index 3c1b32ed..853f6722 100644
--- a/src/features/PageLayout/styled.tsx
+++ b/src/features/PageLayout/styled.tsx
@@ -59,10 +59,11 @@ export const Content = styled.div`
export const BodyBackdrop = styled.div`
position: fixed;
- top: 8.5rem;
+ // высота хедера
+ top: ${isMobileDevice ? '124px' : '8.5rem'} ;
left: 0;
background-color: rgba(0, 0, 0, 0.7);
width: 100vw;
- height: calc(100vh - 8.5rem);
+ height: 100vh;
z-index: 2;
`
diff --git a/src/features/SportsFilter/components/SelectSport/styled.tsx b/src/features/SportsFilter/components/SelectSport/styled.tsx
index 6747afcc..32b16e22 100644
--- a/src/features/SportsFilter/components/SelectSport/styled.tsx
+++ b/src/features/SportsFilter/components/SelectSport/styled.tsx
@@ -21,6 +21,7 @@ export const ScSportsFilter = styled.div`
justify-content: space-between;
min-width: 15.4%;
padding-left: 5px;
+ align-items: flex-start;
${isMobileDevice
? css`
diff --git a/src/features/TournamentList/components/TournamentMobile/index.tsx b/src/features/TournamentList/components/TournamentMobile/index.tsx
index 8bed7cd4..51736d87 100644
--- a/src/features/TournamentList/components/TournamentMobile/index.tsx
+++ b/src/features/TournamentList/components/TournamentMobile/index.tsx
@@ -10,9 +10,10 @@ import {
import { AdType } from 'requests'
+import type { Match } from 'helpers'
+
import { T9n } from 'features/T9n'
import { Icon } from 'features/Icon'
-import type { Match } from 'features/Matches'
import { MatchCard } from 'features/MatchCard'
import {
CountryFlag,
diff --git a/src/features/TournamentList/hooks.tsx b/src/features/TournamentList/hooks.tsx
index 71fdd45a..373cd9c3 100644
--- a/src/features/TournamentList/hooks.tsx
+++ b/src/features/TournamentList/hooks.tsx
@@ -4,10 +4,11 @@ import orderBy from 'lodash/orderBy'
import { ProfileTypes } from 'config'
import { TournamentListProps } from 'features/TournamentList'
-import type { Match } from 'features/Matches'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { useUserFavoritesStore } from 'features/UserFavorites/store'
+import type { Match } from 'helpers'
+
interface TournamentsSortProps {
id: number,
isFavorite: boolean,
@@ -45,7 +46,7 @@ export const useTournaments = (matches: Array) => {
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)) {
const tournament = {
...match.tournament,
@@ -68,7 +69,7 @@ export const useTournaments = (matches: Array) => {
isSuperTournament: Boolean(match.tournament.is_super_tournament),
sportType: match.sportType,
})
- } else if (compareSport(match, selectedSport) && compareLeague(uniqTournamentId)) {
+ } else if (compareSport(match.sportType, selectedSport) && compareLeague(uniqTournamentId)) {
acc[uniqTournamentId] = {
...acc[uniqTournamentId],
tournament: {
diff --git a/src/features/TournamentList/index.tsx b/src/features/TournamentList/index.tsx
index 7407d515..9a2b67fb 100644
--- a/src/features/TournamentList/index.tsx
+++ b/src/features/TournamentList/index.tsx
@@ -1,6 +1,7 @@
import { useRouteMatch } from 'react-router-dom'
-import type { Match } from 'features/Matches'
+import type { Match } from 'helpers'
+
import { MatchCard } from 'features/MatchCard'
import type { TournamentType } from 'requests/getMatches/types'
diff --git a/src/features/UserFavorites/styled.tsx b/src/features/UserFavorites/styled.tsx
index bb588989..6ce8a16e 100644
--- a/src/features/UserFavorites/styled.tsx
+++ b/src/features/UserFavorites/styled.tsx
@@ -14,7 +14,6 @@ export const UserSportFavWrapper = styled.aside`
flex-direction: column;
align-items: center;
margin-top: 1rem;
- padding-bottom: 2.75rem;
left: 0;
top: 0;
bottom: 0;
@@ -29,7 +28,7 @@ export const UserSportFavWrapper = styled.aside`
export const ScrollWrapper = styled.div`
width: 4.35rem;
- padding: 0.472rem 0.30rem 0 0.15rem;
+ padding: 0.472rem 0.30rem 0.472rem 0.15rem;
height: calc(100vh - 14rem);
display: flex;
flex-direction: column;
diff --git a/src/helpers/index.tsx b/src/helpers/index.tsx
index 814a4711..c56ad20e 100644
--- a/src/helpers/index.tsx
+++ b/src/helpers/index.tsx
@@ -17,3 +17,4 @@ export * from './languageUrlParam'
export * from './bodyScrollLock'
export * from './getLocalStorage'
export * from './checkPage'
+export * from './prepareMatches'
diff --git a/src/features/Matches/helpers/prepareMatches.tsx b/src/helpers/prepareMatches/index.tsx
similarity index 65%
rename from src/features/Matches/helpers/prepareMatches.tsx
rename to src/helpers/prepareMatches/index.tsx
index bec2581b..944cf5e4 100644
--- a/src/features/Matches/helpers/prepareMatches.tsx
+++ b/src/helpers/prepareMatches/index.tsx
@@ -1,16 +1,19 @@
import map from 'lodash/map'
import orderBy from 'lodash/orderBy'
+
import format from 'date-fns/format'
import type { User } from 'oidc-client'
-import type { Match } from 'requests'
+import type { MatchDto } from 'requests'
import { parseDate } from 'helpers/parseDate'
-import { getMatchAccess } from './getMatchClickAction'
+import { getMatchAccess } from 'features/Matches/helpers/getMatchClickAction'
+
+export type Match = ReturnType
-const prepareMatch = (match: Match, user?: User | undefined) => {
+const prepareMatch = (match: MatchDto, user?: User | undefined) => {
const {
calc,
country,
@@ -56,14 +59,22 @@ const prepareMatch = (match: Match, user?: User | undefined) => {
}
}
-export const prepareMatches = (matches: Array, user?: User | undefined) => {
+export const prepareMatches = (
+ matches: Array,
+ user?: User | undefined,
+ liveOrder: boolean = true,
+): Array => {
const preparedMatches = map(
matches,
(match) => prepareMatch(match, user),
)
- return orderBy(
- preparedMatches,
- ['live'],
- ['desc'],
- )
+
+ if (liveOrder) {
+ return orderBy(
+ preparedMatches,
+ ['live'],
+ ['desc'],
+ )
+ }
+ return preparedMatches
}
diff --git a/src/pages/HighlightsPage/components/FormHighlights/hooks.tsx b/src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
index 4d165f13..15490349 100644
--- a/src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
+++ b/src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
@@ -16,7 +16,7 @@ import {
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 { getSounds } from 'requests/getSounds'
@@ -409,7 +409,7 @@ export const useHighlightsForm = () => {
sub_only: true,
})
.then(({ broadcast }) => setPlayerMatches(
- broadcast.map((match: Match) => ({ ...match, isChecked: false })),
+ broadcast.map((match: MatchDto) => ({ ...match, isChecked: false })),
))
.finally(() => setIsFetching(false))
diff --git a/src/pages/HighlightsPage/storeHighlightsAtoms.tsx b/src/pages/HighlightsPage/storeHighlightsAtoms.tsx
index 542aed44..e6ee6eca 100644
--- a/src/pages/HighlightsPage/storeHighlightsAtoms.tsx
+++ b/src/pages/HighlightsPage/storeHighlightsAtoms.tsx
@@ -1,8 +1,8 @@
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,
}
diff --git a/src/requests/getMatches/getTimelineMatches.tsx b/src/requests/getMatches/getTimelineMatches.tsx
new file mode 100644
index 00000000..e6bf2f55
--- /dev/null
+++ b/src/requests/getMatches/getTimelineMatches.tsx
@@ -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,
+ online_upcoming: Array<{matches: MatchesDto}>,
+ others: Array,
+ promo: Array,
+}
+
+export const getTimelineMatches = (sportId?: number): Promise => {
+ 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,
+ })
+}
diff --git a/src/requests/getMatches/index.tsx b/src/requests/getMatches/index.tsx
index 3e17f564..607ec0a5 100644
--- a/src/requests/getMatches/index.tsx
+++ b/src/requests/getMatches/index.tsx
@@ -3,3 +3,4 @@ export * from './getHomeMatches'
export * from './getTeamMatches'
export * from './getPlayerMatches'
export * from './getTournamentMatches'
+export * from './getTimelineMatches'
diff --git a/src/requests/getMatches/types.tsx b/src/requests/getMatches/types.tsx
index 4afa6458..e6ba1640 100644
--- a/src/requests/getMatches/types.tsx
+++ b/src/requests/getMatches/types.tsx
@@ -32,7 +32,7 @@ export type SportInfo = {
lexic: number,
}
-export type Match = {
+export type MatchDto = {
access: boolean,
/** тип трансляции */
c_type_broadcast: number,
@@ -61,12 +61,12 @@ export type Match = {
tournament: TournamentType,
}
-export type Matches = Array
+export type MatchesDto = Array
type VideoContent = {
- broadcast: Matches,
- features: Matches,
- highlights: Matches,
+ broadcast: MatchesDto,
+ features: MatchesDto,
+ highlights: MatchesDto,
}
export type MatchesResponse = {
@@ -76,9 +76,9 @@ export type MatchesResponse = {
}
export type MatchesBySection = {
- broadcast: Matches,
- features: Matches,
+ broadcast: MatchesDto,
+ features: MatchesDto,
hasNextPage: boolean,
- highlights: Matches,
+ highlights: MatchesDto,
isVideoSections: boolean,
}