Ott 748 popup players list (#281)

* feat(748): split start and bench players

* refactor(748): moved players avatar preloading to upper component and simplified

* refactor(748): renamed component

* fix(748): added scrolling to players list
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Mirlan 5 years ago committed by GitHub
parent f9702c9d2f
commit dd87080d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      src/features/Common/Image/index.tsx
  2. 1
      src/features/MatchPage/types.tsx
  3. 75
      src/features/MatchPopup/components/GroupedPlayersList/index.tsx
  4. 29
      src/features/MatchPopup/components/GroupedPlayersList/styled.tsx
  5. 49
      src/features/MatchPopup/components/PlayersList/index.tsx
  6. 16
      src/features/MatchPopup/components/PlayersList/styled.tsx
  7. 29
      src/features/MatchPopup/components/PlayersListDesktop/index.tsx
  8. 7
      src/features/MatchPopup/components/PlayersListMobile/index.tsx
  9. 11
      src/features/ProfileLogo/index.tsx
  10. 1
      src/requests/getMatchPlaylists.tsx

@ -1,11 +1,5 @@
import type { BaseSyntheticEvent } from 'react'
import {
RefObject,
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import { useCallback } from 'react'
import styled from 'styled-components/macro'
@ -16,8 +10,7 @@ type Props = {
className?: string,
dataSrc?: string,
fallbackSrc?: string,
imagesList?: Array<RefObject<HTMLImageElement>>,
setImagesList?: (imagesList: Array<RefObject<HTMLImageElement>>) => void,
onLoad?: () => void,
src: string,
title?: string,
}
@ -27,23 +20,10 @@ export const Image = ({
className,
dataSrc,
fallbackSrc,
imagesList = [],
setImagesList,
onLoad,
src,
title,
}: Props) => {
const imgRef = useRef<HTMLImageElement>(null)
const [loaded, setLoaded] = useState(false)
useEffect(
() => {
if (!setImagesList || !loaded) return
setImagesList([...imagesList, imgRef])
},
// eslint-disable-next-line
[loaded],
)
const onError = useCallback((e: BaseSyntheticEvent) => {
// eslint-disable-next-line no-param-reassign
e.target.onError = ''
@ -58,8 +38,7 @@ export const Image = ({
data-src={dataSrc}
className={className}
onError={onError}
onLoad={() => setLoaded(true)}
ref={imgRef}
onLoad={() => onLoad?.()}
title={title}
/>
)

@ -18,6 +18,7 @@ export type PlayerPlaylistOption = {
id: number,
name_eng: string,
name_rus: string,
start?: boolean,
type: PlaylistTypes.PLAYER,
}

@ -0,0 +1,75 @@
import {
useMemo,
useState,
useCallback,
} from 'react'
import map from 'lodash/map'
import every from 'lodash/every'
import groupBy from 'lodash/groupBy'
import type { PlayerPlaylistOptions } from 'features/MatchPage/types'
import { Loader } from 'features/Loader'
import { Teams } from '../../types'
import { PlayersList } from '../PlayersList'
import { ListsWrapper, LoaderWrapper } from './styled'
const useItemsLoadedState = (items: PlayerPlaylistOptions) => {
const [loadedIds, setLoadedIds] = useState<Record<number, number>>({})
const onLoad = useCallback((id: number) => {
setLoadedIds((state) => ({ ...state, [id]: id }))
}, [])
const allLoaded = useMemo(() => {
const ids = map(items, ({ id }) => id)
return every(ids, (id) => Boolean(loadedIds[id]))
}, [items, loadedIds])
return {
allLoaded,
onLoad,
}
}
type Props = {
players: PlayerPlaylistOptions,
team: Teams,
}
export const GroupedPlayersList = ({
players,
team,
}: Props) => {
const { allLoaded, onLoad } = useItemsLoadedState(players)
const { benchedPlayers, startingPlayers } = groupBy(
players,
({ start }) => (start ? 'startingPlayers' : 'benchedPlayers'),
)
return (
<ListsWrapper>
{
!allLoaded && (
<LoaderWrapper>
<Loader color='#515151' />
</LoaderWrapper>
)
}
<PlayersList
team={team}
players={startingPlayers}
allLoaded={allLoaded}
onLoad={onLoad}
/>
<PlayersList
team={team}
players={benchedPlayers}
allLoaded={allLoaded}
onLoad={onLoad}
/>
</ListsWrapper>
)
}

@ -0,0 +1,29 @@
import styled from 'styled-components/macro'
import { devices } from 'config'
import { List } from '../PlayersList/styled'
export const LoaderWrapper = styled.div`
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`
export const ListsWrapper = styled.div`
position: relative;
width: 576px;
display: flex;
flex-direction: column;
${List}:last-child {
margin-top: 25px;
}
@media ${devices.mobile} {
width: 100%;
}
`

@ -1,22 +1,13 @@
import React, {
RefObject,
useEffect,
useState,
} from 'react'
import map from 'lodash/map'
import size from 'lodash/size'
import { ProfileTypes, SportTypes } from 'config'
import { ProfileTypes } from 'config'
import { PlayerPlaylistOptions, PlayerPlaylistOption } from 'features/MatchPage/types'
import { PlayerPlaylistOptions } from 'features/MatchPage/types'
import { useMatchPopupStore } from 'features/MatchPopup/store'
import { Loader } from 'features/Loader'
import { Teams } from '../../types'
import {
List,
LoaderWrapper,
Item,
Logo,
PlayerName,
@ -24,52 +15,34 @@ import {
} from './styled'
type Props = {
onClick: (player: PlayerPlaylistOption) => void,
allLoaded: boolean,
onLoad: (id: number) => void,
players: PlayerPlaylistOptions,
sportType: SportTypes,
team: Teams,
}
export const PlayersList = ({
onClick,
allLoaded,
onLoad,
players,
sportType,
team,
}: Props) => {
const { match } = useMatchPopupStore()
const [imagesList, setImagesList] = useState<
Array<RefObject<HTMLImageElement>>
>([])
const [allImagesReady, setAllImagesReady] = useState(false)
useEffect(() => {
if (size(imagesList) === size(players)) {
setAllImagesReady(true)
}
}, [imagesList, players])
const { handlePlayerClick, match } = useMatchPopupStore()
if (!match) return null
return (
<List team={team}>
{!allImagesReady
&& (
<LoaderWrapper>
<Loader color='#515151' />
</LoaderWrapper>
)}
{
map(players, (player) => (
<Item key={player.id} isReady={allImagesReady}>
<Button onClick={() => onClick(player)}>
<Item key={player.id} isReady={allLoaded}>
<Button onClick={() => handlePlayerClick(player)}>
<Logo
id={player.id}
sportType={sportType}
sportType={match.sportType}
profileType={ProfileTypes.PLAYERS}
team={team}
imagesList={imagesList}
setImagesList={setImagesList}
onLoad={() => onLoad(player.id)}
/>
<PlayerName nameObj={player} />
</Button>

@ -15,23 +15,14 @@ type ItemProps = {
isReady?: boolean,
}
export const LoaderWrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
`
export const List = styled.ul<ListProps>`
width: calc((100% - 30px) / 2);
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: ${({ team }) => (
justify-content: ${({ team }) => (
team === Teams.TEAM1
? 'row-reverse'
: 'row'
? 'flex-end'
: ''
)};
@media ${devices.mobile} {
@ -66,6 +57,7 @@ export const Button = styled.button`
border: none;
background: none;
cursor: pointer;
padding: 0;
width: 100%;
height: 100%;

@ -1,11 +1,12 @@
import styled from 'styled-components/macro'
import { T9n } from 'features/T9n'
import { customScrollbar } from 'features/Common'
import { useMatchPopupStore } from 'features/MatchPopup'
import { Teams } from '../../types'
import { BlockTitle } from '../../styled'
import { PlayersList } from '../PlayersList'
import { GroupedPlayersList } from '../GroupedPlayersList'
const Wrapper = styled.div`
display: flex;
@ -16,15 +17,29 @@ const Wrapper = styled.div`
const ListsWrapper = styled.div`
width: 100%;
height: 325px;
overflow-y: auto;
margin-top: 10px;
display: flex;
flex-direction: row;
justify-content: space-between;
justify-content: space-around;
${customScrollbar};
::-webkit-scrollbar-thumb {
border-radius: 3px;
background: rgba(196, 196, 196, 0.3);
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
border-radius: 3px;
background: rgba(103, 103, 103, 0.3);
}
`
export const PlayersListDesktop = () => {
const {
handlePlayerClick,
match,
matchPlaylists,
} = useMatchPopupStore()
@ -37,17 +52,13 @@ export const PlayersListDesktop = () => {
<T9n t='team_players' />
</BlockTitle>
<ListsWrapper>
<PlayersList
<GroupedPlayersList
team={Teams.TEAM1}
players={matchPlaylists.players.team1}
sportType={match.sportType}
onClick={handlePlayerClick}
/>
<PlayersList
<GroupedPlayersList
team={Teams.TEAM2}
players={matchPlaylists.players.team2}
sportType={match.sportType}
onClick={handlePlayerClick}
/>
</ListsWrapper>
</Wrapper>

@ -6,7 +6,7 @@ import { useMatchPopupStore } from 'features/MatchPopup'
import { Teams } from '../../types'
import { BlockTitle } from '../../styled'
import { PlayersList } from '../PlayersList'
import { GroupedPlayersList } from '../GroupedPlayersList'
import {
Wrapper,
Tabs,
@ -15,7 +15,6 @@ import {
export const PlayersListMobile = () => {
const {
handlePlayerClick,
match,
matchPlaylists,
} = useMatchPopupStore()
@ -46,11 +45,9 @@ export const PlayersListMobile = () => {
<Name nameObj={match.team2} />
</Tab>
</Tabs>
<PlayersList
<GroupedPlayersList
team={selectedTeam}
players={players}
sportType={match.sportType}
onClick={handlePlayerClick}
/>
</Wrapper>
)

@ -1,5 +1,3 @@
import { RefObject } from 'react'
import { ProfileTypes, SportTypes } from 'config'
import { getProfileFallbackLogo, getProfileLogo } from 'helpers'
@ -12,12 +10,11 @@ type ProfileImageProps = {
altNameObj?: ObjectWithName,
className?: string,
id: number,
imagesList?: Array<RefObject<HTMLImageElement>>,
lazy?: boolean,
nameAsTitle?: boolean,
onLoad?: () => void,
prefix?: string,
profileType: ProfileTypes,
setImagesList?: (imagesList: Array<RefObject<HTMLImageElement>>) => void,
size?: number,
sportType: SportTypes,
title?: string,
@ -28,12 +25,11 @@ export const ProfileLogo = ({
altNameObj,
className,
id,
imagesList,
lazy = false,
nameAsTitle,
onLoad,
prefix,
profileType,
setImagesList,
size,
sportType,
title,
@ -58,8 +54,7 @@ export const ProfileLogo = ({
dataSrc={lazy ? src : ''}
fallbackSrc={fallbackSrc}
className={className}
imagesList={imagesList}
setImagesList={setImagesList}
onLoad={onLoad}
title={titleText}
/>
)

@ -40,6 +40,7 @@ type Player = {
name_eng: string,
name_rus: string,
num: string,
start?: boolean,
}
export type Players = Array<Player>

Loading…
Cancel
Save