| 
									
										
										
										
											2017-12-15 13:53:32 -08:00
										 |  |  | import React, { PureComponent } from 'react'; | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  | import PropTypes from 'prop-types'; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | import { connect } from 'react-redux'; | 
					
						
							|  |  |  | import { Col, Row } from 'react-bootstrap'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ns from './ns.json'; | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | import { Loader } from '../helperComponents'; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | import SuperBlock from './Super-Block.jsx'; | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  | import { currentChallengeSelector, superBlocksSelector } from '../redux'; | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | import { fetchMapUi } from './redux'; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const mapStateToProps = state => ({ | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  |   currentChallenge: currentChallengeSelector(state), | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |   superBlocks: superBlocksSelector(state) | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | const mapDispatchToProps = { fetchMapUi }; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | const propTypes = { | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  |   currentChallenge: PropTypes.string, | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  |   fetchMapUi: PropTypes.func.isRequired, | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |   params: PropTypes.object, | 
					
						
							|  |  |  |   superBlocks: PropTypes.array | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class ShowMap extends PureComponent { | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  |   componentDidMount() { | 
					
						
							|  |  |  |     this.setupMapScroll(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   componentDidUpdate() { | 
					
						
							|  |  |  |     this.setupMapScroll(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setupMapScroll() { | 
					
						
							|  |  |  |     this.updateMapScrollAttempts = 0; | 
					
						
							|  |  |  |     this.updateMapScroll(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   updateMapScroll() { | 
					
						
							|  |  |  |     const { currentChallenge } = this.props; | 
					
						
							|  |  |  |     const rowNode = this._row; | 
					
						
							|  |  |  |     const challengeNode = rowNode.querySelector( | 
					
						
							|  |  |  |       `[data-challenge="${currentChallenge}"]` | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( !challengeNode ) { | 
					
						
							|  |  |  |       this.retryUpdateMapScroll(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const containerScrollHeight = rowNode.scrollHeight; | 
					
						
							|  |  |  |     const containerHeight = rowNode.clientHeight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const offset = 100; | 
					
						
							|  |  |  |     const itemTop = challengeNode.offsetTop; | 
					
						
							|  |  |  |     const itemBottom = itemTop + challengeNode.clientHeight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const currentViewBottom = rowNode.scrollTop + containerHeight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( itemBottom + offset < currentViewBottom ) { | 
					
						
							|  |  |  |       // item is visible with enough offset from bottom => no need to scroll
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( containerHeight === containerScrollHeight ) { | 
					
						
							|  |  |  |       /* | 
					
						
							|  |  |  |       * During a first run containerNode scrollHeight may be not updated yet. | 
					
						
							|  |  |  |       * In this case containerNode ignores changes of scrollTop property. | 
					
						
							|  |  |  |       * So we have to wait some time before scrollTop can be updated | 
					
						
							|  |  |  |       * */ | 
					
						
							|  |  |  |       this.retryUpdateMapScroll(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const scrollTop = itemBottom + offset - containerHeight; | 
					
						
							|  |  |  |     rowNode.scrollTop = scrollTop; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   retryUpdateMapScroll() { | 
					
						
							|  |  |  |     const maxAttempts = 5; | 
					
						
							|  |  |  |     this.updateMapScrollAttempts++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this.updateMapScrollAttempts < maxAttempts) { | 
					
						
							|  |  |  |       setTimeout(() => this.updateMapScroll(), 300); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   renderSuperBlocks() { | 
					
						
							|  |  |  |     const { superBlocks } = this.props; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |     if (!Array.isArray(superBlocks) || !superBlocks.length) { | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  |       return ( | 
					
						
							| 
									
										
										
										
											2018-02-24 08:44:12 +00:00
										 |  |  |         <div style={{ height: '300px' }}> | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  |           <Loader /> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     return superBlocks.map(dashedName => ( | 
					
						
							|  |  |  |       <SuperBlock | 
					
						
							|  |  |  |         dashedName={ dashedName } | 
					
						
							|  |  |  |         key={ dashedName } | 
					
						
							|  |  |  |       /> | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   render() { | 
					
						
							|  |  |  |     return ( | 
					
						
							| 
									
										
										
										
											2018-01-09 10:41:44 +03:00
										 |  |  |       <div className = { `${ns}-container`} ref={ ref => { this._row = ref; }}> | 
					
						
							|  |  |  |         <Row> | 
					
						
							|  |  |  |           <Col xs={ 12 }> | 
					
						
							|  |  |  |             <div className={ `${ns}-accordion center-block` }> | 
					
						
							|  |  |  |               { this.renderSuperBlocks() } | 
					
						
							|  |  |  |               <div className='spacer' /> | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           </Col> | 
					
						
							|  |  |  |         </Row> | 
					
						
							|  |  |  |       </div> | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ShowMap.displayName = 'Map'; | 
					
						
							|  |  |  | ShowMap.propTypes = propTypes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default connect( | 
					
						
							|  |  |  |   mapStateToProps, | 
					
						
							|  |  |  |   mapDispatchToProps | 
					
						
							|  |  |  | )(ShowMap); |