Created
June 8, 2016 16:48
-
-
Save xRahul/57221659515146522d4bff6e3beef14a to your computer and use it in GitHub Desktop.
Random Quotes - React
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
<html> | |
<head> | |
<title> | |
Random Quotes | |
</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
</body> | |
</html> |
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
// required variables and functions | |
const render = ReactDOM.render | |
const Redux = window.Redux | |
const Provider = ReactRedux.Provider | |
const createStore = Redux.createStore | |
const applyMiddleware = Redux.applyMiddleware | |
const combineReducers = Redux.combineReducers | |
const bindActionCreators = Redux.bindActionCreators | |
const compose = Redux.compose | |
const ReduxThunk = window.ReduxThunk.default | |
const Component = React.Component | |
const PropTypes = React.PropTypes | |
const connect = ReactRedux.connect | |
const classnames = window.classNames | |
const S = window.S | |
const QUOTE_REQUEST = 'QUOTE_REQUEST' | |
const QUOTE_FAILED = 'QUOTE_FAILED' | |
const QUOTE_SUCCESS = 'QUOTE_SUCCESS' | |
const ENTITY_REQUEST = 'ENTITY_REQUEST' | |
const ENTITY_FAILED = 'ENTITY_FAILED' | |
const ENTITY_SUCCESS = 'ENTITY_SUCCESS' | |
// reducer | |
function quotes (state = { | |
quote: "Built using React & Redux in ES6.", | |
author: "Rahul Jain", | |
category: "reactjs", | |
fetching: false, | |
failure: false | |
}, action) { | |
switch (action.type) { | |
case QUOTE_REQUEST: | |
return Object.assign({}, state, { | |
fetching: true, | |
failure: false | |
}) | |
case QUOTE_FAILED: | |
return Object.assign({}, state, { | |
fetching: false, | |
failure: true | |
}) | |
case QUOTE_SUCCESS: | |
return Object.assign({}, state, { | |
fetching: false, | |
failure: false, | |
quote: action.quote, | |
author: action.author, | |
category: action.category | |
}) | |
default: | |
return state | |
} | |
} | |
function entity (state = { | |
name: "Rahul Jain", | |
url: "http://rahulja.in", | |
description: { | |
short: "Software Engineer", | |
long: "He is a member of FreeCodeCamp creating all this stuff. He's a full stack developer who has worked in backend with laravel/symfony and frontend with React, Redux, Bootstrap ", | |
url: "https://www.freecodecamp.com/xrahul" | |
}, | |
image: { | |
content: 'https://avatars2.githubusercontent.com/u/1639945?v=3&s=460', | |
url: 'https://github.com/xRahul' | |
} | |
}, action) { | |
switch (action.type) { | |
case ENTITY_REQUEST: | |
return Object.assign({}, state, { | |
fetching: true, | |
failure: false | |
}) | |
case ENTITY_FAILED: | |
return Object.assign({}, state, { | |
fetching: false, | |
failure: true | |
}) | |
case ENTITY_SUCCESS: | |
return Object.assign({}, state, { | |
fetching: false, | |
failure: false, | |
name: action.name, | |
url: action.url, | |
description: { | |
short: action.description, | |
long: action.detailedDescription?action.detailedDescription.articleBody:'', | |
url: action.detailedDescription?action.detailedDescription.url:'#' | |
}, | |
image: { | |
content: action.image?action.image.contentUrl:'#', | |
url: action.image?action.image.url:'#' | |
} | |
}) | |
default: | |
return state | |
} | |
} | |
// actions | |
function requestQuote() { | |
return { | |
type: QUOTE_REQUEST | |
} | |
} | |
function receiveQuote(data) { | |
return { | |
type: QUOTE_SUCCESS, | |
...data | |
} | |
} | |
function errorQuote(data) { | |
return { | |
type: QUOTE_FAILED | |
} | |
} | |
// async action to fetch quote | |
function fetchQuote() { | |
return dispatch => { | |
const categories = ['famous', 'movies'] | |
const randomCategory = categories[Math.floor(Math.random()*categories.length)]; | |
dispatch(requestQuote()) | |
return fetch(`https://andruxnet-random-famous-quotes.p.mashape.com/?cat=${randomCategory}`, { | |
method: 'POST', | |
headers: { | |
'X-Mashape-Key': 'jKNg25Khixmsh1VJdphF2MTaHxJRp18QLBGjsnc8uzF3vpAWdE', | |
'Content-Type': 'application/x-www-form-urlencoded', | |
'Accept': 'application/json' | |
} | |
}).then(response => response.json()) | |
.then( (json) => { | |
dispatch(receiveQuote(json)) | |
dispatch(fetchEntityInfo(json.author)) | |
}) | |
.catch(error => dispatch(errorQuote(error))) | |
} | |
} | |
function requestEntity() { | |
return { | |
type: ENTITY_REQUEST | |
} | |
} | |
function receiveEntity(data) { | |
return { | |
type: ENTITY_SUCCESS, | |
...data | |
} | |
} | |
function errorEntity(data) { | |
return { | |
type: ENTITY_FAILED | |
} | |
} | |
function fetchEntityInfo(entity) { | |
return dispatch => { | |
const baseUrl = 'https://kgsearch.googleapis.com/v1/entities:search?' | |
const params = { | |
'query': entity, | |
'limit': 1, | |
'key': 'AIzaSyBTt2yzQBb4hKEu3V9AwqYyVbPhF4IAjm4', | |
'types': 'Person' | |
} | |
dispatch(requestEntity()) | |
return fetch(baseUrl + $.param(params) + '&types=Movie').then(response => response.json()) | |
.then( (json) => { | |
dispatch(receiveEntity(json.itemListElement[0].result)) | |
}) | |
.catch(error => dispatch(errorEntity(error))) | |
} | |
} | |
class Quote extends Component{ | |
render() { | |
return ( | |
<h2 id="quote"> | |
<sup> | |
<i className="fa fa-quote-left " aria-hidden="true"></i> | |
</sup> | |
{' '} | |
{this.props.quote} | |
{' '} | |
<sub> | |
<i className="fa fa-quote-right" aria-hidden="true"></i> | |
</sub> | |
</h2> | |
) | |
} | |
} | |
class Author extends Component{ | |
render() { | |
const icon = this.props.category==="Movies"?"film":"pencil" | |
const iconClass = classnames({ | |
'fa': true, | |
'fa-film': this.props.category==="Movies", | |
'fa-pencil': this.props.category==="Famous", | |
'fa-star': this.props.category==="reactjs" | |
}) | |
return ( | |
<h3 id="author" className="text-right"> | |
<small> | |
<i className={iconClass} aria-hidden="true"></i> | |
</small> | |
{' '} | |
{this.props.author} | |
</h3> | |
) | |
} | |
} | |
class Social extends Component{ | |
render() { | |
const { quote, author, category } = this.props | |
const baseTwitterUrl = 'https://twitter.com/intent/tweet?' | |
const hashtagsUrl = 'hashtags='+S(author).camelize().s+',freeCodeCamp,quote' | |
const textUrl = '&text='+encodeURIComponent('"'+quote.slice(0,110)+'"') | |
const relatedUrl = '&related=xRahulJain,freecodecamp' | |
const twitterUrl = baseTwitterUrl+hashtagsUrl+textUrl+relatedUrl | |
return ( | |
<div className="col-xs-5" id="social"> | |
<a className="col-xs-4" id="twitterButton" | |
target="_blank" | |
href={twitterUrl} > | |
<i className="fa fa-twitter-square fa-3x"></i> | |
</a> | |
{' '} | |
<Category | |
category={category} /> | |
</div> | |
) | |
} | |
} | |
class Category extends Component{ | |
render() { | |
return ( | |
<h4 className="text-left col-xs-8" id="hashtag"> | |
<small><i className="fa fa-hashtag"></i></small> | |
{this.props.category.toLowerCase()} | |
</h4> | |
) | |
} | |
} | |
class Footer extends Component{ | |
render() { | |
const {quote, author, category, fetching, onRandomClick} = this.props.allProps | |
return ( | |
<div className="footer"> | |
<div> | |
<Social | |
quote={quote} | |
author={author} | |
category={category} /> | |
<RandomButton | |
fetching={fetching} | |
onClick={onRandomClick} /> | |
</div> | |
<div className="clearfix"></div> | |
</div> | |
) | |
} | |
} | |
class RandomButton extends Component{ | |
render() { | |
const spinnerIcon = classnames({ | |
'fa': true, | |
'fa-refresh': true, | |
'fa-spin': this.props.fetching | |
}) | |
return ( | |
<div id="randomButton" className="text-right col-xs-7"> | |
<a | |
className="btn btn-default btn-lg" | |
onClick={() => this.props.onClick()}> | |
<i className={spinnerIcon}></i> New Quote | |
</a> | |
</div> | |
) | |
} | |
} | |
class EntityImage extends Component { | |
render() { | |
const { name } = this.props.entity | |
const { content, url } = this.props.entity.image | |
return ( | |
<div className="col-sm-4"> | |
<a href={url} > | |
<img | |
className="img-responsive" | |
src={content} | |
alt={name} /> | |
</a> | |
</div> | |
) | |
} | |
} | |
class EntityDescription extends Component { | |
render() { | |
const { name, url, description, image, fetching } = this.props.entity | |
const spinnerIcon = classnames({ | |
'fa': true, | |
'fa-refresh': true, | |
'fa-spin': true | |
}) | |
const descGrid = classnames({ | |
'col-sm-8': image.content !== '#', | |
'col-sm-12': image.content === '#' | |
}) | |
return ( | |
<div className={descGrid}> | |
<h3> | |
<a href={url} > | |
{name} | |
</a> | |
{' '} | |
{fetching && | |
<small> | |
<i className={spinnerIcon}></i> | |
</small> | |
} | |
</h3> | |
<h3> | |
<small>{description.short}</small> | |
</h3> | |
<p>{description.long}</p> | |
{'Link: '} | |
<a href={description.url} >{description.url}</a> | |
</div> | |
) | |
} | |
} | |
class App extends Component { | |
render() { | |
const { quote, author, category, fetching, failure, onRandomClick, entity } = this.props | |
return ( | |
<div className="container"> | |
<div className="jumbotron col-lg-8 col-lg-offset-2"> | |
<Quote quote={quote} /> | |
<Author | |
author={author} | |
category={category} /> | |
<Footer | |
allProps={this.props} /> | |
</div> | |
<div className="jumbotron col-lg-8 col-lg-offset-2"> | |
{entity.image.content !== '#' && | |
<EntityImage entity={entity} /> | |
} | |
<EntityDescription entity={entity} /> | |
<div className="clearfix"></div> | |
</div> | |
</div> | |
) | |
} | |
} | |
// proptypes required for App component | |
App.propTypes = { | |
quote: PropTypes.string.isRequired, | |
author: PropTypes.string.isRequired, | |
category: PropTypes.string.isRequired, | |
fetching: PropTypes.bool.isRequired, | |
failure: PropTypes.bool.isRequired, | |
onRandomClick: PropTypes.func.isRequired, | |
entity: PropTypes.object.isRequired | |
} | |
// helper functions for app container | |
function mapStateToProps(state) { | |
const { quotes, entity } = state | |
const { quote, author, category, fetching, failure } = quotes | |
return { quote, author, category, fetching, failure, entity } | |
} | |
function mapDispatchToProps(dispatch) { | |
return { | |
onRandomClick: () => dispatch(fetchQuote()) | |
} | |
} | |
// create app container using connect() | |
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App) | |
// create store using middlewares | |
const rootReducer = combineReducers({ | |
quotes, | |
entity | |
}) | |
let store = createStore( | |
rootReducer, | |
compose( | |
applyMiddleware(ReduxThunk), | |
window.devToolsExtension ? window.devToolsExtension() : f => f | |
) | |
) | |
// render the app to the page | |
render( | |
<Provider store={store}> | |
<AppContainer /> | |
</Provider> | |
,document.getElementById('app')); |
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
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> | |
<script src="https://fb.me/react-15.1.0.min.js"></script> | |
<script src="https://fb.me/react-dom-15.1.0.min.js"></script> | |
<script src="https://npmcdn.com/redux@3.5.2/dist/redux.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.4.5/react-redux.min.js"></script> | |
<script src="https://npmcdn.com/redux-thunk@2.0.1/dist/redux-thunk.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.min.js"></script> | |
<script src="https://cdn.bootcss.com/string.js/3.3.1/string.min.js"></script> |
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
body { | |
margin-top: 5em; | |
font-family: 'Josefin Sans', sans-serif; | |
} | |
.footer { | |
margin-top: 2em; | |
} | |
.jumbotron { | |
padding-bottom: 1em; | |
} | |
#hashtag { | |
margin-top: 0.6em; | |
padding-right: 0; | |
} | |
#twitterButton { | |
padding-right: 0; | |
padding-left: 0; | |
} | |
#social { | |
padding-left: 0; | |
padding-right: 0; | |
} | |
#randomButton { | |
padding-left: 0; | |
} |
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
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" /> | |
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment