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

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

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

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

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

@ -25,6 +25,7 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
iconName?: string,
label?: string,
labelAfter?: string | ReactNode,
labelBefore?: string | null,
labelLexic?: string,
labelWidth?: number,
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 (
<Modal
isOpen={changeCardPopupOpen}
withCloseButton={false}
>
<CardStep
title={title ?? 'change_card'}

@ -9,13 +9,18 @@ import {
import debounce from 'lodash/debounce'
import { useRecoilState, useSetRecoilState } from 'recoil'
import {
useRecoilState,
useSetRecoilState,
useRecoilValue,
} from 'recoil'
import { useUserFavoritesStore } from 'features/UserFavorites/store'
import type { Match } from 'requests/getMatches/types'
import { getSportList } from 'requests/getSportList'
import { getSounds } from 'requests/getSounds'
import {
getSportTeams,
Team,
@ -25,6 +30,7 @@ import { getPlayerMatches } from 'requests/getMatches/getPlayerMatches'
import { getTeamPlayers, Player } from 'requests/getTeamPlayers'
import {
checkedMatches,
playerMatchesState,
dataForPayHighlights,
fetchingMatches,
@ -51,9 +57,9 @@ type TeamType = Team & {
}
type Sound = {
asset?: string | null,
id: number,
name: string,
src?: string,
}
type StatsType = {
@ -73,82 +79,6 @@ type FormType = {
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 = [
{
id: 0,
@ -176,12 +106,16 @@ export const useHighlightsForm = () => {
const { playerHighlight } = useUserFavoritesStore()
const [sports, setSports] = useState<Array<SportTypeName>>([])
const [sounds, setSounds] = useState<any>([])
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 {
checkedMatchesLength,
} = useRecoilValue(checkedMatches)
const setDatahighlights = useSetRecoilState(dataForPayHighlights)
const setIsFetching = useSetRecoilState(fetchingMatches)
@ -283,6 +217,25 @@ export const useHighlightsForm = () => {
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(() => {
@ -328,6 +281,16 @@ export const useHighlightsForm = () => {
[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(() => {
if (formState?.teamValue?.length >= 3 && formState?.sport) {
fetchTeams()
@ -339,12 +302,12 @@ export const useHighlightsForm = () => {
if (formState?.selectedPlayer?.id && formState?.sport) {
setDatahighlights({
data: {
background_music: formState?.selectedSound?.src,
background_music: formState?.selectedSound?.asset || null,
duration: Number(formState?.duration) * 60,
lang: 'en',
matches: playerMatches?.filter(({ isChecked }) => (isChecked)).map(({ id }) => id),
player_id: formState?.selectedPlayer?.id,
price: playerMatches?.length * 25,
price: checkedMatchesLength * 25,
sport_id: formState?.sport.id,
stats: Boolean(formState?.stats?.id),
},
@ -378,6 +341,7 @@ export const useHighlightsForm = () => {
&& getPlayerMatches({
limit: 1000,
offset: 0,
p_match_completed: true,
playerId: formState?.selectedPlayer?.id,
sportType: formState?.sport?.id,
sub_only: false,
@ -391,6 +355,7 @@ export const useHighlightsForm = () => {
}, [formState?.selectedPlayer, formState?.selectedTeam])
return {
checkedMatchesLength,
formRef,
formState,
handleSubmit,

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

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

@ -121,6 +121,19 @@ export const ScFakeWrapper = styled.div`
align-items: center;
margin-bottom: 14px;
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`

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

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

@ -1,4 +1,4 @@
import { atom } from 'recoil'
import { atom, selector } from 'recoil'
import type { Match } from 'requests'
@ -10,7 +10,7 @@ export type PlayerMatchesType = Array<MatchType>
type DataForm = {
data: {
background_music: string | undefined,
background_music: string | null,
duration: number,
lang: string,
matches: Array<number>,
@ -42,3 +42,19 @@ export const fetchingMatches = atom({
default: false,
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`
font-weight: 400;
font-size: 14px;
margin: auto 0;
`

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