Compare commits

...

137 Commits

Author SHA1 Message Date
Rakov 8896df9f1f fix(refresh): refresh token fix 2 years ago
Rakov 10bca448dc fix(refresh): resfresh token fix 2 years ago
Rakov 7a2423568a fix(refresh): save refresh lff facr 2 years ago
Rakov ccb265cb2e Revert "fix(#699): auth token" 2 years ago
Rakov 3152372c39 fix(inposrt.live): insport.live makefile build 2 years ago
Rakov 4e43b9aea8 fix(insport.live): insport.live deploy 2 years ago
Rakov 0c5a1aac4f fix(#772): fqtv button lexic 2 years ago
Rakov fc3a3c052c fix(#699): auth token 2 years ago
Rakov 888ac35156 fix(favicon): favicon facr lff fqtv 2 years ago
Rakov 2b9b93ef31 fix(favicon): favicon lff 2 years ago
Rakov 1335d34b07 fix(preview): added preview width 2 years ago
Rakov 42623654bb fix(#720): timeline tile size 2 years ago
Rakov c0d06f8ef9 fix(#720): update penalty score 2 years ago
Rakov 2dba409aa7 fix(#720): fix timeline scroll 2 years ago
Rakov 8dce04b35d fix(#720): add timeline timezone 2 years ago
Rakov 642e3809dc fix(#720): timeline media query fix 2 years ago
Rakov c41acef4ce fix(#720): timeline mode 2 years ago
Rakov af912dca10 fix(toc): tOC link fix 2 years ago
Ruslan Khayrullin d06425df94 feat(in-645): likes 2 years ago
Rakov 0e0f24454e fix(#747): new broadcast endpoint 2 years ago
Rakov 2a95f32aeb fix(#727): save match stats fix 2 years ago
Rakov bef03ff120 fix(#ios): ios player fix 2 years ago
Rakov bd218b8888 fix(#750): check device,toc for india 2 years ago
Margarita 2d321ff6a2 feat(date-filter): date filter fixes 2 years ago
Margarita d4c4bd6e81 feat(in-738): user password fix 2 years ago
Rakov 0b2c38871a fix(#hot-fix): disabled watching for virtual user 2 years ago
andreidekterev 9f0c779246 fix(#734): add watermark for india 2 years ago
Farber Denis 02c72a87f1 fix(#in730): facr header settings popup temp disable 2 years ago
Margarita f70fe23926 feat(in-695): mobile ads fixes 2 years ago
Margarita 5e0b262839 feat(in-717): ads fixes 2 years ago
Ruslan Khayrullin 590ce1bb90 fix(in-723): stats fixes 2 years ago
Rakov 806a2519b6 fix(#677): auto authorize 2 years ago
Margarita efce351342 feat(in-710): personal info language fixes 2 years ago
Rakov f4b96c4668 fix(india): fix india PP nad TaC 2 years ago
Rakov 77f71b852b fix(#phonepe): fix iframe payment for mobile 2 years ago
Ruslan Khayrullin 93e2716817 feat(in-719): change api version 2 years ago
andreidekterev 1edd818afd feat(supertournaments-logo-fix): add condition for extension logo for supertournaments 2 years ago
Margarita 01786d947a feat(in-716): changed jpg to png 2 years ago
Farber Denis f9823d191f fix(#in670): header color fixes 2 years ago
Ruslan Khayrullin d9d4de4072 feat(in-621): subscriptions popup redesign 2 years ago
Margarita 63f7c01e09 feat(in-565): pr fixes 2 years ago
Margarita 0170d0fb06 feat(in-565): ads on home and match pages 2 years ago
Rakov e5552e4d8b fix(rustat): dron rustat deploy 2 years ago
andreidekterev c67bc5a22c fix(#689): add tournament info for default landing 2 years ago
Farber Denis 247a08902d fix(#in701): auth requests fix 2 years ago
andreidekterev 8c88cb7517 fix(#602): add redirect param for change password 2 years ago
Rakov 551460fc14 fix(#622): india payment phonePe 2 years ago
andreidekterev cc595acfd4 Revert "feat(#680): add statsview page" 2 years ago
andreidekterev 541a042597 Revert "feat(#680): add embed component" 2 years ago
andreidekterev 1b7a2eb111 feat(#680): add embed component 2 years ago
andreidekterev e1d82fc86e feat(#680): add statsview page 2 years ago
andreidekterev 17e8794399 Revert "feat(#680): add statsview page" 3 years ago
andreidekterev c90d4fc769 Revert "feat(#680): add embed component" 3 years ago
Ruslan Khayrullin 90f7568a2a fix(in-685): fix access to match 3 years ago
Dmitry Kosolapov home b163307892 add deploy india.insports.tv 3 years ago
Rakov 1e7ffc7366 fix(#622): annual access 3 years ago
andreidekterev cf8515fe9e feat(#680): add embed component 3 years ago
andreidekterev 3a786f97c9 feat(#680): add statsview page 3 years ago
Rakov 403690369d fix(sentry): disable sentry 3 years ago
Rakov 98bb7e24c1 fix(#593): send user id to ga 3 years ago
andreidekterev a055f9df80 fix(#422): change client for new date filter 3 years ago
Rita 4c4b594b3c feat(in-422): facr calendar 3 years ago
Farber Denis d9bd7e68fa style(#in672): fixed styles for user profile wide screens 3 years ago
Rakov 767d7794c3 fix(#658): fqtv build 3 years ago
Dmitry Kosolapov home 4a53d56881 add fqtv 3 years ago
andreidekterev 472949baad fix(langs): fix langs to profile page 3 years ago
andreidekterev 795a8237f7 fix(#664): add params to landing page 3 years ago
andrey.dekterev 27b91ac3a0 revert-ads (#230) 3 years ago
Rakov 1e0a2cfc2d fix(#635): sentry bug fix 3 years ago
Rakov 86934cf6b8 fix(sentry): sentry bug fix 3 years ago
andreidekterev e188c32fcd fix(#564): fix styles ads ads types 3 years ago
andreidekterev e1c8ff491e fix(#564): add fixes 3 years ago
Andrei Dekterev 4129e2730a add ads to main page 3 years ago
Margarita 46b80b8e6c feat(in-652): pr fixes 3 years ago
Margarita 66a53765dc feat(in-652): penalty score 3 years ago
Rakov fcaa0015e5 fix(sentry): sentry bug fix 3 years ago
Ruslan Khayrullin fe18150ee7 fix(in-655): incorrect team stats lexics 3 years ago
Margarita aa41e833c7 feat(in-630): privacy policy fixes 3 years ago
Margarita 83880792cc feat(in-640): header bug fix 3 years ago
Ruslan Khayrullin ca35f3d238 fix(in-617): fix playing new playlist 3 years ago
Andrei Dekterev 877747322b Revert "fix(sentry): add Sentry.ErrorBoundary" 3 years ago
Andrei Dekterev 727aaece68 Revert "fix(sentry): add Sentry" 3 years ago
Margarita 2b3427800c feat(vid-143): lexic change 3 years ago
Farber Denis 00ce4f76ab fix(#in639): google adds file added 3 years ago
Margarita 9990d8eec6 feat(vid-143): match download popup 3 years ago
Rakov 96ab8c588f fix(sentry): add Sentry 3 years ago
Rakov df1ac5508d fix(sentry): sentry fix error/build 3 years ago
Rakov d6112d0db1 fix(sentry): fix sentry error 3 years ago
Andrei Dekterev ec60b7c1bc fix(sentry): add Sentry.ErrorBoundary 3 years ago
Dmitry Kosolapov home 4153cd8444 fix autodeploy authorization 3 years ago
Rakov e3332c4e3f fix(#616): player scrollIntoView 3 years ago
Ruslan Khayrullin 0697dbf072 fix: match stats bugfix 3 years ago
Margarita 061b09d1c4 feat(in-575): pr fixes 3 years ago
Margarita e546a77f99 feat(in-575): mailings bugfix 3 years ago
Ruslan Khayrullin e202aa1d30 feat(in-603): send requests only for open tabs 3 years ago
Ruslan Khayrullin 3d1d9e05cd fix(in-601): reload page after changing filters 3 years ago
Ruslan Khayrullin a56c44521b feat(in-600): remove mp4 player 3 years ago
Rakov ea00f09c7e fix(#578): back to live fix 3 years ago
Farber Denis d783ef64e0 docs(#605): fixed privacy policy docs 3 years ago
Farber Denis ac6383bff9 fix(#in341): fixed live match plays order tab bug 3 years ago
Ruslan Khayrullin 0750331e6a feat(in-570): episodes playback changes 3 years ago
Margarita 27b2449711 feat(in-583): remove supertournaments from favorites 3 years ago
Rakov b0b7ab6073 fix(#518): multiple tabs logout pr fix 3 years ago
Rakov f31a83629d fix(#518): multiple tabs logout 3 years ago
Ruslan Khayrullin 5815744de4 feat(in-512): change requests 3 years ago
Margarita 6a286d46dd feat(in-239): stats scroll bug 3 years ago
Margarita 603f098b12 feat(in-267): password fix 3 years ago
Ruslan Khayrullin e83c4032a9 feat(in-428): episodes playback changes 3 years ago
Andrei Dekterev 786a11281b fix(#560): check matchProfile in matchPage 3 years ago
Andrei Dekterev 9b8242a712 fix(#553): logout then we don't have a refresh token 3 years ago
Rakov 942a895004 fix(#495): india hot fix 3 years ago
Rakov c9868effeb fix(#495): india client fixes 3 years ago
Rakov bd71c5ef0c fix(#548): landing change lang 3 years ago
Farber Denis 62173b545b fix(#in555): purchase ok button id added 3 years ago
Margarita 282c652c63 feat(in-536): mobile header bugfix 3 years ago
Farber Denis 3c7e8c3a75 feat(#in282): resend email btn add 3 years ago
Andrei Dekterev 370c165905 style(highlight btn): change highlight btn id for live mathces 3 years ago
Farber Denis 1081406926 fix(#in546): meta description fixed for lff 3 years ago
Andrei Dekterev 7a5301f891 Revert "feat(sentry): add sentry to project" 3 years ago
Andrei Dekterev 4e681d01b2 Revert "fix(#sentry): remove condition for only prod" 3 years ago
Andrei Dekterev 5edf656d1f fix(#sentry): remove condition for only prod 3 years ago
Andrei Dekterev 437f7d4d70 fix(#google tag): change google tag 3 years ago
Farber Denis a93f2e4c6c style(#in516): landing style fix 3 years ago
Margarita 27f7c3f092 feat(in-537): match progressbar fixes 3 years ago
Rakov 89e07e70e6 fix(#462): match watch second to LS 3 years ago
Andrei Dekterev b0c9b2a6c9 feat(sentry): add sentry to project 3 years ago
Andrei Dekterev 7681462c83 feat(#526): add onchange to search country card 3 years ago
Andrei Dekterev d5cf526725 fix(black screen): timeout logout 3 years ago
Andrei Dekterev 0f067862f9 style(error-boundary): add styles for mobile version 3 years ago
Margarita c0a594d7c8 feat(in-454): pr fix 3 years ago
Margarita a69fbe6c4b feat(in-454): default landing 3 years ago
Andrei Dekterev 6a8d3f9200 fix(#522): hide getHighlights btn 3 years ago
Farber Denis 9205c61454 fix(#in503): comments fix 3 years ago
Farber Denis b7026e08bb fix(#in503): google analylics ids added 3 years ago
Andrei Dekterev e357fa8d6c chore(update): update deps 3 years ago
Andrei Dekterev d5e2964e19 Revert "fix(#462): match watch second to LS" 3 years ago
Farber Denis 44a550a67d style(#in485): header background color changes for some tournaments 3 years ago
  1. 285
      .drone.yml
  2. 10
      .eslintrc
  3. 9
      .gitignore
  4. 44
      Makefile
  5. 20788
      package-lock.json
  6. 19
      package.json
  7. 1
      public/ads.txt
  8. BIN
      public/clients/facr/favicon/android-chrome-192x192.png
  9. BIN
      public/clients/facr/favicon/android-chrome-512x512.png
  10. BIN
      public/clients/facr/favicon/apple-touch-icon.png
  11. BIN
      public/clients/facr/favicon/favicon-16x16.png
  12. BIN
      public/clients/facr/favicon/favicon-32x32.png
  13. BIN
      public/clients/facr/favicon/favicon.ico
  14. BIN
      public/clients/fqtv/favicon/android-chrome-192x192.png
  15. BIN
      public/clients/fqtv/favicon/android-chrome-512x512.png
  16. BIN
      public/clients/fqtv/favicon/apple-touch-icon.png
  17. BIN
      public/clients/fqtv/favicon/favicon-16x16.png
  18. BIN
      public/clients/fqtv/favicon/favicon-32x32.png
  19. BIN
      public/clients/fqtv/favicon/favicon.ico
  20. BIN
      public/clients/india/favicon/android-chrome-192x192.png
  21. BIN
      public/clients/india/favicon/android-chrome-512x512.png
  22. BIN
      public/clients/india/favicon/apple-touch-icon.png
  23. 12
      public/clients/india/favicon/browserconfig.xml
  24. BIN
      public/clients/india/favicon/favicon-16x16.png
  25. BIN
      public/clients/india/favicon/favicon-32x32.png
  26. BIN
      public/clients/india/favicon/favicon.ico
  27. 19
      public/clients/india/favicon/manifest.json
  28. BIN
      public/clients/india/favicon/mstile-144x144.png
  29. BIN
      public/clients/india/favicon/mstile-150x150.png
  30. BIN
      public/clients/india/favicon/mstile-310x310.png
  31. BIN
      public/clients/india/favicon/mstile-70x70.png
  32. 18
      public/clients/india/favicon/safari-pinned-tab.svg
  33. 1903
      public/clients/india/privacy-policy-and-statement.html
  34. 4385
      public/clients/india/terms-and-conditions.html
  35. 527
      public/clients/insports/privacy-policy-and-statement.html
  36. BIN
      public/clients/lff/favicon/android-chrome-192x192.png
  37. BIN
      public/clients/lff/favicon/android-chrome-512x512.png
  38. BIN
      public/clients/lff/favicon/apple-touch-icon.png
  39. BIN
      public/clients/lff/favicon/favicon-16x16.png
  40. BIN
      public/clients/lff/favicon/favicon-32x32.png
  41. BIN
      public/clients/lff/favicon/favicon.ico
  42. BIN
      public/images/checkedRadiobutton.png
  43. 3
      public/images/dollar-sign-grey.svg
  44. 3
      public/images/dollar-sign.svg
  45. BIN
      public/images/downloadIcon.png
  46. 21
      public/images/fqtv-auth-logo.svg
  47. BIN
      public/images/fqtv-background.png
  48. 9
      public/images/fqtv-logo.svg
  49. 4
      public/images/matchTabs/likes.svg
  50. BIN
      public/images/radiobutton.png
  51. 22
      public/index.html
  52. 23
      public/silent-refresh.html
  53. 2
      src/components/AccessTimer/index.tsx
  54. 163
      src/components/Ads/components/AdComponent/hooks.tsx
  55. 91
      src/components/Ads/components/AdComponent/index.tsx
  56. 109
      src/components/Ads/components/AdComponent/styled.tsx
  57. 79
      src/components/Ads/components/MobileAd/index.tsx
  58. 115
      src/components/Ads/components/MobileAd/styled.tsx
  59. 16
      src/components/Ads/helpers/calcMaxDurationAds.tsx
  60. 1
      src/components/Ads/helpers/index.tsx
  61. 2
      src/components/Ads/helpers/isVideo.tsx
  62. 65
      src/components/Ads/hooks.tsx
  63. 31
      src/components/Ads/index.tsx
  64. 16
      src/components/Ads/styled.tsx
  65. 59
      src/components/Ads/types.tsx
  66. 72
      src/components/Carousel/helpers/common.tsx
  67. 274
      src/components/Carousel/helpers/elements.tsx
  68. 4
      src/components/Carousel/helpers/index.tsx
  69. 12
      src/components/Carousel/helpers/mappers.tsx
  70. 44
      src/components/Carousel/helpers/math.tsx
  71. 160
      src/components/Carousel/hooks.tsx
  72. 86
      src/components/Carousel/index.tsx
  73. 25
      src/components/Carousel/styled.tsx
  74. 73
      src/components/Carousel/types.tsx
  75. 15
      src/components/Error/styled.tsx
  76. 54
      src/components/ErrorBoundary/index.tsx
  77. 64
      src/components/PictureInPicture/PiP.tsx
  78. 5
      src/components/SimplePopup/index.tsx
  79. 1
      src/components/SmartBanner/index.tsx
  80. 16
      src/config/clients/facr.tsx
  81. 57
      src/config/clients/fqtv.tsx
  82. 3
      src/config/clients/index.tsx
  83. 11
      src/config/clients/india.tsx
  84. 7
      src/config/clients/insports.tsx
  85. 6
      src/config/clients/instat.tsx
  86. 10
      src/config/clients/lff.tsx
  87. 9
      src/config/clients/tunisia.tsx
  88. 23
      src/config/clients/types.tsx
  89. 2
      src/config/index.tsx
  90. 39
      src/config/lexics/indexLexics.tsx
  91. 9
      src/config/lexics/landingLexics.tsx
  92. 8
      src/config/lexics/matchDownload.tsx
  93. 4
      src/config/lffTournaments.tsx
  94. 1
      src/config/localStorageKeys.tsx
  95. 1
      src/config/pages.tsx
  96. 13
      src/config/payments.tsx
  97. 4
      src/config/procedures.tsx
  98. 1
      src/config/queries.tsx
  99. 31
      src/config/routes.tsx
  100. 8
      src/config/userAgent.tsx
  101. Some files were not shown because too many files have changed in this diff Show More

@ -203,6 +203,35 @@ steps:
depends_on:
- make-diwansport
- name: make-fqtv
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- make fqtv-prod
depends_on:
- npm-install
- name: deploy-fqtv
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_fqtv s3://insports-fqtv-com-au --delete
- aws cloudfront create-invalidation --distribution-id E2MFJBSBIUQSDH --paths "/*" # # fqtv.insports.tv
- aws cloudfront create-invalidation --distribution-id E22TED4Z46Q01V --paths "/*" # fqtv.com.au
depends_on:
- make-fqtv
---
kind: pipeline
type: docker
@ -658,9 +687,28 @@ trigger:
- refs/heads/test-auth
steps:
- name: deploy script
- 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-auth
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- make auth-build
depends_on:
- npm-install
- name: deploy-S3-auth
image: amazon/aws-cli:latest
environment:
AWS_ACCESS_KEY_ID:
from_secret: AWS_ACCESS_KEY_ID
@ -668,23 +716,12 @@ steps:
from_secret: AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION:
from_secret: AWS_DEFAULT_REGION
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK_TEST
SSH_KEY_AUTH_TEST:
from_secret: SSH_KEY_AUTH_TEST
AWS_MAX_ATTEMPTS: 10
commands:
- apk add --no-cache aws-cli bash git openssh-client make rsync
- npm install --legacy-peer-deps
- make auth-build
- eval $(ssh-agent -s)
- echo -n "$SSH_KEY_AUTH_TEST" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- ssh-keyscan auth.test.insports.tv >> ~/.ssh/known_hosts
- rsync -v -r -C build_auth/ ubuntu@auth.test.insports.tv:/home/ubuntu/ott-auth/src/frontend/
- rsync -v -r -C build_auth/clients/* ubuntu@auth.test.insports.tv:/home/ubuntu/ott-auth/src/frontend/templates
- aws s3 sync build_auth s3://auth-insports-test --delete
- aws s3 sync build_auth s3://insports-auth-test --delete
- aws cloudfront create-invalidation --distribution-id E10YI3RFOZZDLZ --paths "/*"
depends_on:
- make-auth
---
@ -808,4 +845,218 @@ steps:
- aws cloudfront create-invalidation --distribution-id E3NJ2G0QSB6MVI --paths "/*" # tunisia.insports.tv
depends_on:
- make-diwansport
- make-diwansport
---
kind: pipeline
type: docker
name: deploy fqtv.insports.tv
concurrency:
limit: 1
platform:
os: linux
arch: amd64
trigger:
ref:
- refs/heads/fqtv.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-fqtv
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- make fqtv-prod
depends_on:
- npm-install
- name: deploy-fqtv
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_fqtv s3://insports-fqtv-com-au --delete
- aws cloudfront create-invalidation --distribution-id E2MFJBSBIUQSDH --paths "/*" # # fqtv.insports.tv
- aws cloudfront create-invalidation --distribution-id E22TED4Z46Q01V --paths "/*" # fqtv.com.au
depends_on:
- make-fqtv
---
kind: pipeline
type: docker
name: deploy india.insports.tv
concurrency:
limit: 1
platform:
os: linux
arch: amd64
trigger:
ref:
- refs/heads/india.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-india
image: node:16-alpine
environment:
REACT_APP_STRIPE_PK:
from_secret: REACT_APP_STRIPE_PK
commands:
- apk add --no-cache make
- make india-prod
depends_on:
- npm-install
- name: deploy-india
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_india s3://insports-india --delete
- aws cloudfront create-invalidation --distribution-id E5DKN8IPOMASO --paths "/*"
depends_on:
- make-india
---
kind: pipeline
type: docker
name: deploy tv.rustatsport.ru
concurrency:
limit: 1
platform:
os: linux
arch: amd64
trigger:
ref:
- refs/heads/tv.rustatsport.ru
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-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-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-rustatsport --delete
- 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

@ -80,8 +80,16 @@
],
"typescript-sort-keys/interface": 1,
"typescript-sort-keys/string-enum": 1,
"react/function-component-definition": [
2,
{
"namedComponents": "arrow-function",
"unnamedComponents": "arrow-function"
},
],
"react/jsx-no-useless-fragment": [2, { "allowExpressions": true }],
"camelcase": "off",
"default-param-last": 0,
"import/no-unresolved": "off",
"import/prefer-default-export": "off",
"indent": "off",

9
.gitignore vendored

@ -11,11 +11,7 @@
/cypress/screenshots
# production
/build
/build_auth
/build_india
/build_facr
/build_lff
/build*
# misc
.DS_Store
@ -34,3 +30,6 @@ yarn.lock
# IntelliJ IDEA products
.idea
.eslintcache
# ssl keys
*.pem

@ -136,12 +136,24 @@ tunisia-build: clean
REACT_APP_CLIENT=tunisia \
npm run build
fqtv-build: clean
REACT_APP_TYPE=ott \
REACT_APP_ENV=staging \
REACT_APP_CLIENT=fqtv \
npm run build
lff-build: clean
REACT_APP_TYPE=ott \
REACT_APP_ENV=staging \
REACT_APP_CLIENT=lff \
npm run build
rustat-build: clean
REACT_APP_TYPE=ott \
REACT_APP_ENV=staging \
REACT_APP_CLIENT=rustat \
npm run build
.PHONY: build
prod: clean
@ -195,9 +207,39 @@ diwansport-prod:
BUILD_PATH=build_tunisia \
npm run build && cp -r .well-known build_tunisia
deploy-all: prod preprod facr-prod lff-prod diwansport-prod india-prod
fqtv-prod:
rm -rf build_fqtv && \
REACT_APP_TYPE=ott \
REACT_APP_ENV=production \
REACT_APP_STRIPE_PK=pk_live_51J5TEYEDSxVnTgDW5XxhC6ntKZKddXgKHq5HOCDmJTdfSKluMYCdLHOcUA3Miuy8HesxG1eS4c0dQRQpMsEHRrQL00USpu5xIq \
REACT_APP_CLIENT=fqtv \
BUILD_PATH=build_fqtv \
npm run build && cp -r .well-known build_fqtv
rustat-prod:
rm -rf build_rustat && \
REACT_APP_TYPE=ott \
REACT_APP_ENV=production \
REACT_APP_CLIENT=rustat \
REACT_APP_STRIPE_PK=pk_live_51J5TEYEDSxVnTgDW5XxhC6ntKZKddXgKHq5HOCDmJTdfSKluMYCdLHOcUA3Miuy8HesxG1eS4c0dQRQpMsEHRrQL00USpu5xIq \
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:
npm test
.PHONY: test
generate-ssl-keys:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365

20788
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -4,7 +4,7 @@
"private": true,
"scripts": {
"start": "react-scripts start",
"start-https": "export HTTPS=true&&SSL_CRT_FILE=cert.pem&&SSL_KEY_FILE=key.pem react-scripts start",
"start-https": "export PORT=443 HTTPS=true&&SSL_CRT_FILE=cert.pem&&SSL_KEY_FILE=key.pem react-scripts start",
"build": "GENERATE_SOURCEMAP=false react-scripts build && gzipper --verbose ./build",
"test": "react-scripts test --testMatch '**/__tests__/*' --passWithNoTests --watchAll=false",
"test:watch": "react-scripts test --testMatch '**/__tests__/*'",
@ -19,14 +19,18 @@
"lff": "REACT_APP_CLIENT=lff react-scripts start",
"india": "REACT_APP_CLIENT=india react-scripts start",
"tunisia": "REACT_APP_CLIENT=tunisia react-scripts start",
"fqtv": "REACT_APP_CLIENT=fqtv react-scripts start",
"insports": "REACT_APP_CLIENT=insports react-scripts start"
},
"dependencies": {
"@reactour/tour": "^3.3.0",
"@sentry/react": "^7.53.1",
"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.13.2",
"babel-polyfill": "^6.26.0",
"date-fns": "^2.14.0",
"dueljs": "^1.2.7",
"eslint-plugin-typescript-sort-keys": "^2.3.0",
"history": "^4.10.1",
"hls.js": "^1.1.1",
"lodash": "^4.17.15",
@ -35,12 +39,12 @@
"react": "^17.0.2",
"react-datepicker": "^3.1.3",
"react-dom": "^17.0.2",
"react-ga": "^3.3.1",
"react-query": "^3.39.3",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.1",
"react-window": "^1.8.6",
"react-youtube": "^7.14.0",
"recoil": "^0.7.4",
"screenfull": "^5.0.2",
"styled-components": "^5.3.3",
@ -53,10 +57,10 @@
"@storybook/addon-actions": "^6.3.12",
"@storybook/addon-docs": "^6.3.12",
"@storybook/addon-links": "^5.3.19",
"@storybook/addons": "^5.3.19",
"@storybook/addons": "^7.0.2",
"@storybook/preset-create-react-app": "^3.0.0",
"@storybook/preset-typescript": "^3.0.0",
"@storybook/react": "^6.3.12",
"@storybook/react": "^7.0.2",
"@testing-library/jest-dom": "^5.15.0",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^7.1.2",
@ -73,8 +77,8 @@
"@types/react-window": "^1.8.5",
"@types/styled-components": "^5.1.15",
"commitizen": "^4.2.4",
"eslint": "^7.0.1",
"eslint-config-airbnb": "18.2.1",
"eslint": "^8.38.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
@ -82,13 +86,12 @@
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-sort-destructure-keys": "^1.3.5",
"eslint-plugin-typescript-sort-keys": "^1.5.0",
"fork-ts-checker-webpack-plugin": "^7.2.13",
"gzipper": "^3.7.0",
"husky": "^4.2.5",
"lint-staged": "^10.2.7",
"miragejs": "^0.1.45",
"storybook-addon-styled-component-theme": "^1.3.0",
"storybook-addon-styled-component-theme": "^2.0.0",
"stylelint": "^13.13.1",
"stylelint-config-recommended": "^5.0.0",
"stylelint-config-styled-components": "^0.1.1",

@ -0,0 +1 @@
google.com, pub-6802442215403184, DIRECT, f08c47fec0942fa0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000" preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
<path d="M1499 6996 c-2 -2 -59 -6 -125 -9 -247 -12 -462 -57 -614 -129 -64
-30 -206 -123 -265 -173 -105 -89 -173 -164 -247 -270 -153 -219 -210 -411
-235 -790 -11 -161 -10 -4130 0 -4270 12 -158 20 -222 43 -331 60 -297 246
-573 515 -766 182 -130 345 -193 584 -225 22 -2 51 -6 65 -8 124 -17 518 -20
2260 -20 1773 0 2220 4 2295 19 11 2 45 7 75 10 75 9 120 17 190 36 121 32
186 61 300 129 87 53 77 46 155 109 140 114 277 283 353 437 83 166 119 330
139 630 11 160 10 4107 0 4260 -29 420 -108 642 -311 880 -175 205 -390 346
-626 412 -47 13 -107 26 -135 29 -27 4 -52 8 -55 9 -3 2 -44 6 -90 10 -47 4
-96 9 -110 13 -26 6 -4154 14 -4161 8z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -86,7 +86,8 @@
</p>
<p>
(inSports TV also referred to as "us", "we", "our" or "the company"; refers to InSport TV Limited)
(inSports TV also referred to as "us", "we", "our" or "the company";
refers to InSport TV Limited)
</p>
<p
@ -105,12 +106,11 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>inSports TV is committed to the privacy of those that we engage with and
this statements details our approach. While using this site or
>inSports TV is committed to the privacy of those that we engage with
and this statements details our approach. While using this site or
providing personal data to us in the course of business, we will
manage your data in accordance with this privacy statement.
</span
>
manage your data in accordance with this privacy statement.
</span>
</p>
<ul style="margin-top: 0in" type="disc">
@ -175,10 +175,10 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>inSports TV provides an online platform that allows to broadcast sports
video content. This is a legitimate interest pursued by inSports TV.
Personal data processed by the business is done so in accordance with
current Data Protection Regulation and GDPR.</span
>inSports TV provides an online platform that allows to broadcast
sports video content. This is a legitimate interest pursued by
inSports TV. Personal data processed by the business is done so in
accordance with current Data Protection Regulation and GDPR.</span
>
</p>
<p
@ -219,7 +219,18 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>We may obtain personal data including name, address, phone numbers, e-mail address, other electronic identifiers, title, images, IP address, company details, information about usage of inSports TV services (e.g., the types of content that you engage with), device information, transaction information including a credit card number and other authentication information (only when the transaction on our products is made) and other information provided by you or by other people when they use our products in accordance with this policy and with data protection law. We may also obtain similar information from other sources such as club, league or broadcast game footage, or from your use of inSports TV services, or when you sign up to our services, or attend events or otherwise engage with the business.</span
>We may obtain personal data including name, address, phone numbers,
e-mail address, other electronic identifiers, title, images, IP
address, company details, information about usage of inSports TV
services (e.g., the types of content that you engage with), device
information, transaction information including a credit card number
and other authentication information (only when the transaction on our
products is made) and other information provided by you or by other
people when they use our products in accordance with this policy and
with data protection law. We may also obtain similar information from
other sources such as club, league or broadcast game footage, or from
your use of inSports TV services, or when you sign up to our services,
or attend events or otherwise engage with the business.</span
>
</p>
@ -261,9 +272,10 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>We use personal data for the purposes for which it was provided to us as
expressed at the point of collection or as is obvious in the context of collection.
Typically, information is collected for the following purposes:</span
>We use personal data for the purposes for which it was provided to us
as expressed at the point of collection or as is obvious in the
context of collection. Typically, information is collected for the
following purposes:</span
>
</p>
@ -601,7 +613,13 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>We take all reasonable measures to protect your personal information while it is in our possession. Your personal information may be transferred to third party service providers who process information on the inSports TV behalf, including providers of information technology, identity management, website hosting and management, network services, data analysis, anti-spam services, data back-up, security, and storage services.</span
>We take all reasonable measures to protect your personal information
while it is in our possession. Your personal information may be
transferred to third party service providers who process information
on the inSports TV behalf, including providers of information
technology, identity management, website hosting and management,
network services, data analysis, anti-spam services, data back-up,
security, and storage services.</span
>
</p>
@ -621,7 +639,12 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Your Personal data may also be transferred to joint sponsors of events, or to certification bodies. We may also provide access to your personal information to law enforcement authorities, revenue commissioners, regulatory or other government agencies, or to other third parties should we receive a valid request compatible with applicable law or regulation.</span
>Your Personal data may also be transferred to joint sponsors of
events, or to certification bodies. We may also provide access to your
personal information to law enforcement authorities, revenue
commissioners, regulatory or other government agencies, or to other
third parties should we receive a valid request compatible with
applicable law or regulation.</span
>
</p>
@ -641,7 +664,14 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Personal data submitted through this site may be transferred to third party service providers or to other companies within the inSports group of companies that are outside of the state, and outside of the European Economic Area (EEA). Where you request goods or services to be provided outside the EEA (European Economic Area), or to be delivered in conjunction with others outside of the EEA, personal data provided by you may be shared with organizations or state bodies (customs, revenue authorities and etc.) to fulfil an agreement.</span
>Personal data submitted through this site may be transferred to third
party service providers or to other companies within the inSports
group of companies that are outside of the state, and outside of the
European Economic Area (EEA). Where you request goods or services to
be provided outside the EEA (European Economic Area), or to be
delivered in conjunction with others outside of the EEA, personal data
provided by you may be shared with organizations or state bodies
(customs, revenue authorities and etc.) to fulfil an agreement.</span
>
</p>
@ -683,7 +713,15 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Customers and other organizations engaging with us for service shall warrant that personal information provided to us for the administration and delivery of goods and services being provided under the Agreement has been obtained fairly and lawfully. Such customers or organizations shall also warrant that subjects are aware of the purpose for which their personal data is being used and that such data may be transferred outside of the EEA for processing or to deliver the service or upon your request, and that the privacy rights of subjects have been upheld.</span
>Customers and other organizations engaging with us for service shall
warrant that personal information provided to us for the
administration and delivery of goods and services being provided under
the Agreement has been obtained fairly and lawfully. Such customers or
organizations shall also warrant that subjects are aware of the
purpose for which their personal data is being used and that such data
may be transferred outside of the EEA for processing or to deliver the
service or upon your request, and that the privacy rights of subjects
have been upheld.</span
>
</p>
@ -725,7 +763,15 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>inSports TV have implemented generally accepted standards of technology and operational security to protect personal data from alteration, unauthorized disclosure or destruction, and from use for unauthorized purposes. Furthermore, we have taken measures to ensure that contracts with all third parties that provide technical and processing services include terms that specify appropriate technical and organizational security measures to prevent accidental, unauthorized or unlawful disclosure or processing of personal data.</span
>inSports TV have implemented generally accepted standards of
technology and operational security to protect personal data from
alteration, unauthorized disclosure or destruction, and from use for
unauthorized purposes. Furthermore, we have taken measures to ensure
that contracts with all third parties that provide technical and
processing services include terms that specify appropriate technical
and organizational security measures to prevent accidental,
unauthorized or unlawful disclosure or processing of personal
data.</span
>
</p>
@ -1191,31 +1237,14 @@
>
</p>
<p style="margin-bottom: 0in; line-height: normal; background: #f2f2f2">
<span
style="
font-size: 9pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>© inSportsTV.com All right reserved.</span
>
</p>
</div>
<div
style="
background-color: white;
max-width: 1264px;
padding: 40px;
margin: 0 auto;
"
>
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
line-height: 1.1;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
@ -1225,7 +1254,7 @@
font-family: 'Trebuchet MS', sans-serif;
color: #002060;
"
>*ERSTE LIGA ADDITIONAL NOTICE</span
>Cookies Policy</span
></b
>
</p>
@ -1235,7 +1264,8 @@
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1244,23 +1274,48 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>In the case of ERSTE LIGA TV channel, data is processed jointly in
accordance with EU Regulation 2016/679 (hereinafter GDPR). When
registering on the inSports site, after accepting this Privacy Policy
and Privacy Statement, the user provides the e-mail address required
to use the service, which is handed over to the Hungarian Ice Hockey
Federation (hereinafter referred to as HIHF), the rights holder of the
ERSTE LIGA TV channel, within the framework of joint data processing.
Details of the joint processing by the HIHF:
>We use cookies, pixels, and other technologies (collectively,
“cookies”) to recognize your browser or device, learn more about your
interests, and provide you with essential features and services and
for additional purposes.<br />
If you opt out of advertising cookies, you may still see /platform
name/ ads on other sites, but those ads will not be customized by us
or our service providers and we will continue to customize your
experience on our website via our use of cookies you have not
refused.<br />
Please note that some of the services will not function as well if
cookies are disabled.
</span>
</p>
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
><span
style="
font-size: 12.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: #002060;
"
>What kind of cookies do we use?</span
></b
>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1269,10 +1324,19 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Purpose of data processing: the HIHF sends marketing, PR and sports
promotional content by electronic messages to users who accept this
Privacy Statement,  to the e-mail address they have provided.
</span>
><i>Analytics and Performance:</i> We make use of analytic cookies to
analyze how our visitors use our Website and to monitor Website
performance. This allows us to provide a high-quality experience by
customizing our offering and quickly identifying and fixing any issues
that arise. For example, we might use performance cookies to keep
track of which pages are most popular, which method of linking between
pages is most effective, and to determine why some pages are receiving
error messages. We might also use these cookies to highlight articles
or site services that we think will be of interest to you based on
your usage of the Website. The information collected by these cookies
is not associated with your personal information by us or by our
contractors.</span
>
</p>
<p
style="
@ -1280,7 +1344,8 @@
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1289,8 +1354,13 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Legal basis for processing: consent of the data subject.
</span>
><i>Website Functionality:</i> We make use of cookies to provide you
with certain functionality. For example, to remember choices you make
(such as your username, language or the region you are in), or to
recognize the platform from which you access the Website, and to
provide enhanced and more personal features. These cookies are not
used to track your browsing on other sites.</span
>
</p>
<p
style="
@ -1298,7 +1368,8 @@
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1307,9 +1378,38 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>The scope of the data processed: the e-mail address of the user
registered on inSports.tv.
</span>
><i>Advertising:</i> Advertising cookies (also known as Third Party or
targeting cookies) collect information about the browsing habits
associated with your device and are used to make advertising more
relevant to you and your interests. Advertisers such as Facebook,
Google, Twitter and others may have buttons embedded on our webpage
that such as ‘Like' or ‘Share' buttons in addition to providing the
requested functionality or if you click on an advertiser’s ad on our
website and are taken to the advertiser’s website (these are Third
Party Cookies).</span
>
</p>
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
><span
style="
font-size: 12.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: #002060;
font-style: italic;
"
>What is the lifespan of cookies?</span
></b
>
</p>
<p
style="
@ -1317,7 +1417,8 @@
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1325,18 +1426,44 @@
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
font-style: italic;
"
>Duration of processing: until the data subject's consent is
withdrawn.
>
Analytics and Performance: 2 Years;<br />
Website Functionality: 100 days;<br />
Advertising: 2 Years;
</span>
</p>
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
><span
style="
font-size: 12.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: #002060;
"
>Local storage</span
></b
>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1345,17 +1472,48 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Method of processing: electronically, in compliance with the
necessary security standards.
>
Local storage is a feature we utilize on our website to enhance your
user experience. It allows us to store your user preferences and
settings, such as volume/mute settings, and enables the smooth
functioning of animated content on our website. Additionally, we can
utilize local storage to remember the point at which you left off
while watching content. This means that if you navigate away from the
page or close your browser, you can easily resume viewing from where
you left off without any hassle. Rest assured that this information is
stored securely and can only be accessed by our website.
</span>
</p>
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
><span
style="
font-size: 12.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: #002060;
"
>Use of IP addresses and web logs</span
></b
>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1364,174 +1522,91 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Data subjects' rights:
>
We may also use your IP address and browser type to help diagnose
problems with our server, to administer our Website and to improve the
services we offer to you. An IP address is a numeric code that
identifies your computer on the internet. Your IP address might also
be used to gather broad demographic information.<br />
We may perform IP lookups to determine which domain you are coming
from (e.g. google.com) to more accurately gauge our users'
demographics.
</span>
</p>
<ol style="margin-top: 0in" start="1" type="a">
<li style="color: black; margin-bottom: 0in; line-height: 1.1">
<span
style="font-size: 10.5pt; font-family: 'Trebuchet MS', sans-serif"
>The data subject has the right to withdraw consent to data
processing at any time. Withdrawal of consent does not affect the
lawfulness of the prior processing.</span
>
</li>
<li style="color: black; margin-bottom: 0in; line-height: 1.1">
<span
style="font-size: 10.5pt; font-family: 'Trebuchet MS', sans-serif"
>The data subject may also exercise his or her right of access to
his or her personal data (right to request information about the
processing), the right to rectification of his or her personal data
(e.g. if his or her e-mail address changes), the right to object to
processing, the right to restriction of processing (e.g. if he or
she does not wish to receive e-mails for a certain period of time),
the right to erasure or blocking of his or her data, and the right
to data portability.
</span>
</li>
<li style="color: black; margin-bottom: 0in; line-height: 1.1">
<span
style="font-size: 10.5pt; font-family: 'Trebuchet MS', sans-serif"
>If the data subject wishes to exercise his or her rights in
relation to the joint processing described in this paragraph, he or
she may do so by the means listed below:
</span>
</li>
<p
style="
margin-top: 22.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
"
>
<span
style="
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Name: Hungarian Ice Hockey Federation (hereinafter referred to as
the "Controller")
</span>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
"
>
<span
style="
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Seat: H-1146 Budapest, Istvánmezei út 1-3.
</span>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
"
>
<span
style="
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Postal address: H-1146 Budapest, Istvánmezei út 1-3.
</span>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
"
>
<span
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
><span
style="
font-size: 10.5pt;
font-size: 12.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
color: #002060;
"
>Represented by: Zsolt Levente Sipos, General Secretary
</span>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
"
>Cookie Notice does not cover third party websites</span
></b
>
<span
style="
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>Phone: +36 1 460 6863
</span>
</p>
<p
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: normal;
background: #f2f2f2;
"
>
<span
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>
<span
Please note that this Cookie Notice does not apply to, and we are not
responsible for, the privacy practices of third-party websites which
may be linked to this Website.
</span>
</p>
<p
style="
margin-top: 22.5pt;
margin-right: 7.5pt;
margin-bottom: 7.5pt;
margin-left: 7.5pt;
line-height: normal;
background: #f2f2f2;
"
>
<b
><span
style="
font-size: 10.5pt;
font-size: 12.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
color: #002060;
"
>Fax: +36 1 460 6864
</span>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
"
>Changes to the Cookie Notice</span
></b
>
<span
style="
font-size: 10.5pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>E-mail: adatvedelem@icehockey.hu
</span>
</p>
</ol>
</p>
<p
style="
margin-top: 7.5pt;
margin-right: 0in;
margin-bottom: 7.5pt;
margin-left: 0in;
line-height: 1.1;
line-height: normal;
background: #f2f2f2;
"
>
<span
@ -1540,11 +1615,23 @@
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>HIHF ERSTE LIGA TV Terms and Conditions of Use, including the Privacy
Statement, can be found at the following link:
"ersteligatv.hu/Felhasznalasi-feltetelek"
>
We may update this Cookie Notice and we would encourage you to review
the notice from time to time to stay informed of how we are using
cookies.
</span>
</p>
<p style="margin-bottom: 0in; line-height: normal; background: #f2f2f2">
<span
style="
font-size: 9pt;
font-family: 'Trebuchet MS', sans-serif;
color: black;
"
>© inSportsTV.com All right reserved.</span
>
</p>
</div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

@ -1,3 +0,0 @@
<svg width="6" height="13" viewBox="0 0 6 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.80998 0.90332V2.65527H2.90764V0.90332H3.80998ZM3.69865 10.4717V12.042H2.80217V10.4717H3.69865ZM4.58342 8.5791C4.58342 8.35254 4.53654 8.15527 4.44279 7.9873C4.35295 7.81543 4.20451 7.66113 3.99748 7.52441C3.79045 7.38379 3.51311 7.25293 3.16545 7.13184C2.66545 6.95605 2.22795 6.75879 1.85295 6.54004C1.48186 6.31738 1.19279 6.04395 0.985762 5.71973C0.782637 5.3916 0.681075 4.9834 0.681075 4.49512C0.681075 4.01465 0.79045 3.59863 1.0092 3.24707C1.23186 2.89551 1.54045 2.62402 1.93498 2.43262C2.32951 2.24121 2.79045 2.14551 3.31779 2.14551C3.72404 2.14551 4.08928 2.20605 4.4135 2.32715C4.74162 2.44824 5.02092 2.62598 5.25139 2.86035C5.48186 3.09473 5.65764 3.38379 5.77873 3.72754C5.90373 4.06738 5.96623 4.45801 5.96623 4.89941H4.55998C4.55998 4.64941 4.53068 4.42676 4.47209 4.23145C4.4174 4.03223 4.33537 3.86426 4.226 3.72754C4.11662 3.58691 3.98381 3.47949 3.82756 3.40527C3.67522 3.33105 3.50139 3.29395 3.30607 3.29395C3.02873 3.29395 2.80022 3.34668 2.62053 3.45215C2.44084 3.55371 2.30803 3.69434 2.22209 3.87402C2.14006 4.05371 2.09904 4.25879 2.09904 4.48926C2.09904 4.71191 2.14201 4.90723 2.22795 5.0752C2.31389 5.24316 2.46037 5.39551 2.6674 5.53223C2.87834 5.66504 3.1674 5.7998 3.53459 5.93652C4.0385 6.11621 4.47404 6.31738 4.84123 6.54004C5.21232 6.75879 5.49748 7.03027 5.6967 7.35449C5.89982 7.6748 6.00139 8.0791 6.00139 8.56738C6.00139 9.07129 5.8842 9.49902 5.64982 9.85059C5.41936 10.2021 5.09514 10.4697 4.67717 10.6533C4.26311 10.833 3.78068 10.9229 3.2299 10.9229C2.88225 10.9229 2.53654 10.8779 2.19279 10.7881C1.85295 10.6943 1.54436 10.542 1.26701 10.3311C0.989669 10.1201 0.768965 9.84082 0.604903 9.49316C0.44084 9.1416 0.358809 8.71191 0.358809 8.2041H1.77678C1.77678 8.50879 1.81779 8.7627 1.89982 8.96582C1.98186 9.16895 2.09318 9.3291 2.23381 9.44629C2.37443 9.56348 2.53068 9.64746 2.70256 9.69824C2.87834 9.74902 3.05412 9.77441 3.2299 9.77441C3.52287 9.77441 3.76897 9.72559 3.96818 9.62793C4.17131 9.52637 4.32365 9.38574 4.42522 9.20605C4.53068 9.02246 4.58342 8.81348 4.58342 8.5791Z" fill="white" fill-opacity="0.7"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

@ -1,3 +0,0 @@
<svg width="12" height="23" viewBox="0 0 12 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.81201 15.5813C8.81201 14.9172 8.60107 14.3704 8.1792 13.9407C7.75732 13.5032 7.0542 13.1125 6.06982 12.7688C5.08545 12.4172 4.31982 12.093 3.77295 11.7961C1.95264 10.8196 1.04248 9.37427 1.04248 7.46021C1.04248 6.16333 1.43701 5.09692 2.22607 4.26099C3.01514 3.42505 4.08545 2.92896 5.43701 2.77271V0.218018H7.31201V2.79614C8.67139 2.99146 9.72217 3.56958 10.4644 4.53052C11.2065 5.48364 11.5776 6.72583 11.5776 8.25708H8.7417C8.7417 7.27271 8.51904 6.49927 8.07373 5.93677C7.63623 5.36646 7.03857 5.0813 6.28076 5.0813C5.53076 5.0813 4.94482 5.28442 4.52295 5.69067C4.10107 6.09692 3.89014 6.67896 3.89014 7.43677C3.89014 8.11646 4.09717 8.66333 4.51123 9.07739C4.93311 9.48364 5.64404 9.87036 6.64404 10.2375C7.64404 10.6047 8.4292 10.9446 8.99951 11.2571C9.56982 11.5696 10.0503 11.929 10.4409 12.3352C10.8315 12.7336 11.1323 13.1946 11.3433 13.718C11.5542 14.2415 11.6597 14.8547 11.6597 15.5579C11.6597 16.8782 11.2534 17.9485 10.4409 18.7688C9.63623 19.5891 8.51123 20.0735 7.06592 20.2219V22.5071H5.20264V20.2336C3.65576 20.0618 2.46436 19.5071 1.62842 18.5696C0.800293 17.6321 0.38623 16.3899 0.38623 14.843H3.23389C3.23389 15.8274 3.47998 16.5891 3.97217 17.1282C4.47217 17.6672 5.17529 17.9368 6.08154 17.9368C6.97217 17.9368 7.64795 17.7219 8.10889 17.2922C8.57764 16.8625 8.81201 16.2922 8.81201 15.5813Z" fill="#333333"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

@ -0,0 +1,4 @@
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.9261 5.8212C12.9307 5.87963 12.9358 5.93231 12.9406 5.97754C12.9332 5.99732 12.9251 6.019 12.9163 6.04251C12.8651 6.17851 12.7898 6.37523 12.6939 6.61585C12.502 7.09769 12.229 7.75251 11.9041 8.44659C11.5783 9.14247 11.2053 9.86744 10.8143 10.4938C10.4154 11.1329 10.0302 11.616 9.69372 11.8772C9.66718 11.8978 9.64228 11.9205 9.61923 11.9449C9.18111 12.41 8.91275 12.9173 8.74763 13.3195L8.7475 13.3194L8.74278 13.3316C8.71208 13.4109 8.6845 13.4866 8.66007 13.556C8.58397 13.7007 8.41439 13.9457 8.03684 14.1757C7.79824 14.321 7.67921 14.6026 7.74121 14.875L9.45857 22.4214C9.51892 22.6866 9.738 22.8862 10.0077 22.9216L10.0924 22.2772C10.0077 22.9216 10.0077 22.9216 10.0078 22.9216L10.008 22.9217L10.0085 22.9217L10.0104 22.922L10.0169 22.9228L10.0413 22.9259L10.1335 22.9373C10.2137 22.9471 10.3304 22.9608 10.4785 22.977C10.7745 23.0094 11.1963 23.052 11.7023 23.0927C12.704 23.1733 14.0447 23.2476 15.3961 23.2207C16.4734 23.3062 17.6651 23.3176 18.6492 23.1041C20.351 22.7378 21.1491 21.9031 21.424 21.0082C21.5524 20.5903 21.5506 20.2065 21.5195 19.9324C21.5184 19.923 21.5173 19.9137 21.5162 19.9045C22.2578 19.2016 22.4576 18.4095 22.4234 17.7496C22.407 17.4337 22.3384 17.1612 22.268 16.9564C22.532 16.603 22.6944 16.243 22.7698 15.8841C22.869 15.4117 22.8081 14.9852 22.6891 14.6367C22.5848 14.3315 22.4352 14.0826 22.3041 13.9021C22.3596 13.7543 22.414 13.5732 22.4473 13.3692C22.5426 12.7857 22.4608 11.9957 21.7482 11.3383C21.301 10.9249 20.7077 10.7201 20.1465 10.6173C19.5762 10.5129 18.9701 10.5014 18.4303 10.5271C17.8871 10.553 17.3897 10.6177 17.0293 10.6755C16.8485 10.7044 16.7006 10.7319 16.5969 10.7524C16.545 10.7626 16.504 10.7711 16.4753 10.7772L16.4465 10.7835C16.2767 10.8127 16.1004 10.8472 15.9166 10.8879C15.9176 10.8109 15.9236 10.7124 15.9386 10.5884C15.9877 10.1833 16.1238 9.57481 16.4163 8.67814C17.0456 6.75441 16.9047 5.43702 16.2643 4.58987C15.6381 3.76143 14.6875 3.5895 14.1627 3.5895C13.6555 3.5895 13.3235 3.89075 13.149 4.21465C12.9929 4.50432 12.9404 4.83899 12.9208 5.09751C12.9001 5.36964 12.9113 5.63254 12.9261 5.8212Z" stroke="white" stroke-width="1.3" stroke-linejoin="round"/>
<path d="M7.34175 23.2783C7.70504 23.2809 8.04815 23.132 8.27682 22.8488C8.50519 22.566 8.57817 22.2007 8.50503 21.8497C8.50503 21.8496 8.50503 21.8496 8.50503 21.8496L7.204 15.603L7.204 15.6029C7.06791 14.9497 6.44949 14.4357 5.77589 14.4357H3.29278C2.93392 14.4357 2.64296 14.7265 2.64278 15.0853L2.63906 22.6279C2.63898 22.8004 2.70742 22.9658 2.82933 23.0878C2.95124 23.2097 3.11662 23.2783 3.28906 23.2783H7.34175ZM7.34175 23.2783C7.34045 23.2783 7.33916 23.2782 7.33786 23.2782L7.34456 22.6283V23.2783H7.34175Z" stroke="white" stroke-width="1.3" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

@ -42,6 +42,13 @@
name="msapplication-config"
content="%PUBLIC_URL%/clients/%REACT_APP_CLIENT%/favicon/browserconfig.xml" />
<% } %>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-5K7GHJB');</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
@ -59,18 +66,11 @@
<!-- Start of ChromeCast script -->
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
<!-- End of ChromeCast script -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LZXGB5GJG0"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LZXGB5GJG0');
</script>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5K7GHJB"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<noscript>
<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5K7GHJB"
height="0" width="0" style="display:none;visibility:hidden"></iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->
</body>
</html>

@ -1,15 +1,28 @@
<!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()
.catch((err) => {
console.error('OIDC: silent refresh callback error', err);
});
// обновляем рефреш токен в локалсторадже
// так как 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>
</html>

@ -107,7 +107,7 @@ export const AccessTimer = ({
<T9n t='sign_in_full_game' />
</SignText>
</TimerContainer>
<SignInBtn onClick={() => logout('saveToken')}>
<SignInBtn onClick={() => logout('saveToken')} id='match_register'>
<T9n t='sign_in' />
</SignInBtn>
</AccessTimerContainer>

@ -0,0 +1,163 @@
import { useEffect, useState } from 'react'
import ReactGA from 'react-ga'
import { updateAdsView } from 'requests'
import { useToggle } from 'hooks'
import { getLocalStorageItem, isMatchPage } from 'helpers'
import {
device,
COUNTRY,
} from 'config'
import { useMatchPageStore } from 'features/MatchPage/store'
import type { AdComponentType } from './index'
import { checkVideo } from '../../helpers'
import {
adsViews,
EventGA,
ViewsType,
} from '../../types'
const countryCode = getLocalStorageItem(COUNTRY)
export const useAd = ({ ad }: AdComponentType) => {
const isMatch = isMatchPage()
const [isOpenAd, setIsOpenAd] = useState(isMatch)
const [isNeedToShow, setIsNeedToShow] = useState(isMatch)
const [shownTime, setShownTime] = useState(0)
const { isFullscreen } = useMatchPageStore()
const views = getLocalStorageItem(adsViews) as ViewsType
const {
duration,
frequency,
id,
media,
name,
time_close,
} = ad
const {
close,
isOpen: isOpenCloseBtn,
open: showCloseBtn,
} = useToggle()
const isNeedBanner = Number(views?.HOME) % frequency === 0
const isVideo = checkVideo(media.url)
const currentAdsTime = duration - shownTime
useEffect(() => {
if (!isFullscreen) {
if (currentAdsTime === 0) {
setShownTime(0)
} else {
const stopWatch = setInterval(() => {
setShownTime((prev) => prev + 1)
}, 1000)
return () => clearInterval(stopWatch)
}
}
return undefined
}, [
isFullscreen,
currentAdsTime,
])
const handleClose = () => {
setIsOpenAd(false)
isMatch && setIsNeedToShow(false)
sendBannerClickEvent(EventGA.CLOSE)
}
const sendBannerClickEvent = (event: EventGA) => {
ReactGA.initialize('Advertisement')
ReactGA.event({
action: event,
category: 'Advertisement',
label: `${name}_${countryCode ?? ''}_${device}`,
value: id,
})
}
useEffect(() => {
setShownTime(0)
if (isMatch) {
const interval = setInterval(() => {
setIsNeedToShow(true)
setIsOpenAd(true)
}, frequency * 1000)
return () => clearInterval(interval)
}
setIsNeedToShow(isNeedBanner)
return setIsOpenAd(isNeedBanner)
}, [
frequency,
isNeedToShow,
views?.HOME,
isNeedBanner,
isMatch,
])
useEffect(() => {
if (isFullscreen || !isOpenAd) return undefined
const timeoutCloseAd = setTimeout(handleClose, currentAdsTime * 1000)
return () => {
clearTimeout(timeoutCloseAd)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
isNeedToShow,
isOpenAd,
isFullscreen,
currentAdsTime,
])
useEffect(() => {
close()
const timeoutCloseBtn = time_close && setTimeout(showCloseBtn, time_close * 1000)
return () => {
time_close && clearTimeout(timeoutCloseBtn)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
isNeedToShow,
isOpenAd,
views?.HOME,
])
useEffect(() => {
if (!isNeedToShow || (!isMatch && !isNeedBanner)) return
(async () => {
await updateAdsView({ adv_id: id })
})()
sendBannerClickEvent(EventGA.DISPLAY)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
id,
isNeedToShow,
isMatch,
])
return {
handleClose,
isNeedToShow,
isOpenAd,
isOpenCloseBtn,
isVideo,
sendBannerClickEvent,
}
}

@ -0,0 +1,91 @@
import type { MouseEvent } from 'react'
import { memo } from 'react'
import type { AdType } from 'requests'
import { useLexicsStore } from 'features/LexicsStore'
import { useAd } from './hooks'
import { EventGA } from '../../types'
import {
AdImg,
AdVideo,
AdWrapper,
LinkWrapper,
AdsCloseButton,
} from './styled'
export type AdComponentType = {
ad: AdType,
}
export const AdComponent = memo(({ ad }: AdComponentType) => {
const {
link,
media,
position,
} = ad
const {
handleClose,
isNeedToShow,
isOpenAd,
isOpenCloseBtn,
isVideo,
sendBannerClickEvent,
} = useAd({ ad })
const { suffix } = useLexicsStore()
const close = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
handleClose()
}
const onLinkClick = () => {
link && sendBannerClickEvent(EventGA.CLICK)
}
return (
position && isOpenAd && isNeedToShow
? (
<AdWrapper
position={position.id}
isOpenAd={isOpenAd}
>
{isOpenCloseBtn && (
<AdsCloseButton
onClick={close}
size={12}
position={position.id}
/>
)}
<LinkWrapper
href={link}
target='_blank'
rel='noreferrer'
onClick={onLinkClick}
>
{isVideo
? (
<AdVideo
muted={isVideo}
autoPlay={isVideo}
loop={isVideo}
src={media.url}
position={position.id}
/>
)
: (
<AdImg
src={media.url}
position={position.id}
alt={`name_${suffix}`}
/>
)}
</LinkWrapper>
</AdWrapper>
) : null
)
})

@ -0,0 +1,109 @@
import styled, { css } from 'styled-components/macro'
import includes from 'lodash/includes'
import { CloseButton } from 'features/PopupComponents'
import {
MATCH_ADS,
PLAYER_ADS,
VIEW_ADS,
} from '../../types'
type Props = {
position: number,
}
const header = [7, 8, 9]
const chooseStyle = (type: number) => {
switch (true) {
case VIEW_ADS.COLUMN === type:
return 'grid-row: 1 / 3; img {max-height: none;}'
case VIEW_ADS.ROW === type:
return 'grid-column: 1 / 3'
case VIEW_ADS.SQUARE === type:
return 'grid-row: 1 / 3; grid-column: 1 / 3; img {max-height: none;}'
case VIEW_ADS.SECOND_COLUMN === type:
return 'grid-column: 2 / 3; grid-row: 1 / 1'
case VIEW_ADS.SECOND_ROW === type:
return 'grid-column: 1 / 2; grid-row: 2 / 3;'
case MATCH_ADS.PLAYS_TOP === type:
return 'margin-left: 14px; height: 48px'
case MATCH_ADS.PLAYS_BOTTOM === type:
return 'grid-row: 4; margin-bottom: 12px; height: 48px;'
case PLAYER_ADS.LEFT_BOTTOM === type:
return css`
height: auto;
width: 42.4%;
bottom: 100px;
left: 20px;
`
case PLAYER_ADS.CENTER_BOTTOM === type:
return css`
height: 18.3%;
width: 81.3%;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
`
case PLAYER_ADS.RIGHT === type:
return css`
height: 87.2%;
width: 18.3%;
bottom: 90px;
right: 18px;
`
case PLAYER_ADS.FULL_SCREEN === type:
return 'bottom: 0; left: 0;'
default:
return ''
}
}
export const AdImg = styled.img<Props>`
width: 100%;
min-height: ${({ position }) => (!includes(header, position) && '100%')};
max-height: ${({ position }) => (includes(header, position) ? '13rem' : '100%')};
cursor: pointer;
border-radius: 3px;
`
export const AdVideo = styled.video<Props>`
object-fit: contain;
width: 100%;
cursor: pointer;
max-height: ${({ position }) => (includes(header, position) ? '283px' : '100%')};
background-color: black;
border-radius: 3px;
`
export const AdWrapper = styled.div<Props & {isOpenAd: boolean}>`
position: ${({ position }) => (includes(PLAYER_ADS, position) ? 'absolute' : 'relative')};
width: 100%;
height: 100%;
z-index: 1;
${({ position }) => chooseStyle(position)};
display: ${({ isOpenAd }) => (isOpenAd ? '' : 'none')};
`
export const AdsCloseButton = styled(CloseButton)<Props>`
position: absolute;
right: ${({ position }) => (position === PLAYER_ADS.FULL_SCREEN ? '10px' : '0')};
top: ${({ position }) => (position === PLAYER_ADS.FULL_SCREEN ? '10px' : '0')};
background: none;
border-radius: 0;
z-index: 2;
cursor: pointer;
color: #9B9B9B;
width: 28px;
height: 28px;
:hover {
background: none;
}
`
export const LinkWrapper = styled.a``

@ -0,0 +1,79 @@
import type { MouseEvent } from 'react'
import includes from 'lodash/includes'
import type { AdType } from 'requests'
import { useLexicsStore } from 'features/LexicsStore'
import { useAd } from '../AdComponent/hooks'
import { EventGA, PLAYER_MOBILE_FULL_SCREEN } from '../../types'
import {
AdsCloseButton,
Img,
MobileAdWrapper,
Video,
} from './styled'
type MobileAdTypes = {
ad: AdType,
}
export const MobileAd = ({ ad }: MobileAdTypes) => {
const {
link,
media,
position,
} = ad
const { suffix } = useLexicsStore()
const {
handleClose,
isNeedToShow,
isOpenAd,
isOpenCloseBtn,
isVideo,
sendBannerClickEvent,
} = useAd({ ad })
const close = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
handleClose()
}
const onLinkClick = () => {
if (link) {
sendBannerClickEvent(EventGA.CLICK)
window.open(link, '_blank')
}
}
return (
position && isOpenAd && isNeedToShow ? (
<MobileAdWrapper
position={position.id}
onClick={onLinkClick}
>
{isOpenCloseBtn
&& (
<AdsCloseButton
position={position.id}
onClick={close}
size={includes(PLAYER_MOBILE_FULL_SCREEN, position.id) ? 12 : 8}
/>
)}
{isVideo
? <Video position={position.id} src={media.url} />
: (
<Img
position={position.id}
src={media.url}
alt={`name_${suffix}`}
/>
)}
</MobileAdWrapper>
) : null
)
}

@ -0,0 +1,115 @@
import styled, { css } from 'styled-components/macro'
import includes from 'lodash/includes'
import { CloseButton } from 'features/PopupComponents'
import {
MATCH_ADS,
PLAYER_MOBILE_FULL_SCREEN,
PLAYER_MOBILE_ADS,
MATCH_PAGE_MOBILE_ADS,
VIEW_ADS,
} from '../../types'
type Props = {
position: number,
}
const chooseStyle = (type: number) => {
switch (type) {
case MATCH_ADS.PLAYS_BOTTOM_MOBILE:
return css`
grid-row: 4;
margin-bottom: 12px;
height: 48px;
`
case PLAYER_MOBILE_ADS:
return css`
position: absolute;
width: 92%;
bottom: 50px;
left: 15px;
`
case PLAYER_MOBILE_FULL_SCREEN.VERTICAL_FULL_SCREEN:
case PLAYER_MOBILE_FULL_SCREEN.HORIZONTAL_FULL_SCREEN:
return css`
position: absolute;
top: 0;
left: 0;
height: 100%;
padding: 5px;
background-color: rgba(0, 0, 0, 0.7);
`
case VIEW_ADS.MOBILE_IN_COLLAPSE_FOOTER:
case VIEW_ADS.MOBILE_IN_COLLAPSE_HEADER:
return 'margin-top: 6px'
default:
return ''
}
}
export const MobileAdWrapper = styled.div<Props>`
position: relative;
width: 100%;
z-index: ${({ position }) => (includes(PLAYER_MOBILE_FULL_SCREEN, position) ? '101' : '4')};
${({ position }) => chooseStyle(position)};
`
export const AdsCloseButton = styled(CloseButton)<Props>`
position: absolute;
right: ${({ position }) => (includes(PLAYER_MOBILE_FULL_SCREEN, position) ? '-5px' : '-10px')};
top: ${({ position }) => (includes(PLAYER_MOBILE_FULL_SCREEN, position) ? '15px' : '10px')};
background: none;
border-radius: 0;
transform: translate(-50%, -50%);
z-index: 2;
color: #9B9B9B;
`
export const Img = styled.img<Props>`
border-radius: 2px;
width: 100%;
object-fit: ${({ position }) => {
switch (true) {
case position === PLAYER_MOBILE_FULL_SCREEN.VERTICAL_FULL_SCREEN:
return 'fill'
case position === MATCH_ADS.PLAYS_TOP_MOBILE:
return 'contain'
default:
return 'cover'
}
}};
height: ${({ position }) => {
switch (true) {
case position === 10:
return '50px'
case includes(MATCH_PAGE_MOBILE_ADS, position):
return '100%'
default:
return '75px'
}
}}
`
export const Video = styled.video<Props>`
max-height: 100%;
object-fit: cover;
min-width: 100%;
height: ${({ position }) => (position === 10 ? '50px' : '75px')};
border-radius: 2px;
height: ${({ position }) => {
switch (true) {
case position === 10:
return '50px'
case position === MATCH_ADS.PLAYS_TOP_MOBILE:
return '48px'
default:
return '75px'
}
}}
`

@ -0,0 +1,16 @@
import type { AdResponse, AdsListType } from 'requests'
export const calcMaxAdDurationAds = (advertisements: AdResponse) => {
const allAds = Object.values(advertisements)
const combineAds = allAds.reduce((result, currentAd) => {
result.push(...currentAd)
return result
}, [] as AdsListType)
const maxDuration = combineAds
.reduce((result, { duration }) => Math.max(result, duration), 0)
return maxDuration
}

@ -0,0 +1 @@
export * from './isVideo'

@ -0,0 +1,2 @@
const regexp = /^https?:\/\/\S+(?:mp4)$/
export const checkVideo = (url: string) => regexp.test(url)

@ -0,0 +1,65 @@
import { useMemo } from 'react'
import { useQuery } from 'react-query'
import { useRecoilState } from 'recoil'
import { isMobileDevice, querieKeys } from 'config'
import { getAds } from 'requests'
import { isMatchPage } from 'helpers/isMatchPage'
import { useLang } from 'features/LexicsStore/hooks/useLang'
import { useAuthStore } from 'features/AuthStore'
import {
DeviceType,
PageType,
} from './types'
import { calcMaxAdDurationAds } from './helpers/calcMaxDurationAds'
import { adsStore } from '../../pages/HighlightsPage/storeHighlightsAtoms'
type Props = {
matchId?: number,
sportType?: number,
tournamentId?: number,
}
export const useAds = ({
matchId,
sportType,
tournamentId,
}: Props) => {
const [ads, setAds] = useRecoilState(adsStore)
const { lang } = useLang()
const { user } = useAuthStore()
useQuery({
enabled: isMatchPage() ? (!!user && !!tournamentId) : !!user,
queryFn: async () => {
const adsList = await getAds({
client_type: isMobileDevice ? DeviceType.MOBILE : DeviceType.WEB,
language: lang,
type_id: isMatchPage() ? PageType.MATCH : PageType.HOME,
...isMatchPage() && {
matches: [{
match_id: matchId,
sport_id: sportType,
}],
tournaments: [{
sport_id: sportType,
tournament_id: tournamentId,
}],
},
})
adsList && setAds(adsList)
return adsList
},
queryKey: [querieKeys.ads, matchId],
staleTime: useMemo(() => Math.max(calcMaxAdDurationAds(ads), 60 * 1000), [ads]),
})
return {
ads,
}
}

@ -0,0 +1,31 @@
import type { AdType } from 'requests'
import { isMobileDevice } from 'config'
import type { AdsPropsType } from './types'
import { AdComponent } from './components/AdComponent'
import { MobileAd } from './components/MobileAd'
import {
HeaderWrapAd,
} from './styled'
export const HeaderAds = ({ ads }: AdsPropsType) => (
ads?.length ? (
<HeaderWrapAd column={ads?.length}>
{ads.map((ad: AdType) => (
!isMobileDevice ? (
<AdComponent
ad={ad}
key={ad.id}
/>
) : (
<MobileAd
ad={ad}
key={ad.id}
/>
)
))}
</HeaderWrapAd>
) : null
)

@ -0,0 +1,16 @@
import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config'
export const HeaderWrapAd = styled.div<{column: number}>`
width: 100%;
margin-bottom: 0.7rem;
display: grid;
grid-column-gap: 0.9rem;
grid-template-columns: ${({ column }) => (column > 1 ? `repeat(${column},${16.3 * 6 / column}%)` : 'repeat(1, 98.7%)')};
${isMobileDevice && css`
padding: 0 0.71rem;
grid-template-columns: none;
`}}
`

@ -0,0 +1,59 @@
import type { AdsListType } from 'requests'
export enum PageType {
HOME = 1,
MATCH = 2,
}
export enum DeviceType {
MOBILE = 'mobile',
WEB = 'web'
}
export type ViewsType = Partial<Record<keyof typeof PageType, number>>
export enum EventGA {
CLICK = 'banner_click',
CLOSE = 'banner_close',
DISPLAY = 'banner_display'
}
export enum VIEW_ADS {
ROW = 4,
COLUMN = 5,
SQUARE = 6,
SECOND_COLUMN = 2,
SECOND_ROW = 3,
MOBILE_IN_COLLAPSE_HEADER = 12,
MOBILE_IN_COLLAPSE_FOOTER = 25
}
export const HEADER_MOBILE_ADS = [10, 11]
export enum MATCH_ADS {
WATCH_TOP = 13,
PLAYS_TOP = 14,
PLAYS_BOTTOM = 15,
PLAYS_TOP_MOBILE = 16,
PLAYS_BOTTOM_MOBILE = 17,
}
export enum PLAYER_ADS {
LEFT_BOTTOM = 18,
CENTER_BOTTOM = 19,
RIGHT = 20,
FULL_SCREEN = 21,
}
export const PLAYER_MOBILE_ADS = 22
export enum PLAYER_MOBILE_FULL_SCREEN {
VERTICAL_FULL_SCREEN = 23,
HORIZONTAL_FULL_SCREEN = 24,
}
export const MATCH_PAGE_MOBILE_ADS = [16, 17, 22, 23, 24]
export type AdsPropsType = Record<'ads', AdsListType | undefined>
export const adsViews = 'adsViews'

@ -0,0 +1,72 @@
import type { Props, State } from '../types'
import {
createVariableSizingTransformationSet,
createClones,
createDefaultTransformationSet,
getElementDimensions,
getItemsCount,
getItemsOffset,
getTransitionProperty,
getTranslate3dProperty,
getItemsInSlide,
} from './elements'
import { getActiveIndex, getStartIndex } from './math'
export const calculateInitialState = (props: Props, el: HTMLElement | null): State => {
const {
activeIndex: propsActiveIndex = 0,
animationDuration = 0,
infinite = false,
variableSizing = false,
} = props
const clones = createClones(props)
const transition = getTransitionProperty()
const itemsCount = getItemsCount(props)
const itemsOffset = getItemsOffset(props)
const itemsInSlide = getItemsInSlide(itemsCount, props)
const startIndex = getStartIndex(propsActiveIndex, itemsCount)
const activeIndex = getActiveIndex({
infinite,
itemsCount,
startIndex,
})
const { width: listWidth } = getElementDimensions(el)
const transformationSet = variableSizing
? createVariableSizingTransformationSet({
el,
infinite,
listWidth,
props,
}).coords
: createDefaultTransformationSet({
children: clones,
infinite,
itemsInSlide,
listWidth,
props,
}).coords
const translate3d = getTranslate3dProperty(activeIndex, {
infinite,
itemsInSlide,
itemsOffset,
transformationSet,
variableSizing,
})
return {
activeIndex,
animationDuration,
clones,
infinite,
itemsCount,
itemsInSlide,
itemsOffset,
listWidth,
transformationSet,
transition,
translate3d,
variableSizing,
}
}

@ -0,0 +1,274 @@
import type { CSSProperties, ReactNode } from 'react'
import { Children } from 'react'
import type {
Transformations,
ItemCoords,
Props,
State,
Transition,
} from '../types'
import { mapPartialCoords, mapPositionCoords } from './mappers'
import { getShiftIndex } from './math'
export const getSlides = ({ children }: Props) => Children.toArray(children)
export const getItemsCount = (props: Props) => getSlides(props).length
export const getItemsOffset = ({ infinite }: Props) => (infinite ? 1 : 0)
export const createClones = (props: Props) => {
const slides = getSlides(props)
if (!props.infinite) return slides
const itemsCount = getItemsCount(props)
const itemsOffset = getItemsOffset(props)
const itemsInSlide = getItemsInSlide(itemsCount, props)
const cursor = Math.min(itemsInSlide, itemsCount) + itemsOffset
const clonesAfter = slides.slice(0, cursor)
const clonesBefore = slides.slice(-cursor)
if (itemsOffset && itemsInSlide === itemsCount) {
const afterOffsetClone = slides[0]
const [beforeOffsetClone] = slides.slice(-1)
clonesBefore.unshift(beforeOffsetClone)
clonesAfter.push(afterOffsetClone)
}
return clonesBefore.concat(slides, clonesAfter)
}
type CreateVariableSizingTransformationSetArgs = {
el: HTMLElement | null,
infinite: boolean,
listWidth: number,
props: Props,
}
export const createVariableSizingTransformationSet = ({
el,
infinite,
listWidth,
props,
}: CreateVariableSizingTransformationSetArgs) => {
let content = 0
let partial = true
let coords: Array<ItemCoords> = []
const { spaceBetween = 0 } = props
const children: Array<HTMLElement | Element> = Array.from(el?.children || [])
coords = children.reduce<Array<ItemCoords>>((
acc,
child,
i,
) => {
let position = 0
const previewsChildCursor = i - 1
const previewsChild = acc[previewsChildCursor]
const { width = 0 } = getElementDimensions(child?.firstChild as HTMLElement)
content += width + spaceBetween
partial = listWidth >= content
if (previewsChild) {
position = previewsChildCursor === 0
? previewsChild.width + spaceBetween
: previewsChild.width + previewsChild.position + spaceBetween
}
acc.push({ position, width })
return acc
}, [])
if (!infinite) {
if (partial) {
coords = mapPartialCoords(coords)
} else {
const position = content - listWidth
coords = mapPositionCoords(coords, position)
}
}
return {
content,
coords,
partial,
}
}
type CreateDefaultTransformationSetArgs = {
children: Array<ReactNode>,
infinite: boolean,
itemsInSlide: number,
listWidth: number,
props: Props,
}
export const createDefaultTransformationSet = ({
children,
infinite,
itemsInSlide,
listWidth,
props,
}: CreateDefaultTransformationSetArgs): Transformations => {
let content = 0
let partial = true
let coords: Array<ItemCoords> = []
const { spaceBetween = 0 } = props
const width = getItemWidth({
galleryWidth: listWidth,
itemsInSlide,
props,
})
coords = children.reduce<Array<ItemCoords>>((
acc,
_,
i,
) => {
let position = 0
const previewsChild = acc[i - 1]
content += width + spaceBetween
partial = listWidth >= content
if (previewsChild) {
position = width + spaceBetween + previewsChild.position || 0
}
acc.push({ position, width })
return acc
}, [])
if (!infinite) {
if (partial) {
coords = mapPartialCoords(coords)
} else {
const position = content - listWidth
coords = mapPositionCoords(coords, position)
}
}
return {
content,
coords,
partial,
}
}
type GetItemWidthArgs = {
galleryWidth: number,
itemsInSlide: number,
props: Props,
}
export const getItemWidth = ({
galleryWidth,
itemsInSlide,
props: { spaceBetween = 0 },
}: GetItemWidthArgs) => (itemsInSlide > 0
? (galleryWidth - spaceBetween * (itemsInSlide - 1)) / itemsInSlide
: galleryWidth)
export const getElementDimensions = (element: HTMLElement | null) => {
const { height, width } = element?.getBoundingClientRect() || { height: 0, width: 0 }
return { height, width }
}
export const getTransitionProperty = (options?: Transition): string => {
const { animationDuration = 0, animationTimingFunction = 'ease' } = options || {}
return `transform ${animationDuration}ms ${animationTimingFunction} 0ms`
}
export const getListElementStyles = (
{ translate3d }: Partial<State>,
currentStyles: CSSProperties,
): CSSProperties => {
const transform = `translate3d(${-(translate3d || 0)}px, 0, 0)`
return { ...currentStyles, transform }
}
export const getItemStyles = (index: number, state: State): CSSProperties => {
const { transformationSet } = state
const { width } = transformationSet[index] || {}
return {
width,
}
}
export const getTranslate3dProperty = (nextIndex: number, state: Partial<State>) => {
let cursor = nextIndex
const {
infinite,
itemsInSlide = 0,
itemsOffset = 0,
transformationSet = [],
} = state
if (infinite) {
cursor = nextIndex + getShiftIndex(itemsInSlide, itemsOffset)
}
return (transformationSet[cursor] || {}).position || 0
}
export const isDisplayedItem = (i = 0, state: State) => {
const {
activeIndex,
infinite,
itemsInSlide,
itemsOffset,
variableSizing,
} = state
const shiftIndex = getShiftIndex(itemsInSlide, itemsOffset)
if (variableSizing && infinite) {
return i - shiftIndex === activeIndex + itemsOffset
}
const index = activeIndex + shiftIndex
if (!infinite) {
return i >= activeIndex && i < index
}
return i >= index && i < index + itemsInSlide
}
export const getItemsInSlide = (itemsCount: number, props: Props) => {
let itemsInSlide = 1
const {
breakpoints = {},
infinite,
variableSizing,
} = props
if (variableSizing) {
return infinite ? itemsCount : itemsInSlide
}
const configKeys = Object.keys(breakpoints)
configKeys.forEach((key) => {
if (Number(key) <= window.innerWidth) {
const { items, itemsFit = 'fill' } = breakpoints[key]
itemsInSlide = itemsFit === 'contain' ? items : Math.min(items, itemsCount)
}
})
return itemsInSlide || 1
}

@ -0,0 +1,4 @@
export * from './common'
export * from './elements'
export * from './math'
export * from './mappers'

@ -0,0 +1,12 @@
import type { ItemCoords } from '../types'
export const mapPartialCoords = (coords: Array<ItemCoords>) => (
coords.map(({ width }) => ({ position: 0, width }))
)
export const mapPositionCoords = (coords: Array<ItemCoords>, position = 0) => coords.map((item) => {
if (item.position > position) {
return { ...item, position }
}
return item
})

@ -0,0 +1,44 @@
import type { ItemCoords } from '../types'
export const getShiftIndex = (itemsInSlide = 0, itemsOffset = 0) => itemsInSlide + itemsOffset
export const getStartIndex = (index = 0, itemsCount = 0) => {
if (itemsCount) {
if (index >= itemsCount) {
return itemsCount - 1
}
if (index > 0) {
return index
}
}
return 0
}
export const getActiveIndex = ({
infinite = false,
itemsCount = 0,
startIndex = 0,
}) => (infinite ? startIndex : getStartIndex(startIndex, itemsCount))
export const getUpdateSlidePositionIndex = (activeIndex: number, itemsCount: number) => {
if (activeIndex < 0) return itemsCount - 1
if (activeIndex >= itemsCount) return 0
return activeIndex
}
export const shouldRecalculateSlideIndex = (activeIndex: number, itemsCount: number) => (
activeIndex < 0 || activeIndex >= itemsCount
)
export const shouldCancelSlideAnimation = (activeIndex: number, itemsCount: number) => (
activeIndex < 0 || activeIndex >= itemsCount
)
export const getTransformationItemIndex = (
transformationSet: Array<ItemCoords> = [],
position = 0,
) => transformationSet.findIndex((item) => item.position >= Math.abs(position))

@ -0,0 +1,160 @@
import {
useRef,
useEffect,
useLayoutEffect,
type ReactNode,
} from 'react'
import { KEYBOARD_KEYS } from 'config'
import { useEventListener, useObjectState } from 'hooks'
import type { State, Props } from './types'
import * as Utils from './helpers'
import { ListItem } from './styled'
export const useCarousel = (props: Props) => {
const [state, setState] = useObjectState<State>(Utils.calculateInitialState(props, null))
const isAnimationDisabledRef = useRef(false)
const slideEndTimeoutIdRef = useRef<number | null>(null)
const listElementRef = useRef<HTMLUListElement>(null)
const rootElementRef = useRef<HTMLDivElement>(null)
const {
activeIndex,
animationDuration,
clones,
itemsCount,
itemsInSlide,
transition,
translate3d,
} = state
const {
animationTimingFunction,
infinite,
onSlideChange,
useKeyboardNavigation,
} = props
const listElementStyles = Utils.getListElementStyles({ translate3d }, { transition })
const clearSlideEndTimeout = () => {
slideEndTimeoutIdRef.current && clearTimeout(slideEndTimeoutIdRef.current)
slideEndTimeoutIdRef.current = null
}
const slidePrev = () => {
const newActiveIndex = activeIndex - 1
handleSlideTo(newActiveIndex)
}
const slideNext = () => {
const newActiveIndex = activeIndex + 1
handleSlideTo(newActiveIndex)
}
const handleUpdateSlidePosition = (index: number) => {
const newTranslate3d = Utils.getTranslate3dProperty(index, state)
const newTransition = Utils.getTransitionProperty({ animationDuration: 0 })
setState({
activeIndex: index,
transition: newTransition,
translate3d: newTranslate3d,
})
}
const handleBeforeSlideEnd = (index: number) => {
if (Utils.shouldRecalculateSlideIndex(index, itemsCount)) {
const nextIndex = Utils.getUpdateSlidePositionIndex(index, itemsCount)
handleUpdateSlidePosition(nextIndex)
}
isAnimationDisabledRef.current = false
}
const handleSlideTo = (newActiveIndex = 0) => {
if (
isAnimationDisabledRef.current
|| newActiveIndex === activeIndex
|| (!infinite && Utils.shouldCancelSlideAnimation(newActiveIndex, itemsCount))
) return
isAnimationDisabledRef.current = true
clearSlideEndTimeout()
const newTranslate3d = Utils.getTranslate3dProperty(newActiveIndex, state)
const newTransition = Utils.getTransitionProperty({
animationDuration,
animationTimingFunction,
})
onSlideChange?.(newActiveIndex)
setState({
activeIndex: newActiveIndex,
transition: newTransition,
translate3d: newTranslate3d,
})
slideEndTimeoutIdRef.current = setTimeout(
() => handleBeforeSlideEnd(newActiveIndex),
animationDuration,
)
}
const renderItem = (item: ReactNode, index: number) => {
const styles = Utils.getItemStyles(index, state)
return (
<ListItem
key={index}
style={styles}
aria-hidden={!Utils.isDisplayedItem(index, state)}
>
{item}
</ListItem>
)
}
useEventListener({
callback: (e) => {
if (!useKeyboardNavigation) return
if (e.key === KEYBOARD_KEYS.ArrowLeft) slidePrev()
if (e.key === KEYBOARD_KEYS.ArrowRight) slideNext()
},
event: 'keydown',
})
useLayoutEffect(() => {
const setInitialState = () => {
const initialState = Utils.calculateInitialState(props, listElementRef.current)
setState(initialState)
}
setInitialState()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.activeIndex])
useEffect(() => () => {
slideEndTimeoutIdRef.current && clearTimeout(slideEndTimeoutIdRef.current)
}, [])
return {
activeIndex,
clones,
itemsCount,
itemsInSlide,
listElementRef,
listElementStyles,
renderItem,
rootElementRef,
slideNext,
slidePrev,
}
}

@ -0,0 +1,86 @@
import { ArrowButton, Arrow } from 'features/HeaderFilters/components/DateFilter/styled'
import type { Props } from './types'
import { useCarousel } from './hooks'
import {
Wrapper,
List,
ButtonsWrapper,
} from './styled'
export * from './types'
type NavButtonProps = {
direction: 'left' | 'right',
disabled?: boolean,
onClick: () => void,
}
const NavButton = ({
direction,
disabled,
onClick,
}: NavButtonProps) => (
<ArrowButton
aria-label={direction === 'left' ? 'Previous' : 'Next'}
disabled={disabled}
onClick={onClick}
>
<Arrow direction={direction} />
</ArrowButton>
)
export const Carousel = (props: Props) => {
const {
infinite,
renderNextButton,
renderPrevButton,
} = props
const {
activeIndex,
clones,
itemsCount,
itemsInSlide,
listElementRef,
listElementStyles,
renderItem,
rootElementRef,
slideNext,
slidePrev,
} = useCarousel(props)
return (
<Wrapper ref={rootElementRef}>
<List ref={listElementRef} style={listElementStyles}>
{clones.map(renderItem)}
</List>
<ButtonsWrapper>
{renderPrevButton
? renderPrevButton({
disabled: !infinite && activeIndex === 0,
onClick: slidePrev,
})
: (
<NavButton
disabled={!infinite && activeIndex === 0}
onClick={slidePrev}
direction='left'
/>
)}
{renderNextButton
? renderNextButton({
disabled: !infinite && itemsInSlide + activeIndex === itemsCount,
onClick: slideNext,
})
: (
<NavButton
disabled={!infinite && itemsInSlide + activeIndex === itemsCount}
onClick={slideNext}
direction='right'
/>
)}
</ButtonsWrapper>
</Wrapper>
)
}

@ -0,0 +1,25 @@
import styled from 'styled-components/macro'
export const Wrapper = styled.div`
width: 100%;
margin: auto;
overflow: hidden;
`
export const List = styled.ul`
display: flex;
gap: 20px;
width: 100%;
height: 100%;
`
export const ListItem = styled.li`
flex-shrink: 0;
height: 100%;
`
export const ButtonsWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
`

@ -0,0 +1,73 @@
import type { ReactNode } from 'react'
type RenderButtonArgs = {
disabled?: boolean,
onClick: () => void,
}
export type Transition = {
animationDuration?: number,
animationTimingFunction?: string,
}
export type Breakpoints = {
[key: string]: {
// Число элементов в слайде
items: number,
// Определяет, как элемент должен заполнять контейнер в соответствии с шириной слайда
itemsFit?: 'contain' | 'fill',
},
}
export type Transformations = {
content: number,
coords: Array<ItemCoords>,
partial: boolean,
}
export type ItemCoords = {
position: number,
width: number,
}
export type Props = {
/** Текущая позиция */
activeIndex?: number,
/** Длительность анимации */
animationDuration?: number,
/** animation-timing-function */
animationTimingFunction?: string,
/** Объект с брэкпойнтами */
breakpoints?: Breakpoints,
/** Элементы карусели */
children: ReactNode,
/** Бесконечный режим прокрутки */
infinite?: boolean,
/** Колбэк при прокрутке */
onSlideChange?: (activeIndex: number) => void,
/** Рендер-функция кнопки прокрутки вперед */
renderNextButton?: (args: RenderButtonArgs) => ReactNode,
/** Рендер-функция кнопки прокрутки назад */
renderPrevButton?: (args: RenderButtonArgs) => ReactNode,
/** Расстояние между элементами карусели */
spaceBetween?: number,
/** Использование клавиатуры для навигации */
useKeyboardNavigation?: boolean,
/** Использование произвольной ширины элементов карусели */
variableSizing?: boolean,
}
export type State = {
activeIndex: number,
animationDuration?: number,
clones: Array<ReactNode>,
infinite?: boolean,
itemsCount: number,
itemsInSlide: number,
itemsOffset: number,
listWidth: number,
transformationSet: Array<ItemCoords>,
transition: string,
translate3d: number,
variableSizing: boolean,
}

@ -1,4 +1,6 @@
import styled from 'styled-components/macro'
import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config'
export const WrapperError = styled.div`
position: fixed;
@ -14,4 +16,15 @@ export const WrapperError = styled.div`
text-align: center;
z-index: 1000000;
white-space: pre-wrap;
${isMobileDevice
&& css`
position: absolute;
width: auto;
top: 10%;
left: 50%;
max-width: 70vw;
transform: translateX(-50%)
`
}
`

@ -1,8 +1,8 @@
// eslint-disable react/destructuring-assignment
import React from 'react'
import { Component } from 'react'
import type{ ReactNode } from 'react'
import type{ ErrorInfo, ReactNode } from 'react'
import * as Sentry from '@sentry/react'
import { Error } from '../Error'
@ -10,41 +10,15 @@ interface Props {
children?: ReactNode,
}
interface State {
hasError: boolean,
}
class ErrorBoundary extends Component<Props, State> {
// eslint-disable-next-line react/state-in-constructor
public state: State = {
hasError: false,
}
public static getDerivedStateFromError(_: Error): State {
// Update state so the next render will show the fallback UI.
return { hasError: true }
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// eslint-disable-next-line no-console
console.error(
'Uncaught error:',
error,
errorInfo,
)
}
public render() {
const { hasError } = this.state
const { children } = this.props
return (
<>
{hasError && <Error />}
{children}
</>
)
}
}
export const ErrorBoundary = ({ children }: Props) => (
<Sentry.ErrorBoundary fallback={(
<>
{children}
<Error />
</>
)}
>
{ children }
</Sentry.ErrorBoundary>
)
export default ErrorBoundary

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
memo,
RefObject,
useEffect,
} from 'react'
@ -9,6 +9,8 @@ import styled from 'styled-components/macro'
import { Icon } from 'features/Icon'
import { useAuthStore } from 'features/AuthStore'
import { isIOS } from 'config'
const PipWrapper = styled.div`
cursor: pointer;
margin-left: 25px;
@ -17,44 +19,80 @@ const PipWrapper = styled.div`
`
type PipProps = {
isPlaying: boolean,
videoRef: RefObject<HTMLVideoElement>,
}
export const PiP = memo(({ isPlaying, videoRef }: PipProps) => {
export const PiP = ({ videoRef }: PipProps) => {
const { user } = useAuthStore()
const togglePip = async () => {
const isInPIP = () => Boolean(document.pictureInPictureElement)
const closePIP = async () => {
if (!isInPIP()) return
if (isIOS) {
await (videoRef.current as any)?.webkitSetPresentationMode('inline')
}
await document.exitPictureInPicture()
}
const openPIP = async () => {
if (isInPIP()) return
if (isIOS) {
await (videoRef.current as any)?.webkitSetPresentationMode('picture-in-picture')
}
await videoRef.current?.requestPictureInPicture()
}
try {
if (
document.pictureInPictureEnabled && videoRef.current !== document.pictureInPictureElement
) {
await videoRef.current?.requestPictureInPicture()
await openPIP()
} else {
await document.exitPictureInPicture()
await closePIP()
}
} catch (err) {
await document.exitPictureInPicture()
await closePIP()
}
}
useEffect(() => {
window.addEventListener('visibilitychange', async () => {
const onVisibilityChange = async () => {
if (
document.hidden === true
&& document.pictureInPictureEnabled
&& videoRef.current !== document.pictureInPictureElement
&& videoRef.current?.hidden === false
&& isPlaying
&& !videoRef.current?.paused
&& document.visibilityState !== 'visible'
&& user
) {
await videoRef.current?.requestPictureInPicture()
try {
await videoRef.current?.requestPictureInPicture()
} catch (error) { /* empty */ }
}
if (
document.visibilityState === 'visible'
&& document.pictureInPictureEnabled
&& videoRef.current === document.pictureInPictureElement
) {
try {
await document.exitPictureInPicture()
} catch (error) { /* empty */ }
}
})
}, [videoRef, isPlaying, user])
}
window.addEventListener('visibilitychange', onVisibilityChange)
return () => window.removeEventListener('visibilitychange', onVisibilityChange)
}, [videoRef, user])
return (
<PipWrapper>
<PipWrapper id='match_video_pic_in_pic'>
<Icon refIcon='PiP' onClick={togglePip} />
</PipWrapper>
)
})
}

@ -58,7 +58,10 @@ export const SimplePopup = (props: Props) => {
{buttonName
&& (
<Footer>
<ScApplyButton onClick={onHandle}>
<ScApplyButton
onClick={onHandle}
id='match_afterlimit_register'
>
<T9n t={buttonName} />
</ScApplyButton>
</Footer>

@ -51,6 +51,7 @@ export const SmartBanner = ({ setIsOpenDownload }: SmartBannerProps) => (
)
setIsOpenDownload(false)
}}
id='download_app'
>
<ScBtnDownload>Download</ScBtnDownload>
</a>

@ -1,5 +1,7 @@
import { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent'
import { PROCEDURES } from '../procedures'
import {
@ -9,7 +11,7 @@ import {
} from './types'
const randomHash = () => (
(Math.random() ** Math.random()) * 9999999999999999
(Math.random() ** Math.random()) * 100000000000000
)
const params = {
@ -21,9 +23,15 @@ export const facr: ClientConfig = {
clientId: ClientIds.Facr,
metaDataUrlParams: `?hash=${randomHash()}`,
},
currencyBadge: {
color: '#333333',
secondColor: 'rgba(255, 255, 255, 0.7)',
sign: 'Dollar',
},
defaultLanguage: 'cs',
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,
host: 'facr.tv',
name: ClientNames.Facr,
privacyLink: '/privacy-policy-and-statement',
requests: {
@ -36,8 +44,10 @@ export const facr: ClientConfig = {
styles: {
background: '',
homePageHeader: css`
background: linear-gradient(83.42deg, #00257A 53.04%, rgba(0, 0, 0, 0) 94.83%), #000000;
`,
background: ${(isMobileDevice
? 'linear-gradient(180deg, #00257A 0%, rgba(0, 37, 122, 0) 100%), #000000'
: 'linear-gradient(83.42deg, #00257A 53.04%, rgba(0, 0, 0, 0) 94.83%), #000000'
)};`,
logo: 'facr-logo.svg',
logoHeight: 6.3,
logoLeft: 1.1,

@ -0,0 +1,57 @@
import { css } from 'styled-components/macro'
import {
ClientConfig,
ClientIds,
ClientNames,
} from './types'
export const fqtv: ClientConfig = {
auth: {
clientId: ClientIds.Fqtv,
},
currencyBadge: {
color: '#333333',
secondColor: 'rgba(255, 255, 255, 0.7)',
sign: 'Dollar',
},
defaultLanguage: 'en',
description: 'Queensland’s streamed competitions, including NPL Men, NPL Women and McDonald\'s FQPL Leagues.',
disabledPreferences: true,
host: 'fqtv.com.au',
name: ClientNames.Fqtv,
privacyLink: '/privacy-policy-and-statement?client_id=insports-ott-web',
showSearch: true,
showSmartBanner: true,
styles: {
background: '',
homePageHeader: css`
background: linear-gradient(0deg, rgba(0, 13, 47, 0.8), rgba(0, 13, 47, 0.8)),
linear-gradient(0deg, #0061DC, #0061DC),
no-repeat center center url(/images/fqtv-background.png);
background-size: 130% 105%;
background-blend-mode: normal, hard-light, normal;
`,
logo: 'fqtv-logo.svg',
logoHeight: 2.465,
logoLeft: 0.71,
logoTop: 1,
logoWidth: 6.37,
matchLogoHeight: 2.465,
matchLogoWidth: 6.37,
matchPageMobileHeaderLogo: css`
width: 90px;
height: 27px;
top: 0;
`,
mobileHeaderLogo: css`
width: 86px;
height: 33px;
`,
userAccountLogo: css`
width: 6.37rem;
height: 2.465rem;
`,
},
termsLink: '/terms-and-conditions?client_id=insports-ott-web',
title: 'FQTV is the official home of Football',
}

@ -6,6 +6,7 @@ import { lff } from './lff'
import { insports } from './insports'
import { india } from './india'
import { tunisia } from './tunisia'
import { fqtv } from './fqtv'
export const currentClient = process.env.REACT_APP_CLIENT || 'insports'
@ -15,9 +16,11 @@ export const isInstatClient = currentClient === 'instat'
export const isFacrClient = currentClient === 'facr'
export const isIndiaClient = currentClient === 'india'
export const isTunisClient = currentClient === 'tunisia'
export const isFqtvClient = currentClient === 'fqtv'
const clients = {
facr,
fqtv,
india,
insports,
instat,

@ -8,9 +8,18 @@ import { insports } from './insports'
export const india: ClientConfig = {
...insports,
about_the_project: 'https://prsolution.pro',
auth: {
clientId: ClientIds.India,
},
currencyBadge: {
color: '#000000',
secondColor: 'rgba(255, 255, 255, 0.7)',
sign: 'Rupee',
},
disabledHighlights: true,
host: 'india.insports.tv',
name: ClientNames.India,
privacyLink: '/privacy-policy-and-statement?client_id=india-ott-web',
termsLink: '/terms-and-conditions?client_id=india-ott-web',
userAccountCardsHidden: true,
}

@ -1,4 +1,5 @@
import { css } from 'styled-components/macro'
import {
ClientConfig,
ClientIds,
@ -9,9 +10,15 @@ export const insports: ClientConfig = {
auth: {
clientId: ClientIds.Insports,
},
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: 'insports.tv',
name: ClientNames.Insports,
privacyLink: '/privacy-policy-and-statement?client_id=insports-ott-web',
showSearch: true,

@ -9,9 +9,15 @@ export const instat: ClientConfig = {
auth: {
clientId: ClientIds.Instat,
},
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: 'instat.tv',
name: ClientNames.Instat,
privacyLink: '/privacy-policy-and-statement',
showSearch: true,

@ -9,12 +9,18 @@ export const lff: ClientConfig = {
auth: {
clientId: ClientIds.Lff,
},
currencyBadge: {
color: '#333333',
secondColor: 'rgba(255, 255, 255, 0.7)',
sign: 'Dollar',
},
defaultLanguage: 'lv',
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.',
description: 'Latvijas Futbola federācija (LFF) pašmāju futbola spēļu tiešraižu, apskatu un ierakstu platforma.',
disabledHighlights: true,
disabledPreferences: true,
host: 'tv.lff.lv',
name: ClientNames.Lff,
privacyLink: '/clients/instat/terms-and-conditions.html',
privacyLink: '/privacy-policy-and-statement',
showSearch: true,
styles: {
background: 'background-image: url(/images/Checker.png);',

@ -7,7 +7,7 @@ import {
} from './types'
const randomHash = () => (
(Math.random() ** Math.random()) * 9999999999999999
(Math.random() ** Math.random()) * 100000000000000
)
export const tunisia: ClientConfig = {
@ -15,11 +15,17 @@ export const tunisia: ClientConfig = {
clientId: ClientIds.Tunisia,
metaDataUrlParams: `?hash=${randomHash()}`,
},
currencyBadge: {
color: '#333333',
secondColor: 'rgba(255, 255, 255, 0.7)',
sign: 'Dollar',
},
defaultLanguage: 'fr',
description: '',
disabledFilters: true,
disabledHighlights: true,
disabledPreferences: true,
host: 'diwansport.net',
name: ClientNames.Tunisia,
privacyLink: '/privacy-policy-and-statement?client_id=insports-ott-web',
showSearch: true,
@ -55,4 +61,5 @@ export const tunisia: ClientConfig = {
},
termsLink: '/terms-and-conditions?client_id=insports-ott-web',
title: 'Diwan Sport - The home of Tunisian Ligue Professionnelle 1',
userAccountCardsHidden: true,
}

@ -7,6 +7,7 @@ type StyledCss = ReturnType<typeof css>
export enum ClientIds {
Facr = 'facr-ott-web',
Fqtv = 'fqtv-ott-web',
India = 'india-ott-web',
Insports = 'insports-ott-web',
Instat = 'ott-web',
@ -15,13 +16,14 @@ export enum ClientIds {
}
export enum ClientNames {
Brasil = 'brasil',
Facr = 'facr',
India = 'india',
Insports = 'insports',
Instat = 'instat',
Lff = 'lff',
Tunisia = 'tunisia'
Brasil = 'brasil', // Бразилия
Facr = 'facr', // Чехия
Fqtv = 'fqtv', // Австралия
India = 'india', // Индия
Insports = 'insports', // Глобал
Instat = 'instat', // Глобал
Lff = 'lff', // Латвия
Tunisia = 'tunisia' // Тунис
}
export type ClientConfig = {
@ -30,11 +32,17 @@ export type ClientConfig = {
clientId: ClientIds,
metaDataUrlParams?: string,
},
currencyBadge: {
color: string,
secondColor: string,
sign: 'Dollar' | 'Rupee',
},
defaultLanguage: string,
description: string,
disabledFilters?: boolean,
disabledHighlights?: boolean,
disabledPreferences?: boolean,
host: string,
name: ClientNames,
privacyLink: string,
requests?: Record<ProcedureName, RequestParameters>,
@ -57,5 +65,6 @@ export type ClientConfig = {
},
termsLink: string,
title: string,
userAccountCardsHidden?: boolean,
userAccountLinksDisabled?: boolean,
}

@ -12,3 +12,5 @@ export * from './userAgent'
export * from './queries'
export * from './keyboardKeys'
export * from './clients'
export * from './localStorageKeys'
export * from './payments'

@ -3,6 +3,8 @@ import { publicLexics } from './public'
import { highlightsPageLexic } from './highlightsPageLexic'
import { mailingsLexics } from './mailings'
import { sportsLexic } from './sportsLexic'
import { landingLexics } from './landingLexics'
import { matchDownload } from './matchDownload'
const matchPopupLexics = {
actions: 1020,
@ -88,11 +90,16 @@ const confirmPopup = {
const buyMatchPopupLexics = {
add: 15075,
adding_card: 15074,
all_away_games_of: 20186,
all_games_of: 20182,
all_home_games_of: 20185,
auto_renewal: 16624,
best_choice: 20175,
buy_for: 14095,
buy_subscription: 13565,
cancel_anytime: 20171,
change_card: 13564,
choose_subscription: 13563,
choose_subscription: 20174,
completed: 14072,
description_all_season_matches: 15069,
description_all_team_matches: 15070,
@ -103,7 +110,11 @@ const buyMatchPopupLexics = {
for_month: 13561,
for_view: 15064,
for_year: 13562,
how_to_watch: 20199,
in: 20183,
in_season: 20184,
next_choose: 15156,
one_off_payment: 20172,
pass_league: 15065,
pass_match_access: 15067,
pass_team: 15066,
@ -112,8 +123,10 @@ const buyMatchPopupLexics = {
pay: 15073,
payment: 14096,
payment_confirmation: 14094,
per_month: 13573,
per_month: 20173,
per_year: 13574,
purchase: 12623,
subscription: 15604,
subscription_done: 2668,
success_subscription: 14097,
}
@ -142,6 +155,11 @@ const newDevicePopup = {
}
export const indexLexics = {
'1st_half': 1031,
'2nd_half': 1032,
'3rd_half': 20262,
'4th_half': 20263,
'5th_half': 20264,
add_to_favorites: 14967,
add_to_favorites_error: 12943,
all_competitions: 17926,
@ -157,7 +175,9 @@ export const indexLexics = {
cm: 817,
features: 13051,
football: 6958,
ft: 20261,
full_game: 13028,
full_time: 20258,
futsal: 17670,
game: 9680,
game_finished: 13026,
@ -173,9 +193,11 @@ export const indexLexics = {
hide_score: 12982,
highlights: 13033,
hockey: 6959,
ht: 20260,
interview: 13031,
kg: 652,
kickoff_in: 13027,
likes: 16628,
live: 13024,
loading: 3527,
logout: 4306,
@ -185,11 +207,15 @@ export const indexLexics = {
match_status_live: 12984,
match_status_soon: 12986,
match_video: 13025,
month_title: 2202,
my_likes: 20254,
no_match_access_body: 13419,
no_match_access_title: 13418,
player: 14975,
players: 164,
players_video: 13032,
pm: 20259,
pre_match: 20257,
privacy_policy_and_statement: 15404,
round_highilights: 13050,
save: 828,
@ -198,14 +224,21 @@ export const indexLexics = {
sport: 12993,
team: 14973,
terms_and_conditions: 15738,
timeline_title: 20266,
to_home: 13376,
total_likes: 20253,
tournament: 14974,
upcoming: 17925,
user_account: 12928,
user_liked_this: 20267,
users_liked_this: 20255,
volleyball: 9761,
watch_from_beginning: 13021,
watch_from_last_pause: 13022,
watch_now: 13020,
week_title: 6584,
you_and: 20256,
you_liked_this: 20265,
...filterPopup,
...confirmPopup,
@ -219,4 +252,6 @@ export const indexLexics = {
...paymentLexics,
...sportsLexic,
...sportsPopup,
...landingLexics,
...matchDownload,
}

@ -0,0 +1,9 @@
export const landingLexics = {
default_season: 19983,
inactive_button: 20083,
inactive_description_1: 20084,
inactive_description_2: 20086,
inactive_period: 801,
inactive_title_1: 20087,
inactive_title_2: 20088,
}

@ -0,0 +1,8 @@
export const matchDownload = {
choose_what_to_download: 20193,
download_files_for_periods: 20197,
download_full_match: 20198,
download_single_file: 20196,
entire_record: 20195,
in_game_time_only: 20194,
}

@ -7,4 +7,8 @@ export const lffTournamentsList = [
5975,
5976,
6004,
1000045,
1000046,
1000047,
1000048,
]

@ -0,0 +1 @@
export const COUNTRY = 'COUNTRY'

@ -7,6 +7,7 @@ export const PAGES = {
mailings: '/useraccount/mailings',
match: '/matches',
player: '/players',
subscriptions: '/subscriptions',
team: '/teams',
thanksForSubscribe: '/thanks-for-subscription',
tournament: '/tournaments',

@ -3,20 +3,27 @@ import { ClientNames } from './clients/types'
export enum PaymentSystem {
PagBrazil = 'pag_brasil',
Paymee = 'paymee',
Paytm = 'paytm',
PhonePe = 'phonePe',
Stripe = 'stripe'
}
export enum CountryCode {
BR = 'BR', // Бразилия
IN = 'IN', // Индия
TN = 'TN' // Тунис
}
type PaymentsType = {
[key in ClientNames]: PaymentSystem
}
export const payments: PaymentsType = {
[ClientNames.Tunisia]: PaymentSystem.Paymee,
brasil: PaymentSystem.PagBrazil,
[ClientNames.India]: PaymentSystem.Paytm,
[ClientNames.Brasil]: PaymentSystem.PagBrazil,
[ClientNames.India]: PaymentSystem.PhonePe,
[ClientNames.Insports]: PaymentSystem.Stripe,
[ClientNames.Instat]: PaymentSystem.Stripe,
[ClientNames.Facr]: PaymentSystem.Stripe,
[ClientNames.Lff]: PaymentSystem.Stripe,
[ClientNames.Fqtv]: PaymentSystem.Stripe,
}

@ -3,7 +3,9 @@ export const PROCEDURES = {
get_cities: 'get_cities',
get_languages: 'get_languages',
get_match_info: 'get_match_info',
get_match_plays: 'get_match_plays',
get_match_subscriptions: 'get_match_subscriptions',
get_match_watch: 'get_match_watch',
get_matches: 'get_matches',
get_objects: 'get_objects',
get_player_info: 'get_player_info',
@ -28,8 +30,6 @@ export const PROCEDURES = {
get_view_user_match: 'get_view_user_match',
landing_get_match_info: 'landing_get_match_info',
lst_c_country: 'lst_c_country',
ott_match_events: 'ott_match_events',
ott_match_popup: 'ott_match_popup',
ott_match_popup_actions: 'ott_match_popup_actions',
ott_match_popup_player_playlist: 'ott_match_popup_player_playlist',
param_lexical: 'param_lexical',

@ -1,4 +1,5 @@
export const querieKeys = {
ads: 'ads',
liveMatchScores: 'liveMatchScores',
matchScore: 'matchScore',
sportsList: 'sportsList',

@ -5,15 +5,18 @@ import { ENV, isProduction } from './env'
export const APIS = {
preproduction: {
api: 'https://api.insports.tv',
auth: 'https://auth.insports.tv',
auth: 'https://api.auth.insports.tv',
auth_old: 'https://auth.insports.tv',
},
production: {
api: 'https://api.insports.tv',
auth: 'https://auth.insports.tv',
auth: 'https://api.auth.insports.tv',
auth_old: 'https://auth.insports.tv',
},
staging: {
api: 'https://api.test.insports.tv',
auth: 'https://auth.test.insports.tv',
auth: 'https://api.auth.test.insports.tv',
auth_old: 'https://auth.test.insports.tv',
},
}
@ -29,11 +32,33 @@ const STATS_APIS = {
staging: 'https://statistic-stage.insports.tv',
}
const ADS_APIS = {
preproduction: 'https://ads.insports.tv/v1/ads',
production: 'https://ads.insports.tv/v1/ads',
staging: 'https://ads-test.insports.tv/v1/ads',
}
const PAYMENT_APIS = {
preproduction: 'https://pay.insports.tv',
production: 'https://pay.insports.tv',
staging: 'https://pay.test.insports.tv',
}
const LIKES_API = {
preproduction: 'wss://ws.insports.tv/v1/events',
production: 'wss://ws.insports.tv/v1/events',
staging: 'wss://ws-test.insports.tv/v1/events',
}
const env = isProduction ? ENV : readSelectedApi() ?? ENV
export const VIEWS_API = VIEWS_APIS[env]
export const AUTH_SERVICE = APIS[env].auth
export const AUTH_SERVICE_OLD = APIS[env].auth_old
export const API_ROOT = APIS[env].api
export const DATA_URL = `${API_ROOT}/data`
export const URL_AWS = 'https://cf-aws.insports.tv'
export const STATS_API_URL = STATS_APIS[env]
export const ADS_API_URL = ADS_APIS[env]
export const PAYMENT_API_URL = PAYMENT_APIS[env]
export const LIKES_API_URL = LIKES_API[env]

@ -1,5 +1,7 @@
export const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
export const device = navigator.userAgent
export const isAndroid = /Android/.test(navigator.userAgent)
export const isIOS = /iPad|iPhone|iPod/.test(device)
export const isMobileDevice = /iPhone|Android/.test(navigator.userAgent)
export const isAndroid = /Android/.test(device)
export const isMobileDevice = /iPhone|Android/.test(device)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save