Compare commits

..

3 Commits

Author SHA1 Message Date
Rakov 80f2f4386c fix(#720): media query fqtv 2 years ago
Rakov fb4acfbebd fix(#720): fix media query 2 years ago
Rakov 71eff354a9 fix(#720): timeline mode 2 years ago
  1. 53
      .drone.yml
  2. 11
      Makefile
  3. BIN
      public/clients/facr/favicon/android-chrome-192x192.png
  4. BIN
      public/clients/facr/favicon/android-chrome-512x512.png
  5. BIN
      public/clients/facr/favicon/apple-touch-icon.png
  6. BIN
      public/clients/facr/favicon/favicon-16x16.png
  7. BIN
      public/clients/facr/favicon/favicon-32x32.png
  8. BIN
      public/clients/facr/favicon/favicon.ico
  9. BIN
      public/clients/fqtv/favicon/android-chrome-192x192.png
  10. BIN
      public/clients/fqtv/favicon/android-chrome-512x512.png
  11. BIN
      public/clients/fqtv/favicon/apple-touch-icon.png
  12. BIN
      public/clients/fqtv/favicon/favicon-16x16.png
  13. BIN
      public/clients/fqtv/favicon/favicon-32x32.png
  14. BIN
      public/clients/fqtv/favicon/favicon.ico
  15. BIN
      public/clients/lff/favicon/android-chrome-192x192.png
  16. BIN
      public/clients/lff/favicon/android-chrome-512x512.png
  17. BIN
      public/clients/lff/favicon/apple-touch-icon.png
  18. BIN
      public/clients/lff/favicon/favicon-16x16.png
  19. BIN
      public/clients/lff/favicon/favicon-32x32.png
  20. BIN
      public/clients/lff/favicon/favicon.ico
  21. 15
      public/silent-refresh.html
  22. 6
      src/features/AuthServiceApp/components/ConfirmPopup/index.tsx
  23. 2
      src/features/AuthServiceApp/config/lexics.tsx
  24. 26
      src/features/AuthStore/hooks/useAuth.tsx
  25. 1
      src/features/Common/Image/index.tsx
  26. 2
      src/features/HeaderFilters/store/hooks/index.tsx
  27. 11
      src/features/MatchCard/CardFrontside/hooks.tsx
  28. 33
      src/features/MatchesSlider/hooks.tsx
  29. 58
      src/features/MatchesSlider/index.tsx
  30. 38
      src/features/MatchesSlider/styled.tsx
  31. 71
      src/features/MatchesTimeline/hooks.tsx
  32. 62
      src/features/MatchesTimeline/index.tsx
  33. 34
      src/features/MatchesTimeline/styled.tsx
  34. 5
      src/features/SystemSettings/hooks.tsx
  35. 13
      src/helpers/token/index.tsx
  36. 9
      src/requests/getMatches/getTimelineMatches.tsx

@ -1007,56 +1007,3 @@ steps:
- aws cloudfront create-invalidation --distribution-id E15IFY23VM147K --paths "/*"
depends_on:
- make-rustat
---
kind: pipeline
type: docker
name: deploy insport.live
concurrency:
limit: 1
platform:
os: linux
arch: amd64
trigger:
ref:
- refs/heads/insport.live
steps:
- name: npm-install
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- npm install --legacy-peer-deps
- name: make-insport-live
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- make insport-live-prod
depends_on:
- npm-install
- name: deploy-insport-live
image: amazon/aws-cli:latest
environment:
AWS_ACCESS_KEY_ID:
from_secret: AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION:
from_secret: AWS_DEFAULT_REGION
AWS_MAX_ATTEMPTS: 10
commands:
- aws s3 sync build_insport_live s3://insports-live --delete
- aws cloudfront create-invalidation --distribution-id E1LBC88VYP6XVB --paths "/*"
depends_on:
- make-insport-live

@ -46,7 +46,7 @@ build-c: clean
build-d: clean
REACT_APP_TYPE=ott \
REACT_APP_ENV=staging \
REACT_APP_CLIENT=insports \
REACT_APP_CLIENT=fqtv \
REACT_APP_STAGE=test-d \
npm run build
@ -225,15 +225,6 @@ rustat-prod:
BUILD_PATH=build_rustat \
npm run build && cp -r .well-known build_rustat
insport-live-prod:
rm -rf build_insport_live && \
REACT_APP_TYPE=ott \
REACT_APP_ENV=staging \
REACT_APP_CLIENT=lff \
REACT_APP_STRIPE_PK=pk_live_51J5TEYEDSxVnTgDW5XxhC6ntKZKddXgKHq5HOCDmJTdfSKluMYCdLHOcUA3Miuy8HesxG1eS4c0dQRQpMsEHRrQL00USpu5xIq \
BUILD_PATH=build_insport_live \
npm run build && cp -r .well-known build_insport_live
deploy-all: prod preprod facr-prod lff-prod diwansport-prod india-prod fqtv-prod rustat-prod
test:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 102 KiB

@ -1,28 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.11.5/oidc-client.min.js"
integrity="sha512-pGtU1n/6GJ8fu6bjYVGIOT9Dphaw5IWPwVlqkpvVgqBxFkvdNbytUh0H8AP15NYF777P4D3XEeA/uDWFCpSQ1g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.11.5/oidc-client.min.js" integrity="sha512-pGtU1n/6GJ8fu6bjYVGIOT9Dphaw5IWPwVlqkpvVgqBxFkvdNbytUh0H8AP15NYF777P4D3XEeA/uDWFCpSQ1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
new Oidc.UserManager().signinSilentCallback()
// обновляем рефреш токен в локалсторадже
// так как safari не дает доступ к кукам
.then(() => {
const refreshToken = localStorage.getItem('refresh_token');
if (refreshToken) {
localStorage.setItem('refresh_token', new URLSearchParams(document.location.search).get('refresh_token'));
}
})
.catch((err) => {
console.error('OIDC: silent refresh callback error', err);
});
</script>
</body>
</html>

@ -1,7 +1,7 @@
import { T9n } from 'features/T9n'
import { client } from 'config/clients'
import { AUTH_SERVICE_OLD } from 'config/routes'
import { AUTH_SERVICE } from 'config/routes'
import {
ScBody,
@ -43,11 +43,11 @@ export const ConfirmPopup = (props: Props) => {
</ScText>
<ScText>
<T9n t='by_clicking' />
<ScLink href={`${AUTH_SERVICE_OLD}${client.termsLink}`} target='_blank' id='personal_t_k'>
<ScLink href={`${AUTH_SERVICE}${client.termsLink}`} target='_blank' id='personal_t_k'>
<T9n t='terms_and_conditions' />
</ScLink>&nbsp;
<T9n t='and' />
<ScLink href={`${AUTH_SERVICE_OLD}${client.privacyLink}`} target='_blank' id='personal_policy'>
<ScLink href={`${AUTH_SERVICE}${client.privacyLink}`} target='_blank' id='personal_policy'>
<T9n t='privacy_policy_and_statement' />
</ScLink>
</ScText>

@ -34,7 +34,7 @@ export const lexics = {
go_back: 1907,
i_accept: 15737,
i_agree: 15430,
login: 20304,
login: 13404,
ok: 724,
or_continue_with: 15118,
password_changed_success: 17824,

@ -24,10 +24,6 @@ import {
setCookie,
removeCookie,
isMatchPage,
REFRESH_TOKEN_KEY,
removeRefreshToken,
writeRefreshToken,
readRefreshToken,
} from 'helpers'
import {
@ -81,7 +77,6 @@ export const useAuth = () => {
userManager.signoutRedirect({ post_logout_redirect_uri: urlWithLang })
})
removeToken()
removeRefreshToken()
if (key !== 'saveToken') {
removeCookie('access_token')
}
@ -163,15 +158,12 @@ export const useAuth = () => {
}
}
const signinRedirectCallback = useCallback((refreshToken: string | null) => {
const signinRedirectCallback = useCallback(() => {
setPage(history.location.pathname)
userManager.signinRedirectCallback()
.then((loadedUser) => {
storeUser(loadedUser)
if (refreshToken) writeRefreshToken(refreshToken)
queryParamStorage.clear()
if (page.includes(PAGES.useraccount)) {
history.push(PAGES.home)
@ -195,13 +187,10 @@ export const useAuth = () => {
const searchToken = urlSearch.get('access_token')
const searchRefToken = urlSearch.get('id_token')
const searchExp = urlSearch.get('expires_in')
const refreshToken = urlSearch.get(REFRESH_TOKEN_KEY)
const isRedirectedBackFromAuthProvider = Boolean(searchToken && searchRefToken && searchExp)
isRedirectedBackFromAuthProvider
? signinRedirectCallback(refreshToken)
: checkUser()
isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
checkUser,
@ -232,7 +221,7 @@ export const useAuth = () => {
}, [reChekNewDevice])
useEffect(() => {
if (!needCheckNewDeviсe || !user) return undefined
if (!needCheckNewDeviсe && !user) return undefined
const startCheckDevice = setInterval(checkNewDevice, 20000)
isNewDeviceLogin && clearInterval(startCheckDevice)
return () => clearInterval(startCheckDevice)
@ -242,7 +231,6 @@ export const useAuth = () => {
checkNewDevice,
isNewDeviceLogin,
setIsNewDeviceLogin,
user,
])
duel.channel('active_page') // поле в LS, определяющее активность вкладки
@ -252,13 +240,7 @@ export const useAuth = () => {
// библиотека oidc-client не поддерживает обновление токена только на 1 вкладке
// @ts-ignore
if (window.isMaster()) {
// safari ограничивает доступ к куке через крос доменные запросы
// передаем рефреш токен через квери параметры
userManager.signinSilent({
extraQueryParams: {
refresh_token: readRefreshToken(),
},
}).catch(logout)
userManager.signinSilent().catch(logout)
}
}
// если запросы вернули 401 | 403

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

@ -31,7 +31,7 @@ export const useFilters = () => {
})
const sportList = getLocalStorageItem(querieKeys.sportsList)
const [selectedMode, setSelectedMode] = useState<Tabs>(Tabs.WEEK)
const [selectedMode, setSelectedMode] = useState<Tabs>(isMobileDevice ? Tabs.WEEK : Tabs.TIMELINE)
const [selectedMonthModeDate, setSelectedMonthModeDate] = useState(startOfMonth(new Date()))
const isMonthMode = selectedMode === Tabs.MONTH
const isTimelineMode = selectedMode === Tabs.TIMELINE && !isMobileDevice

@ -4,20 +4,20 @@ import {
useState,
} from 'react'
import { readToken } from 'helpers'
export type TUseCardFrontside = {
preview?: string,
previewURL?: string,
}
const PREVIEW_WIDTH = 400 // макс. 1920
export const useCardPreview = ({
preview,
previewURL,
}: TUseCardFrontside) => {
const [previewImage, setPreviewImage] = useState('')
const currentPreviewURL = useMemo(() => (
previewURL ? `${previewURL}?width=${PREVIEW_WIDTH}` : preview
previewURL ? `${previewURL}?access_token=${readToken()}` : preview
), [preview, previewURL])
useEffect(() => {
@ -25,8 +25,9 @@ export const useCardPreview = ({
if (!currentPreviewURL) return
try {
const image = await fetch(String(currentPreviewURL))
.then(async (result) => ({
const image = await fetch(String(currentPreviewURL), {
headers: { Authorization: `Bearer ${readToken()}` },
}).then(async (result) => ({
blob: await result.blob(),
status: result.status,
}))

@ -9,9 +9,11 @@ import {
import throttle from 'lodash/throttle'
import { MATCH_CARD_WIDTH, MATCH_CARD_GAP } from 'features/MatchCard/config'
import type { Match } from 'helpers'
const MATCHES_TO_SCROLL = 3
const MATCHES_TO_SCROLL = 12
const SCROLLING_DELAY = 350
@ -22,8 +24,6 @@ export const useMatchesSlider = (matches: Array<Match>) => {
const [showRightArrow, setShowRigthArrow] = useState(false)
const [isLeftArrowDisabled, setIsLeftArrowDisabled] = useState(false)
const [isRightArrowDisabled, setIsRightArrowDisabled] = useState(false)
const [sliderLiWidth, setSliderLiWidth] = useState(0)
const [sliderLiGap, setSliderLiGap] = useState(0)
useEffect(() => {
const {
@ -70,32 +70,13 @@ export const useMatchesSlider = (matches: Array<Match>) => {
setShowRigthArrow(false)
}
useEffect(() => {
const liFirst = slidesRef.current?.querySelectorAll('li')[0]?.getBoundingClientRect()
const liWidth = liFirst?.width
const liFirstLeft = liFirst?.left
const liSecondLeft = slidesRef.current?.querySelectorAll('li')[1]?.getBoundingClientRect().left
if (liWidth && liFirstLeft && liSecondLeft) {
setSliderLiWidth(liWidth)
setSliderLiGap(liSecondLeft - liFirstLeft - liWidth)
}
}, [])
const slideLeft = useMemo(() => throttle(() => {
slidesRef.current!.scrollBy({
behavior: 'smooth',
left: -((sliderLiWidth + sliderLiGap) * MATCHES_TO_SCROLL),
top: 0,
})
}, SCROLLING_DELAY), [sliderLiGap, sliderLiWidth])
slidesRef.current!.scrollBy(-((MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL), 0)
}, SCROLLING_DELAY), [])
const slideRight = useMemo(() => throttle(() => {
slidesRef.current!.scrollBy({
behavior: 'smooth',
left: ((sliderLiWidth + sliderLiGap) * MATCHES_TO_SCROLL),
top: 0,
})
}, SCROLLING_DELAY), [sliderLiGap, sliderLiWidth])
slidesRef.current!.scrollBy((MATCH_CARD_WIDTH + MATCH_CARD_GAP) * MATCHES_TO_SCROLL, 0)
}, SCROLLING_DELAY), [])
return {
isLeftArrowDisabled,

@ -1,11 +1,18 @@
import { useEffect } from 'react'
import { useQuery } from 'react-query'
import map from 'lodash/map'
import isEmpty from 'lodash/isEmpty'
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 {
@ -16,13 +23,10 @@ import {
} from './styled'
type MatchesSliderProps = {
cardSize?: number,
matches: Array<Match>,
}
const PADDING_PARENT = 10
export const MatchesSlider = ({ cardSize, matches }: MatchesSliderProps) => {
export const MatchesSlider = ({ matches }: MatchesSliderProps) => {
const {
isLeftArrowDisabled,
isRightArrowDisabled,
@ -36,19 +40,27 @@ export const MatchesSlider = ({ cardSize, 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 match = slidesRef.current!.querySelectorAll('li')[index]
const offsetLeftCount = match.offsetLeft
const offsetLeftCount = slidesRef.current!.querySelectorAll('li')[index].offsetLeft
const PADDING_PARENT = 10
const offsetLeft = offsetLeftCount - PADDING_PARENT
setTimeout(() => {
slidesRef.current!.scrollBy({
// @ts-ignore
behavior: 'instant',
left: offsetLeft,
})
}, 0)
slidesRef.current!.scrollBy(offsetLeft, 0)
}
// скролл к лайв матчам или сегодняшней дате
@ -67,15 +79,7 @@ export const MatchesSlider = ({ cardSize, matches }: MatchesSliderProps) => {
const slidesRefClientWidth = slidesRef.current!.clientWidth
const slidesRefScrollWidth = slidesRef.current!.scrollWidth
setTimeout(() => {
slidesRef.current!.scrollBy({
// @ts-ignore
behavior: 'instant',
left: slidesRefScrollWidth - slidesRefClientWidth + PADDING_PARENT,
})
}, 0)
slidesRef.current!.scrollBy(slidesRefScrollWidth - slidesRefClientWidth, 0)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
@ -98,15 +102,15 @@ export const MatchesSlider = ({ cardSize, matches }: MatchesSliderProps) => {
/>
</ArrowButton>
)}
<Slides
ref={slidesRef}
onScroll={onScroll}
size={cardSize}
>
<Slides ref={slidesRef} onScroll={onScroll}>
{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>

@ -8,9 +8,7 @@ export const Wrapper = styled.div`
padding-right: 5px;
`
export const Slides = styled.ul<{
size?: number,
}>`
export const Slides = styled.ul`
display: flex;
scroll-behavior: smooth;
overflow-x: auto;
@ -28,13 +26,37 @@ export const Slides = styled.ul<{
${CardWrapper} {
position: relative;
height: ${({ size }) => (size ? `${size}px` : '12.9rem')};
width: ${({ size }) => (size ? `${size}px` : '12.9rem')};
transition: scale 0.2s;
height: 12.9rem;
width: 12.9rem;
&:hover {
scale: 1.04;
}
@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: 1600px) {
height: 13rem;
width: 13rem;
}
@media screen and (max-width: 1440px) {
height: 12.8rem;
width: 12.8rem;
}
@media screen and (max-width: 1280px) {
height: 12.6rem;
width: 12.6rem;
}
}
`
@ -77,13 +99,13 @@ export const ArrowButton = styled.button<ArrowProps>`
z-index: 3;
top: 50%;
transform: translate(-50%, -50%);
left: ${({ direction }) => (direction === 'left' ? '1.6rem' : 'calc(100% - 2.3rem)')};
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% - 2.8rem)'};
left: ${direction === 'left' ? '2rem' : 'calc(100% - 2rem)'};
${Arrow} {
width: 1.5rem;

@ -1,4 +1,3 @@
/* eslint-disable no-param-reassign */
import {
useCallback,
useEffect,
@ -6,16 +5,11 @@ import {
useState,
} from 'react'
import { useQuery } from 'react-query'
import { querieKeys, PAGES } from 'config'
import { useRouteMatch } from 'react-router-dom'
import {
MatchDto,
MatchesTimeline,
TimelineTournamentDto,
getLiveScores,
getTimelineMatches,
} from 'requests'
@ -23,7 +17,8 @@ import { Match, prepareMatches } from 'helpers'
import { useAuthStore } from 'features/AuthStore'
import { useHeaderFiltersStore } from 'features/HeaderFilters'
import { useMatchSwitchesStore } from 'features/MatchSwitches'
import { PAGES } from 'config'
export type TimelineTournamentList = Array<Omit<TimelineTournamentDto, 'matches'> & {
matches: Array<Match>,
@ -40,12 +35,10 @@ export const useTimeline = () => {
selectedSport,
setSportIds,
} = useHeaderFiltersStore()
const { isScoreHidden } = useMatchSwitchesStore()
const [isTimelineFetching, setIsTimelineFetching] = useState(true)
const [onlineUpcomingMatches, setOnlineUpcomingMatches] = useState<Array<Match>>([])
const [tournamentList, setTournamentList] = useState<TimelineTournamentList>([])
const [timeline, setTimeline] = useState<MatchesTimeline>()
const prepareMatchesDto = useCallback((matches: Array<MatchDto>) => prepareMatches(
matches,
@ -57,22 +50,21 @@ export const useTimeline = () => {
(async () => {
setIsTimelineFetching(true)
try {
const timelineFetched = await getTimelineMatches()
setTimeline(timelineFetched)
const convertedMatches = timelineFetched.online_upcoming[0].matches
const timeline = await getTimelineMatches()
const convertedMatches = timeline.online_upcoming[0].matches
const preparedMatches = prepareMatchesDto(convertedMatches)
setOnlineUpcomingMatches(preparedMatches)
setTournamentList([
...timelineFetched.favorite.map((item) => ({
...timeline.favorite.map((item) => ({
...item,
matches: prepareMatchesDto(item.matches),
})),
...timelineFetched.promo.map((item) => ({
...timeline.promo.map((item) => ({
...item,
matches: prepareMatchesDto(item.matches),
})),
...timelineFetched.others.map((item) => ({
...timeline.others.map((item) => ({
...item,
matches: prepareMatchesDto(item.matches),
})),
@ -84,55 +76,6 @@ export const useTimeline = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useQuery({
queryFn: async () => {
let isLiveMatch = false
if (timeline) {
for (const [, value] of Object.entries(timeline)) {
if (isLiveMatch) break
// eslint-disable-next-line no-loop-func
value.forEach((t) => {
const liveMatch = t.matches.find((match) => match.live)
if (liveMatch) {
isLiveMatch = true
}
})
}
}
if (!isScoreHidden && isLiveMatch) {
const scores = await getLiveScores()
tournamentList.forEach((tournament) => {
tournament.matches.forEach((match) => {
const score = scores.find((s) => (
s.match_id === match.id && s.sport_id === match.sportType
))
if (score) {
match.team1.score = score?.team1.score ?? match.team1.score
match.team1.penalty_score = score.team1.penalty_score ?? match.team1.penalty_score
match.team2.score = score?.team2.score ?? match.team2.score
match.team2.penalty_score = score.team2.penalty_score ?? match.team2.penalty_score
}
})
})
onlineUpcomingMatches.forEach((upcomingMatch) => {
const score = scores.find((s) => (
s.match_id === upcomingMatch.id && s.sport_id === upcomingMatch.sportType
))
if (score) {
upcomingMatch.team1.score = score?.team1.score ?? upcomingMatch.team1.score
upcomingMatch.team1.penalty_score = score.team1.penalty_score
?? upcomingMatch.team1.penalty_score
upcomingMatch.team2.score = score?.team2.score ?? upcomingMatch.team2.score
upcomingMatch.team2.penalty_score = score.team2.penalty_score
?? upcomingMatch.team2.penalty_score
}
})
}
},
queryKey: querieKeys.liveMatchScores,
refetchInterval: 30000,
})
const filteredTournamentsBySport = useMemo(() => {
if (isHomePage && selectedSport) {
return tournamentList.filter((t) => compareSport(t.sport_id, selectedSport))

@ -1,13 +1,10 @@
import {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react'
import isEmpty from 'lodash/isEmpty'
import { MatchesSlider } from 'features/MatchesSlider'
import { TimelineTournamentList, useTimeline } from 'features/MatchesTimeline/hooks'
import { InfiniteScroll } from 'features/InfiniteScroll'
@ -26,11 +23,7 @@ import {
TournamentSubtitleWrapper,
} from './styled'
const TOURNAMENT_LIMIT = 10
const MATCH_COUNT = 6
const PADDING = 30
const GAP_COUNT = 5
const SLIDER_ITEM_GAP = 0.9 // rem
const TOURNAMENT_LIMIT = 6
export const MatchesTimeline = () => {
const {
@ -43,25 +36,6 @@ export const MatchesTimeline = () => {
const pageRef = useRef(0)
const isLastPageRef = useRef(false)
const wrapperRef = useRef<HTMLDivElement>(null)
const [cardSize, setCardSize] = useState(0)
// вычисляем размеры плитки
useLayoutEffect(() => {
const offsetWidth = wrapperRef.current?.offsetWidth
const ulItemGapFromRem = SLIDER_ITEM_GAP * parseFloat(
getComputedStyle(document.documentElement).fontSize,
)
if (offsetWidth) {
const size = Math.round(
((offsetWidth - (ulItemGapFromRem * GAP_COUNT) - PADDING) / MATCH_COUNT),
)
setCardSize(size)
}
}, [])
const getTournaments = useCallback(() => tournamentList.slice(
pageRef.current * TOURNAMENT_LIMIT,
@ -86,29 +60,25 @@ export const MatchesTimeline = () => {
useEffect(() => {
if (tournamentList.length) {
pageRef.current = 0
isLastPageRef.current = false
tournamentList.length > 0 && setTournaments(getTournaments())
tournamentList.length && setTournaments(getTournaments())
pageRef.current = 1
}
}, [getTournaments, tournamentList])
if (isTimelineFetching) {
return <Loading><T9n t='loading' />...</Loading>
}
return (
<InfiniteScroll fullPageScroll onFetchMore={getMoreTournaments}>
<Wrapper ref={wrapperRef}>
{isTimelineFetching && <Loading><T9n t='loading' />...</Loading>}
{(!isEmpty(onlineUpcomingMatches)) && (
<Wrapper>
{onlineUpcomingMatches.length > 0 && (
<RowWrapper>
<Content>
<Tournament
size={cardSize}
isOnlineUpcoming
>
<Tournament isOnlineUpcoming>
LIVE & UPCOMING
</Tournament>
<MatchesSlider
cardSize={cardSize}
matches={onlineUpcomingMatches}
/>
<MatchesSlider matches={onlineUpcomingMatches} />
</Content>
</RowWrapper>
)}
@ -118,7 +88,7 @@ export const MatchesTimeline = () => {
tournament,
tournament_id,
}) => (
<RowWrapper key={`${tournament_id}_${sport_id}`}>
<RowWrapper key={tournament_id}>
<TournamentSubtitleWrapper>
<TournamentSubtitle
sportInfo={matches[0].sportInfo}
@ -128,20 +98,14 @@ export const MatchesTimeline = () => {
/>
</TournamentSubtitleWrapper>
<Content>
<Tournament
size={cardSize}
gradientColor={tournament.color}
>
<Tournament gradientColor={tournament.color}>
<TournamentLogo
id={tournament_id}
profileType={ProfileTypes.TOURNAMENTS}
sportType={sport_id}
/>
</Tournament>
<MatchesSlider
cardSize={cardSize}
matches={matches}
/>
<MatchesSlider matches={matches} />
</Content>
</RowWrapper>
))}

@ -12,12 +12,12 @@ export const RowWrapper = styled.div``
export const Content = styled.div`
display: flex;
`
gap: 10px;
`
export const Tournament = styled.div<{
gradientColor?: string,
isOnlineUpcoming?: boolean,
size?: number,
}>`
${({ gradientColor }) => (gradientColor
? css`background: linear-gradient(187deg, ${gradientColor} -4.49%, #000000 68.29%), #000000;`
@ -29,10 +29,10 @@ export const Tournament = styled.div<{
`}
position: relative;
height: ${({ size }) => (size ? `${size}px` : '12.9rem')};
width: ${({ size }) => (size ? `${size}px` : '12.9rem')};
height: 12.9rem;
width: 12.9rem;
flex-shrink: 0;
margin: 10px 0.93rem 10px 0px;
margin: 10px 0;
display: flex;
align-items: center;
justify-content: center;
@ -42,14 +42,32 @@ export const Tournament = styled.div<{
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;
width: 100%;
height: 100%;
object-fit: scale-down;
`
export const Loading = styled.div`

@ -9,8 +9,8 @@ import { SELECTED_API_KEY } from 'helpers/selectedApi'
import { useToggle } from 'hooks/useToggle'
import { useLocalStore } from 'hooks/useStorage'
import { removeRefreshToken, removeToken } from 'helpers'
import { removeCookie } from 'helpers/cookie'
import { removeToken } from '../../helpers'
import { removeCookie } from '../../helpers/cookie'
type FormElement = HTMLFormElement & {
api: HTMLInputElement & {
@ -37,7 +37,6 @@ export const useSystemSettings = () => {
const { api } = e.currentTarget
setSelectedApi(api.value)
removeToken()
removeRefreshToken()
removeCookie('access_token')
window.location.reload()
}

@ -1,5 +1,4 @@
export const TOKEN_KEY = 'token'
export const REFRESH_TOKEN_KEY = 'refresh_token'
export const readToken = () => (
localStorage.getItem(TOKEN_KEY)
@ -12,15 +11,3 @@ export const writeToken = (token: string) => (
export const removeToken = () => (
localStorage.removeItem(TOKEN_KEY)
)
export const removeRefreshToken = () => {
localStorage.removeItem(REFRESH_TOKEN_KEY)
}
export const writeRefreshToken = (token: string) => (
localStorage.setItem(REFRESH_TOKEN_KEY, token)
)
export const readRefreshToken = () => (
localStorage.getItem(REFRESH_TOKEN_KEY)
)

@ -18,14 +18,7 @@ export type MatchesTimeline = {
}
export const getTimelineMatches = (sportId?: number): Promise<MatchesTimeline> => {
const getTimezoneOffset = () => {
const offset = new Date().getTimezoneOffset()
if (offset === 0) return offset
return -(offset)
}
const timeZoneOffset = getTimezoneOffset()
const url = new URL(`${API_ROOT}/v1/broadcasts/timeline/${timeZoneOffset}`)
const url = new URL(`${API_ROOT}/v1/matches/timeline`)
if (sportId) {
url.searchParams.append('sport_id', `${sportId}`)

Loading…
Cancel
Save