Compare commits

...

3 Commits

Author SHA1 Message Date
Rakov a740a79ef0 feat(#789): share highlight 2 years ago
Rakov 24dc9cbf57 feat(#787): added views peaks 2 years ago
Rakov 55e15e2b08 feat(rustat): rustat client_id 2 years ago
  1. 55
      .drone.yml
  2. 5
      public/images/share.svg
  3. 3
      src/config/clients/index.tsx
  4. 51
      src/config/clients/rustat.tsx
  5. 8
      src/config/clients/types.tsx
  6. 2
      src/config/env.tsx
  7. 1
      src/config/lexics/indexLexics.tsx
  8. 1
      src/config/payments.tsx
  9. 2
      src/features/AuthServiceApp/config/clients/index.tsx
  10. 42
      src/features/AuthServiceApp/config/clients/rustat.tsx
  11. 3
      src/features/AuthStore/helpers.tsx
  12. 4
      src/features/Icon/index.tsx
  13. 3
      src/features/MatchPage/store/hooks/index.tsx
  14. 22
      src/features/MatchPage/store/hooks/useViewsPeaks.tsx
  15. 69
      src/features/MatchSidePlaylists/components/MatchPlaylists/index.tsx
  16. 3
      src/features/MatchSidePlaylists/components/PlayButton/index.tsx
  17. 32
      src/features/StreamPlayer/components/Peaks/index.tsx
  18. 15
      src/features/StreamPlayer/components/Peaks/styled.tsx
  19. 8
      src/features/StreamPlayer/components/ProgressBar/index.tsx
  20. 1
      src/libs/index.ts
  21. 14
      src/libs/objects/Share.tsx
  22. 2
      src/react-app-env.d.ts
  23. 23
      src/requests/getViewPeaks.tsx
  24. 1
      src/requests/index.tsx
  25. 20
      src/requests/share/getHighlight.tsx
  26. 1
      src/requests/share/index.ts

@ -1059,4 +1059,57 @@ steps:
- aws s3 sync build_insport_live s3://insports-live --delete
- aws cloudfront create-invalidation --distribution-id E1LBC88VYP6XVB --paths "/*"
depends_on:
- make-insport-live
- make-insport-live
---
kind: pipeline
type: docker
name: deploy demo.insports.tv
concurrency:
limit: 1
platform:
os: linux
arch: amd64
trigger:
ref:
- refs/heads/demo.insports.tv
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-demo-rustat
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- make rustat-prod
depends_on:
- npm-install
- name: deploy-demo-rustat
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_rustat s3://insports-tv-demo --delete
- aws cloudfront create-invalidation --distribution-id ERAVP3O9V0PWR --paths "/*"
depends_on:
- make-demo-rustat

@ -0,0 +1,5 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15 17.8332C14.3056 17.8332 13.7153 17.5901 13.2292 17.104C12.7431 16.6179 12.5 16.0276 12.5 15.3332C12.5 15.2359 12.5069 15.1351 12.5208 15.0307C12.5347 14.9262 12.5556 14.8326 12.5833 14.7498L6.70833 11.3332C6.47222 11.5415 6.20833 11.7048 5.91667 11.8232C5.625 11.9415 5.31944 12.0004 5 11.9998C4.30556 11.9998 3.71528 11.7568 3.22917 11.2707C2.74306 10.7846 2.5 10.1943 2.5 9.49984C2.5 8.80539 2.74306 8.21512 3.22917 7.729C3.71528 7.24289 4.30556 6.99984 5 6.99984C5.31944 6.99984 5.625 7.059 5.91667 7.17734C6.20833 7.29567 6.47222 7.45873 6.70833 7.6665L12.5833 4.24984C12.5556 4.1665 12.5347 4.07289 12.5208 3.969C12.5069 3.86511 12.5 3.76428 12.5 3.6665C12.5 2.97206 12.7431 2.38178 13.2292 1.89567C13.7153 1.40956 14.3056 1.1665 15 1.1665C15.6944 1.1665 16.2847 1.40956 16.7708 1.89567C17.2569 2.38178 17.5 2.97206 17.5 3.6665C17.5 4.36095 17.2569 4.95123 16.7708 5.43734C16.2847 5.92345 15.6944 6.1665 15 6.1665C14.6806 6.1665 14.375 6.10761 14.0833 5.98984C13.7917 5.87206 13.5278 5.70873 13.2917 5.49984L7.41667 8.9165C7.44444 8.99984 7.46528 9.09373 7.47917 9.19817C7.49306 9.30261 7.5 9.40317 7.5 9.49984C7.5 9.59706 7.49306 9.69789 7.47917 9.80234C7.46528 9.90678 7.44444 10.0004 7.41667 10.0832L13.2917 13.4998C13.5278 13.2915 13.7917 13.1284 14.0833 13.0107C14.375 12.8929 14.6806 12.8337 15 12.8332C15.6944 12.8332 16.2847 13.0762 16.7708 13.5623C17.2569 14.0484 17.5 14.6387 17.5 15.3332C17.5 16.0276 17.2569 16.6179 16.7708 17.104C16.2847 17.5901 15.6944 17.8332 15 17.8332Z"
fill="white" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -7,6 +7,7 @@ import { insports } from './insports'
import { india } from './india'
import { tunisia } from './tunisia'
import { fqtv } from './fqtv'
import { rustat } from './rustat'
export const currentClient = process.env.REACT_APP_CLIENT || 'insports'
@ -17,6 +18,7 @@ export const isFacrClient = currentClient === 'facr'
export const isIndiaClient = currentClient === 'india'
export const isTunisClient = currentClient === 'tunisia'
export const isFqtvClient = currentClient === 'fqtv'
export const isRustatClient = currentClient === 'rustat'
const clients = {
facr,
@ -25,6 +27,7 @@ const clients = {
insports,
instat,
lff,
rustat,
tunisia,
}

@ -0,0 +1,51 @@
import { css } from 'styled-components/macro'
import {
ClientConfig,
ClientIds,
ClientNames,
} from './types'
export const rustat: ClientConfig = {
auth: {
clientId: ClientIds.Rustat,
},
currencyBadge: {
color: '#333333',
secondColor: 'rgba(255, 255, 255, 0.7)',
sign: 'Dollar',
},
defaultLanguage: 'en',
description: 'Live sports streaming platform. Football, basketball, ice hockey and more. Access to various player playlists and game highlights. Multiple subscription options. Available across all devices.',
disabledPreferences: true,
host: 'demo.insports.tv',
name: ClientNames.Rustat,
privacyLink: '/privacy-policy-and-statement?client_id=insports-ott-web',
showSearch: true,
showSmartBanner: true,
styles: {
background: 'background-image: url(/images/Checker.png);',
logo: 'insports-logo.svg',
logoHeight: 2.465,
logoLeft: 1.7,
logoTop: 1.5,
logoWidth: 6.37,
matchLogoHeight: 2.465,
matchLogoWidth: 6.37,
matchPageMobileHeaderLogo: css`
width: 90px;
height: 27px;
top: 0;
`,
mobileHeaderLogo: css`
width: 77px;
height: 24px;
`,
userAccountLogo: css`
width: 6.37rem;
height: 2.465rem;
`,
},
termsLink: '/terms-and-conditions?client_id=insports-ott-web',
title: 'InSports TV - The Home of Sports Streaming',
}

@ -12,7 +12,8 @@ export enum ClientIds {
Insports = 'insports-ott-web',
Instat = 'ott-web',
Lff = 'lff-ott-web',
Tunisia = 'tunisia-ott-web',
Rustat = 'rustat-ott-web',
Tunisia = 'tunisia-ott-web'
}
export enum ClientNames {
@ -22,8 +23,9 @@ export enum ClientNames {
India = 'india', // Индия
Insports = 'insports', // Глобал
Instat = 'instat', // Глобал
Lff = 'lff', // Латвия
Tunisia = 'tunisia' // Тунис
Lff = 'lff', // Тунис
Rustat = 'rustat', // Латвия
Tunisia = 'tunisia' // Россия
}
export type ClientConfig = {

@ -8,7 +8,7 @@ export const isValidEnv = (value: string): value is ENVType => (
Boolean(value) && includes(apis, value)
)
export const ENV = process.env.REACT_APP_ENV || 'staging'
export const ENV = process.env.REACT_APP_ENV || 'production'
export const isAvailable = true

@ -198,6 +198,7 @@ export const indexLexics = {
kg: 652,
kickoff_in: 13027,
likes: 16628,
link_copied_to_clipboard: 20305,
live: 13024,
loading: 3527,
logout: 4306,

@ -26,4 +26,5 @@ export const payments: PaymentsType = {
[ClientNames.Facr]: PaymentSystem.Stripe,
[ClientNames.Lff]: PaymentSystem.Stripe,
[ClientNames.Fqtv]: PaymentSystem.Stripe,
[ClientNames.Rustat]: PaymentSystem.Stripe,
}

@ -7,6 +7,7 @@ import { lff } from './lff'
import { india } from './india'
import { tunisia } from './tunisia'
import { fqtv } from './fqtv'
import { rustat } from './rustat'
export const clients = {
[ClientIds.Facr]: facr,
@ -16,6 +17,7 @@ export const clients = {
[ClientIds.Insports]: insports,
[ClientIds.India]: india,
[ClientIds.Tunisia]: tunisia,
[ClientIds.Rustat]: rustat,
}
const params = new URLSearchParams(window.location.search)

@ -0,0 +1,42 @@
import { css } from 'styled-components'
import { rustat as platformRustat } from 'config/clients/rustat'
import { isMobileDevice } from 'config/userAgent'
import { Background } from 'features/Background'
import type { ClientConfig } from './types'
export const rustat: ClientConfig = {
...platformRustat,
background: Background,
styles: {
centerBlock: css`
margin-top: 9.15rem;
${isMobileDevice ? css`
margin-top: 107px;
@media screen and (orientation: landscape) {
width: 290px;
margin: auto;
}
` : ''};
`,
logo: css`
background-image: url(/images/insports-logo.svg);
background-position: center;
height: 85px;
width: 275px;
margin-bottom: 1.82rem;
${isMobileDevice ? css`
margin-bottom: 15px;
width: 165px;
height: 50px;
@media screen and (orientation: landscape){
width: 92px;
height: 22px;
}
` : ''}
`,
},
}

@ -29,6 +29,8 @@ export const getClientNameByRedirectUri = () => {
return 'diwan.insports'
case ClientNames.Fqtv:
return 'fqtv.insports'
case ClientNames.Rustat:
return 'demo.insports'
case ClientNames.Facr:
return ClientNames.Facr
case ClientNames.Instat:
@ -43,6 +45,7 @@ const clientsForRedirect = {
fqtv: ClientNames.Fqtv,
india: ClientNames.India,
lff: ClientNames.Lff,
rustat: ClientNames.Rustat,
tunisia: ClientNames.Tunisia,
}

@ -1,11 +1,11 @@
import React, { CSSProperties } from 'react'
import React, { CSSProperties, SyntheticEvent } from 'react'
import * as icons from '../../libs/index'
export type IconProps = {
className?: string,
color?: string,
direction?: number,
onClick?: () => void,
onClick?: (e: SyntheticEvent) => void,
refIcon: keyof typeof icons | string,
size?: number | string,
styles?: CSSProperties,

@ -39,6 +39,7 @@ import { useTeamsStats } from './useTeamsStats'
import { useStatsTab } from './useStatsTab'
import { usePlayersStats } from './usePlayersStats'
import { useLexicsStore } from '../../../LexicsStore'
import { useViewsPeaks } from './useViewsPeaks'
type PlayingData = {
player: {
@ -89,6 +90,7 @@ export const useMatchPage = () => {
const { suffix } = useLexicsStore()
const { profileId: matchId, sportType } = usePageParams()
const { peaks } = useViewsPeaks()
const { ads } = useAds({
matchId,
@ -443,6 +445,7 @@ export const useMatchPage = () => {
likeImage,
likeToggle,
matchPlaylists,
peaks,
playEpisodes,
playNextEpisode: isStatsPlaylist ? playStatsNextEpisode : playNextEpisode,
playStatsEpisodes,

@ -0,0 +1,22 @@
import { usePageParams } from 'hooks'
import { useEffect, useState } from 'react'
import { Peak, getViewPeaks } from 'requests/getViewPeaks'
export const useViewsPeaks = () => {
const { profileId, sportType } = usePageParams()
const [peaks, setPeaks] = useState<Array<Peak>>([])
useEffect(() => {
(async () => {
const res = await getViewPeaks({ match_id: profileId, sport_id: sportType })
if (res) {
setPeaks(res)
}
})()
}, [profileId, sportType])
return (
{ peaks }
)
}

@ -1,4 +1,4 @@
import type { ForwardedRef } from 'react'
import type { ForwardedRef, SyntheticEvent } from 'react'
import { forwardRef } from 'react'
import styled, { css } from 'styled-components/macro'
@ -19,9 +19,18 @@ import { isEqual } from 'features/MatchSidePlaylists/helpers'
import { T9n } from 'features/T9n'
import { useMatchPageStore } from 'features/MatchPage/store'
import { useLexicsStore } from 'features/LexicsStore'
import { Icon } from 'features/Icon'
import { Loader } from 'features/Loader'
import { MATCH_ADS } from 'components/Ads/types'
import { getShareHighlight } from 'requests'
import {
usePageParams,
useRequest,
useToggle,
} from 'hooks'
import { PlayButton } from '../PlayButton'
import { MatchDownloadButton } from '../MatchDownloadButton'
@ -59,6 +68,24 @@ export const Item = styled.li`
}
`
const ShareWrapper = styled.div`
display: flex;
position: absolute;
right: 7%;
top: 55%;
transform: translateY(-50%);
`
const Tooltip = styled.div`
position: absolute;
bottom: 100%;
right: -50%;
background-color: #FFFFFF;
white-space: nowrap;
font-size: 14px;
padding: 10px;
border-radius: 6px;
`
export const MatchPlaylists = forwardRef(
(
{
@ -71,6 +98,16 @@ export const MatchPlaylists = forwardRef(
) => {
const { ads, setEpisodeInfo } = useMatchPageStore()
const { translate } = useLexicsStore()
const { profileId, sportType } = usePageParams()
const {
isFetching: isShareHighlightFetching,
request: shareHighLightRequest,
} = useRequest(getShareHighlight)
const {
close,
isOpen,
open,
} = useToggle()
const handleButtonClick = (playlist: MatchPlaylistOption) => {
onSelect?.(playlist)
@ -82,6 +119,18 @@ export const MatchPlaylists = forwardRef(
})
}
const handleShareClick = async (e: SyntheticEvent) => {
e.stopPropagation()
const res = await shareHighLightRequest(sportType, profileId)
if (res) {
open()
navigator.clipboard.writeText(res.url)
setTimeout(() => {
close()
}, 2000)
}
}
return (
<List
ref={ref}
@ -99,6 +148,24 @@ export const MatchPlaylists = forwardRef(
live={live}
disabled={playlist.id !== FULL_GAME_KEY && isEmpty(playlist.episodes)}
onClick={() => handleButtonClick(playlist)}
shareContent={playlist.id === 'highlights' && (
<ShareWrapper>
{isOpen && (
<Tooltip>
<T9n t='link_copied_to_clipboard' />
</Tooltip>
)}
{isShareHighlightFetching
? <Loader color='white' diameter={0.3} />
: (
<Icon
onClick={handleShareClick}
refIcon='Share'
styles={{ cursor: 'pointer', display: 'flex' }}
/>
)}
</ShareWrapper>
)}
>
<T9n t={playlist.lexic} />
</PlayButton>

@ -19,6 +19,7 @@ type Props = {
leftContent?: ReactNode,
live?: boolean,
onClick: () => void,
shareContent?: ReactNode,
}
type TLiveBtn = {
@ -47,6 +48,7 @@ export const PlayButton = ({
leftContent,
live,
onClick,
shareContent,
}: Props) => (
<Button
className={className}
@ -67,5 +69,6 @@ export const PlayButton = ({
)
: <Duration>{secondsToHms(duration)}</Duration>
)}
{shareContent}
</Button>
)

@ -0,0 +1,32 @@
import { useMatchPageStore } from 'features/MatchPage/store'
import { memo, useMemo } from 'react'
import {
PeakContainer,
PeaksList,
} from './styled'
interface Props {
duration: number,
}
export const Peaks: React.FC<Props> = memo(({ duration }) => {
const { peaks } = useMatchPageStore()
const peaksPrepared = useMemo(() => peaks.map((p) => (
{
...p,
left: p.s / (duration / 1000) * 100,
width: (p.e - p.s) / (duration / 1000) * 100,
}
// eslint-disable-next-line react-hooks/exhaustive-deps
)), [peaks.length, duration])
return (
<PeaksList>
{peaksPrepared.map((p) => (
<PeakContainer style={{ left: `${p.left}%`, width: `${p.width}%` }} />
))}
</PeaksList>
)
})

@ -0,0 +1,15 @@
import styled from 'styled-components/macro'
export const PeaksList = styled.div`
width: 100%;
height: 100%;
display: flex;
position: absolute;
z-index: 3;
`
export const PeakContainer = styled.div`
position: relative;
height: 100%;
background-color: #5CDD86;
`

@ -2,13 +2,18 @@ import { useSlider } from 'features/StreamPlayer/hooks/useSlider'
import { TimeTooltip } from 'features/StreamPlayer/components/TimeTooltip'
import { Scrubber, ScrubberContainer } from 'features/StreamPlayer/components/ProgressBar/styled'
import { Peaks } from 'features/StreamPlayer/components/Peaks'
import { Chapters } from '../Chapters'
import type { Props } from './hooks'
import { useProgressBar } from './hooks'
import { ProgressBarList } from './styled'
export const ProgressBar = (props: Props) => {
const { isScrubberVisible, onPlayedProgressChange } = props
const {
duration,
isScrubberVisible,
onPlayedProgressChange,
} = props
const progressBarRef = useSlider({ onChange: onPlayedProgressChange })
const {
calculatedChapters,
@ -19,6 +24,7 @@ export const ProgressBar = (props: Props) => {
<ProgressBarList
ref={progressBarRef}
>
<Peaks duration={duration} />
<Chapters chapters={calculatedChapters} />
{isScrubberVisible && (
<ScrubberContainer>

@ -15,3 +15,4 @@ export { PoweredByInstat } from './objects/PoweredByInstat'
export { PoweredByInsports } from './objects/PoweredByInsports'
export { Info } from './objects/Info'
export { Rupee } from './objects/Rupee'
export { Share } from './objects/Share'

@ -0,0 +1,14 @@
export const Share = () => (
<svg
width='20'
height='21'
viewBox='0 0 20 21'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M15 17.8332C14.3056 17.8332 13.7153 17.5901 13.2292 17.104C12.7431 16.6179 12.5 16.0276 12.5 15.3332C12.5 15.2359 12.5069 15.1351 12.5208 15.0307C12.5347 14.9262 12.5556 14.8326 12.5833 14.7498L6.70833 11.3332C6.47222 11.5415 6.20833 11.7048 5.91667 11.8232C5.625 11.9415 5.31944 12.0004 5 11.9998C4.30556 11.9998 3.71528 11.7568 3.22917 11.2707C2.74306 10.7846 2.5 10.1943 2.5 9.49984C2.5 8.80539 2.74306 8.21512 3.22917 7.729C3.71528 7.24289 4.30556 6.99984 5 6.99984C5.31944 6.99984 5.625 7.059 5.91667 7.17734C6.20833 7.29567 6.47222 7.45873 6.70833 7.6665L12.5833 4.24984C12.5556 4.1665 12.5347 4.07289 12.5208 3.969C12.5069 3.86511 12.5 3.76428 12.5 3.6665C12.5 2.97206 12.7431 2.38178 13.2292 1.89567C13.7153 1.40956 14.3056 1.1665 15 1.1665C15.6944 1.1665 16.2847 1.40956 16.7708 1.89567C17.2569 2.38178 17.5 2.97206 17.5 3.6665C17.5 4.36095 17.2569 4.95123 16.7708 5.43734C16.2847 5.92345 15.6944 6.1665 15 6.1665C14.6806 6.1665 14.375 6.10761 14.0833 5.98984C13.7917 5.87206 13.5278 5.70873 13.2917 5.49984L7.41667 8.9165C7.44444 8.99984 7.46528 9.09373 7.47917 9.19817C7.49306 9.30261 7.5 9.40317 7.5 9.49984C7.5 9.59706 7.49306 9.69789 7.47917 9.80234C7.46528 9.90678 7.44444 10.0004 7.41667 10.0832L13.2917 13.4998C13.5278 13.2915 13.7917 13.1284 14.0833 13.0107C14.375 12.8929 14.6806 12.8337 15 12.8332C15.6944 12.8332 16.2847 13.0762 16.7708 13.5623C17.2569 14.0484 17.5 14.6387 17.5 15.3332C17.5 16.0276 17.2569 16.6179 16.7708 17.104C16.2847 17.5901 15.6944 17.8332 15 17.8332Z'
fill='white'
/>
</svg>
)

@ -3,7 +3,7 @@
declare namespace NodeJS {
export interface ProcessEnv {
REACT_APP_CLIENT: 'instat' | 'facr' | 'lff' | 'insports' | 'india' | 'tunisia' | 'fqtv',
REACT_APP_CLIENT: 'instat' | 'facr' | 'lff' | 'insports' | 'india' | 'tunisia' | 'fqtv' | 'rustat',
REACT_APP_ENV: 'production' | 'preproduction' | 'staging',
REACT_APP_STAGE: 'staging' | 'test-a' | 'test-b' | 'test-c' | 'test-d' | 'test-e' | 'test-f' | 'test-g' | 'test-h' | 'test-i' | 'test-j' | 'test',
REACT_APP_TYPE: 'auth-service' | 'ott',

@ -0,0 +1,23 @@
import { API_ROOT } from 'config'
import { callApi } from 'helpers'
export interface Peak {
e: number,
s: number,
}
export type Props = {
match_id: number,
sport_id: number,
}
export const getViewPeaks = async ({ match_id, sport_id }: Props): Promise<Array<Peak>> => {
const config = {
method: 'GET',
}
return callApi({
config,
url: `${API_ROOT}/v1/views/peaks/${sport_id}/${match_id}`,
})
}

@ -40,3 +40,4 @@ export * from './getAds'
export * from './getFavouriteTeam'
export * from './getCountryCode'
export * from './getAgreements'
export * from './share'

@ -0,0 +1,20 @@
import {
API_ROOT,
} from 'config'
import { callApi } from 'helpers'
interface Response {
url: string,
}
export const getShareHighlight = (sport_id: number, match_id: number): Promise<Response> => {
const config = {
method: 'GET',
}
return callApi({
config,
url: `${API_ROOT}/v1/share/highlight/${sport_id}/${match_id}`,
})
}

@ -0,0 +1 @@
export * from './getHighlight'
Loading…
Cancel
Save