fix(#2526): add loaders for fetching matches and teams

keep-around/78e030bc4b652b05b2a3e2f86799c9eb3658419d
Andrei Dekterev 3 years ago
parent 58c96bda11
commit 0661e13e54
  1. 2
      Makefile
  2. 1
      src/config/lexics/indexLexics.tsx
  3. 100
      src/features/App/AuthenticatedApp.tsx
  4. 13
      src/features/BuyMatchPopup/components/CardStep/index.tsx
  5. 8
      src/features/Combobox/index.tsx
  6. 21
      src/features/Combobox/styled.tsx
  7. 1
      src/features/Combobox/types.tsx
  8. 6
      src/features/TournamentList/components/CollapseTournament/index.tsx
  9. 3
      src/features/UserAccount/components/ChangeCardPopup/index.tsx
  10. 57
      src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
  11. 4
      src/pages/HighlightsPage/components/FormHighlights/index.tsx
  12. 25
      src/pages/HighlightsPage/components/MatchesHighlights/index.tsx
  13. 11
      src/pages/HighlightsPage/components/MatchesHighlights/styled.tsx
  14. 1
      src/pages/HighlightsPage/components/ThanksPopup/index.tsx
  15. 7
      src/pages/HighlightsPage/index.tsx
  16. 10
      src/pages/HighlightsPage/storeHighlightsAtoms.tsx

@ -166,7 +166,7 @@ lff-prod: clean
deploy-all: prod preprod facr-prod lff-prod
stage: build-stage
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-staging@staging.instat.tv:/usr/local/www/ott-staging/wwwroot/
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-staging@10.0.3.8:/usr/local/www/ott-staging/wwwroot/
a-stage: build-a
rsync -zavP --delete-before build/ -e 'ssh -p 666' ott-staging@10.0.3.8:/usr/local/www/ott-staging/a-wwwroot/

@ -100,6 +100,7 @@ export const indexLexics = {
available_matches_shown: 13385,
basketball: 6960,
broadcast: 13049,
broadcasts: 2891,
by_clicking: 17879,
check_connection: 15700,
choose_sport: 17927,

@ -41,58 +41,58 @@ export const AuthenticatedApp = () => {
useLexicsConfig(indexLexics)
return (
<StripeElements>
<CardsStore>
<MatchSwitchesStore>
<UserFavoritesStore>
<ExtendedSearchStore>
<PreferencesPopupStore>
<TournamentPopupStore>
<MatchPopupStore>
<BuyMatchPopupStore>
<NoNetworkPopupStore>
<MatchPopup />
<BuyMatchPopup />
{ client.name === 'facr' ? <TournamentsPopup /> : <PreferencesPopup /> }
<NoNetworkPopup />
<RecoilRoot>
<CardsStore>
<MatchSwitchesStore>
<UserFavoritesStore>
<ExtendedSearchStore>
<PreferencesPopupStore>
<TournamentPopupStore>
<MatchPopupStore>
<BuyMatchPopupStore>
<NoNetworkPopupStore>
<MatchPopup />
<BuyMatchPopup />
{ client.name === 'facr' ? <TournamentsPopup /> : <PreferencesPopup /> }
<NoNetworkPopup />
{/* в Switch как прямой children
можно рендерить только Route или Redirect */}
<Switch>
<Route path={PAGES.useraccount}>
<UserAccount />
</Route>
<Route exact path={PAGES.home}>
<HomePage />
</Route>
<Route path={`/:sportName${PAGES.tournament}/:pageId`}>
<TournamentPage />
</Route>
<Route path={`/:sportName${PAGES.team}/:pageId`}>
<TeamPage />
</Route>
<Route path={`/:sportName${PAGES.player}/:pageId`}>
<PlayerPage />
</Route>
<Route path={`/:sportName${PAGES.match}/:pageId`}>
<MatchPage />
</Route>
<Route path={`${PAGES.highlights}`}>
<RecoilRoot>
{/* в Switch как прямой children
можно рендерить только Route или Redirect */}
<Switch>
<Route path={PAGES.useraccount}>
<UserAccount />
</Route>
<Route exact path={PAGES.home}>
<HomePage />
</Route>
<Route path={`/:sportName${PAGES.tournament}/:pageId`}>
<TournamentPage />
</Route>
<Route path={`/:sportName${PAGES.team}/:pageId`}>
<TeamPage />
</Route>
<Route path={`/:sportName${PAGES.player}/:pageId`}>
<PlayerPage />
</Route>
<Route path={`/:sportName${PAGES.match}/:pageId`}>
<MatchPage />
</Route>
<Route path={`${PAGES.highlights}`}>
<HighlightsPage />
</RecoilRoot>
</Route>
<Redirect to={PAGES.home} />
</Switch>
{!isProduction && <SystemSettings />}
</NoNetworkPopupStore>
</BuyMatchPopupStore>
</MatchPopupStore>
</TournamentPopupStore>
</PreferencesPopupStore>
</ExtendedSearchStore>
</UserFavoritesStore>
</MatchSwitchesStore>
</CardsStore>
</Route>
<Redirect to={PAGES.home} />
</Switch>
{!isProduction && <SystemSettings />}
</NoNetworkPopupStore>
</BuyMatchPopupStore>
</MatchPopupStore>
</TournamentPopupStore>
</PreferencesPopupStore>
</ExtendedSearchStore>
</UserFavoritesStore>
</MatchSwitchesStore>
</CardsStore>
</RecoilRoot>
</StripeElements>
)
}

@ -23,11 +23,16 @@ import {
type CardStepType = {
btnName?: string,
closeHandle?: () => void,
title?: string,
}
export const CardStep = ({ btnName, title }: CardStepType) => {
const { cards } = useCardsStore()
export const CardStep = ({
btnName,
closeHandle,
title,
}: CardStepType) => {
const { cards, isHighlightsPage } = useCardsStore()
const { close, goBack } = useBuyMatchPopupStore()
const emptyCards = isEmpty(cards)
@ -36,13 +41,13 @@ export const CardStep = ({ btnName, title }: CardStepType) => {
<Wrapper width={642}>
<Header>
<ButtonPrevious onClick={goBack}>
<Arrow direction='left' />
{isHighlightsPage ? '' : <Arrow direction='left' />}
</ButtonPrevious>
<HeaderTitle>
<T9n t={title ?? 'pay'} />
</HeaderTitle>
<HeaderActions position='right'>
<CloseButton onClick={close} />
<CloseButton onClick={isHighlightsPage && closeHandle ? closeHandle : close} />
</HeaderActions>
</Header>
<Body padding='12px 40px 0 40px'>

@ -10,6 +10,7 @@ import { PAGES } from 'config'
import { AudioPlayer } from 'components/AudioPlayer'
import { T9n } from 'features/T9n'
import { Loader } from 'features/Loader'
import { Icon } from 'features/Icon'
import { OutsideClick } from 'features/OutsideClick'
import {
@ -29,6 +30,7 @@ import {
ListOption,
WrapperIcon,
ScAudioWrap,
ScLoaderWrapper,
} from './styled'
import { Arrow } from './components/Arrow'
@ -43,6 +45,7 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
labelAfter,
labelLexic,
labelWidth,
loading,
maxLength,
noSearch,
title,
@ -98,6 +101,11 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
/>
{labelAfter && query && <LabelAfter>{labelAfter}</LabelAfter>}
</Label>
{loading ? (
<ScLoaderWrapper>
<Loader color='#363636' />
</ScLoaderWrapper>
) : ''}
{iconName ? (
<WrapperIcon>
<Icon refIcon={iconName} />

@ -1,4 +1,6 @@
import styled from 'styled-components/macro'
import styled, { css } from 'styled-components/macro'
import { isMobileDevice } from 'config/userAgent'
import { customScrollbar, customStylesMixin } from 'features/Common'
@ -62,3 +64,20 @@ export const WrapperIcon = styled.span`
export const ScAudioWrap = styled.div`
margin-left: 20px;
`
export const ScLoaderWrapper = styled.div`
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
${isMobileDevice
? css`
max-width: 95vw;
left: 0;
border-radius: 4px;
@media screen and (orientation: landscape){
max-width: 368px;
}
`
: ''};
`

@ -27,6 +27,7 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
labelAfter?: string | ReactNode,
labelLexic?: string,
labelWidth?: number,
loading?: boolean,
maxLength?: number,
noSearch?: boolean,
onChange?: (event: ChangeEvent<HTMLInputElement>) => void,

@ -86,11 +86,7 @@ export const CollapseTournament = ({
<CountMatches>
{tournamentMatches.length}
&nbsp;
<T9n t={
tournamentMatches.length > 1
? 'games' : 'game'
}
/>
<T9n t='broadcasts' />
</CountMatches>
</Info>
</CardWrapper>

@ -30,12 +30,11 @@ export const ChangeCardPopup = ({
return (
<Modal
isOpen={changeCardPopupOpen}
close={() => setChangeCardPopupOpen(false)}
withCloseButton={closeButton}
>
<CardStep
title={title ?? 'change_card'}
btnName={btnName ?? 'change'}
closeHandle={() => setChangeCardPopupOpen(false)}
/>
</Modal>
)

@ -24,7 +24,11 @@ import {
import { getPlayerMatches } from 'requests/getMatches/getPlayerMatches'
import { getTeamPlayers, Player } from 'requests/getTeamPlayers'
import { playerMatchesState, dataForPayHighlights } from '../../storeHighlightsAtoms'
import {
playerMatchesState,
dataForPayHighlights,
fetchingMatches,
} from '../../storeHighlightsAtoms'
export type SportType = {
id: number,
@ -73,6 +77,7 @@ const sounds = [
{
id: 0,
name: 'No',
src: '',
},
{
id: 1,
@ -171,12 +176,14 @@ export const useHighlightsForm = () => {
const { playerHighlight } = useUserFavoritesStore()
const [sports, setSports] = useState<Array<SportTypeName>>([])
const [isFetchingTeams, setIsFetchingTeams] = useState(false)
const [teams, setTeams] = useState<Array<TeamType>>([])
const [playersData, setPlayersData] = useState<Array<PlayerType>>([])
const [players, setPlayers] = useState<Array<PlayerType>>([])
const [formState, setFormState] = useState<FormType>(defaultValues)
const [playerMatches, setPlayerMatches] = useRecoilState(playerMatchesState)
const setDatahighlights = useSetRecoilState(dataForPayHighlights)
const setIsFetching = useSetRecoilState(fetchingMatches)
const formRef = useRef<HTMLFormElement>(null)
@ -298,18 +305,25 @@ export const useHighlightsForm = () => {
}, [sports, playerHighlight, teams])
const fetchTeams = useMemo(
() => debounce(() => formState?.sport?.id && getSportTeams(
formState?.sport?.id,
-1,
formState.teamValue,
).then(
({ data }: SportTeamsType) => setTeams(
data?.map((team: Team) => ({
...team,
name: team.name_eng,
})),
),
), 1000),
() => debounce(() => {
setIsFetchingTeams(true)
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
formState?.sport?.id
&& getSportTeams(
formState?.sport?.id,
-1,
formState.teamValue,
)
.then(({ data }: SportTeamsType) => {
setTeams(
data?.map((team: Team) => ({
...team,
name: team.name_eng,
})),
)
})
.finally(() => setIsFetchingTeams(false))
}, 1000),
// eslint-disable-next-line react-hooks/exhaustive-deps
[formState.teamValue],
)
@ -325,7 +339,8 @@ export const useHighlightsForm = () => {
if (formState?.selectedPlayer?.id && formState?.sport) {
setDatahighlights({
data: {
duration: Number(formState?.duration),
background_music: formState?.selectedSound?.src,
duration: Number(formState?.duration) * 60,
lang: 'en',
matches: playerMatches?.filter(({ isChecked }) => (isChecked)).map(({ id }) => id),
player_id: formState?.selectedPlayer?.id,
@ -346,16 +361,19 @@ export const useHighlightsForm = () => {
formState?.sport?.id || playerHighlight.sportType, formState?.selectedTeam?.id
|| playerHighlight?.profile?.additionalInfo?.id,
)
.then((state) => setPlayersData(state?.map((player: Player) => ({
...player,
name: `${player?.firstname_eng} ${player?.lastname_eng}`,
}))))
.then((state) => {
setPlayersData(state?.map((player: Player) => ({
...player,
name: `${player?.firstname_eng} ${player?.lastname_eng}`,
})))
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formState?.selectedTeam, playerHighlight])
useEffect(() => {
if (!formState?.selectedPlayer || !formState?.sport) return
setIsFetching(true)
formState?.selectedPlayer
&& getPlayerMatches({
limit: 1000,
@ -367,6 +385,8 @@ export const useHighlightsForm = () => {
.then(({ broadcast }) => setPlayerMatches(
broadcast.map((match: Match) => ({ ...match, isChecked: false })),
))
.finally(() => setIsFetching(false))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formState?.selectedPlayer, formState?.selectedTeam])
@ -374,6 +394,7 @@ export const useHighlightsForm = () => {
formRef,
formState,
handleSubmit,
isFetchingTeams,
onChangeMaxDuration,
onChangePlayer,
onChangeTeam,

@ -37,6 +37,7 @@ export const FormHighlights = ({ price }: PriceInfoType) => {
stats,
teamValue,
},
isFetchingTeams,
onChangeMaxDuration,
onChangePlayer,
onChangeTeam,
@ -86,7 +87,7 @@ export const FormHighlights = ({ price }: PriceInfoType) => {
wrapperHeight={wrapperHeight}
/>
<Combobox
disabled={!sport}
disabled={!sport || isFetchingTeams}
selected={Boolean(selectedTeam?.name_eng)}
labelLexic='team_highlight'
labelWidth={labelWidth}
@ -99,6 +100,7 @@ export const FormHighlights = ({ price }: PriceInfoType) => {
wrapperHeight={wrapperHeight}
iconName='Search'
className='FormHighlights__select__teams'
loading={isFetchingTeams}
/>
<Combobox
disabled={!sport || !selectedTeam}

@ -1,9 +1,15 @@
import { useRecoilValue } from 'recoil'
import { format } from 'date-fns'
import { T9n } from 'features/T9n'
import { Checkbox } from 'features/Common/Checkbox'
import { SportIcon } from 'features/SportIcon'
import { ArrowLoader } from 'features/ArrowLoader'
import { isMobileDevice } from 'config/userAgent'
import { format } from 'date-fns'
import { MatchType, fetchingMatches } from '../../storeHighlightsAtoms'
import { useHighlighMatches } from './hooks'
@ -22,10 +28,16 @@ import {
ScFakeCheckbox,
ScFakeWrapper,
ScCountMatches,
ScLoaderWrapper,
} from './styled'
export const MatchesHighlights = () => {
const { onChangeSelectedMatches, playerMatches } = useHighlighMatches()
const {
onChangeSelectedMatches,
playerMatches,
} = useHighlighMatches()
const isFetching = useRecoilValue(fetchingMatches)
return (
<ScMatchesWrapper>
@ -45,10 +57,10 @@ export const MatchesHighlights = () => {
team1,
team2,
tournament,
}: any) => (
}: MatchType) => (
<Checkbox
key={id}
id={id}
id={id.toString()}
checked={isChecked}
onChange={onChangeSelectedMatches}
label={(
@ -76,6 +88,11 @@ export const MatchesHighlights = () => {
</ScFakeTournament>
</ScFakeWrapper>
)))}
{isFetching ? (
<ScLoaderWrapper>
<ArrowLoader />
</ScLoaderWrapper>
) : '' }
</ScMatchesList>
</ScMatchesWrapper>
)

@ -120,13 +120,18 @@ export const ScFakeWrapper = styled.div`
flex-direction: row;
align-items: center;
margin-bottom: 14px;
position: relative;
`
export const ScLoaderWrapper = styled.div`
position: absolute;
left: 30%;
top: 50%;
z-index: 1;
top: 0;
left: 0;
width: 75%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`
export const ScCountMatches = styled.span`

@ -20,6 +20,7 @@ export const ThanksPopup = () => {
<ScModal
isOpen={dataHighlights.isOpenThanksPopup}
close={() => setDataHighlights({ ...dataHighlights, isOpenThanksPopup: false })}
withCloseButton={false}
>
<ScHeader>
<T9n t='thank_you' />

@ -44,6 +44,7 @@ const HighlightsPage = () => {
|| !data?.matches.length
|| !isBoolean(data?.stats)
|| data?.matches.length > 10
|| data.background_music === undefined
const price = playerMatches?.filter(
({ isChecked }: MatchType) => isChecked,
@ -61,7 +62,10 @@ const HighlightsPage = () => {
<FormHighlights price={price} />
<MatchesHighlights />
</ScWrapperContent>
<ScButtonWrap disabled={isNotEmpty} onClick={() => setIsOpenPopupChangeCard(true)}>
<ScButtonWrap
disabled={isNotEmpty}
onClick={() => !isNotEmpty && setIsOpenPopupChangeCard(true)}
>
<ScButton>
<T9n t='order_and_buy' />
<ScPrice>
@ -75,7 +79,6 @@ const HighlightsPage = () => {
changeCardPopupOpen={isOpenPopupChangeCard}
setChangeCardPopupOpen={setIsOpenPopupChangeCard}
title='payment'
closeButton={false}
/>
<ThanksPopup />
</ScWrapper>

@ -2,14 +2,15 @@ import { atom } from 'recoil'
import type { Match } from 'requests'
export type PlayerMatchesType = Array<MatchType>
export type MatchType = Match & {
isChecked: boolean,
}
export type PlayerMatchesType = Array<MatchType>
type DataForm = {
data: {
background_music: string | undefined,
duration: number,
lang: string,
matches: Array<number>,
@ -36,3 +37,8 @@ export const dataForPayHighlights = atom({
default: {} as DataForm,
key: 'dataForPayHighlights',
})
export const fetchingMatches = atom({
default: false,
key: 'fetchingMatches',
})

Loading…
Cancel
Save