@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"applinks": { |
||||||
|
"apps": [], |
||||||
|
"details": [{ |
||||||
|
"appID": "2X9NY5X2LZ.com.insports.ott", |
||||||
|
"paths": ["*"] |
||||||
|
}] |
||||||
|
} |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<browserconfig> |
||||||
|
<msapplication> |
||||||
|
<tile> |
||||||
|
<square70x70logo src="/mstile-70x70.png"/> |
||||||
|
<square144x144logo src="/mstile-144x144.png"/> |
||||||
|
<square150x150logo src="/mstile-150x150.png"/> |
||||||
|
<square310x310logo src="/mstile-310x310.png"/> |
||||||
|
<TileColor>#da532c</TileColor> |
||||||
|
</tile> |
||||||
|
</msapplication> |
||||||
|
</browserconfig> |
||||||
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,19 @@ |
|||||||
|
{ |
||||||
|
"name": "", |
||||||
|
"short_name": "", |
||||||
|
"icons": [ |
||||||
|
{ |
||||||
|
"src": "/android-chrome-192x192.png", |
||||||
|
"sizes": "192x192", |
||||||
|
"type": "image/png" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"src": "/android-chrome-512x512.png", |
||||||
|
"sizes": "512x512", |
||||||
|
"type": "image/png" |
||||||
|
} |
||||||
|
], |
||||||
|
"theme_color": "#ffffff", |
||||||
|
"background_color": "#ffffff", |
||||||
|
"display": "standalone" |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 229 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 908 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 329 B |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.0 KiB |
@ -1,15 +0,0 @@ |
|||||||
<!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> |
|
||||||
new Oidc.UserManager().signinSilentCallback() |
|
||||||
.catch((err) => { |
|
||||||
console.error('OIDC: silent refresh callback error', err); |
|
||||||
}); |
|
||||||
</script> |
|
||||||
</body> |
|
||||||
</html> |
|
||||||
@ -0,0 +1,128 @@ |
|||||||
|
import { |
||||||
|
useEffect, |
||||||
|
useState, |
||||||
|
RefObject, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
import { Icon } from 'features/Icon' |
||||||
|
import { useAuthStore } from 'features/AuthStore' |
||||||
|
|
||||||
|
import { |
||||||
|
AccessTimerContainer, |
||||||
|
PreviewInfo, |
||||||
|
SignInBtn, |
||||||
|
SignText, |
||||||
|
Timer, |
||||||
|
TimerContainer, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
import { SimplePopup } from '../SimplePopup' |
||||||
|
import { secondsToHms } from '../../helpers' |
||||||
|
import { getViewMatchDuration } from '../../requests/getViewMatchDuration' |
||||||
|
import { usePageParams } from '../../hooks' |
||||||
|
import { getUserInfo } from '../../requests' |
||||||
|
|
||||||
|
type AccessTimerType = { |
||||||
|
access: boolean, |
||||||
|
isFullscreen: boolean, |
||||||
|
onFullscreenClick: () => void, |
||||||
|
playing: boolean, |
||||||
|
videoRef?: RefObject<HTMLVideoElement> | null, |
||||||
|
} |
||||||
|
|
||||||
|
const ACCESS_TIME = 60 |
||||||
|
|
||||||
|
export const AccessTimer = ({ |
||||||
|
access, |
||||||
|
isFullscreen, |
||||||
|
onFullscreenClick, |
||||||
|
playing, |
||||||
|
videoRef, |
||||||
|
}: AccessTimerType) => { |
||||||
|
const { |
||||||
|
logout, |
||||||
|
setUserInfo, |
||||||
|
userInfo, |
||||||
|
} = useAuthStore() |
||||||
|
|
||||||
|
const { profileId, sportType } = usePageParams() |
||||||
|
|
||||||
|
const [timeIsFinished, setTimeIsFinished] = useState(true) |
||||||
|
const [time, setTime] = useState(ACCESS_TIME) |
||||||
|
const isTimeExpired = time <= 0 || !access |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (isTimeExpired) { |
||||||
|
document.pictureInPictureEnabled && document.exitPictureInPicture() |
||||||
|
setTimeIsFinished(true) |
||||||
|
videoRef?.current?.pause() |
||||||
|
setTime(0) |
||||||
|
if (isFullscreen) onFullscreenClick() |
||||||
|
} |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [access, playing, profileId]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
let stopWatch: ReturnType<typeof setInterval> | null = null |
||||||
|
if (playing) { |
||||||
|
stopWatch = setInterval(() => { |
||||||
|
setTime((prev) => prev - 1) |
||||||
|
}, 1000) |
||||||
|
} else { |
||||||
|
stopWatch && clearInterval(stopWatch) |
||||||
|
} |
||||||
|
return () => { |
||||||
|
stopWatch && clearInterval(stopWatch) |
||||||
|
} |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [playing, profileId]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
(async () => { |
||||||
|
setTime(ACCESS_TIME) |
||||||
|
const updatedUserInfo = await getUserInfo() |
||||||
|
setUserInfo(updatedUserInfo) |
||||||
|
const timeViewed = await getViewMatchDuration({ |
||||||
|
matchId: profileId, |
||||||
|
sportType, |
||||||
|
userId: Number(updatedUserInfo?.email), |
||||||
|
}) |
||||||
|
setTime(isTimeExpired ? 0 : (ACCESS_TIME - Number(timeViewed.duration))) |
||||||
|
})() |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [profileId]) |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
{(!userInfo || Number(userInfo?.email) < 0) && access && time > 0 |
||||||
|
? ( |
||||||
|
<AccessTimerContainer isFullscreen={isFullscreen}> |
||||||
|
<TimerContainer> |
||||||
|
<PreviewInfo> |
||||||
|
<Timer>{secondsToHms(time)} </Timer> |
||||||
|
<T9n t='game_preview' className='gamePreview' /> |
||||||
|
</PreviewInfo> |
||||||
|
<SignText> |
||||||
|
<T9n t='sign_in_full_game' /> |
||||||
|
</SignText> |
||||||
|
</TimerContainer> |
||||||
|
<SignInBtn onClick={() => logout('saveToken')}> |
||||||
|
<T9n t='sign_in' /> |
||||||
|
</SignInBtn> |
||||||
|
</AccessTimerContainer> |
||||||
|
) |
||||||
|
: ( |
||||||
|
<SimplePopup |
||||||
|
isModalOpen={timeIsFinished} |
||||||
|
withCloseButton={false} |
||||||
|
headerName='sec_60' |
||||||
|
mainText='continue_watching' |
||||||
|
buttonName='sign_in' |
||||||
|
onHandle={() => logout('saveToken')} |
||||||
|
icon={<Icon refIcon='ExclamationPoint' />} |
||||||
|
/> |
||||||
|
)} |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,124 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { isMobileDevice } from 'config' |
||||||
|
|
||||||
|
import { ButtonSolid } from 'features/Common' |
||||||
|
|
||||||
|
export const AccessTimerContainer = styled.div<{isFullscreen?: boolean}>` |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
|
||||||
|
position: absolute; |
||||||
|
left: 50%; |
||||||
|
bottom: 5.7rem; |
||||||
|
transform: translateX(-50%); |
||||||
|
background-color: #333333; |
||||||
|
border-radius: 5px; |
||||||
|
padding: 20px 50px; |
||||||
|
|
||||||
|
gap: 40px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
padding: 9px 7px 9px 15px; |
||||||
|
bottom: 6.5rem; |
||||||
|
|
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
max-width: 95%; |
||||||
|
bottom: 4rem; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
|
||||||
|
${({ isFullscreen }) => isFullscreen && css` |
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
padding: 9px 7px 9px 15px; |
||||||
|
bottom: 12rem; |
||||||
|
|
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
max-width: 95%; |
||||||
|
bottom: 6rem; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
`}
|
||||||
|
` |
||||||
|
|
||||||
|
export const SignInBtn = styled(ButtonSolid)` |
||||||
|
width: 246px; |
||||||
|
border-radius: 5px; |
||||||
|
height: 50px; |
||||||
|
font-weight: 600; |
||||||
|
font-size: 20px; |
||||||
|
white-space: nowrap; |
||||||
|
padding: 0 35px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
font-size: 12px; |
||||||
|
white-space: nowrap; |
||||||
|
padding: 0px 16px; |
||||||
|
width: 140px; |
||||||
|
height: 30px; |
||||||
|
|
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
font-size: 12px; |
||||||
|
padding: 0px 8px; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
|
||||||
|
` |
||||||
|
|
||||||
|
export const TimerContainer = styled.div` |
||||||
|
color: white; |
||||||
|
font-weight: 700; |
||||||
|
font-size: 20px; |
||||||
|
line-height: 24px; |
||||||
|
white-space: nowrap; |
||||||
|
max-width: 215px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
font-size: 12px; |
||||||
|
line-height: 28px; |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const PreviewInfo = styled.div` |
||||||
|
display: flex; |
||||||
|
line-height: 24px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
line-height: 14px; |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const Timer = styled.div` |
||||||
|
min-width: 67px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
min-width: 40px; |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const SignText = styled.span` |
||||||
|
display: block; |
||||||
|
font-size: 16px; |
||||||
|
line-height: 20px; |
||||||
|
font-weight: 400; |
||||||
|
white-space: break-spaces; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
font-size: 9px; |
||||||
|
line-height: 14px |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
import type { ReactNode } from 'react' |
||||||
|
|
||||||
|
import { T9n } from 'features/T9n' |
||||||
|
|
||||||
|
import { |
||||||
|
Header, |
||||||
|
Footer, |
||||||
|
ScBody, |
||||||
|
Modal, |
||||||
|
ScApplyButton, |
||||||
|
ScHeaderTitle, |
||||||
|
ScText, |
||||||
|
Wrapper, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
buttonName?: string, |
||||||
|
headerName?: string, |
||||||
|
icon?: ReactNode, |
||||||
|
isModalOpen: boolean, |
||||||
|
mainText: string, |
||||||
|
onHandle?: () => void, |
||||||
|
withCloseButton: boolean, |
||||||
|
} |
||||||
|
|
||||||
|
export const SimplePopup = (props: Props) => { |
||||||
|
const { |
||||||
|
buttonName, |
||||||
|
headerName, |
||||||
|
icon, |
||||||
|
isModalOpen, |
||||||
|
mainText, |
||||||
|
onHandle, |
||||||
|
withCloseButton, |
||||||
|
} = props |
||||||
|
|
||||||
|
return ( |
||||||
|
<Modal |
||||||
|
isOpen={isModalOpen} |
||||||
|
withCloseButton={withCloseButton} |
||||||
|
> |
||||||
|
<Wrapper> |
||||||
|
<Header> |
||||||
|
{icon} |
||||||
|
<ScHeaderTitle> |
||||||
|
<T9n t={headerName ?? ''} /> |
||||||
|
</ScHeaderTitle> |
||||||
|
</Header> |
||||||
|
<ScBody> |
||||||
|
<ScText> |
||||||
|
<T9n t={mainText} /> |
||||||
|
</ScText> |
||||||
|
</ScBody> |
||||||
|
{buttonName |
||||||
|
&& ( |
||||||
|
<Footer> |
||||||
|
<ScApplyButton onClick={onHandle}> |
||||||
|
<T9n t={buttonName} /> |
||||||
|
</ScApplyButton> |
||||||
|
</Footer> |
||||||
|
)} |
||||||
|
</Wrapper> |
||||||
|
</Modal> |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,146 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { isMobileDevice } from 'config' |
||||||
|
|
||||||
|
import { ModalWindow } from 'features/Modal/styled' |
||||||
|
import { |
||||||
|
ApplyButton, |
||||||
|
Body, |
||||||
|
Modal as BaseModal, |
||||||
|
HeaderTitle, |
||||||
|
} from 'features/AuthServiceApp/components/RegisterPopup/styled' |
||||||
|
import { Header as BaseHeader } from 'features/PopupComponents' |
||||||
|
import { client } from 'features/AuthServiceApp/config/clients/index' |
||||||
|
|
||||||
|
export const Modal = styled(BaseModal)` |
||||||
|
|
||||||
|
|
||||||
|
${ModalWindow} { |
||||||
|
width: 705px; |
||||||
|
height: 472px; |
||||||
|
|
||||||
|
padding: 60px 100px; |
||||||
|
min-height: auto; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
max-width: 95vw; |
||||||
|
padding: 50px 20px 80px 20px; |
||||||
|
|
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
max-height: 80vh; |
||||||
|
max-width: 95vw; |
||||||
|
padding: 24px 100px 60px 100px; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const Wrapper = styled.div` |
||||||
|
gap: 30px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
max-height: 80vh; |
||||||
|
gap: 10px; |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const ScHeaderTitle = styled(HeaderTitle)` |
||||||
|
text-align: center; |
||||||
|
font-weight: 700; |
||||||
|
font-size: 34px; |
||||||
|
line-height: 34px; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
font-size: 20px; |
||||||
|
line-height: 24px; |
||||||
|
|
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
font-size: 17px; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const ScBody = styled(Body)` |
||||||
|
padding: 16px 0 0 0; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
max-width: 491px; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const ScText = styled.span` |
||||||
|
text-align: center; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
font-size: 14px; |
||||||
|
line-height: 22px; |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const ScApplyButton = styled(ApplyButton)` |
||||||
|
width: 300px; |
||||||
|
height: 60px; |
||||||
|
margin: 0; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
font-size: 14px; |
||||||
|
margin: 20px 0; |
||||||
|
width: 293px; |
||||||
|
height: 50px; |
||||||
|
|
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
margin: 0; |
||||||
|
width: 225px; |
||||||
|
height: 44px; |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
${client.styles.popupApplyButton} |
||||||
|
` |
||||||
|
|
||||||
|
export const Header = styled(BaseHeader)` |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: center; |
||||||
|
height: auto; |
||||||
|
justify-content: center; |
||||||
|
gap: 30px; |
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
@media screen and (orientation: landscape) { |
||||||
|
gap: 10px; |
||||||
|
|
||||||
|
svg { |
||||||
|
width: 40px; |
||||||
|
height: 40px; |
||||||
|
} |
||||||
|
} |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
|
|
||||||
|
export const Footer = styled.div` |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
padding: 33px 0 65px 0; |
||||||
|
|
||||||
|
${isMobileDevice |
||||||
|
? css` |
||||||
|
padding-top: 30px; |
||||||
|
` |
||||||
|
: ''}; |
||||||
|
` |
||||||
@ -1,57 +0,0 @@ |
|||||||
import { css } from 'styled-components/macro' |
|
||||||
|
|
||||||
import { |
|
||||||
ClientConfig, |
|
||||||
ClientIds, |
|
||||||
ClientNames, |
|
||||||
} from './types' |
|
||||||
|
|
||||||
const randomHash = () => ( |
|
||||||
(Math.random() ** Math.random()) * 9999999999999999 |
|
||||||
) |
|
||||||
|
|
||||||
export const tunis: ClientConfig = { |
|
||||||
auth: { |
|
||||||
clientId: ClientIds.Tunis, |
|
||||||
metaDataUrlParams: `?hash=${randomHash()}`, |
|
||||||
}, |
|
||||||
defaultLanguage: 'fr', |
|
||||||
description: 'Live sports streaming platform. All matches playing under the auspices of Czech Republic FA. Access to full matches, various player playlists, and highlights. Free access in the Czech Republic. Available across all devices', |
|
||||||
disabledPreferences: false, |
|
||||||
name: ClientNames.Tunis, |
|
||||||
privacyLink: '/privacy-policy-and-statement', |
|
||||||
showSearch: false, |
|
||||||
styles: { |
|
||||||
background: '', |
|
||||||
homePageHeader: css` |
|
||||||
background: radial-gradient( |
|
||||||
160.34% 257.27% at -7.45% 162.22%, |
|
||||||
#2AB7AA 3.27%, |
|
||||||
#02505C 43.69%, #0B2E4D 100%); |
|
||||||
`,
|
|
||||||
logo: 'tunis-logo.svg', |
|
||||||
logoHeight: 6.3, |
|
||||||
logoLeft: 1.1, |
|
||||||
logoTop: 1.74, |
|
||||||
logoWidth: 8.25, |
|
||||||
matchLogoHeight: 3.4, |
|
||||||
matchLogoTopMargin: 0.9, |
|
||||||
matchLogoWidth: 4.5, |
|
||||||
matchPageMobileHeaderLogo: css` |
|
||||||
width: 35px; |
|
||||||
height: 25px; |
|
||||||
top: 2px; |
|
||||||
`,
|
|
||||||
mobileHeaderLogo: css` |
|
||||||
width: 48px; |
|
||||||
height: 37px; |
|
||||||
`,
|
|
||||||
userAccountLogo: css` |
|
||||||
width: 4.56rem; |
|
||||||
height: 3.488rem; |
|
||||||
`,
|
|
||||||
}, |
|
||||||
termsLink: '/terms-and-conditions?client_id=facr-ott-web', |
|
||||||
title: 'FACR.TV - The home of Czech football streaming', |
|
||||||
userAccountLinksDisabled: true, |
|
||||||
} |
|
||||||
@ -0,0 +1,58 @@ |
|||||||
|
import { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { |
||||||
|
ClientConfig, |
||||||
|
ClientIds, |
||||||
|
ClientNames, |
||||||
|
} from './types' |
||||||
|
|
||||||
|
const randomHash = () => ( |
||||||
|
(Math.random() ** Math.random()) * 9999999999999999 |
||||||
|
) |
||||||
|
|
||||||
|
export const tunisia: ClientConfig = { |
||||||
|
auth: { |
||||||
|
clientId: ClientIds.Tunisia, |
||||||
|
metaDataUrlParams: `?hash=${randomHash()}`, |
||||||
|
}, |
||||||
|
defaultLanguage: 'fr', |
||||||
|
description: '', |
||||||
|
disabledFilters: true, |
||||||
|
disabledHighlights: true, |
||||||
|
disabledPreferences: true, |
||||||
|
name: ClientNames.Tunisia, |
||||||
|
privacyLink: '/privacy-policy-and-statement?client_id=insports-ott-web', |
||||||
|
showSearch: true, |
||||||
|
styles: { |
||||||
|
background: '', |
||||||
|
homePageHeader: css` |
||||||
|
background: radial-gradient( |
||||||
|
160.34% 257.27% at -7.45% 162.22%, |
||||||
|
#2AB7AA 3.27%, |
||||||
|
#02505C 43.69%, #0B2E4D 100%); |
||||||
|
`,
|
||||||
|
logo: 'tunis-logo.svg', |
||||||
|
logoHeight: 1.922, |
||||||
|
logoLeft: 1.1, |
||||||
|
logoTop: 1.14, |
||||||
|
logoWidth: 9, |
||||||
|
matchLogoHeight: 1.922, |
||||||
|
matchLogoTopMargin: 1, |
||||||
|
matchLogoWidth: 9, |
||||||
|
matchPageMobileHeaderLogo: css` |
||||||
|
width: 100px; |
||||||
|
height: 21px; |
||||||
|
top: 5px; |
||||||
|
`,
|
||||||
|
mobileHeaderLogo: css` |
||||||
|
width: 100px; |
||||||
|
height: 21px; |
||||||
|
`,
|
||||||
|
userAccountLogo: css` |
||||||
|
width: 9rem; |
||||||
|
height: 1.922rem; |
||||||
|
`,
|
||||||
|
}, |
||||||
|
termsLink: '/terms-and-conditions?client_id=insports-ott-web', |
||||||
|
title: 'Diwan Sport - The home of Tunisian Ligue Professionnelle 1', |
||||||
|
} |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
export enum KEYBOARD_KEYS { |
||||||
|
Enter = 'Enter', |
||||||
|
} |
||||||
@ -1,25 +0,0 @@ |
|||||||
import { createInstance } from '@jonkoops/matomo-tracker-react' |
|
||||||
import { InstanceParams } from '@jonkoops/matomo-tracker-react/lib/types' |
|
||||||
|
|
||||||
import { PAGES } from 'config/pages' |
|
||||||
|
|
||||||
const getMatomoInstance = () => { |
|
||||||
let matomoInstance: InstanceParams = { |
|
||||||
siteId: 999, |
|
||||||
urlBase: 'link.to.domain', |
|
||||||
} |
|
||||||
|
|
||||||
switch (process.env.REACT_APP_CLIENT) { |
|
||||||
case 'insports': |
|
||||||
matomoInstance = { |
|
||||||
...matomoInstance, |
|
||||||
siteId: 1, |
|
||||||
urlBase: PAGES.matomoInstatBaseUrl, |
|
||||||
} |
|
||||||
break |
|
||||||
} |
|
||||||
|
|
||||||
return createInstance(matomoInstance) |
|
||||||
} |
|
||||||
|
|
||||||
export const matomoInstance = getMatomoInstance() |
|
||||||
@ -0,0 +1,22 @@ |
|||||||
|
import { ClientNames } from './clients/types' |
||||||
|
|
||||||
|
export enum PaymentSystem { |
||||||
|
PagBrazil = 'pag_brasil', |
||||||
|
Paymee = 'paymee', |
||||||
|
Paytm = 'paytm', |
||||||
|
Stripe = 'stripe' |
||||||
|
} |
||||||
|
|
||||||
|
type PaymentsType = { |
||||||
|
[key in ClientNames]: PaymentSystem |
||||||
|
} |
||||||
|
|
||||||
|
export const payments: PaymentsType = { |
||||||
|
[ClientNames.Tunisia]: PaymentSystem.Paymee, |
||||||
|
brasil: PaymentSystem.PagBrazil, |
||||||
|
[ClientNames.India]: PaymentSystem.Paytm, |
||||||
|
[ClientNames.Insports]: PaymentSystem.Stripe, |
||||||
|
[ClientNames.Instat]: PaymentSystem.Stripe, |
||||||
|
[ClientNames.Facr]: PaymentSystem.Stripe, |
||||||
|
[ClientNames.Lff]: PaymentSystem.Stripe, |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
export const querieKeys = { |
||||||
|
liveMatchScores: 'liveMatchScores', |
||||||
|
matchScore: 'matchScore', |
||||||
|
} |
||||||
@ -1,19 +1,35 @@ |
|||||||
export enum SportTypes { |
export enum SportTypes { |
||||||
FOOTBALL = 1, |
FOOTBALL = 1, |
||||||
HANDBALL = 7, |
|
||||||
HOCKEY = 2, |
HOCKEY = 2, |
||||||
BASKETBALL = 3, |
BASKETBALL = 3, |
||||||
|
TENNIS = 4, |
||||||
VOLLEYBALL = 6, |
VOLLEYBALL = 6, |
||||||
|
HANDBALL = 7, |
||||||
STREETBALL = 9, |
STREETBALL = 9, |
||||||
BOXING = 12 |
BOXING = 12, |
||||||
|
FIELD_HOCKEY = 14, |
||||||
|
FIGURE_SKATING = 15, |
||||||
|
AMERICAN_FOOTBALL = 16, |
||||||
|
FUTSAL = 17, |
||||||
|
FLOORBALL = 18, |
||||||
|
CRICKET = 19, |
||||||
|
BASEBALL = 20 |
||||||
} |
} |
||||||
|
|
||||||
export const SPORT_NAMES = { |
export const SPORT_NAMES = { |
||||||
[SportTypes.BASKETBALL]: 'basketball', |
[SportTypes.BASKETBALL]: 'basketball', |
||||||
[SportTypes.FOOTBALL]: 'football', |
[SportTypes.FOOTBALL]: 'football', |
||||||
|
[SportTypes.TENNIS]: 'tennis', |
||||||
[SportTypes.HANDBALL]: 'handball', |
[SportTypes.HANDBALL]: 'handball', |
||||||
[SportTypes.HOCKEY]: 'hockey', |
[SportTypes.HOCKEY]: 'hockey', |
||||||
[SportTypes.VOLLEYBALL]: 'volleyball', |
[SportTypes.VOLLEYBALL]: 'volleyball', |
||||||
[SportTypes.STREETBALL]: 'streetball', |
[SportTypes.STREETBALL]: 'streetball', |
||||||
[SportTypes.BOXING]: 'boxing', |
[SportTypes.BOXING]: 'boxing', |
||||||
|
[SportTypes.BASEBALL]: 'baseball', |
||||||
|
[SportTypes.FIELD_HOCKEY]: 'field_hockey', |
||||||
|
[SportTypes.FIGURE_SKATING]: 'figure_skating', |
||||||
|
[SportTypes.AMERICAN_FOOTBALL]: 'american_football', |
||||||
|
[SportTypes.FUTSAL]: 'futsal', |
||||||
|
[SportTypes.FLOORBALL]: 'floorball', |
||||||
|
[SportTypes.CRICKET]: 'cricket', |
||||||
} as const |
} as const |
||||||
|
|||||||
@ -0,0 +1,5 @@ |
|||||||
|
import { UserManager } from 'oidc-client' |
||||||
|
|
||||||
|
import { getClientSettings } from './helpers' |
||||||
|
|
||||||
|
export const userManager = new UserManager(getClientSettings()) |
||||||
@ -1,101 +0,0 @@ |
|||||||
import { |
|
||||||
useEffect, |
|
||||||
useState, |
|
||||||
MouseEvent, |
|
||||||
} from 'react' |
|
||||||
|
|
||||||
import { useHistory } from 'react-router-dom' |
|
||||||
|
|
||||||
import { ProfileTypes } from 'config' |
|
||||||
|
|
||||||
import isNumber from 'lodash/isNumber' |
|
||||||
|
|
||||||
import { useLexicsStore } from 'features/LexicsStore' |
|
||||||
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup/store' |
|
||||||
import { getProfileUrl } from 'features/ProfileLink/helpers' |
|
||||||
|
|
||||||
import { getMatchInfo } from 'requests/getMatchInfo' |
|
||||||
import { getBrazilPaymentUrl } from 'requests/getBrazilPaymentUrl' |
|
||||||
|
|
||||||
import type { Props } from './index' |
|
||||||
|
|
||||||
type ResponsePayment = { |
|
||||||
url: string, |
|
||||||
} |
|
||||||
|
|
||||||
type ResponsePaymentArray = ResponsePayment | null |
|
||||||
|
|
||||||
export const useBrazilPayment = ({ |
|
||||||
match, |
|
||||||
open, |
|
||||||
selectedPackage, |
|
||||||
setIsOpenBrasilian, |
|
||||||
}: Props) => { |
|
||||||
const history = useHistory() |
|
||||||
const { close } = useBuyMatchPopupStore() |
|
||||||
|
|
||||||
const [src, setSrc] = useState('') |
|
||||||
const [error, setError] = useState('') |
|
||||||
const { translate } = useLexicsStore() |
|
||||||
|
|
||||||
const { id, sportType } = match |
|
||||||
|
|
||||||
const { |
|
||||||
name, |
|
||||||
nameLexic, |
|
||||||
originalObject, |
|
||||||
pass, |
|
||||||
} = selectedPackage |
|
||||||
|
|
||||||
const teams = isNumber(nameLexic) ? translate(String(nameLexic)) : name |
|
||||||
const pack = translate(String(pass)) |
|
||||||
|
|
||||||
const matchLink = getProfileUrl({ |
|
||||||
id, |
|
||||||
profileType: ProfileTypes.MATCHES, |
|
||||||
sportType, |
|
||||||
}) |
|
||||||
|
|
||||||
const closePopup = async (e?: MouseEvent) => { |
|
||||||
e?.stopPropagation() |
|
||||||
setIsOpenBrasilian(false) |
|
||||||
setError('') |
|
||||||
|
|
||||||
const accessMatch = await getMatchInfo(sportType, id) |
|
||||||
if (accessMatch?.access) { |
|
||||||
close() |
|
||||||
history.push(matchLink) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
window.onmessage = function (event) { |
|
||||||
if (event.data === 'close') { |
|
||||||
closePopup() |
|
||||||
} |
|
||||||
} |
|
||||||
useEffect(() => { |
|
||||||
if (open) { |
|
||||||
(async () => { |
|
||||||
try { |
|
||||||
const json: ResponsePaymentArray = await getBrazilPaymentUrl({ |
|
||||||
action: pass === 'pass_match_access' ? 'one_payment' : 'create_subscription', |
|
||||||
item: originalObject, |
|
||||||
product_name: `${pack} ${teams}`, |
|
||||||
}) |
|
||||||
setSrc(json?.url || '') |
|
||||||
} catch (err) { |
|
||||||
setError('error_payment_unsuccessful') |
|
||||||
} |
|
||||||
})() |
|
||||||
} |
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [selectedPackage, open]) |
|
||||||
|
|
||||||
return { |
|
||||||
closePopup, |
|
||||||
error, |
|
||||||
matchLink, |
|
||||||
src, |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,173 @@ |
|||||||
|
import { |
||||||
|
MouseEvent, |
||||||
|
useCallback, |
||||||
|
useEffect, |
||||||
|
useState, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import { PAGES, ProfileTypes } from 'config' |
||||||
|
import { ClientNames } from 'config/clients/types' |
||||||
|
import { payments, PaymentSystem } from 'config/payments' |
||||||
|
|
||||||
|
import isNumber from 'lodash/isNumber' |
||||||
|
|
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
import { useBuyMatchPopupStore } from 'features/BuyMatchPopup/store' |
||||||
|
import { getProfileUrl } from 'features/ProfileLink/helpers' |
||||||
|
|
||||||
|
import { getMatchInfo } from 'requests/getMatchInfo' |
||||||
|
import { getPaymentUrl } from 'requests/getPaymentUrl' |
||||||
|
|
||||||
|
import { redirectToUrl } from 'helpers' |
||||||
|
|
||||||
|
import type { Props } from './index' |
||||||
|
|
||||||
|
type ResponsePayment = { |
||||||
|
url: string, |
||||||
|
} |
||||||
|
|
||||||
|
type ResponsePaymentArray = ResponsePayment | null |
||||||
|
|
||||||
|
export const useIframePayment = ({ |
||||||
|
match, |
||||||
|
open, |
||||||
|
paymentSystem, |
||||||
|
selectedPackage, |
||||||
|
setIsOpenIframe, |
||||||
|
}: Props) => { |
||||||
|
const { close } = useBuyMatchPopupStore() |
||||||
|
|
||||||
|
const [src, setSrc] = useState('') |
||||||
|
const [error, setError] = useState('') |
||||||
|
const [isPaymentProcessing, setIsPaymentProcessing] = useState(false) |
||||||
|
const { translate } = useLexicsStore() |
||||||
|
|
||||||
|
const { id, sportType } = match |
||||||
|
|
||||||
|
const { |
||||||
|
name, |
||||||
|
nameLexic, |
||||||
|
originalObject, |
||||||
|
pass, |
||||||
|
} = selectedPackage |
||||||
|
|
||||||
|
const teams = isNumber(nameLexic) ? translate(String(nameLexic)) : name |
||||||
|
const pack = translate(String(pass)) |
||||||
|
|
||||||
|
const matchLink = getProfileUrl({ |
||||||
|
id, |
||||||
|
profileType: ProfileTypes.MATCHES, |
||||||
|
sportType, |
||||||
|
}) |
||||||
|
|
||||||
|
const closePopup = useCallback(async (e?: MouseEvent) => { |
||||||
|
e?.stopPropagation() |
||||||
|
|
||||||
|
if (error) { |
||||||
|
setIsOpenIframe(false) |
||||||
|
setError('') |
||||||
|
} |
||||||
|
|
||||||
|
const accessMatch = await getMatchInfo(sportType, id) |
||||||
|
if (accessMatch?.access) { |
||||||
|
setIsPaymentProcessing(false) |
||||||
|
setIsOpenIframe(false) |
||||||
|
setError('') |
||||||
|
close() |
||||||
|
redirectToUrl(matchLink) |
||||||
|
} |
||||||
|
}, [close, error, id, matchLink, setIsOpenIframe, sportType]) |
||||||
|
|
||||||
|
const paymentRequest = async () => { |
||||||
|
let url_cancel |
||||||
|
let url_return |
||||||
|
let action: Parameters<typeof getPaymentUrl>[0]['action'] |
||||||
|
|
||||||
|
switch (paymentSystem) { |
||||||
|
case PaymentSystem.Paymee: |
||||||
|
url_cancel = `${window.origin}/failed-paymee` |
||||||
|
url_return = null |
||||||
|
// paymee не умеет работать с подписками
|
||||||
|
action = 'one_payment' |
||||||
|
break |
||||||
|
default: |
||||||
|
url_return = `${window.location.origin}${PAGES.thanksForSubscribe}` |
||||||
|
action = pass === 'pass_match_access' ? 'one_payment' : 'create_subscription' |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
const payment: ResponsePaymentArray = await getPaymentUrl({ |
||||||
|
action, |
||||||
|
item: originalObject, |
||||||
|
product_name: `${pack} ${teams}`, |
||||||
|
service: paymentSystem, |
||||||
|
url_cancel, |
||||||
|
url_return, |
||||||
|
}) |
||||||
|
setSrc(payment?.url || '') |
||||||
|
} |
||||||
|
|
||||||
|
if (paymentSystem === payments[ClientNames.Brasil]) { |
||||||
|
// eslint-disable-next-line
|
||||||
|
window.onmessage = function (event) { |
||||||
|
if (event.data === 'close') { |
||||||
|
closePopup() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
let interval: ReturnType<typeof setInterval> |
||||||
|
let timeout: ReturnType<typeof setTimeout> |
||||||
|
|
||||||
|
const paymentCallback = (event: MessageEvent<{ event_id: string }>) => { |
||||||
|
if (event.data.event_id === 'paymee.complete') { |
||||||
|
setIsPaymentProcessing(true) |
||||||
|
interval = setInterval(() => closePopup(), 2000) |
||||||
|
timeout = setTimeout(() => { |
||||||
|
clearInterval(interval) |
||||||
|
setIsPaymentProcessing(false) |
||||||
|
setError('failed_paymee') |
||||||
|
setSrc('') |
||||||
|
}, 60000) |
||||||
|
} |
||||||
|
} |
||||||
|
if (paymentSystem === payments[ClientNames.Tunisia]) { |
||||||
|
window.addEventListener( |
||||||
|
'message', |
||||||
|
paymentCallback, |
||||||
|
false, |
||||||
|
) |
||||||
|
} |
||||||
|
return () => { |
||||||
|
window.removeEventListener( |
||||||
|
'message', |
||||||
|
paymentCallback, |
||||||
|
false, |
||||||
|
) |
||||||
|
clearInterval(interval) |
||||||
|
clearTimeout(timeout) |
||||||
|
} |
||||||
|
}, [closePopup, paymentSystem]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (open) { |
||||||
|
(async () => { |
||||||
|
try { |
||||||
|
await paymentRequest() |
||||||
|
} catch (err) { |
||||||
|
setError('error_payment_unsuccessful') |
||||||
|
} |
||||||
|
})() |
||||||
|
} |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectedPackage, open]) |
||||||
|
|
||||||
|
return { |
||||||
|
closePopup, |
||||||
|
error, |
||||||
|
isPaymentProcessing, |
||||||
|
matchLink, |
||||||
|
src, |
||||||
|
} |
||||||
|
} |
||||||