Last active
May 29, 2017 01:20
-
-
Save dwwoelfel/14b169bcd06e423548987b535bceaa16 to your computer and use it in GitHub Desktop.
OneGraph YouTube Demo
This file contains hidden or 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
const VideoQuery = gql` | |
query videoPlayerQuery { | |
ytVideo(id: $videoId) { | |
player { | |
embedHtml | |
} | |
statistics { | |
viewCount | |
likeCount | |
dislikeCount | |
} | |
snippet { | |
title | |
publishedAtTs | |
channelId | |
uploadChannel { | |
contentDetails { | |
relatedPlaylists { | |
favorites | |
uploads | |
uploadsList { | |
snippet { | |
title | |
} | |
playlistItems { | |
items { | |
id | |
snippet { | |
title | |
thumbnails { | |
default { | |
url | |
width | |
height | |
} | |
} | |
description | |
resourceId { | |
kind | |
videoId | |
videoUrl | |
} | |
} | |
} | |
} | |
snippet { | |
title | |
description | |
} | |
status { | |
privacyStatus | |
} | |
} | |
watchHistory | |
watchLater | |
} | |
} | |
statistics { | |
viewCount | |
} | |
topicDetails { | |
topicIds | |
topicCategories | |
} | |
snippet { | |
title | |
thumbnails { | |
default { | |
url | |
} | |
high { | |
url | |
} | |
custom(size: 9000) { | |
url | |
} | |
} | |
} | |
} | |
description | |
channelTitle | |
tags | |
categoryId | |
liveBroadcastContent | |
statistics { | |
viewCount | |
} | |
} | |
} | |
}`; | |
const SearchQuery = gql` | |
query { | |
ytSearch(q: $q, maxResults: 1000) { | |
items { | |
totalCount | |
pageInfo { | |
nextCursor | |
prevCursor | |
totalResults | |
} | |
edges { | |
node { | |
id | |
itemKind | |
snippet { | |
title | |
publishedAtMs | |
description | |
thumbnails { | |
default { | |
url | |
width | |
height | |
} | |
high { | |
url | |
width height | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
}`; | |
function formatPublishedAt(publishedAt) { | |
return moment(publishedAt).format('MMM DD, YYYY'); | |
} | |
class VideoPlayer extends React.Component { | |
render() { | |
let videoChangeHandler = this.props.onVideoChange; | |
if (this.props.data.loading) { | |
return <div>Loading...</div>; | |
} | |
const {ytVideo} = this.props.data; | |
One.inspect(this.props.data); | |
if (!ytVideo) { | |
return <div>Uh oh, something went wrong :....</div>; | |
} | |
return ( | |
<div style={styles.container}> | |
<div dangerouslySetInnerHTML={{__html: ytVideo.player.embedHtml}} /> | |
<div style={styles.titleRow}> | |
<img | |
style={styles.profileImage} | |
src={ytVideo.snippet.uploadChannel.snippet.thumbnails.default.url} | |
/> | |
<div> | |
<div style={styles.title}> | |
{ytVideo.snippet.title} | |
</div> | |
<div style={styles.author}>{ytVideo.snippet.channelTitle}</div> | |
</div> | |
</div> | |
<div style={styles.pubDate}> | |
Published on {formatPublishedAt(ytVideo.snippet.publishedAtTs)} | |
</div> | |
<div> | |
Watched | |
{' '} | |
{ytVideo.statistics.viewCount} | |
{' '} | |
times | Liked by | |
{' '} | |
{ytVideo.statistics.likeCount} | |
{' '} | |
people | Disliked by | |
{' '} | |
{ytVideo.statistics.dislikeCount} | |
{' '} | |
people | |
</div> | |
<div style={styles.description}> | |
{ytVideo.snippet.description} | |
</div> | |
<ul> | |
{ytVideo.snippet.uploadChannel.contentDetails.relatedPlaylists.uploadsList.playlistItems[ | |
0 | |
].items.map(item => { | |
return ( | |
<li key={item.id}> | |
<a | |
href={item.snippet.resourceId.videoUrl} | |
target="_blank" | |
onClick={event => { | |
event.preventDefault(); | |
event.stopPropagation(); | |
videoChangeHandler(item.snippet.resourceId.videoId); | |
}}> | |
<img | |
width={item.snippet.thumbnails.default.width} | |
height={item.snippet.thumbnails.default.height} | |
src={item.snippet.thumbnails.default.url} | |
/> | |
{item.snippet.title} | |
</a> | |
</li> | |
); | |
})} | |
</ul> | |
</div> | |
); | |
} | |
} | |
class VideoSearch extends React.Component { | |
render() { | |
const {ytSearch} = this.props.data; | |
if (this.props.data.loading && !!this.props.ytSearch) { | |
return <div>Loading search results...</div>; | |
} | |
One.inspect(this.props); | |
if (!ytSearch && !!this.props.query) { | |
return <div>Uh oh, something went wrong :....</div>; | |
} | |
console.log(ytSearch); | |
return ( | |
<div style={styles.container}> | |
<button | |
onClick={event => this.props.onUiStateChange(uiStates.VideoPlayer)}> | |
Back to videos | |
</button> | |
<div> | |
{this.props.data.loading || !ytSearch | |
? <img src="/ajax-loader.gif" /> | |
: <div> | |
{ytSearch.items.totalCount} matches for {this.props.query}<br /> | |
<ul> | |
{ytSearch.items.edges.map((edge, idx) => { | |
return ( | |
<li key={edge.node.snippet.title + idx}> | |
<a | |
href={edge.node.snippet.videoUrl} | |
target="_blank" | |
onClick={event => { | |
event.preventDefault(); | |
event.stopPropagation(); | |
this.props.onSearchVideoSelected(edge.node.id); | |
}}> | |
<img | |
width={edge.node.snippet.thumbnails.default.width} | |
height={edge.node.snippet.thumbnails.default.height} | |
src={edge.node.snippet.thumbnails.default.url} | |
/> | |
{edge.node.snippet.title} | |
</a> | |
</li> | |
); | |
})} | |
</ul> | |
<pre>Nice, huh?</pre> | |
</div>} | |
</div> | |
</div> | |
); | |
} | |
} | |
let uiStates = { | |
VideoPlayer: 'VideoPlayer', | |
Search: 'Search', | |
}; | |
class OneYoutubeWrapper extends React.Component { | |
onVideoChange(id) { | |
console.log('Setting video id to:', id); | |
this.setState(s => { | |
return {...s, videoId: id}; | |
}); | |
} | |
onSearchQueryChange(query) { | |
console.log('Setting query to:', query, 'and uiState to Search'); | |
this.setState(s => { | |
return {...s, query, uiState: uiStates.Search}; | |
}); | |
} | |
onSearchVideoSelected(videoId) { | |
console.log('Setting videoId to:', videoId, ' and uiState to VideoPlayer'); | |
this.setState(s => { | |
return {...s, videoId, uiState: uiStates.VideoPlayer}; | |
}); | |
} | |
onUiStateChange(screenName) { | |
console.log('Setting ui screen to:', screenName); | |
this.setState(s => { | |
return {...s, uiState: screenName}; | |
}); | |
} | |
constructor(props) { | |
super(props); | |
this.state = {videoId: 'IZeWPScnolo', uiState: uiStates.Search}; | |
this.onVideoChange = this.onVideoChange.bind(this); | |
this.onSearchQueryChange = this.onSearchQueryChange.bind(this); | |
this.onSearchVideoSelected = this.onSearchVideoSelected.bind(this); | |
this.onUiStateChange = this.onUiStateChange.bind(this); | |
} | |
render() { | |
let screen = this.state.uiState; | |
let searchChangeHandler = this.onSearchQueryChange; | |
return (<div> | |
<input | |
id="searchValue" | |
type="text" | |
defaultValue={this.props.query} | |
placeholder="Search" | |
onKeyDown={event => event.which == 13 ? searchChangeHandler(event.target.value) : null} | |
/> | |
{(screen == uiStates.VideoPlayer) ? <VideoPlayerWithData | |
videoId={this.state.videoId} | |
onUiStateChange={this.onUiStateChange} | |
onVideoChange={this.onVideoChange} | |
onSearchQueryChange={this.onSearchQueryChange} | |
uiState={this.state.uiState} | |
/> | |
: <VideoSearchWithData | |
query={this.state.query} | |
videoId={this.state.videoId} | |
onUiStateChange={this.onUiStateChange} | |
onVideoChange={this.onVideoChange} | |
onSearchQueryChange={this.onSearchQueryChange} | |
onSearchVideoSelected={this.onSearchVideoSelected} | |
uiState={this.state.uiState} | |
/> | |
} | |
</div>); | |
} | |
} | |
const VideoPlayerWithData = graphql(VideoQuery, { | |
options: ({videoId, uiState, query}) => ({variables: {videoId, q: query}}), | |
})(VideoPlayer); | |
const VideoSearchWithData = graphql(SearchQuery, { | |
options: data => { | |
let {query, cursor} = data; | |
return {variables: {q: query || '', cursor: cursor}}; | |
}, | |
})(VideoSearch); | |
const styles = { | |
container: {fontFamily: 'Roboto, arial, sans-serif'}, | |
titleRow: { | |
display: 'flex', | |
alignItems: 'center', | |
marginTop: '12px', | |
marginBottom: '12px', | |
}, | |
title: {fontSize: '20px'}, | |
profileImage: {width: '48px', height: '48px', marginRight: '8px'}, | |
pubDate: {fontSize: '13px', lineHeight: '14px', fontWeight: 'bold'}, | |
description: {fontSize: '13px', lineHeight: '14px', maxWidth: '624px'}, | |
}; | |
const getInitialState = props => { | |
return {message: 'works'}; | |
}; | |
const node = document.getElementById('demo'); | |
ReactDOM.render( | |
<ApolloProvider client={OMNI_GRAPHQL_CLIENT}> | |
<OneYoutubeWrapper /> | |
</ApolloProvider>, | |
node, | |
); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment