feat(#175): access to platform without authorization
parent
e3f4f737b2
commit
20d5c3eae4
@ -0,0 +1,124 @@ |
||||
import { useEffect, useState } 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, |
||||
onPause: () => void, |
||||
playing: boolean, |
||||
} |
||||
|
||||
const ACCESS_TIME = 60 |
||||
|
||||
export const AccessTimer = ({ |
||||
access, |
||||
isFullscreen, |
||||
onFullscreenClick, |
||||
onPause, |
||||
playing, |
||||
}: 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) |
||||
onPause() |
||||
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/userAgent' |
||||
|
||||
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 { 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,150 @@ |
||||
import styled, { css } from 'styled-components/macro' |
||||
|
||||
import { isMobileDevice } from 'config/userAgent' |
||||
|
||||
import { ModalWindow } from 'features/Modal/styled' |
||||
import { |
||||
ApplyButton, |
||||
Body, |
||||
Modal as BaseModal, |
||||
HeaderTitle, |
||||
} from 'features/AuthServiceApp/components/RegisterPopup/styled' |
||||
|
||||
import { client } from 'features/AuthServiceApp/config/clients/index' |
||||
import { Header as BaseHeader } from '../../features/PopupComponents' |
||||
|
||||
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; |
||||
|
||||
@media screen and (orientation: landscape) { |
||||
} |
||||
` |
||||
: ''}; |
||||
` |
||||
@ -0,0 +1,30 @@ |
||||
export const ExclamationPoint = (): JSX.Element => ( |
||||
<svg |
||||
xmlns='http://www.w3.org/2000/svg' |
||||
width='88' |
||||
height='88' |
||||
fill='none' |
||||
viewBox='0 0 88 88' |
||||
> |
||||
<circle cx='44' cy='44' r='44' fill='#fff' /> |
||||
<rect |
||||
width='3' |
||||
height='30' |
||||
x='45' |
||||
y='56' |
||||
fill='#000' |
||||
rx='1.5' |
||||
transform='rotate(-180 45 56)' |
||||
/> |
||||
<rect |
||||
width='3' |
||||
height='30' |
||||
x='45' |
||||
y='56' |
||||
fill='#000' |
||||
rx='1.5' |
||||
transform='rotate(-180 45 56)' |
||||
/> |
||||
<circle cx='43.5' cy='60.5' r='2.5' fill='#000' /> |
||||
</svg> |
||||
) |
||||
@ -0,0 +1,16 @@ |
||||
import { AUTH_SERVICE } from '../config/routes' |
||||
import { client } from '../config/clients' |
||||
|
||||
export const getTokenVirtualUser = async () => { |
||||
const url = `${AUTH_SERVICE}/v1/user/create?client_id=${client.auth.clientId}` |
||||
|
||||
const config = { |
||||
method: 'POST', |
||||
} |
||||
|
||||
const response = await fetch(url, config) |
||||
|
||||
const body = await response.json() |
||||
|
||||
return body |
||||
} |
||||
@ -0,0 +1,43 @@ |
||||
import { |
||||
DATA_URL, |
||||
PROCEDURES, |
||||
SportTypes, |
||||
} from 'config' |
||||
|
||||
import { callApi } from 'helpers' |
||||
|
||||
const proc = PROCEDURES.get_view_user_match |
||||
|
||||
type ResponseType = { |
||||
duration?: number, |
||||
error?: string, |
||||
status: 1 | 2, |
||||
} |
||||
|
||||
type ViewMatchDurationType = { |
||||
matchId: number, |
||||
sportType: SportTypes, |
||||
userId: number, |
||||
} |
||||
|
||||
export const getViewMatchDuration = ({ |
||||
matchId, |
||||
sportType, |
||||
userId, |
||||
}: ViewMatchDurationType): Promise<ResponseType> => { |
||||
const config = { |
||||
body: { |
||||
params: { |
||||
_p_match_id: matchId, |
||||
_p_sport_id: sportType, |
||||
_p_user_id: userId, |
||||
}, |
||||
proc, |
||||
}, |
||||
} |
||||
|
||||
return callApi({ |
||||
config, |
||||
url: DATA_URL, |
||||
}) |
||||
} |
||||
Loading…
Reference in new issue