Skip to content

Instantly share code, notes, and snippets.

@xRahul
Created June 8, 2016 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save xRahul/57221659515146522d4bff6e3beef14a to your computer and use it in GitHub Desktop.
Save xRahul/57221659515146522d4bff6e3beef14a to your computer and use it in GitHub Desktop.
Random Quotes - React
<html>
<head>
<title>
Random Quotes
</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
// 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'));
<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>
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;
}
<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