Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add to reduxstagram #23

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions learn-redux/client/actions/actionCreators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Creates actions including what happened and the payload of information that is needed to execute. When the actions get dispatched they are handled by a reducer and then the reducer is responsible for updating the state.

// increment
export function increment(index) {
return {
type: 'INCREMENT_LIKES',
index: index
}
}

// add commnet
export function addComment(postId, author, comment) {
return {
type: 'ADD_COMMENT',
postId: postId,
author: author,
comment: comment
}
}

// remove comment
export function removeComment(postId, i) {
return {
type: 'REMOVE_COMMENT',
i: i,
postId: postId
}
}
37 changes: 37 additions & 0 deletions learn-redux/client/components/Comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';

const Comments = React.createClass({
renderComment(comment, i) {
return (
<div className="comment" key={i}>
<p>
<strong>{comment.user}</strong>
{comment.text}
<button className="remove-comment" onClick={this.props.removeComment.bind(null, this.props.params.postId, i)}>&times;</button>
</p>
</div>
)
},
handleSubmit(e) {
e.preventDefault();
const { postId } = this.props.params;
const author = this.refs.author.value;
const comment = this.refs.comment.value;
this.props.addComment(postId, author, comment);
this.refs.commentForm.reset();
},
render() {
return (
<div className="comments">
{this.props.postComments.map(this.renderComment)}
<form ref="commentForm" className="comment-form" onSubmit={this.handleSubmit}>
<input type="text" ref="author" placeholder="author"/>
<input type="text" ref="comment" placeholder="comment"/>
<input type="submit" hidden/>
</form>
</div>
)
}
})

export default Comments;
18 changes: 18 additions & 0 deletions learn-redux/client/components/Main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { Link } from 'react-router';

const Main = React.createClass({
render() {
return (
<div>
<h1>
<Link to="/">Reduxstagram</Link>
</h1>
{React.cloneElement(this.props.children, this.props)}
// Takes the props of the parent componenet, clones them and then passes them down to the child components.
</div>
)
}
});

export default Main;
39 changes: 39 additions & 0 deletions learn-redux/client/components/Photo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Link } from 'react-router';
import CSSTransitionGroup from 'react-addons-css-transition-group';

const Photo = React.createClass({
render() {
const { post, i, comments } = this.props;
return (
<figure className="grid-figure">
<div className="grid-photo-wrap">
<Link to={`/view/${post.code}`}>
<img src={post.display_src} alt={post.caption} className="grid-photo" />
</Link>


<CSSTransitionGroup transitionName="like" transitionEnterTimeout={500} transitionLeaveTimeout={500}>
<span key={post.likes} className="likes-heart">{post.likes}</span>
</CSSTransitionGroup>

</div>

<figcaption>
<p>{post.caption}</p>
<div className="control-buttons">
<button onClick={this.props.increment.bind(null, i)} className="likes">&hearts; {post.likes}</button>
<Link className = "button" to={`/view/${post.code}`}>
<span className="comment-count">
<span className="speech-bubble"></span>
{comments[post.code] ? comments[post.code].length : 0}
</span>
</Link>
</div>
</figcaption>
</figure>
)
}
})

export default Photo
14 changes: 14 additions & 0 deletions learn-redux/client/components/PhotoGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import Photo from './Photo';

const PhotoGrid = React.createClass({
render() {
return (
<div className="photo-grid">
{this.props.posts.map((post, i) => <Photo {...this.props} key={i} i={i} post={post} />)}
</div>
)
}
});

export default PhotoGrid;
24 changes: 24 additions & 0 deletions learn-redux/client/components/Single.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { Link } from 'react-router';
import Photo from './Photo';
import Comments from './Comments';

const Single = React.createClass({
render() {
//index of the post
const { postId } = this.props.params;
const i = this.props.posts.findIndex((post) => post.code === postId);
// get us the post
const post = this.props.posts[i];
const postComments = this.props.comments[postId] || [];

return (
<div className="single-photo">
<Photo i={i} post={post} {...this.props} />
<Comments postComments={postComments} {...this.props}/>
</div>
)
}
});

export default Single;
19 changes: 19 additions & 0 deletions learn-redux/client/components/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
import Main from './Main'

function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments
}
}

function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}

const App = connect(mapStateToProps, mapDispatchToProps)(Main);

export default App;
32 changes: 32 additions & 0 deletions learn-redux/client/reducers/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
function postComments(state = [], action) {
switch(action.type){
case 'ADD_COMMENT':
// return the new state with the new comment
return [...state, {
user: action.author,
text: action.comment
}]
case 'REMOVE_COMMENT':
return [
...state.slice(0, action.i),
...state.slice(action.i + 1)
]
default:
return state;
}
}

function comments(state = [], action) {
if(typeof action.postId !== 'undefined') {
return {
// take the current state
...state,
// overwrite this post with new one
[action.postId]: postComments(state[action.postId], action)
}
}
return state;
}


export default comments;
9 changes: 9 additions & 0 deletions learn-redux/client/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';

import posts from './posts';
import comments from './comments';
// combines all reducers from reducers folder and prepare for export
const rootReducer = combineReducers({posts, comments, routing: routerReducer});

export default rootReducer;
22 changes: 22 additions & 0 deletions learn-redux/client/reducers/posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// A reducer's job is to take in action and store, process the action and then return the store.

// A reducer takes in two things:
// 1) the action (info about what happened)
// 2) copy of current state

function posts(state = [], action) {
switch(action.type) {
case 'INCREMENT_LIKES' :
const i = action.index;
return [
...state.slice(0,i), // before the one we are updating
{...state[i], likes: state[i].likes + 1},
...state.slice(i + 1) // after the one we are updating
]
// always have default returning the updated state
default:
return state;
}
}

export default posts;
32 changes: 31 additions & 1 deletion learn-redux/client/reduxstagram.js
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
// let's go!
import React from 'react';
import { render } from 'react-dom';

// Import css
import css from './styles/style.styl';

// Import Components
import App from './components/App';
import Single from './components/Single';
import PhotoGrid from './components/PhotoGrid';

// import react router dependecies
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import store, { history } from './store';

const router = (
<Provider store={store}>
<Router history={history}>
// Router componenent needs a history object, which here is browserHistory which allows you to do push state without having to reload the page.
<Route path="/" component={App}>
// If the url matches "/" or a further extension of, grab the Main component.
<IndexRoute component={PhotoGrid}></IndexRoute>
<Route path="/view/:postId" component={Single}></Route>
// Then depending on the URL structure, either pass 'Main', 'PhotoGrid' or 'Single'.
</Route>
</Router>
</Provider>
)

render(router, document.getElementById('root'));
50 changes: 50 additions & 0 deletions learn-redux/client/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createStore, compose } from 'redux';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router';

//import the root reducer
import rootReducer from './reducers/index'; // rootReducer is just a hub for importing all the individual reducers and exporting them as a whole. Syntax => const rootReducer = combineReducers({posts, comments, routing: routerReducer});

import comments from './data/comments'; // importing the initial data
import posts from './data/posts'; // importing the initial data

// Passing initial data to defaultState
const defaultState = {
posts: posts,
comments: comments
};

// ________________________________________________
// SUGAR ------> INSTALL REDUX DEV TOOLS.
// Install Redux dev tools into our store by using a store enhancer. This enables Redux Dev Tools in chrome to recognise your store.
const enhancers = compose(
// compose infuses our store with any of the enhancers we want.
window.devToolsExtension ? window.devToolsExtension() : f => f
// If dev tools is in the window, install it. If not just return the store.
);
// Then pass createStore the enhancers when creating the store below.
// ________________________________________________


// Create the store using collection of reducers and collection of data
const store = createStore(rootReducer, defaultState, enhancers);

export const history = syncHistoryWithStore(browserHistory, store);


// ________________________________________________
// SUGAR ------> HOT RELOAD REDUCERS. Done by accepting the hot reload & then re-requiring the reducer.
if(module.hot) {
// First we check if the module is hot
module.hot.accept('./reducers/', () => {
// If it is hot, we accept it and run a function that is going to re-require and swap out the module for us.
const nextRootReducer = require('./reducers/index').default;
// Grab the main reducer (which is the top level index one). Use require because you cannot use an ES6 import statement inside of a function, must be done at top level.
store.replaceReducer(nextRootReducer)
// Finally we just replace the entire reducer with store.replaceReducer() and pass it the nextRootReducer.
});
}
// ________________________________________________

// Export the newly created store
export default store;