fix(#2626): add animation and change method for get sounds

keep-around/78e030bc4b652b05b2a3e2f86799c9eb3658419d
Andrei Dekterev 3 years ago
parent 0661e13e54
commit 5f663eeb9e
  1. 19
      src/components/AudioPlayer/hooks.tsx
  2. 6
      src/components/AudioPlayer/index.tsx
  3. 1
      src/features/Combobox/hooks/index.tsx
  4. 19
      src/features/Combobox/index.tsx
  5. 3
      src/features/Combobox/styled.tsx
  6. 1
      src/features/Combobox/types.tsx
  7. 5
      src/features/Common/Input/styled.tsx
  8. 1
      src/features/UserAccount/components/ChangeCardPopup/index.tsx
  9. 125
      src/pages/HighlightsPage/components/FormHighlights/hooks.tsx
  10. 8
      src/pages/HighlightsPage/components/FormHighlights/index.tsx
  11. 6
      src/pages/HighlightsPage/components/MatchesHighlights/index.tsx
  12. 13
      src/pages/HighlightsPage/components/MatchesHighlights/styled.tsx
  13. 2
      src/pages/HighlightsPage/components/ThanksPopup/index.tsx
  14. 2
      src/pages/HighlightsPage/index.tsx
  15. 20
      src/pages/HighlightsPage/storeHighlightsAtoms.tsx
  16. 1
      src/pages/HighlightsPage/styled.tsx
  17. 3
      src/requests/getMatches/getPlayerMatches.tsx
  18. 18
      src/requests/getSounds.tsx

@ -1,21 +1,24 @@
import { useEffect, useState } from 'react' import {
import { getSound } from 'requests/getSound' SyntheticEvent,
useEffect,
useState,
} from 'react'
import { readToken } from 'helpers' import { readToken } from 'helpers'
export const useAudioPlayer = (id: number | string) => { export const useAudioPlayer = (asset: string) => {
const [audio, setAudio] = useState<HTMLAudioElement>() const [audio, setAudio] = useState<HTMLAudioElement>()
const [playing, setPlaying] = useState(false) const [playing, setPlaying] = useState(false)
const toggle = () => { const toggle = (e: SyntheticEvent) => {
e.preventDefault()
e.stopPropagation()
setPlaying(!playing) setPlaying(!playing)
} }
useEffect(() => { useEffect(() => {
getSound(id).then(({ asset }) => { asset && setAudio(new Audio(`${asset}?access_token=${readToken()}`))
setAudio(new Audio(`${asset}?access_token=${readToken()}`))
})
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]) }, [asset])
useEffect(() => { useEffect(() => {
if (!audio) return if (!audio) return

@ -6,11 +6,11 @@ import { ScAudioContainer } from './styled'
import { useAudioPlayer } from './hooks' import { useAudioPlayer } from './hooks'
type AudioPropsType = { type AudioPropsType = {
id: number | string, asset: string,
} }
export const AudioPlayer = memo(({ id }: AudioPropsType) => { export const AudioPlayer = memo(({ asset }: AudioPropsType) => {
const { playing, toggle } = useAudioPlayer(id) const { playing, toggle } = useAudioPlayer(asset)
return ( return (
<ScAudioContainer onClick={toggle} playing={playing}> <ScAudioContainer onClick={toggle} playing={playing}>

@ -98,6 +98,7 @@ export const useCombobox = <T extends Option>({
const target = event.relatedTarget as HTMLElement | null const target = event.relatedTarget as HTMLElement | null
// клик по элементу списка тоже вызывает onBlur // клик по элементу списка тоже вызывает onBlur
// если кликали элемент списка то событие обрабатывает onOptionSelect // если кликали элемент списка то событие обрабатывает onOptionSelect
if (isOptionClicked(target)) return if (isOptionClicked(target)) return
onBlur?.(event) onBlur?.(event)

@ -21,6 +21,7 @@ import {
Label, Label,
LabelTitle, LabelTitle,
LabelAfter, LabelAfter,
LabelBefore,
} from 'features/Common/Input/styled' } from 'features/Common/Input/styled'
import { Props, Option } from './types' import { Props, Option } from './types'
@ -29,7 +30,6 @@ import {
PopOver, PopOver,
ListOption, ListOption,
WrapperIcon, WrapperIcon,
ScAudioWrap,
ScLoaderWrapper, ScLoaderWrapper,
} from './styled' } from './styled'
@ -43,6 +43,7 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
iconName, iconName,
label, label,
labelAfter, labelAfter,
labelBefore,
labelLexic, labelLexic,
labelWidth, labelWidth,
loading, loading,
@ -84,6 +85,11 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
> >
{labelLexic ? <T9n t={labelLexic} /> : label} {labelLexic ? <T9n t={labelLexic} /> : label}
</LabelTitle> </LabelTitle>
{labelBefore && (
<LabelBefore>
<AudioPlayer asset={labelBefore} />
</LabelBefore>
)}
<InputStyled <InputStyled
maxLength={maxLength} maxLength={maxLength}
onBlur={onInputBlur} onBlur={onInputBlur}
@ -98,6 +104,7 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
placeholder={translate(labelLexic || '')} placeholder={translate(labelLexic || '')}
isUserAccountPage={isUserAccountPage} isUserAccountPage={isUserAccountPage}
style={{ cursor: noSearch ? 'pointer' : '' }}
/> />
{labelAfter && query && <LabelAfter>{labelAfter}</LabelAfter>} {labelAfter && query && <LabelAfter>{labelAfter}</LabelAfter>}
</Label> </Label>
@ -118,21 +125,13 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
<PopOver ref={popoverRef}> <PopOver ref={popoverRef}>
{map(options, (option, i) => ( {map(options, (option, i) => (
<ListOption <ListOption
onClick={ onClick={(e) => onOptionSelect(option.name, e)}
(e) => ((e.target as Element)?.id ? onOptionSelect(option.name, e) : '')
}
aria-selected={index === i} aria-selected={index === i}
isHighlighted={index === i} isHighlighted={index === i}
key={option.id} key={option.id}
id={option.id.toString()} id={option.id.toString()}
> >
{option.name} {option.name}
{option?.src
? (
<ScAudioWrap key={option.id}>
<AudioPlayer id={option?.id ?? ''} />
</ScAudioWrap>
) : ''}
</ListOption> </ListOption>
))} ))}
</PopOver> </PopOver>

@ -57,8 +57,7 @@ export const WrapperIcon = styled.span`
top: 50%; top: 50%;
width: 15px; width: 15px;
height: 15px; height: 15px;
transform: translateY(-50%); transform: translateY(-60%);
` `
export const ScAudioWrap = styled.div` export const ScAudioWrap = styled.div`

@ -25,6 +25,7 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
iconName?: string, iconName?: string,
label?: string, label?: string,
labelAfter?: string | ReactNode, labelAfter?: string | ReactNode,
labelBefore?: string | null,
labelLexic?: string, labelLexic?: string,
labelWidth?: number, labelWidth?: number,
loading?: boolean, loading?: boolean,

@ -214,3 +214,8 @@ export const LabelAfter = styled.span<TitleProps>`
` `
: ''}; : ''};
` `
export const LabelBefore = styled(LabelAfter)`
margin-left: -40px;
padding-top: 5px;
`

@ -30,6 +30,7 @@ export const ChangeCardPopup = ({
return ( return (
<Modal <Modal
isOpen={changeCardPopupOpen} isOpen={changeCardPopupOpen}
withCloseButton={false}
> >
<CardStep <CardStep
title={title ?? 'change_card'} title={title ?? 'change_card'}

@ -9,13 +9,18 @@ import {
import debounce from 'lodash/debounce' import debounce from 'lodash/debounce'
import { useRecoilState, useSetRecoilState } from 'recoil' import {
useRecoilState,
useSetRecoilState,
useRecoilValue,
} from 'recoil'
import { useUserFavoritesStore } from 'features/UserFavorites/store' import { useUserFavoritesStore } from 'features/UserFavorites/store'
import type { Match } from 'requests/getMatches/types' import type { Match } from 'requests/getMatches/types'
import { getSportList } from 'requests/getSportList' import { getSportList } from 'requests/getSportList'
import { getSounds } from 'requests/getSounds'
import { import {
getSportTeams, getSportTeams,
Team, Team,
@ -25,6 +30,7 @@ import { getPlayerMatches } from 'requests/getMatches/getPlayerMatches'
import { getTeamPlayers, Player } from 'requests/getTeamPlayers' import { getTeamPlayers, Player } from 'requests/getTeamPlayers'
import { import {
checkedMatches,
playerMatchesState, playerMatchesState,
dataForPayHighlights, dataForPayHighlights,
fetchingMatches, fetchingMatches,
@ -51,9 +57,9 @@ type TeamType = Team & {
} }
type Sound = { type Sound = {
asset?: string | null,
id: number, id: number,
name: string, name: string,
src?: string,
} }
type StatsType = { type StatsType = {
@ -73,82 +79,6 @@ type FormType = {
teamValue: string, teamValue: string,
} }
const sounds = [
{
id: 0,
name: 'No',
src: '',
},
{
id: 1,
lexic: 19469,
name: 'Main theme',
src: '/sounds/background_track.mp3',
},
{
id: 2,
lexic: 19470,
name: 'First music',
src: 'sounds/1.mp3',
},
{
id: 3,
lexic: 19471,
name: 'Basement by Monako',
src: '/sounds/basement by monako Artlist.mp3',
},
{
id: 4,
lexic: 19472,
name: 'Buss-it by Yarin Primak',
src: '/sounds/buss-it---instrumental-version by yarin-primak Artlist.mp3',
},
{
id: 5,
lexic: 19473,
name: "Can't stop me now by Brightout",
src:
'/sounds/cant-stop-me-now-instrumental-brightout-musicbed-licensed.mp3',
},
{
id: 6,
lexic: 19474,
name: 'Follow me by ShyGhy',
src: '/sounds/follow-me-shyghy-musicbed-licensed.mp3',
},
{
id: 7,
lexic: 19475,
name: 'Gravity by Stanley Gurvich',
src: '/sounds/gravity by stanley-gurvich Artlist.mp3',
},
{
id: 8,
lexic: 19476,
name: 'Light it up by Bloom & the Bliss',
src:
'/sounds/light-it-up-instrumental-bloom-the-bliss-musicbed-licensed.mp3',
},
{
id: 9,
lexic: 19477,
name: 'Living future memories by Generdyn',
src: '/sounds/living-future-memories-generdyn-musicbed-licensed.mp3',
},
{
id: 10,
lexic: 19478,
name: 'Look at me now by Paper kings',
src:
'/sounds/look-at-me-now-instrumental-paper-kings-musicbed-licensed.mp3',
},
{
id: 11,
lexic: 19479,
name: 'Unbroken by Roary',
src: '/sounds/unbroken-roary-musicbed-licensed.mp3',
},
]
const summaryStats = [ const summaryStats = [
{ {
id: 0, id: 0,
@ -176,12 +106,16 @@ export const useHighlightsForm = () => {
const { playerHighlight } = useUserFavoritesStore() const { playerHighlight } = useUserFavoritesStore()
const [sports, setSports] = useState<Array<SportTypeName>>([]) const [sports, setSports] = useState<Array<SportTypeName>>([])
const [sounds, setSounds] = useState<any>([])
const [isFetchingTeams, setIsFetchingTeams] = useState(false) const [isFetchingTeams, setIsFetchingTeams] = useState(false)
const [teams, setTeams] = useState<Array<TeamType>>([]) const [teams, setTeams] = useState<Array<TeamType>>([])
const [playersData, setPlayersData] = useState<Array<PlayerType>>([]) const [playersData, setPlayersData] = useState<Array<PlayerType>>([])
const [players, setPlayers] = useState<Array<PlayerType>>([]) const [players, setPlayers] = useState<Array<PlayerType>>([])
const [formState, setFormState] = useState<FormType>(defaultValues) const [formState, setFormState] = useState<FormType>(defaultValues)
const [playerMatches, setPlayerMatches] = useRecoilState(playerMatchesState) const [playerMatches, setPlayerMatches] = useRecoilState(playerMatchesState)
const {
checkedMatchesLength,
} = useRecoilValue(checkedMatches)
const setDatahighlights = useSetRecoilState(dataForPayHighlights) const setDatahighlights = useSetRecoilState(dataForPayHighlights)
const setIsFetching = useSetRecoilState(fetchingMatches) const setIsFetching = useSetRecoilState(fetchingMatches)
@ -283,6 +217,25 @@ export const useHighlightsForm = () => {
name: sport?.name_eng, name: sport?.name_eng,
})), })),
)) ))
getSounds()
.then((state) => {
setSounds([{
asset: null,
id: 100,
name: 'No',
},
...state,
])
setFormState((prev) => ({
...prev,
selectedSound: {
asset: null,
id: 100,
name: 'No',
},
}))
})
}, []) }, [])
useEffect(() => { useEffect(() => {
@ -328,6 +281,16 @@ export const useHighlightsForm = () => {
[formState.teamValue], [formState.teamValue],
) )
useEffect(() => {
if (Number(formState?.duration) < checkedMatchesLength * 2) {
setFormState((prev) => ({
...prev,
duration: (checkedMatchesLength * 2).toString(),
}))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [checkedMatchesLength])
useEffect(() => { useEffect(() => {
if (formState?.teamValue?.length >= 3 && formState?.sport) { if (formState?.teamValue?.length >= 3 && formState?.sport) {
fetchTeams() fetchTeams()
@ -339,12 +302,12 @@ export const useHighlightsForm = () => {
if (formState?.selectedPlayer?.id && formState?.sport) { if (formState?.selectedPlayer?.id && formState?.sport) {
setDatahighlights({ setDatahighlights({
data: { data: {
background_music: formState?.selectedSound?.src, background_music: formState?.selectedSound?.asset || null,
duration: Number(formState?.duration) * 60, duration: Number(formState?.duration) * 60,
lang: 'en', lang: 'en',
matches: playerMatches?.filter(({ isChecked }) => (isChecked)).map(({ id }) => id), matches: playerMatches?.filter(({ isChecked }) => (isChecked)).map(({ id }) => id),
player_id: formState?.selectedPlayer?.id, player_id: formState?.selectedPlayer?.id,
price: playerMatches?.length * 25, price: checkedMatchesLength * 25,
sport_id: formState?.sport.id, sport_id: formState?.sport.id,
stats: Boolean(formState?.stats?.id), stats: Boolean(formState?.stats?.id),
}, },
@ -378,6 +341,7 @@ export const useHighlightsForm = () => {
&& getPlayerMatches({ && getPlayerMatches({
limit: 1000, limit: 1000,
offset: 0, offset: 0,
p_match_completed: true,
playerId: formState?.selectedPlayer?.id, playerId: formState?.selectedPlayer?.id,
sportType: formState?.sport?.id, sportType: formState?.sport?.id,
sub_only: false, sub_only: false,
@ -391,6 +355,7 @@ export const useHighlightsForm = () => {
}, [formState?.selectedPlayer, formState?.selectedTeam]) }, [formState?.selectedPlayer, formState?.selectedTeam])
return { return {
checkedMatchesLength,
formRef, formRef,
formState, formState,
handleSubmit, handleSubmit,

@ -1,7 +1,6 @@
import { Combobox } from 'features/Combobox' import { Combobox } from 'features/Combobox'
import { Input } from 'features/Common' import { Input } from 'features/Common'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Icon } from 'features/Icon'
import { isMobileDevice } from 'config/userAgent' import { isMobileDevice } from 'config/userAgent'
@ -115,11 +114,10 @@ export const FormHighlights = ({ price }: PriceInfoType) => {
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
iconName='Search' iconName='Search'
className='FormHighlights__select__players'
/> />
<Input <Input
disabled={!sport} disabled={!sport}
value={duration?.toString() || ''} value={duration?.toString()}
labelLexic='maximal_duration' labelLexic='maximal_duration'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={onChangeMaxDuration} onChange={onChangeMaxDuration}
@ -142,11 +140,11 @@ export const FormHighlights = ({ price }: PriceInfoType) => {
maxLength={500} maxLength={500}
withError={false} withError={false}
wrapperHeight={wrapperHeight} wrapperHeight={wrapperHeight}
labelAfter={<Icon refIcon='Sound' />} labelBefore={selectedSound?.asset}
className='FormHighlights__input__sound' className='FormHighlights__input__sound'
/> />
<Combobox <Combobox
disabled={!sport} disabled
noSearch noSearch
selected selected
labelLexic='add_summary' labelLexic='add_summary'

@ -81,10 +81,10 @@ export const MatchesHighlights = () => {
/> />
))) : (Array.from(Array(12)).map(() => ( ))) : (Array.from(Array(12)).map(() => (
<ScFakeWrapper key={Math.random()}> <ScFakeWrapper key={Math.random()}>
<ScFakeCheckbox /> <ScFakeCheckbox className='skeleton' />
<ScFakeTournament> <ScFakeTournament>
<ScFakeDate /> <ScFakeDate className='skeleton' />
<ScFakeTournamentName /> <ScFakeTournamentName className='skeleton' />
</ScFakeTournament> </ScFakeTournament>
</ScFakeWrapper> </ScFakeWrapper>
)))} )))}

@ -121,6 +121,19 @@ export const ScFakeWrapper = styled.div`
align-items: center; align-items: center;
margin-bottom: 14px; margin-bottom: 14px;
position: relative; position: relative;
.skeleton {
animation: skeleton-loading 1s linear infinite alternate;
}
@keyframes skeleton-loading {
0% {
background-color: rgba(78, 78, 78, 0.4);
}
100% {
background-color: rgba(78, 78, 78, 1);
}
}
` `
export const ScLoaderWrapper = styled.div` export const ScLoaderWrapper = styled.div`

@ -34,7 +34,7 @@ export const ThanksPopup = () => {
</ScText> </ScText>
<ScButton onClick={() => { <ScButton onClick={() => {
setDataHighlights({ ...dataHighlights, isOpenThanksPopup: false }) setDataHighlights({ ...dataHighlights, isOpenThanksPopup: false })
window.location.href = '/' window.location.reload()
}} }}
> >
Ok Ok

@ -69,7 +69,7 @@ const HighlightsPage = () => {
<ScButton> <ScButton>
<T9n t='order_and_buy' /> <T9n t='order_and_buy' />
<ScPrice> <ScPrice>
{isMobileDevice ? ` | $${price}` : ''} {` | $${price}`}
</ScPrice> </ScPrice>
</ScButton> </ScButton>
</ScButtonWrap> </ScButtonWrap>

@ -1,4 +1,4 @@
import { atom } from 'recoil' import { atom, selector } from 'recoil'
import type { Match } from 'requests' import type { Match } from 'requests'
@ -10,7 +10,7 @@ export type PlayerMatchesType = Array<MatchType>
type DataForm = { type DataForm = {
data: { data: {
background_music: string | undefined, background_music: string | null,
duration: number, duration: number,
lang: string, lang: string,
matches: Array<number>, matches: Array<number>,
@ -42,3 +42,19 @@ export const fetchingMatches = atom({
default: false, default: false,
key: 'fetchingMatches', key: 'fetchingMatches',
}) })
export const checkedMatches = selector({
get: ({ get }) => {
const matches = get(playerMatchesState)
const checkedPlayerMatches = matches.filter(({ isChecked }) => isChecked)
const idsCheckedMatches = checkedPlayerMatches.map(({ id }) => id)
const checkedMatchesLength = checkedPlayerMatches.length
return {
checkedMatchesLength,
checkedPlayerMatches,
idsCheckedMatches,
}
},
key: 'checkedMatches',
})

@ -59,4 +59,5 @@ export const ScButtonWrap = styled.div<{disabled: boolean}>`
export const ScPrice = styled.span` export const ScPrice = styled.span`
font-weight: 400; font-weight: 400;
font-size: 14px; font-size: 14px;
margin: auto 0;
` `

@ -13,6 +13,7 @@ const proc = PROCEDURES.get_player_matches
type Args = { type Args = {
limit: number, limit: number,
offset: number, offset: number,
p_match_completed?: boolean,
playerId: number, playerId: number,
sportType: SportTypes, sportType: SportTypes,
sub_only?: boolean, sub_only?: boolean,
@ -21,6 +22,7 @@ type Args = {
export const getPlayerMatches = async ({ export const getPlayerMatches = async ({
limit, limit,
offset, offset,
p_match_completed,
playerId, playerId,
sportType, sportType,
sub_only, sub_only,
@ -29,6 +31,7 @@ export const getPlayerMatches = async ({
body: { body: {
params: { params: {
_p_limit: limit, _p_limit: limit,
_p_match_completed: p_match_completed,
_p_offset: offset, _p_offset: offset,
_p_player_id: playerId, _p_player_id: playerId,
_p_sport: sportType, _p_sport: sportType,

@ -0,0 +1,18 @@
import { callApi } from 'helpers'
import { API_ROOT } from 'config'
type ResponseSound = {
asset: string,
name: string,
}
export const getSounds = async (): Promise<Array<ResponseSound>> => {
const config = {
method: 'GET',
}
return callApi({
config,
url: `${API_ROOT}/v1/aws/highlights/music`,
})
}
Loading…
Cancel
Save