Skip to content

Commit

Permalink
first files
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Sevilleja committed Oct 19, 2014
1 parent cf9cc4b commit 65e4200
Show file tree
Hide file tree
Showing 18 changed files with 697 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ node_modules

# Users Environment Variables
.lock-wscript

.DS_Store
13 changes: 13 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** @jsx React.DOM */

var React = require('react');
var TweetsApp = require('./components/TweetsApp.react');

// Snag the initial state that was passed from the server side
var initialState = JSON.parse(document.getElementById('initial-state').innerHTML)

// Render the components, picking up where react left off on the server
React.renderComponent(
<TweetsApp tweets={initialState}/>,
document.getElementById('react-app')
);
13 changes: 13 additions & 0 deletions components/Loader.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** @jsx React.DOM */

var React = require('react');

module.exports = Loader = React.createClass({
render: function(){
return (
<div className={"loader " + (this.props.paging ? "active" : "")}>
<img src="svg/loader.svg" />
</div>
)
}
});
14 changes: 14 additions & 0 deletions components/NotificationBar.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @jsx React.DOM */

var React = require('react');

module.exports = NotificationBar = React.createClass({
render: function(){
var count = this.props.count;
return (
<div className={"notification-bar" + (count > 0 ? ' active' : '')}>
<p>There are {count} new tweets! <a href="#top" onClick={this.props.onShowNewTweets}>Click here to see them.</a></p>
</div>
)
}
});
21 changes: 21 additions & 0 deletions components/Tweet.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @jsx React.DOM */

var React = require('react');

module.exports = Tweet = React.createClass({
render: function(){
var tweet = this.props.tweet;
return (
<li className={"tweet" + (tweet.active ? ' active' : '')}>
<img src={tweet.avatar} className="avatar"/>
<blockquote>
<cite>
<a href={"http://www.twitter.com/" + tweet.screenname}>{tweet.author}</a>
<span className="screen-name">@{tweet.screenname}</span>
</cite>
<span className="content">{tweet.body}</span>
</blockquote>
</li>
)
}
});
25 changes: 25 additions & 0 deletions components/Tweets.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/** @jsx React.DOM */

var React = require('react');
var Tweet = require('./Tweet.react.js');

module.exports = Tweets = React.createClass({

// Render our tweets
render: function(){

// Build list items of single tweet components using map
var content = this.props.tweets.map(function(tweet){
return (
<Tweet key={tweet.twid} tweet={tweet} />
)
});

// Return ul filled with our mapped tweets
return (
<ul className="tweets">{content}</ul>
)

}

});
184 changes: 184 additions & 0 deletions components/TweetsApp.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/** @jsx React.DOM */

var React = require('react');
var Tweets = require('./Tweets.react.js');
var Loader = require('./Loader.react.js');
var NotificationBar = require('./NotificationBar.react.js');

// Export the TweetsApp component
module.exports = TweetsApp = React.createClass({

// Method to add a tweet to our timeline
addTweet: function(tweet){

// Get current application state
var updated = this.state.tweets;

// Increment the unread count
var count = this.state.count + 1;

// Increment the skip count
var skip = this.state.skip + 1;

// Add tweet to the beginning of the tweets array
updated.unshift(tweet);

// Set application state
this.setState({tweets: updated, count: count, skip: skip});

},

// Method to get JSON from server by page
getPage: function(page){

// Setup our ajax request
var request = new XMLHttpRequest(), self = this;
request.open('GET', 'page/' + page + "/" + this.state.skip, true);
request.onload = function() {

// If everything is cool...
if (request.status >= 200 && request.status < 400){

// Load our next page
self.loadPagedTweets(JSON.parse(request.responseText));

} else {

// Set application state (Not paging, paging complete)
self.setState({paging: false, done: true});

}
};

// Fire!
request.send();

},

// Method to show the unread tweets
showNewTweets: function(){

// Get current application state
var updated = this.state.tweets;

// Mark our tweets active
updated.forEach(function(tweet){
tweet.active = true;
});

// Set application state (active tweets + reset unread count)
this.setState({tweets: updated, count: 0});

},

// Method to load tweets fetched from the server
loadPagedTweets: function(tweets){

// So meta lol
var self = this;

// If we still have tweets...
if(tweets.length > 0) {

// Get current application state
var updated = this.state.tweets;

// Push them onto the end of the current tweets array
tweets.forEach(function(tweet){
updated.push(tweet);
});

// This app is so fast, I actually use a timeout for dramatic effect
// Otherwise you'd never see our super sexy loader svg
setTimeout(function(){

// Set application state (Not paging, add tweets)
self.setState({tweets: updated, paging: false});

}, 1000);

} else {

// Set application state (Not paging, paging complete)
this.setState({done: true, paging: false});

}
},

// Method to check if more tweets should be loaded, by scroll position
checkWindowScroll: function(){

// Get scroll pos & window data
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
var s = document.body.scrollTop;
var scrolled = (h + s) > document.body.offsetHeight;

// If scrolled enough, not currently paging and not complete...
if(scrolled && !this.state.paging && !this.state.done) {

// Set application state (Paging, Increment page)
this.setState({paging: true, page: this.state.page + 1});

// Get the next page of tweets from the server
this.getPage(this.state.page);

}
},

// Set the initial component state
getInitialState: function(props){

props = props || this.props;

// Set initial application state using props
return {
tweets: props.tweets,
count: 0,
page: 0,
paging: false,
skip: 0,
done: false
};

},

componentWillReceiveProps: function(newProps, oldProps){
this.setState(this.getInitialState(newProps));
},

// Called directly after component rendering, only on client
componentDidMount: function(){

// Preserve self reference
var self = this;

// Initialize socket.io
var socket = io.connect();

// On tweet event emission...
socket.on('tweet', function (data) {

// Add a tweet to our queue
self.addTweet(data);

});

// Attach scroll event to the window for infinity paging
window.addEventListener('scroll', this.checkWindowScroll);

},

// Render the component
render: function(){

return (
<div className="tweets-app">
<Tweets tweets={this.state.tweets} />
<Loader paging={this.state.paging}/>
<NotificationBar count={this.state.count} onShowNewTweets={this.showNewTweets}/>
</div>
)

}

});
8 changes: 8 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
twitter: {
consumer_key: 'put-yours-here',
consumer_secret: 'put-yours-here',
access_token_key: 'put-yours-here',
access_token_secret: 'put-yours-here'
}
}
39 changes: 39 additions & 0 deletions models/Tweet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var mongoose = require('mongoose');

// Create a new schema for our tweet data
var schema = new mongoose.Schema({
twid : String
, active : Boolean
, author : String
, avatar : String
, body : String
, date : Date
, screenname : String
});

// Create a static getTweets method to return tweet data from the db
schema.statics.getTweets = function(page, skip, callback) {

var tweets = [],
start = (page * 10) + (skip * 1);

// Query the db, using skip and limit to achieve page chunks
Tweet.find({},'twid active author avatar body date screenname',{skip: start, limit: 10}).sort({date: 'desc'}).exec(function(err,docs){

// If everything is cool...
if(!err) {
tweets = docs; // We got tweets
tweets.forEach(function(tweet){
tweet.active = true; // Set them to active
});
}

// Pass them back to the specified callback
callback(tweets);

});

};

// Return a Tweet model based upon the defined schema
module.exports = Tweet = mongoose.model('Tweet', schema);
35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "react-isomorph",
"version": "0.0.0",
"description": "Isomorphic React Example",
"main": "app.js",
"scripts": {
"watch": "watchify app.js -o public/js/bundle.js -v",
"browserify": "browserify app.js | uglifyjs > public/js/bundle.js",
"build": "npm run browserify ",
"start": "npm run watch & nodemon server.js"
},
"author": "Ken Wheeler",
"license": "MIT",
"dependencies": {
"express": "~4.9.7",
"express-handlebars": "~1.1.0",
"mongoose": "^3.8.17",
"node-jsx": "~0.11.0",
"ntwitter": "^0.5.0",
"react": "~0.11.2",
"socket.io": "^1.1.0"
},
"devDependencies": {
"browserify": "~6.0.3",
"nodemon": "^1.2.1",
"reactify": "~0.14.0",
"uglify-js": "~2.4.15",
"watchify": "~2.0.0"
},
"browserify": {
"transform": [
"reactify"
]
}
}
Loading

0 comments on commit 65e4200

Please sign in to comment.