Created
February 16, 2019 21:37
-
-
Save codesnakers/83e732153566e29f7982b109cad6b0cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable */ | |
import React, { Component } from 'react'; | |
import Axios from 'axios'; | |
import MovieIcon from '@material-ui/icons/Movie'; | |
import AvTimerIcon from '@material-ui/icons/AvTimer'; | |
import VisibilityIcon from '@material-ui/icons/Visibility'; | |
import FavoriteIcon from '@material-ui/icons/Favorite'; | |
import WarningIcon from '@material-ui/icons/Warning'; | |
import PropTypes from 'prop-types'; | |
import {VideoClass} from '../../models/video.class'; | |
import { YoutubeService } from '../../services/youtube/Youtube'; | |
import './Youtube.scss'; | |
import { appConfig } from '../../config'; | |
const service = new YoutubeService(); | |
export const splitNumToArray = (n, limit) => { | |
var result = []; | |
while(n>0){ | |
if(n>limit){ result.push(limit); } | |
else{ result.push(n); } | |
n = n - limit; | |
} | |
return result; | |
} | |
class Youtube extends Component { | |
overWriteTrends = false; | |
constructor(props) { | |
super(props); | |
this.state = { | |
trends: [], | |
isError: false, | |
isLoading: false, | |
totalResults: null | |
}; | |
this.handlePromise = this.handlePromise.bind(this); | |
var _self = this; | |
// Binds our scroll event handler | |
window.onscroll = () => { | |
const {isError, isLoading} = this.state; | |
// Bails early if: | |
// * there's an error | |
// * it's already loading | |
// * there's nothing left to load | |
/*if (isError || isLoading || !hasMore) return;*/ | |
if (isError || isLoading) return; | |
// Checks that the page has scrolled to the bottom | |
/*console.log(window.innerHeight , document.documentElement.scrollTop , document.documentElement.scrollHeight, document.documentElement.offsetHeight); | |
*/ | |
/*if (window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight){*/ | |
/*console.log(window.innerHeight + document.documentElement.scrollTop, document.documentElement.scrollHeight);*/ | |
if (window.innerHeight + document.documentElement.scrollTop === document.documentElement.scrollHeight){ | |
/*console.log("loading more videos...");*/ | |
if(_self.state.trends.length>0 && _self.state.totalResults!==null && _self.state.trends.length<_self.state.totalResults){ | |
_self.setState({ isLoading: true }, _self.loadVideos); | |
} | |
} | |
}; | |
} | |
resetState(cb){ | |
if(this.state.isLoading===false){ | |
this.setState({isLoading:true, totalResults: null, trends:[]}, cb); | |
} | |
service.nextPageTokenClassVar = null; | |
this.overWriteTrends = true; | |
} | |
componentWillMount() { | |
var _self = this; | |
this.props.setTitle('YOUTUBE'); | |
this.loadCategories(); | |
this.props.onChanges(() => { | |
if(_self.state.isLoading===false){ | |
_self.setState({ isLoading: true }, this.loadVideos); | |
} | |
}); | |
} | |
componentDidUpdate(prevProps){ | |
const config = appConfig; | |
var prev = prevProps.filterObj, cur = this.props.filterObj; | |
/* | |
If user increases the slider value we reset the nextPageToken | |
so if previously 25 was slider value and user slides to 30 | |
30 videos are requested from youtube API and overwritten in trends state | |
and thanks to React only the new 5 videos are added to the dom | |
*/ | |
if(prev[config.CATEGORY_FILTER]===cur[config.CATEGORY_FILTER] && | |
prev[config.COUNTRY_FILTER]===cur[config.COUNTRY_FILTER] && | |
prev[config.MAXVIDEO_FILTER]!=cur[config.MAXVIDEO_FILTER] ){ | |
this.overWriteTrends = true; | |
service.nextPageTokenClassVar = null; | |
} | |
/* if user changes category or country reset trends, total results, nextPageToken and loadVideos */ | |
else if(prev[config.CATEGORY_FILTER]!==cur[config.CATEGORY_FILTER] || | |
prev[config.COUNTRY_FILTER]!==cur[config.COUNTRY_FILTER] || | |
prev[config.MAXVIDEO_FILTER]!==cur[config.MAXVIDEO_FILTER] ){ | |
/*this.resetState(this.loadVideos);*/ | |
this.resetState(); | |
} | |
} | |
async loadCategories(){ | |
const _self = this; | |
Axios.all(await service.getCategories(_self.props.filterObj) ) | |
.then((data) => { | |
data = data.flat(); | |
/*console.log(data);*/ | |
_self.props.setCategories(data); | |
}) | |
.catch((err) => { | |
_self.setState({isError: true}); | |
console.log(err); | |
}); | |
} | |
/* | |
Initially totalResults = null , trends.length = 0 | |
so remaining = 0 | |
so videosToLoad = filterObj[appConfig.MAXVIDEO_FILTER] i.e. slider value | |
after initial load we get totalResults value in youtube api response | |
so lets say slider value was 25, so initially 25 videos loaded | |
and lets say totalResults = 200 | |
so remaining = totalResults - trends.length = 200 - 25 = 175 | |
this will be used when next load happens like when user scrolls | |
*/ | |
getVideosToLoad(totalResults, trendsLen, maxVideosToLoad, overWriteTrends){ | |
var remaining = totalResults - trendsLen; | |
var videosToLoad; | |
if(remaining>0 && remaining < maxVideosToLoad && overWriteTrends===false){ | |
videosToLoad = remaining; | |
}else{ | |
videosToLoad = maxVideosToLoad; | |
} | |
return videosToLoad; | |
} | |
getTrendingVideosPromise(videosToLoad, maxYTVideoLimit, filterObj){ | |
/* | |
youtube api only returns max 50 videos in 1 request so we split requests | |
splitNumToArray( 150, 50 ) => returns [50,50,50] | |
splitNumToArray( 161, 50 ) => returns [50,50,50,11] | |
*/ | |
var splitBy50 = splitNumToArray(videosToLoad, maxYTVideoLimit); | |
/*console.log(splitBy50, videosToLoad, maxYTVideoLimit);*/ | |
var prom; | |
if(splitBy50.length > 1){ | |
/* https://decembersoft.com/posts/promises-in-serial-with-array-reduce/ */ | |
/* | |
this technique found in url above will work only if we have >=2 values in array | |
*/ | |
prom = splitBy50.reduce((accumulator, currentVal)=>{ | |
var prom = typeof accumulator === "number"?service.getTrendingVideos(accumulator, filterObj):accumulator; | |
return prom.then((result)=>{ | |
result = [result]; | |
result = result.flat(); | |
service.nextPageTokenClassVar = result[result.length-1].data.nextPageToken; | |
/* | |
if resultant nextPageToken is undefined means all videos are loaded and we dont have next page to load | |
so we return result | |
*/ | |
if(result[result.length-1].data.nextPageToken===undefined){ | |
return result; | |
} | |
return service.getTrendingVideos(currentVal, filterObj).then((result2)=>{ | |
service.nextPageTokenClassVar = result2.data.nextPageToken; | |
result.push(result2); | |
return result; | |
}); | |
}); | |
}); | |
}else{ | |
/*if slider value has <=50 we just need a single request */ | |
prom = service.getTrendingVideos(splitBy50[0], filterObj); | |
} | |
return prom; | |
} | |
handlePromise(allResults){ | |
const _self = this; | |
/** if slider value is <=50 we get an object, */ | |
if(allResults.length === undefined){ | |
allResults = [allResults]; | |
} | |
var totalResults = allResults[allResults.length-1].data.pageInfo.totalResults; | |
service.nextPageTokenClassVar = allResults[allResults.length-1].data.nextPageToken; | |
allResults = allResults.map((res)=>{ | |
return res.data.items.map((item) => new VideoClass(item)).filter((item) => item.id !== ''); | |
}); | |
/** if only slder was change we overwrite trends otherwise we add to the trends */ | |
_self.setState({ | |
trends: _self.overWriteTrends?[...allResults.flat()]:[..._self.state.trends, ...allResults.flat()], | |
totalResults: totalResults, | |
isError: false, | |
isLoading: false | |
}); | |
} | |
async loadVideos() { | |
/*Axios.all(await service.getTrendingVideos(this.props.config.maxVideosToLoad))*/ | |
const _self = this; | |
const { trends, totalResults } = _self.state; | |
/*const {filterObj, nextPageToken} = _self.props;*/ | |
const {filterObj} = _self.props; | |
const config = appConfig; | |
/*console.log(trends.length, totalResults);*/ | |
/* 0>=null is true #JavascriptTheWeirdParts :D */ | |
/*if(nextPageToken!==null && trends.length > 0 && totalResults!==null && trends.length >= totalResults){*/ | |
var videosToLoad = this.getVideosToLoad(totalResults, trends.length, filterObj[config.MAXVIDEO_FILTER], _self.overWriteTrends); | |
if(trends.length > 0 && totalResults!==null && trends.length >= totalResults && filterObj[config.MAXVIDEO_FILTER]>=totalResults){ | |
/*console.log('all videos loaded');*/ | |
_self.setState({ | |
isError: false, | |
isLoading: false | |
}); | |
return; | |
} | |
var prom = this.getTrendingVideosPromise(videosToLoad, config.maxYTVideoLimit, filterObj); | |
prom.then(this.handlePromise).catch((err) => { | |
_self.setState({isError: true,isLoading: false}); | |
console.log(err); | |
}).then(()=>{ | |
/** reset overWriteTrends value back to false */ | |
_self.overWriteTrends = false; | |
}); | |
} | |
openVideo() { | |
return window.location.href = '/youtube/' + this; | |
} | |
youtubeCard() { | |
if(this.state.trends.length===0 && this.state.isLoading===false && this.state.totalResults!==null){ | |
return <div className="noVideos">No Trending Videos</div>; | |
} | |
return this.state.trends.map((videos, index) => | |
<div key={index} className="card-container"> | |
<div className="card" onClick={this.openVideo.bind(videos.id)}> | |
<div className="img-container"> | |
<img src={videos.thumbnail} alt={videos.title}/> | |
<MovieIcon/> | |
</div> | |
<div className="video-statistic"> | |
<div className="publishedAt"> | |
<AvTimerIcon/> | |
<span>{videos.publishedAt}</span> | |
</div> | |
<div className="viewCount"> | |
<VisibilityIcon/> | |
<span>{videos.viewCount}</span> | |
</div> | |
<div className="likeCount"> | |
<FavoriteIcon/> | |
<span>{videos.likeCount}</span> | |
</div> | |
</div> | |
<p className="video-title text-ellipsis"> | |
{videos.title} | |
</p> | |
</div> | |
</div> | |
); | |
} | |
errorOnPage() { | |
return <div className="error-plate"> | |
<WarningIcon/> | |
<span>Error loading. Please try again later.</span> | |
</div>; | |
} | |
render() { | |
const { trends, totalResults, isLoading } = this.state; | |
return !this.state.isError ? ( <div id="youtube"> | |
<div className="row"> | |
{this.youtubeCard()} | |
</div> | |
{(trends.length > 0 && totalResults!==null && trends.length >= totalResults)? | |
<div className="loadingVideos"></div>: | |
(isLoading?<div className="loadingVideos">Loading...</div>:'') | |
} | |
</div>) : (this.errorOnPage()); | |
} | |
} | |
Youtube.propTypes = { | |
setTitle : PropTypes.func, | |
onChanges: PropTypes.func, | |
setCategories: PropTypes.func, | |
/*setNextPageToken: PropTypes.func, | |
nextPageToken: PropTypes.string*/ | |
}; | |
export default Youtube; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment