11import React , { useState , useEffect } from 'react' ;
22
33import axios from 'axios' ;
4+ import cn from 'classnames' ;
45import { camelizeKeys } from 'humps' ;
5- import { useSelector } from 'react-redux' ;
6+ import { useDispatch , useSelector } from 'react-redux' ;
67
8+ import { loadUserOpponents } from '@/middlewares/Users' ;
79import {
810 selectDefaultAvatarUrl ,
911 currentUserIsAdminSelector ,
12+ userByIdSelector ,
1013} from '@/selectors' ;
1114
1215import i18n from '../../../i18n' ;
16+ import { actions } from '../../slices' ;
1317
1418import CodebattleLeagueDescription from './CodebattleLeagueDescription' ;
1519import TournamentListItem , { activeIcon } from './TournamentListItem' ;
1620
1721const contestDatesText = 'Season: Oct 16 - Dec 21' ;
1822
23+ const OpponentInfo = ( { id } ) => {
24+ const user = useSelector ( userByIdSelector ( id ) ) ;
25+
26+ return (
27+ < div className = "d-flex py-2 mx-1 stat-line" >
28+ < div className = "d-flex align-items-center w-100" >
29+ < UserLogo user = { user } size = "25px" />
30+ < span
31+ title = { user ?. name }
32+ className = {
33+ cn (
34+ 'text-white text-truncate ml-2' ,
35+ { 'cb-text-skeleton w-100' : ! user } ,
36+ )
37+ }
38+ style = { { maxWidth : '70px' } }
39+ >
40+ { user ?. name }
41+ </ span >
42+ </ div >
43+ < div className = "d-flex flex-column text-center py-1 w-100" >
44+ < span
45+ className = {
46+ cn (
47+ 'stat-value d-block cb-text-danger' ,
48+ { 'd-inline cb-text-skeleton w-25 mx-auto' : ! user } ,
49+ )
50+ }
51+ >
52+ { user ? user . rank : '' }
53+ </ span >
54+ < span className = "stat-label text-uppercase" > Place</ span >
55+ </ div >
56+ < div className = "d-flex flex-column text-center py-1 w-100" >
57+ < span
58+ className = {
59+ cn (
60+ 'stat-value d-block cb-text-danger' ,
61+ { 'd-inline cb-text-skeleton w-25 mx-auto' : ! user } ,
62+ )
63+ }
64+ >
65+ { user ? user . points : '' }
66+ </ span >
67+ < span className = "stat-label text-uppercase" > Points</ span >
68+ </ div >
69+ </ div >
70+ ) ;
71+ } ;
72+
73+ const SeasonOpponents = ( { user, opponents } ) => {
74+ const dispatch = useDispatch ( ) ;
75+ const [ loading , setLoading ] = useState ( ! ! user . points ) ;
76+
77+ useEffect ( ( ) => {
78+ if ( ! user . points ) {
79+ const abortController = new AbortController ( ) ;
80+
81+ const onSuccess = payload => {
82+ if ( ! abortController . signal . aborted ) {
83+ dispatch ( actions . setOpponents ( payload . data ) ) ;
84+ dispatch ( actions . updateUsers ( payload . data ) ) ;
85+ setLoading ( false ) ;
86+ }
87+ } ;
88+ const onError = ( ) => {
89+ setLoading ( false ) ;
90+ } ;
91+
92+ setLoading ( true ) ;
93+ loadUserOpponents ( abortController , onSuccess , onError ) ;
94+
95+ return abortController . abort ;
96+ }
97+
98+ return ( ) => { } ;
99+ } , [ dispatch , setLoading , user ?. points ] ) ;
100+
101+ if ( ! user . points || ( ! loading && opponents . length === 0 ) ) {
102+ return < > </ > ;
103+ }
104+
105+ return (
106+ < div className = "cb-bg-panel cb-rounded mt-2" >
107+ < div className = "d-flex flex-column" >
108+ < div className = "cb-bg-highlight-panel text-center cb-rounded-top" >
109+ < span className = "text-white text-uppercase p-1 pt-2" > Closest opponents</ span >
110+ </ div >
111+ { loading ? (
112+ < >
113+ < OpponentInfo />
114+ < OpponentInfo />
115+ </ >
116+ ) : opponents . map ( id => < OpponentInfo id = { id } /> ) }
117+ </ div >
118+ </ div >
119+ ) ;
120+ } ;
121+
19122const UserLogo = ( { user, size = '70px' } ) => {
20123 const [ userInfo , setUserInfo ] = useState ( ) ;
21124 const defaultAvatarUrl = useSelector ( selectDefaultAvatarUrl ) ;
22- const avatarUrl = user . avatarUrl || userInfo ?. avatarUrl || defaultAvatarUrl ;
125+ const avatarUrl = user ? .avatarUrl || userInfo ?. avatarUrl || defaultAvatarUrl ;
23126
24127 useEffect ( ( ) => {
25- const userId = user . id ;
26- const controller = new AbortController ( ) ;
27-
28- axios
29- . get ( `/api/v1/user/${ userId } /stats` , {
30- signal : controller . signal ,
31- } )
32- . then ( response => {
33- if ( ! controller . signal . aborted ) {
34- setUserInfo ( camelizeKeys ( response . data . user ) ) ;
35- }
36- } ) ;
128+ if ( user ) {
129+ const userId = user . id ;
130+ const controller = new AbortController ( ) ;
131+
132+ axios
133+ . get ( `/api/v1/user/${ userId } /stats` , {
134+ signal : controller . signal ,
135+ } )
136+ . then ( response => {
137+ if ( ! controller . signal . aborted ) {
138+ setUserInfo ( camelizeKeys ( response . data . user ) ) ;
139+ }
140+ } ) ;
141+
142+ return controller . abort ;
143+ }
37144
38- return ( ) => {
39- controller . abort ( ) ;
40- } ;
41- } , [ setUserInfo , user . id ] ) ;
145+ return ( ) => { } ;
146+ // eslint-disable-next-line
147+ } , [ setUserInfo , user ?. id ] ) ;
42148
43149 return (
44150 < img
@@ -53,6 +159,7 @@ const UserLogo = ({ user, size = '70px' }) => {
53159const SeasonProfilePanel = ( {
54160 seasonTournaments = [ ] ,
55161 liveTournaments = [ ] ,
162+ opponents,
56163 user,
57164 controls,
58165} ) => {
@@ -133,7 +240,7 @@ const SeasonProfilePanel = ({
133240 </ div >
134241 < div className = "col-12 col-lg-4 col-md-4 d-flex flex-column my-2 my-lg-0 my-md-0" >
135242 < div className = "cb-bg-panel cb-rounded" >
136- < div className = "text-center p-2 py-3 " >
243+ < div className = "text-center py-2 " >
137244 < UserLogo user = { user } />
138245 < span className = "clan-tag mt-2" > { user . name } </ span >
139246 < span className = "h1 clan-title m-0 text-white text-uppercase" >
@@ -175,10 +282,11 @@ const SeasonProfilePanel = ({
175282 </ div >
176283 </ div >
177284
178- < div className = "d-flex justify-content-center cb-font-size-small py-2 px-3 text-white" >
285+ < div className = "d-flex justify-content-center cb-font-size-small px-3 py-2 text-white" >
179286 < span className = "d-block" > { contestDatesText } </ span >
180287 </ div >
181288 </ div >
289+ < SeasonOpponents user = { user } opponents = { opponents } />
182290 { controls }
183291 </ div >
184292 </ div >
0 commit comments