feat(in-204): landings

pull/98/head
Rita 3 years ago
parent 6b19ad2ec3
commit 59766a9601
  1. 38838
      package-lock.json
  2. 1
      src/config/pages.tsx
  3. 4
      src/features/App/AuthenticatedApp.tsx
  4. 34
      src/features/AuthStore/hooks/useAuth.tsx
  5. 30
      src/features/MatchPage/store/hooks/index.tsx
  6. 38
      src/features/TournamentLanding/TeamLogoImg/index.tsx
  7. 16
      src/features/TournamentLanding/helpers.tsx
  8. 95
      src/features/TournamentLanding/hooks.tsx
  9. 131
      src/features/TournamentLanding/index.tsx
  10. 362
      src/features/TournamentLanding/styled.tsx
  11. 21
      src/requests/getLandingStatus.tsx
  12. 60
      src/requests/getTournamentLanding.tsx
  13. 1
      src/requests/index.tsx

38838
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -3,6 +3,7 @@ export const PAGES = {
failedPaymee: '/failed-paymee',
highlights: '/highlights',
home: '/',
landing: '/landing',
mailings: '/useraccount/mailings',
match: '/matches',
player: '/players',

@ -36,6 +36,7 @@ const MatchPage = lazy(() => import('features/MatchPage'))
const PlayerPage = lazy(() => import('features/PlayerPage'))
const TournamentPage = lazy(() => import('features/TournamentPage'))
const SystemSettings = lazy(() => import('features/SystemSettings'))
const TournamentLanding = lazy(() => import('features/TournamentLanding'))
const HighlightsPage = lazy(() => import('pages/HighlightsPage'))
const ThanksPage = lazy(() => import('pages/ThanksPage'))
@ -94,6 +95,9 @@ export const AuthenticatedApp = () => {
<Route path={`${PAGES.highlights}`}>
<HighlightsPage />
</Route>
<Route path={`${PAGES.landing}`}>
<TournamentLanding />
</Route>
<Redirect to={PAGES.home} />
</Switch>
{!isProduction && <SystemSettings />}

@ -27,18 +27,23 @@ import {
setCookie,
removeCookie,
} from 'helpers/cookie'
import { isMatchPage } from 'helpers/isMatchPage'
import { useLocalStore, useToggle } from 'hooks'
import {
useLocalStore,
useSessionStore,
useToggle,
} from 'hooks'
import { useLexicsStore } from 'features/LexicsStore'
import { queryParamStorage } from 'features/QueryParamsStorage'
import { getUserInfo, UserInfo } from 'requests/getUserInfo'
import { checkDevice, FailedResponse } from 'requests/checkDevice'
import { getTokenVirtualUser } from 'requests/getTokenVirtualUser'
// eslint-disable-next-line
import { getClientSettings, needCheckNewDeviсe } from '../helpers'
import { getTokenVirtualUser } from '../../../requests'
export const useAuth = () => {
const { changeLang, lang } = useLexicsStore()
@ -116,6 +121,23 @@ export const useAuth = () => {
validator: isString,
})
const [isFromLanding, setIsFromLanding] = useSessionStore({
clearOnUnmount: true,
defaultValue: false,
key: 'isFromLanding',
validator: isBoolean,
})
useEffect(() => {
if (isMatchPage()) setPage(history.location.pathname)
if (history.location.pathname !== page) setIsFromLanding(false)
}, [
history.location.pathname,
page,
setIsFromLanding,
setPage,
])
const getTemporaryToken = async () => {
try {
const { access_token } = await getTokenVirtualUser()
@ -246,10 +268,14 @@ export const useAuth = () => {
const auth = useMemo(() => ({
fetchUserInfo,
isFromLanding,
isNewDeviceLogin,
loadingUser,
login,
logout,
page,
setIsFromLanding,
setPage,
setSearch,
setUserInfo,
user,
@ -263,7 +289,11 @@ export const useAuth = () => {
login,
loadingUser,
setSearch,
setPage,
setUserInfo,
page,
setIsFromLanding,
isFromLanding,
])
return auth

@ -10,13 +10,17 @@ import isEmpty from 'lodash/isEmpty'
import { useAuthStore } from 'features/AuthStore'
import { PAGES } from 'config/pages'
import type { MatchInfo } from 'requests/getMatchInfo'
import { getMatchInfo } from 'requests/getMatchInfo'
import { getViewMatchDuration } from 'requests/getViewMatchDuration'
import { getLandingStatus } from 'requests/getLandingStatus'
import { usePageParams } from 'hooks/usePageParams'
import { useToggle } from 'hooks/useToggle'
import { redirectToUrl } from 'helpers/redirectToUrl'
import { parseDate } from 'helpers/parseDate'
import { useTournamentData } from './useTournamentData'
@ -32,7 +36,16 @@ export const useMatchPage = () => {
const [access, setAccess] = useState(true)
const { profileId: matchId, sportType } = usePageParams()
const { user, userInfo } = useAuthStore()
useEffect(() => {
sessionStorage.removeItem('isFromLanding')
}, [])
const {
isFromLanding,
setIsFromLanding,
user,
userInfo,
} = useAuthStore()
const {
close: hideProfileCard,
@ -87,6 +100,21 @@ export const useMatchPage = () => {
}
}))
useEffect(() => {
if (user || isFromLanding) return
getLandingStatus({ matchId, sportType })
.then(({ landing_id }) => {
setIsFromLanding(false)
if (landing_id) redirectToUrl(`${PAGES.landing}/${landing_id}`)
})
}, [
isFromLanding,
matchId,
setIsFromLanding,
sportType,
user,
])
useEffect(() => {
getMatchInfo(sportType, matchId).then(setMatchProfile)
}, [sportType, matchId])

@ -0,0 +1,38 @@
import { useState } from 'react'
import styled from 'styled-components/macro'
type LogoImgProps = {
isLogoError: boolean,
}
export const LogoImg = styled.img<LogoImgProps>`
height: 25px;
width: 25px;
margin-right: 15px;
display: ${({ isLogoError }) => (isLogoError ? 'none' : '')};
filter: grayscale(1);
:hover {
filter: none;
}
`
type Props = {
src: string,
}
export const TeamLogoImg = ({
src,
}: Props) => {
const [isLogoError, setIsImgError] = useState(false)
const onError = () => setIsImgError(true)
return (
<LogoImg
src={src}
onError={onError}
isLogoError={isLogoError}
/>
)
}

@ -0,0 +1,16 @@
import { isPast } from 'date-fns'
export const getLandingName = () => {
const splitPath = window.location.pathname.split('/')
return splitPath[2]
}
const convertToDate = (date: string) => {
const d = date.split('.')
return new Date(`${d[2]}/${d[1]}/${d[0]}`)
}
export const isPastLandingDate = (data: string) => {
const landingDate = convertToDate(data)
return isPast(landingDate)
}

@ -0,0 +1,95 @@
import {
useEffect,
useState,
} from 'react'
import size from 'lodash/size'
import includes from 'lodash/includes'
import type { TournamentLanding } from 'requests/getTournamentLanding'
import { getTournamentLanding } from 'requests/getTournamentLanding'
import { PAGES } from 'config/pages'
import { redirectToUrl } from 'helpers/redirectToUrl'
import { useLexicsStore } from 'features/LexicsStore'
import { useAuthStore } from 'features/AuthStore'
import { getLandingName, isPastLandingDate } from './helpers'
export const useTournamentLanding = () => {
const [tournamentInfo, setTournamentInfo] = useState<TournamentLanding | null>(null)
const { addLexicsConfig } = useLexicsStore()
const { page, setIsFromLanding } = useAuthStore()
const buttonLexic = tournamentInfo?.lexic_button || ''
const period = tournamentInfo?.lexic_period || ''
const title = tournamentInfo?.lexic_title || ''
const description = tournamentInfo?.lexic_description || ''
const gallery = tournamentInfo?.media.gallery
useEffect(() => {
const lexics = [buttonLexic, period, title, description]
addLexicsConfig(lexics)
}, [
addLexicsConfig,
buttonLexic,
description,
period,
title,
])
const redirectToHomePage = () => redirectToUrl(PAGES.home)
const onButtonClick = () => {
if (includes(page, 'matches')) {
setIsFromLanding(true)
redirectToUrl(page)
} else {
redirectToUrl(tournamentInfo?.url_button || '')
}
}
useEffect(() => {
getTournamentLanding(getLandingName())
.then((data) => (
isPastLandingDate(data.date_to)
? redirectToHomePage()
: setTournamentInfo(data)
))
.catch(redirectToHomePage)
}, [])
const [sliderItemId, setSliderItemId] = useState(0)
const onSliderSwitchClick = (itemId: number) => setSliderItemId(itemId)
const imgCounter = size(gallery)
useEffect(() => {
if (sliderItemId === imgCounter) {
setSliderItemId(0)
}
const getSliderInterval = setInterval(() => {
setSliderItemId(sliderItemId + 1)
}, 5000)
return () => clearInterval(getSliderInterval)
}, [imgCounter, sliderItemId])
return {
buttonColor: tournamentInfo?.button_color,
buttonLexic,
description,
gallery,
logo: tournamentInfo?.media.logo,
logoInsports: tournamentInfo?.logo_insports,
onButtonClick,
onSliderSwitchClick,
period,
redirectToHomePage,
sliderItemId,
teams: tournamentInfo?.teams,
title,
}
}

@ -0,0 +1,131 @@
import format from 'date-fns/format'
import map from 'lodash/map'
import { isMobileDevice } from 'config/userAgent'
import { T9n } from 'features/T9n'
import { useTournamentLanding } from './hooks'
import { TeamLogoImg } from './TeamLogoImg'
import {
Wrapper,
InsportsLogo,
HeaderWrapper,
Footer,
BlockWrapper,
TournamentInfo,
DateInfo,
InsportsImg,
TournamentMedia,
TournamentLogo,
TournamentTitle,
TournamentButton,
MainInfoContainer,
TournamentDescription,
TeamsLogo,
SliderContainer,
SliderWrapper,
MainLogoImg,
MainLogoWrapper,
SliderSwitch,
SliderSwitchItem,
SliderImg,
LogoBackground,
TournamentInfoContainer,
} from './styled'
const TournamentLanding = () => {
const {
buttonColor,
buttonLexic,
description,
gallery,
logo,
logoInsports,
onButtonClick,
onSliderSwitchClick,
period,
redirectToHomePage,
sliderItemId,
teams,
title,
} = useTournamentLanding()
const currentYear = format(new Date(), 'Y')
return (
<Wrapper>
<HeaderWrapper>
{isMobileDevice && <TournamentLogo src={logo} />}
<InsportsLogo onClick={redirectToHomePage} />
</HeaderWrapper>
<MainInfoContainer>
<BlockWrapper>
{
gallery
? (
<SliderWrapper>
<SliderContainer>
{map(gallery, (img, itemId) => (
<SliderImg
isAnimatedImg={itemId === sliderItemId}
key={img.id}
src={img.url}
/>
))}
</SliderContainer>
<SliderSwitch>
{map(gallery, (img, itemId) => (
<SliderSwitchItem
onClick={() => onSliderSwitchClick(itemId)}
slideOpacity={itemId === sliderItemId}
key={img.id}
/>
))}
</SliderSwitch>
</SliderWrapper>
)
: (
<MainLogoWrapper>
<LogoBackground />
<MainLogoImg src={logo} />
</MainLogoWrapper>
)
}
<TournamentInfoContainer>
<TournamentInfo>
<DateInfo t={period} />
<TournamentTitle t={title} />
<TournamentDescription t={description} />
<TournamentButton
buttonColor={buttonColor}
onClick={onButtonClick}
>
<T9n t={buttonLexic} />
</TournamentButton>
</TournamentInfo>
<TournamentMedia>
{gallery && <TournamentLogo src={logo} />}
{teams && (
<TeamsLogo>
{map(teams, (item) => (
<TeamLogoImg
key={item.id}
src={item.logo}
/>
))}
</TeamsLogo>
)}
{logoInsports && <InsportsImg src='/images/insports-logo.svg' />}
</TournamentMedia>
</TournamentInfoContainer>
</BlockWrapper>
</MainInfoContainer>
<Footer>©inSports.tv {currentYear}</Footer>
</Wrapper>
)
}
export default TournamentLanding

@ -0,0 +1,362 @@
import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent'
import { ButtonSolid } from 'features/Common'
import { Logo } from 'features/Logo'
import { T9n } from 'features/T9n'
type ButtonProps = {
buttonColor?: string,
}
type SliderSwitchProps = {
slideOpacity: boolean,
}
type SliderImgProps = {
isAnimatedImg: boolean,
}
export const Wrapper = styled.div`
width: 100vw;
height: 100vh;
color: white;
display: flex;
flex-direction: column;
`
export const HeaderWrapper = styled.div`
background-color: rgba(19, 21, 27, 0.7);
padding: 20px 0;
padding-left: 15%;
${isMobileDevice
? css`
background-color: black;
height: 40px;
padding-left: 25px;
display: flex;
align-items: center;
`
: ''};
`
export const InsportsLogo = styled(Logo)`
height: 26px;
width: 80px;
cursor: pointer;
${isMobileDevice
? css`
width: 57px;
height: 18px;
`
: ''};
`
export const MainInfoContainer = styled.div`
height: 100%;
${isMobileDevice
? css`
overflow: scroll;
position: relative;
`
: ''};
`
export const BlockWrapper = styled.div`
height: 100%;
display: flex;
align-items: center;
${isMobileDevice
? css`
display: block;
@media screen and (orientation: landscape){
height: auto;
}
`
: ''};
`
export const SliderWrapper = styled.div`
position: relative;
width: 50%;
margin-right: 1%;
height: 100%;
${isMobileDevice
? css`
height: 55%;
width: 100%;
`
: ''};
`
export const MainLogoWrapper = styled.div`
display: flex;
justify-content: center;
width: 50%;
position: relative;
align-items: center;
${isMobileDevice
? css`
height: 55%;
width: 100%;
align-items: flex-start;
padding-top: 25px;
`
: ''};
`
export const MainLogoImg = styled.img`
width: 35%;
height: 35%;
position: relative;
${isMobileDevice
? css`
height: 160px;
width: 160px;
`
: ''};
`
export const LogoBackground = styled.div`
background: #294FC4;
width: 65%;
opacity: 0.7;
filter: blur(104.135px);
height: 50%;
position: absolute;
left: 50%;
transform: translateX(-50%);
${isMobileDevice
? css`
opacity: 0.8;
filter: blur(44.1346px);
width: 100%;
height: 35%;
top: 10%;
`
: ''};
`
export const SliderContainer = styled.div`
height: 100%;
position: relative;
overflow: hidden;
${isMobileDevice
? css`
:before {
content: '';
z-index: 10;
width: 100%;
height: 100%;
position: absolute;
background: linear-gradient(0deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 30%);
}
`
: ''};
`
export const SliderImg = styled.img<SliderImgProps>`
height: 100%;
width: 100%;
opacity: ${({ isAnimatedImg }) => (isAnimatedImg ? '1' : '0')};
position: absolute;
animation-name: ${({ isAnimatedImg }) => (isAnimatedImg ? 'sliderAnimation' : '')};
animation-iteration-count: 1;
animation-timing-function: ease-out;
animation-duration: 5s;
@keyframes sliderAnimation {
0% {
transform: scale(1);
}
100% {
transform: scale(1.1);
}
`
export const SliderSwitch = styled.div`
position: absolute;
display: flex;
justify-content: center;
left: 50%;
top: 90%;
width: 100%;
transform: translateX(-50%);
${isMobileDevice
? css`
display: none;
`
: ''};
`
export const SliderSwitchItem = styled.div<SliderSwitchProps>`
width: 10%;
height: 4px;
border-radius: 2px;
background-color: white;
opacity: ${({ slideOpacity }) => (slideOpacity ? '1' : '.3')};;
margin-right: 10px;
cursor: pointer;
transition: .7s;
`
export const TournamentInfoContainer = styled.div`
width: 50%;
display: flex;
flex-direction: column;
justify-content: space-between;
${isMobileDevice
? css`
position: absolute;
width: 100%;
top: 40%;
padding: 0 25px;
z-index: 100;
@media screen and (orientation: landscape){
padding-top: 0;
}
`
: css`
height: 100%;
`};
`
export const TournamentInfo = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
margin-top: ${(isMobileDevice ? 'none' : '90px')};
`
export const DateInfo = styled(T9n)`
text-transform: uppercase;
background-color: rgba(0, 0, 0, 0.4);
padding: 8px 25px;
color: #B9B9B9;
width: fit-content;
border-radius: 5px;
font-size: 13px;
font-weight: 600;
${isMobileDevice
? css`
font-size: 10px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.7);
padding: 0.7em 2.5rem;`
: ''};
`
export const TournamentTitle = styled(T9n)`
font-weight: 700;
font-size: 50px;
margin-top: 50px;
${isMobileDevice
? css`
font-size: 24px;
margin: 15px 0 0;
`
: css`
width: 50%;
`};
`
export const TournamentButton = styled(ButtonSolid)<ButtonProps>`
width: 320px;
height: fit-content;
font-size: 24px;
font-weight: 600;
border-radius: 5px;
margin-bottom: 90px;
padding: 20px 0;
background-color: ${({ buttonColor }) => (buttonColor ? `${buttonColor}` : '#294FC3')};
${isMobileDevice
? css`
width: 100%;
border-radius: 10px;
font-size: 17px;
padding: 20px 50px;
margin-bottom: 0;
`
: ''};
`
export const TournamentDescription = styled(T9n)`
max-width: 400px;
margin: 50px 0;
font-size: 17px;
${isMobileDevice
? css`
font-size: 12px;
margin: 30px 0;
`
: ''};
`
export const TournamentMedia = styled.div`
display: flex;
align-items: center;
height: 130px;
margin-bottom: 25px;
${isMobileDevice
? css`
display: none;
`
: ''};
`
export const TournamentLogo = styled.img`
${isMobileDevice
? css`
margin-right: 10px;
height: 26px;
`
: css`
margin-right: 50px;
height: 100%;
`};
`
export const TeamsLogo = styled.div`
width: 400px;
height: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
`
export const InsportsImg = styled.img`
height: 200px;
width: 200px;
`
export const Footer = styled.div`
font-size: 14px;
background-color: black;
padding: 16px 0;
padding-left: 15%;
opacity: .5;
${isMobileDevice
? css`
display: none;
`
: ''};
`

@ -0,0 +1,21 @@
import { API_ROOT, SportTypes } from 'config'
import { callApi } from 'helpers'
type Args = {
matchId: number,
sportType: SportTypes,
}
export const getLandingStatus = async ({
matchId,
sportType,
}: Args): Promise<{landing_id: number | null}> => {
const config = {
method: 'GET',
}
return callApi({
config,
url: `${API_ROOT}/v1/landings/${sportType}/${matchId}/status`,
})
}

@ -0,0 +1,60 @@
import { API_ROOT } from 'config'
import { callApi } from 'helpers'
type Tournaments = {
season: string,
season_id: number,
sport_eng: string,
sport_id: number,
sport_rus: string,
tournament_eng: string,
tournament_id: number,
tournament_rus: string,
}
type Teams = {
id: number,
logo: string,
name_eng: string,
name_rus: string,
sport_id: number,
}
type Gallery = {
id: string,
url: string,
}
export type TournamentLanding = {
button_color?: string,
date_from: string,
date_to: string,
id: number,
lexic_button?: number,
lexic_description?: number,
lexic_period?: number,
lexic_title?: number,
logo_insports: boolean,
logo_team: boolean,
media: {
gallery: Array<Gallery>,
logo: string,
},
name: string,
teams: Array<Teams>,
tournaments: Array<Tournaments>,
url_button?: string,
}
export const getTournamentLanding = async (
landingName: number | string,
): Promise<TournamentLanding> => {
const config = {
method: 'GET',
}
return callApi({
config,
url: `${API_ROOT}/v1/landings/${landingName}`,
})
}

@ -8,6 +8,7 @@ export * from './getUserSportFavs'
export * from './modifyUserSportFavs'
export * from './getSportTournaments'
export * from './getTournamentInfo'
export * from './getTournamentLanding'
export * from './getTeamInfo'
export * from './getUserInfo'
export * from './getMatchInfo'

Loading…
Cancel
Save