develop #194

Merged
andrey.dekterev merged 5 commits from develop into master 3 years ago
  1. 46440
      package-lock.json
  2. 1
      package.json
  3. 4
      src/config/procedures.tsx
  4. 1
      src/features/AuthStore/helpers.tsx
  5. 47
      src/features/AuthStore/hooks/useAuth.tsx
  6. 3
      src/features/MatchPage/components/FinishedMatch/index.tsx
  7. 3
      src/features/MatchPage/components/LiveMatch/index.tsx
  8. 6
      src/features/MatchPage/components/MatchDescription/styled.tsx
  9. 32
      src/features/MatchPage/store/hooks/useMatchData.tsx
  10. 1
      src/features/MatchPage/store/hooks/useMatchPlaylists.tsx
  11. 7
      src/features/MatchPage/styled.tsx
  12. 1
      src/features/MatchPopup/store/hooks/index.tsx
  13. 2
      src/features/MatchSidePlaylists/index.tsx
  14. 6
      src/features/MatchSidePlaylists/styled.tsx
  15. 42
      src/features/UserFavorites/hooks/index.tsx
  16. 25
      src/features/UserFavorites/index.tsx
  17. 2
      src/hooks/useInterval.tsx
  18. 4
      src/requests/checkDevice.tsx
  19. 15
      src/requests/getMatchEvents.tsx
  20. 22
      src/requests/getMatchPlaylists.tsx

46440
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -27,6 +27,7 @@
"@stripe/stripe-js": "^1.13.2", "@stripe/stripe-js": "^1.13.2",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"date-fns": "^2.14.0", "date-fns": "^2.14.0",
"dueljs": "^1.2.7",
"eslint-plugin-typescript-sort-keys": "^2.3.0", "eslint-plugin-typescript-sort-keys": "^2.3.0",
"history": "^4.10.1", "history": "^4.10.1",
"hls.js": "^1.1.1", "hls.js": "^1.1.1",

@ -3,7 +3,9 @@ export const PROCEDURES = {
get_cities: 'get_cities', get_cities: 'get_cities',
get_languages: 'get_languages', get_languages: 'get_languages',
get_match_info: 'get_match_info', get_match_info: 'get_match_info',
get_match_plays: 'get_match_plays',
get_match_subscriptions: 'get_match_subscriptions', get_match_subscriptions: 'get_match_subscriptions',
get_match_watch: 'get_match_watch',
get_matches: 'get_matches', get_matches: 'get_matches',
get_objects: 'get_objects', get_objects: 'get_objects',
get_player_info: 'get_player_info', get_player_info: 'get_player_info',
@ -28,8 +30,6 @@ export const PROCEDURES = {
get_view_user_match: 'get_view_user_match', get_view_user_match: 'get_view_user_match',
landing_get_match_info: 'landing_get_match_info', landing_get_match_info: 'landing_get_match_info',
lst_c_country: 'lst_c_country', 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_actions: 'ott_match_popup_actions',
ott_match_popup_player_playlist: 'ott_match_popup_player_playlist', ott_match_popup_player_playlist: 'ott_match_popup_player_playlist',
param_lexical: 'param_lexical', param_lexical: 'param_lexical',

@ -61,7 +61,6 @@ const redirectUrl = () => {
export const getClientSettings = (): Settings => ({ export const getClientSettings = (): Settings => ({
authority: AUTH_SERVICE, authority: AUTH_SERVICE,
automaticSilentRenew: true,
client_id: client.auth.clientId, client_id: client.auth.clientId,
filterProtocolClaims: false, filterProtocolClaims: false,
loadUserInfo: false, loadUserInfo: false,

@ -7,6 +7,8 @@ import {
import { useHistory } from 'react-router' import { useHistory } from 'react-router'
import type { User } from 'oidc-client' import type { User } from 'oidc-client'
// @ts-expect-error
import duel from 'dueljs'
import isString from 'lodash/isString' import isString from 'lodash/isString'
import isBoolean from 'lodash/isBoolean' import isBoolean from 'lodash/isBoolean'
@ -21,14 +23,12 @@ import {
readToken, readToken,
setCookie, setCookie,
removeCookie, removeCookie,
TOKEN_KEY,
} from 'helpers' } from 'helpers'
import { import {
useLocalStore, useLocalStore,
useSessionStore, useSessionStore,
useToggle, useToggle,
useEventListener,
} from 'hooks' } from 'hooks'
import { useLexicsStore } from 'features/LexicsStore' import { useLexicsStore } from 'features/LexicsStore'
@ -172,27 +172,6 @@ export const useAuth = () => {
markUserLoaded, markUserLoaded,
]) ])
useEventListener({
callback: useCallback(async (e: StorageEvent) => {
const loadedUser = await userManager.getUser()
if (
e.storageArea !== localStorage
|| e.key !== TOKEN_KEY
|| !e.newValue
|| !loadedUser
|| loadedUser.access_token === e.newValue
) return
userManager.storeUser({
...loadedUser,
access_token: e.newValue,
toStorageString: loadedUser.toStorageString,
})
}, []),
event: 'storage',
})
useEffect(() => { useEffect(() => {
const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect' const isRedirectedBackFromAuthProvider = history.location.pathname === '/redirect'
isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser() isRedirectedBackFromAuthProvider ? signinRedirectCallback() : checkUser()
@ -238,28 +217,24 @@ export const useAuth = () => {
setIsNewDeviceLogin, setIsNewDeviceLogin,
]) ])
duel.channel('active_page') // поле в LS, определяющее активность вкладки
useEffect(() => { useEffect(() => {
// попытаемся обновить токен используя refresh_token // попытаемся обновить токен используя refresh_token
const tryRenewToken = () => { const tryRenewToken = () => {
const tokenLastUpdated = Number(localStorage.getItem('token_updated')) // библиотека oidc-client не поддерживает обновление токена только на 1 вкладке
// предотвращаем одновременное обновление токена в разных окнах/вкладках // @ts-ignore
const needRenewToken = Date.now() - tokenLastUpdated >= 2 * 1e3 if (window.isMaster()) {
userManager.signinSilent().catch(logout)
if (!needRenewToken) return }
localStorage.setItem('token_updated', String(Date.now()))
userManager.signinSilent()
.catch(logout)
} }
// если запросы вернули 401 | 403 // если запросы вернули 401 | 403
window.addEventListener('FORBIDDEN_REQUEST', tryRenewToken) window.addEventListener('FORBIDDEN_REQUEST', tryRenewToken)
// и если токен истек // и если токен истекает (по дефолту за 60 секунд)
userManager.events.addAccessTokenExpired(tryRenewToken) userManager.events.addAccessTokenExpiring(tryRenewToken)
return () => { return () => {
window.removeEventListener('FORBIDDEN_REQUEST', tryRenewToken) window.removeEventListener('FORBIDDEN_REQUEST', tryRenewToken)
userManager.events.removeAccessTokenExpired(tryRenewToken) userManager.events.removeAccessTokenExpiring(tryRenewToken)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [logout]) }, [logout])

@ -18,6 +18,7 @@ export const FinishedMatch = () => {
access, access,
isOpenFiltersPopup, isOpenFiltersPopup,
profile, profile,
profileCardShown,
} = useMatchPageStore() } = useMatchPageStore()
const { const {
chapters, chapters,
@ -46,7 +47,7 @@ export const FinishedMatch = () => {
/> />
</Modal> </Modal>
<Container> <Container isHidden={!profileCardShown}>
{!isEmpty(chapters) && ( {!isEmpty(chapters) && (
<Fragment> <Fragment>
<MultiSourcePlayer <MultiSourcePlayer

@ -15,6 +15,7 @@ import { MatchDescription } from '../MatchDescription'
export const LiveMatch = () => { export const LiveMatch = () => {
const { const {
profile, profile,
profileCardShown,
selectedPlaylist, selectedPlaylist,
} = useMatchPageStore() } = useMatchPageStore()
@ -29,7 +30,7 @@ export const LiveMatch = () => {
} = useLiveMatch() } = useLiveMatch()
return ( return (
<Fragment> <Fragment>
<Container> <Container isHidden={!profileCardShown}>
{profile?.youtube_link ? ( {profile?.youtube_link ? (
<YoutubePlayer <YoutubePlayer
chapters={chapters} chapters={chapters}

@ -17,10 +17,8 @@ export const Description = styled.div<{isHidden?: boolean}>`
: ''}; : ''};
${({ isHidden }) => (isHidden && isMobileDevice ? css` ${({ isHidden }) => (isHidden && isMobileDevice ? css`
height: 0; display: none;
opacity: 0; ` : '')}
margin-bottom: 0;
` : '')}
` `
export const DescriptionInnerBlock = styled.div` export const DescriptionInnerBlock = styled.div`

@ -1,11 +1,9 @@
import { import {
useEffect, useEffect,
useMemo,
useState, useState,
useCallback,
} from 'react' } from 'react'
import debounce from 'lodash/debounce'
import type { MatchInfo } from 'requests/getMatchInfo' import type { MatchInfo } from 'requests/getMatchInfo'
import { usePageParams, useInterval } from 'hooks' import { usePageParams, useInterval } from 'hooks'
@ -19,8 +17,7 @@ import { useMatchPlaylists } from './useMatchPlaylists'
import { useEvents } from './useEvents' import { useEvents } from './useEvents'
import { initialPlaylist } from './useSelectedPlaylist' import { initialPlaylist } from './useSelectedPlaylist'
const MATCH_DATA_POLL_INTERVAL = 60000 const MATCH_DATA_POLL_INTERVAL = 5000
const MATCH_PLAYLISTS_DELAY = 5000
type UseMatchDataArgs = { type UseMatchDataArgs = {
matchProfile: MatchInfo, matchProfile: MatchInfo,
@ -42,37 +39,38 @@ export const useMatchData = ({ matchProfile: profile, selectedTab }: UseMatchDat
const { events, fetchMatchEvents } = useEvents() const { events, fetchMatchEvents } = useEvents()
const fetchPlaylistsDebounced = useMemo(
() => debounce(fetchMatchPlaylists, MATCH_PLAYLISTS_DELAY),
[fetchMatchPlaylists],
)
const chaptersDuration = useDuration(chapters) / 1000 const chaptersDuration = useDuration(chapters) / 1000
const fullMatchDuration = matchDuration const fullMatchDuration = matchDuration
useEffect(() => { useEffect(() => {
if (!profile || (profile.live && Number(profile.c_match_calc_status) <= 1)) return if (!profile || profile.live) return
fetchMatchPlaylists({ fetchMatchPlaylists({
fullMatchDuration, fullMatchDuration,
id: matchId, id: matchId,
sportType, sportType,
}) })
fetchMatchEvents() fetchMatchEvents()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [
profile, profile?.live,
fullMatchDuration, fullMatchDuration,
matchId, matchId,
sportType, sportType,
fetchMatchPlaylists,
fetchMatchEvents,
]) ])
const intervalCallback = () => { const intervalCallback = useCallback(() => {
fetchPlaylistsDebounced({ fetchMatchPlaylists({
fullMatchDuration, fullMatchDuration,
id: matchId, id: matchId,
sportType, sportType,
}) })
fetchMatchEvents() fetchMatchEvents()
} // eslint-disable-next-line react-hooks/exhaustive-deps
}, [
fullMatchDuration,
matchId,
sportType,
])
const { start, stop } = useInterval({ const { start, stop } = useInterval({
callback: intervalCallback, callback: intervalCallback,
@ -86,7 +84,7 @@ export const useMatchData = ({ matchProfile: profile, selectedTab }: UseMatchDat
} else { } else {
stop() stop()
} }
}, [profile?.live, profile?.c_match_calc_status, start, stop]) }, [profile?.live, profile?.c_match_calc_status, start, stop, matchId])
useEffect(() => { useEffect(() => {
selectedPlaylist?.id === FULL_GAME_KEY && setMatchDuration(chaptersDuration) selectedPlaylist?.id === FULL_GAME_KEY && setMatchDuration(chaptersDuration)

@ -67,7 +67,6 @@ export const useMatchPlaylists = ({
getMatchPlaylists({ getMatchPlaylists({
fullMatchDuration, fullMatchDuration,
matchId: id, matchId: id,
selectedActions: [],
sportType, sportType,
}).then(fetchLexics) }).then(fetchLexics)
.then(buildPlaylists) .then(buildPlaylists)

@ -35,7 +35,7 @@ export const Wrapper = styled.div<WrapperProps>`
: '')}; : '')};
` `
export const Container = styled.div` export const Container = styled.div<{isHidden?: boolean}>`
width: 100%; width: 100%;
max-height: 896px; max-height: 896px;
display: flex; display: flex;
@ -63,4 +63,9 @@ export const Container = styled.div`
} }
` `
: ''}; : ''};
${({ isHidden }) => (isHidden && isMobileDevice
? css`
margin-bottom: 0;`
: '')}
` `

@ -95,7 +95,6 @@ export const useMatchPopup = () => {
getMatchPlaylists({ getMatchPlaylists({
fullMatchDuration, fullMatchDuration,
matchId: id, matchId: id,
selectedActions: [],
sportType, sportType,
withFullMatchDuration, withFullMatchDuration,
}).then(fetchLexics) }).then(fetchLexics)

@ -60,6 +60,7 @@ export const MatchSidePlaylists = ({
hideProfileCard, hideProfileCard,
matchPlaylists: playlists, matchPlaylists: playlists,
profile, profile,
profileCardShown,
selectedTab, selectedTab,
showProfileCard, showProfileCard,
tournamentData, tournamentData,
@ -137,6 +138,7 @@ export const MatchSidePlaylists = ({
data-step={Steps.Welcome} data-step={Steps.Welcome}
highlighted={Boolean(isOpen) && currentStep === Steps.Welcome} highlighted={Boolean(isOpen) && currentStep === Steps.Welcome}
isTourOpen={Boolean(isOpen)} isTourOpen={Boolean(isOpen)}
isHidden={!profileCardShown}
> >
<TabsWrapper> <TabsWrapper>
<TabsGroup hasLessThanFourTabs={hasLessThanFourTabs}> <TabsGroup hasLessThanFourTabs={hasLessThanFourTabs}>

@ -11,6 +11,7 @@ import { T9n } from 'features/T9n'
type WrapperProps = { type WrapperProps = {
highlighted?: boolean, highlighted?: boolean,
isHidden?: boolean,
isTourOpen?: boolean, isTourOpen?: boolean,
} }
@ -38,6 +39,11 @@ export const Wrapper = styled.div<WrapperProps>`
` `
: ''}; : ''};
${({ isHidden }) => (isHidden && isMobileDevice
? css`
padding-top: 50px;`
: '')}
${({ isTourOpen }) => (isTourOpen ${({ isTourOpen }) => (isTourOpen
? css` ? css`
overflow-y: initial; overflow-y: initial;

@ -1,18 +1,14 @@
import { import {
useCallback, useCallback,
useEffect, useEffect,
useMemo,
useState, useState,
} from 'react' } from 'react'
import find from 'lodash/find' import find from 'lodash/find'
import forEach from 'lodash/forEach'
import { reduce } from 'lodash'
import type { ObjectWithName } from 'features/Name' import type { ObjectWithName } from 'features/Name'
import { import {
FavoritesActions,
UserFavorites, UserFavorites,
modifyUserFavorites, modifyUserFavorites,
getUserFavorites, getUserFavorites,
@ -53,29 +49,6 @@ export const useUserFavorites = () => {
open, open,
} = useToggle() } = useToggle()
const superTournaments = useMemo(() => {
const uniqueTournamnetIds: Record<number, number> = {}
return reduce(
userFavorites,
(acc, item) => {
if (
item?.info?.super_tournament
&& !uniqueTournamnetIds[item.info.super_tournament.id]
) {
uniqueTournamnetIds[item.info.super_tournament.id] = item.info.super_tournament.id
acc.push({
...item.info.super_tournament,
sport: item.sport,
type: ProfileTypes.SUPERTOURNAMENTS,
})
}
return acc
},
[] as Array<SuperTournament>,
)
}, [userFavorites])
const fetchFavorites = useCallback(() => { const fetchFavorites = useCallback(() => {
getUserFavorites().then((value) => { getUserFavorites().then((value) => {
if (Array.isArray(value)) { if (Array.isArray(value)) {
@ -85,19 +58,6 @@ export const useUserFavorites = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [userInfo?.email]) }, [userInfo?.email])
const removeSuperTournament = (id: number) => {
forEach(userFavorites, (item) => {
if (item.info.super_tournament?.id === id) {
addRemoveFavorite({
action: FavoritesActions.REMOVE,
id: item.id,
sport: item.sport,
type: item.type,
})
}
})
}
useEffect(() => { useEffect(() => {
if (!user) return if (!user) return
@ -122,9 +82,7 @@ export const useUserFavorites = () => {
isOpen, isOpen,
open, open,
playerHighlight, playerHighlight,
removeSuperTournament,
setPlayerHighlight, setPlayerHighlight,
superTournaments,
userFavorites, userFavorites,
} }
} }

@ -37,8 +37,6 @@ export const UserFavorites = ({ marginTop }: Props) => {
addRemoveFavorite, addRemoveFavorite,
close, close,
isOpen, isOpen,
removeSuperTournament,
superTournaments,
userFavorites, userFavorites,
} = useUserFavoritesStore() } = useUserFavoritesStore()
@ -54,29 +52,6 @@ export const UserFavorites = ({ marginTop }: Props) => {
<UserSportFavWrapper> <UserSportFavWrapper>
<UserSportFavStar marginTop={marginTop} /> <UserSportFavStar marginTop={marginTop} />
<ScrollWrapper> <ScrollWrapper>
{
map(superTournaments, (item) => (
<UserSportFavItemLogoWrapper
onFocus={getPosition}
onMouseOver={getPosition}
key={`${item.type}_${item.sport}_${item.id}`}
>
<UserSportFavXWrapper onClick={() => removeSuperTournament(item.id)}>
<Close size={9} />
<FavoritesToolip
topPosition={position}
superTournament={item}
/>
</UserSportFavXWrapper>
<UserSportFavImgWrapper
id={item.id}
altNameObj={item}
sportType={item.sport}
profileType={item.type}
/>
</UserSportFavItemLogoWrapper>
))
}
{ {
map(userFavorites, (item) => ( map(userFavorites, (item) => (
<UserSportFavItemLogoWrapper <UserSportFavItemLogoWrapper

@ -31,7 +31,7 @@ export const useInterval = ({
const id = setInterval(savedCallback.current, intervalDuration) const id = setInterval(savedCallback.current, intervalDuration)
return () => clearInterval(id) return () => clearInterval(id)
}, [isRunning, intervalDuration]) }, [isRunning, intervalDuration, callback])
return { start, stop } return { start, stop }
} }

@ -1,4 +1,4 @@
import { AUTH_SERVICE } from '../config/routes' import { API_ROOT } from 'features/AuthServiceApp/config/routes'
export type FailedResponse = { export type FailedResponse = {
error?: string, error?: string,
@ -10,7 +10,7 @@ export type SuccessResponse = {
} }
export const checkDevice = async (token: string) => { export const checkDevice = async (token: string) => {
const url = `${AUTH_SERVICE}/authorize/check-device?access_token=${token}` const url = `${API_ROOT}/authorize/check-device?access_token=${token}`
const config = { const config = {
method: 'GET', method: 'GET',

@ -1,14 +1,10 @@
import { import { STATS_API_URL, PROCEDURES } from 'config'
DATA_URL,
PROCEDURES,
} from 'config'
import { Episode, Episodes } from 'requests' import { Episode, Episodes } from 'requests'
import { callApi, getSportLexic } from 'helpers' import { callApi } from 'helpers'
const proc = PROCEDURES.ott_match_events const proc = PROCEDURES.get_match_plays
type Args = { type Args = {
matchId: number, matchId: number,
@ -66,7 +62,8 @@ export const getMatchEvents = async ({
const config = { const config = {
body: { body: {
params: { params: {
_p_match_id: matchId, match_id: matchId,
sport_id: sportType,
}, },
proc, proc,
}, },
@ -74,7 +71,7 @@ export const getMatchEvents = async ({
const response: Response = await callApi({ const response: Response = await callApi({
config, config,
url: `${DATA_URL}/${getSportLexic(sportType)}`, url: `${STATS_API_URL}/data/stats`,
}) })
if (!response?.data) return Promise.reject(response) if (!response?.data) return Promise.reject(response)

@ -1,18 +1,11 @@
import isEmpty from 'lodash/isEmpty' import { PROCEDURES, STATS_API_URL } from 'config'
import { callApi } from 'helpers'
import { const proc = PROCEDURES.get_match_watch
DATA_URL,
PROCEDURES,
} from 'config'
import { callApi, getSportLexic } from 'helpers'
const proc = PROCEDURES.ott_match_popup
type Args = { type Args = {
fullMatchDuration?: number, fullMatchDuration?: number,
matchId: number, matchId: number,
selectedActions: Array<number>,
sportType: number, sportType: number,
withFullMatchDuration?: boolean, withFullMatchDuration?: boolean,
} }
@ -79,16 +72,13 @@ type Response = {
export const getMatchPlaylists = async ({ export const getMatchPlaylists = async ({
fullMatchDuration, fullMatchDuration,
matchId, matchId,
selectedActions,
sportType, sportType,
}: Args): Promise<MatchPlaylists> => { }: Args): Promise<MatchPlaylists> => {
const actions = isEmpty(selectedActions) ? null : selectedActions
const config = { const config = {
body: { body: {
params: { params: {
_p_actions: actions, match_id: matchId,
_p_match_id: matchId, sport_id: sportType,
}, },
proc, proc,
}, },
@ -102,7 +92,7 @@ export const getMatchPlaylists = async ({
try { try {
const playlist: Response = await callApi({ const playlist: Response = await callApi({
config, config,
url: `${DATA_URL}/${getSportLexic(sportType)}`, url: `${STATS_API_URL}/data/stats`,
}) })
if (playlist.data) { if (playlist.data) {
return { ...playlist.data, full_game } return { ...playlist.data, full_game }

Loading…
Cancel
Save