Skip to content

Instantly share code, notes, and snippets.

@dwwoelfel
Last active May 29, 2017 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dwwoelfel/14b169bcd06e423548987b535bceaa16 to your computer and use it in GitHub Desktop.
Save dwwoelfel/14b169bcd06e423548987b535bceaa16 to your computer and use it in GitHub Desktop.
OneGraph YouTube Demo
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