Julian Steinwachs
10 years ago
73 changed files with 3716 additions and 334 deletions
@ -0,0 +1,102 @@ |
|||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
var Router = require('react-router') |
||||||
|
, RouteHandler = Router.RouteHandler |
||||||
|
, Route = Router.Route; |
||||||
|
|
||||||
|
|
||||||
|
var ReactRouterBootstrap = require('react-router-bootstrap') |
||||||
|
, NavItemLink = ReactRouterBootstrap.NavItemLink |
||||||
|
, ButtonLink = ReactRouterBootstrap.ButtonLink |
||||||
|
, ListGroupItemLink = ReactRouterBootstrap.ListGroupItemLink; |
||||||
|
*/ |
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
var Router = require('react-router'); |
||||||
|
var { Route, DefaultRoute, RouteHandler, Link } = Router; |
||||||
|
|
||||||
|
var Timeline = require("./Timeline.js"); |
||||||
|
|
||||||
|
App = React.createClass({displayName: "App", |
||||||
|
|
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
|
||||||
|
getHandlerKey: function () { |
||||||
|
var childDepth = 1; // assuming App is top-level route
|
||||||
|
var { router } = this.context; |
||||||
|
//console.log(router.getCurrentParams())
|
||||||
|
if ( router.getCurrentRoutes()[childDepth] ) { |
||||||
|
var key = router.getCurrentRoutes()[childDepth].name; |
||||||
|
var id = JSON.stringify(router.getCurrentParams()); |
||||||
|
if (id) { key += id; } |
||||||
|
return key; |
||||||
|
} else {return "none"} |
||||||
|
}, |
||||||
|
|
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement(Nav, {bsStyle: "pills"}, |
||||||
|
React.createElement(NavItem, {href: "#timeline/tschaul"}, |
||||||
|
"tschaul" |
||||||
|
), |
||||||
|
React.createElement(NavItem, {href: "#timeline/timbuktu"}, |
||||||
|
"timbuktu" |
||||||
|
), |
||||||
|
React.createElement(NavItem, {href: "#timeline/pampalulu"}, |
||||||
|
"pampalulu" |
||||||
|
) |
||||||
|
), |
||||||
|
React.createElement(RouteHandler, {pollInterval: "60", key: this.getHandlerKey()}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
var routes = ( |
||||||
|
React.createElement(Route, {handler: App, path: "/"}, |
||||||
|
React.createElement(Route, {name: "timeline", path: "timeline/:timelineUser", handler: Timeline}) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
var intitializeApp = function(res){ |
||||||
|
|
||||||
|
Router.run(routes, function (Handler) { |
||||||
|
React.render(React.createElement(Handler, null), document.getElementById('content')); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
///////// LOAD TWISTER FROM CACHE AND INITIALIZE
|
||||||
|
|
||||||
|
|
||||||
|
Twister.init({ |
||||||
|
host: 'http://user:pwd@localhost:28332', |
||||||
|
errorfunc: function(error){console.log(this,error)} |
||||||
|
}); |
||||||
|
|
||||||
|
loadCache(); |
||||||
|
|
||||||
|
setInterval(saveCache,300000); |
||||||
|
|
||||||
|
Twister.loadServerAccounts(intitializeApp); |
||||||
|
|
||||||
|
////// INIT EVENTLISTENERS ON WINDOW
|
||||||
|
|
||||||
|
window.onscroll = function(ev) { |
||||||
|
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { |
||||||
|
var event = new Event('scrolledtobottom'); |
||||||
|
window.dispatchEvent(event); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,160 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('react-mixin-safe-state-change'); |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 24*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=30;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(60); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this.handleResize); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this.handleResize); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
var EventListenerMixin = require('./EventListenerMixin.js') |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 12*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(thisComponent.props.pollInterval); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange -= 6*60*60; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,60 @@ |
|||||||
|
module.exports = StreamMixin = { |
||||||
|
|
||||||
|
addPost: function(post) { |
||||||
|
|
||||||
|
var postid = post.getUsername() + ":post" + post.getId(); |
||||||
|
|
||||||
|
if (this.isMounted() && !this.state.postIdentifiers[postid] && this.verifyPost(post)) { |
||||||
|
|
||||||
|
this.setState(function(previousState, currentProps) { |
||||||
|
|
||||||
|
previousState.postIdentifiers[postid] = true; |
||||||
|
|
||||||
|
if (post.isRetwist()){ |
||||||
|
|
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getRetwistedUser(), |
||||||
|
retwistingUser: post.getUsername(), |
||||||
|
content: post.getRetwistedContent(), |
||||||
|
id: post.getRetwistedId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: true |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getUsername(), |
||||||
|
content: post.getContent(), |
||||||
|
id: post.getId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: false |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.push(postdata) |
||||||
|
|
||||||
|
var compare = function (a,b) { |
||||||
|
if (a.timestamp < b.timestamp) |
||||||
|
return 1; |
||||||
|
if (a.timestamp > b.timestamp) |
||||||
|
return -1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.sort(compare); |
||||||
|
|
||||||
|
return {data: previousState.data, postIdentifiers: previousState.postIdentifiers }; |
||||||
|
}); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,162 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
var EventListenerMixin = require('./EventListenerMixin.js') |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 24*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=30;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(60); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,162 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
var EventListenerMixin = require('./EventListenerMixin.js') |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 12*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=30;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(60); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,60 @@ |
|||||||
|
module.exports = StreamMixin = { |
||||||
|
|
||||||
|
addPost: function(post) { |
||||||
|
|
||||||
|
var postid = post.getUsername() + ":post" + post.getId(); |
||||||
|
|
||||||
|
if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps) { |
||||||
|
|
||||||
|
previousState.postIdentifiers[postid] = true; |
||||||
|
|
||||||
|
if (post.isRetwist()){ |
||||||
|
|
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getRetwistedUser(), |
||||||
|
retwistingUser: post.getUsername(), |
||||||
|
content: post.getRetwistedContent(), |
||||||
|
id: post.getRetwistedId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: true |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getUsername(), |
||||||
|
content: post.getContent(), |
||||||
|
id: post.getId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: false |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.push(postdata) |
||||||
|
|
||||||
|
var compare = function (a,b) { |
||||||
|
if (a.timestamp < b.timestamp) |
||||||
|
return 1; |
||||||
|
if (a.timestamp > b.timestamp) |
||||||
|
return -1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.sort(compare); |
||||||
|
|
||||||
|
return {data: previousState.data, postIdentifiers: previousState.postIdentifiers }; |
||||||
|
}); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
var Router = require('react-router') |
||||||
|
, RouteHandler = Router.RouteHandler |
||||||
|
, Route = Router.Route; |
||||||
|
|
||||||
|
|
||||||
|
var ReactRouterBootstrap = require('react-router-bootstrap') |
||||||
|
, NavItemLink = ReactRouterBootstrap.NavItemLink |
||||||
|
, ButtonLink = ReactRouterBootstrap.ButtonLink |
||||||
|
, ListGroupItemLink = ReactRouterBootstrap.ListGroupItemLink; |
||||||
|
*/ |
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
var Router = require('react-router'); |
||||||
|
var { Route, DefaultRoute, RouteHandler, Link } = Router; |
||||||
|
|
||||||
|
var Timeline = require("./Timeline.js"); |
||||||
|
|
||||||
|
App = React.createClass({displayName: "App", |
||||||
|
|
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
|
||||||
|
getHandlerKey: function () { |
||||||
|
var childDepth = 1; // assuming App is top-level route
|
||||||
|
var { router } = this.context; |
||||||
|
//console.log(router.getCurrentParams())
|
||||||
|
if ( router.getCurrentRoutes()[childDepth] ) { |
||||||
|
var key = router.getCurrentRoutes()[childDepth].name; |
||||||
|
var id = JSON.stringify(router.getCurrentParams()); |
||||||
|
if (id) { key += id; } |
||||||
|
return key; |
||||||
|
} else {return "none"} |
||||||
|
}, |
||||||
|
|
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement(Nav, {bsStyle: "pills"}, |
||||||
|
React.createElement(NavItem, {href: "#timeline/tschaul"}, |
||||||
|
"tschaul" |
||||||
|
), |
||||||
|
React.createElement(NavItem, {href: "#timeline/timbuktu"}, |
||||||
|
"timbuktu" |
||||||
|
), |
||||||
|
React.createElement(NavItem, {href: "#timeline/pampalulu"}, |
||||||
|
"pampalulu" |
||||||
|
) |
||||||
|
), |
||||||
|
React.createElement(RouteHandler, {pollInterval: "60000", key: this.getHandlerKey()}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
var routes = ( |
||||||
|
React.createElement(Route, {handler: App, path: "/"}, |
||||||
|
React.createElement(Route, {name: "timeline", path: "timeline/:timelineUser", handler: Timeline}) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
var intitializeApp = function(res){ |
||||||
|
|
||||||
|
Router.run(routes, function (Handler) { |
||||||
|
React.render(React.createElement(Handler, null), document.getElementById('content')); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
///////// LOAD TWISTER FROM CACHE AND INITIALIZE
|
||||||
|
|
||||||
|
|
||||||
|
Twister.init({ |
||||||
|
host: 'http://user:pwd@localhost:28332', |
||||||
|
errorfunc: function(error){console.log(this,error)} |
||||||
|
}); |
||||||
|
|
||||||
|
loadCache(); |
||||||
|
|
||||||
|
setInterval(saveCache,300000); |
||||||
|
|
||||||
|
Twister.loadServerAccounts(intitializeApp); |
||||||
|
|
||||||
|
////// INIT EVENTLISTENERS ON WINDOW
|
||||||
|
|
||||||
|
window.onscroll = function(ev) { |
||||||
|
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { |
||||||
|
var event = new Event('scrolledtobottom'); |
||||||
|
window.dispatchEvent(event); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+eventtype]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+eventtype]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
var Router = require('react-router') |
||||||
|
, RouteHandler = Router.RouteHandler |
||||||
|
, Route = Router.Route; |
||||||
|
|
||||||
|
|
||||||
|
var ReactRouterBootstrap = require('react-router-bootstrap') |
||||||
|
, NavItemLink = ReactRouterBootstrap.NavItemLink |
||||||
|
, ButtonLink = ReactRouterBootstrap.ButtonLink |
||||||
|
, ListGroupItemLink = ReactRouterBootstrap.ListGroupItemLink; |
||||||
|
*/ |
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
var Router = require('react-router'); |
||||||
|
var { Route, DefaultRoute, RouteHandler, Link } = Router; |
||||||
|
|
||||||
|
var Timeline = require("./Timeline.js"); |
||||||
|
|
||||||
|
App = React.createClass({displayName: "App", |
||||||
|
|
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
|
||||||
|
getHandlerKey: function () { |
||||||
|
var childDepth = 1; // assuming App is top-level route
|
||||||
|
var { router } = this.context; |
||||||
|
//console.log(router.getCurrentParams())
|
||||||
|
if ( router.getCurrentRoutes()[childDepth] ) { |
||||||
|
var key = router.getCurrentRoutes()[childDepth].name; |
||||||
|
var id = JSON.stringify(router.getCurrentParams()); |
||||||
|
if (id) { key += id; } |
||||||
|
return key; |
||||||
|
} else {return "none"} |
||||||
|
}, |
||||||
|
|
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement(Nav, {bsStyle: "pills"}, |
||||||
|
React.createElement(NavItem, {href: "#timeline/tschaul"}, |
||||||
|
"tschaul" |
||||||
|
), |
||||||
|
React.createElement(NavItem, {href: "#timeline/timbuktu"}, |
||||||
|
"timbuktu" |
||||||
|
), |
||||||
|
React.createElement(NavItem, {href: "#timeline/pampalulu"}, |
||||||
|
"pampalulu" |
||||||
|
) |
||||||
|
), |
||||||
|
React.createElement(RouteHandler, {pollInterval: "60000", key: this.getHandlerKey()}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
var routes = ( |
||||||
|
React.createElement(Route, {handler: App, path: "/"}, |
||||||
|
React.createElement(Route, {name: "timeline", path: "timeline/:timelineUser", handler: Timeline}) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
var intitializeApp = function(res){ |
||||||
|
|
||||||
|
Router.run(routes, function (Handler) { |
||||||
|
React.render(React.createElement(Handler, null), document.getElementById('content')); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
///////// LOAD TWISTER FROM CACHE AND INITIALIZE
|
||||||
|
|
||||||
|
|
||||||
|
Twister.init({ |
||||||
|
host: 'http://user:pwd@localhost:28332', |
||||||
|
errorfunc: function(error){console.log(this,error)} |
||||||
|
}); |
||||||
|
|
||||||
|
loadCache(); |
||||||
|
|
||||||
|
setInterval(saveCache,300000); |
||||||
|
|
||||||
|
Twister.loadServerAccounts(intitializeApp); |
||||||
|
|
||||||
|
////// INIT EVENTLISTENERS ON WINDOW
|
||||||
|
|
||||||
|
window.onscroll = function(ev) { |
||||||
|
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { |
||||||
|
alert("you're at the bottom of the page"); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,160 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 24*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=30;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(60); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+scrolledtobottom]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+scrolledtobottom]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
var EventListenerMixin = require('./EventListenerMixin.js') |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 12*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(thisComponent.props.pollInterval); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange+= 6*60*60; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,166 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
var EventListenerMixin = require('./EventListenerMixin.js') |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 12*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=30;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(60); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval); |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
alert("scrolled to bottom !!!") |
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,62 @@ |
|||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
//result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null;
|
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,172 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
|
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var Postboard = require("./Postboard.js"); |
||||||
|
var SetIntervalMixin = require("./SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("./StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
var EventListenerMixin = require('./EventListenerMixin.js') |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
verifyPost: function (post) { |
||||||
|
|
||||||
|
var verified = false; |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
if (post.getUsername()==this.state.usernames[i]) { verified = true } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return verified; |
||||||
|
|
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
timelineUser: [], |
||||||
|
postrange: ( Date.now()/1000 - 12*60*60 ), |
||||||
|
min_posts: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
addUser: function(username) { |
||||||
|
|
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
previousState.usernames.push(username); |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doLatestPostsUntil(function(post){ |
||||||
|
if (post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post) |
||||||
|
} |
||||||
|
},{outdatedLimit: 60*60*24}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
removeUser: function(username) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
|
||||||
|
var newusers = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.usernames.length; i++) { |
||||||
|
if (previousState.usernames[i]!=username) { |
||||||
|
newusers.push(previousState.usernames[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.usernames = newusers; |
||||||
|
|
||||||
|
var newdata = []; |
||||||
|
|
||||||
|
for (var i = 0; i<previousState.data.length; i++) { |
||||||
|
if (previousState.data[i].username!=username) { |
||||||
|
newusers.push(previousState.data[i]); |
||||||
|
} else { |
||||||
|
previousState.postIdentifiers[previousState.data[i].postid]=false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data = newdata; |
||||||
|
|
||||||
|
return previousState; |
||||||
|
|
||||||
|
}); |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
for (var i = 0; i<this.state.usernames.length; i++) { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.usernames[i]; |
||||||
|
|
||||||
|
Twister.getUser(thisUsername).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.context.router.getCurrentParams().timelineUser; |
||||||
|
|
||||||
|
Twister.getAccount(username).activateTorrents(function(){ |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
|
||||||
|
|
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
thisComponent.updatePosts(thisComponent.props.pollInterval); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setSateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange+= 6*60*60; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement("div", null, |
||||||
|
React.createElement("h3", null, 'Timeline of '+this.context.router.getCurrentParams().timelineUser), |
||||||
|
React.createElement(Postboard, {data: this.state.data}) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1 @@ |
|||||||
|
{".js":"08c714844c29536ebc79f9d30805d704b52fd613.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"5f1e941d8495d0f750d535161ad70ea16a9e4746.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"62b320e94d738a654a51f0072466d00e804e745d.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"6bdf1b84f178854813606fd7d13938769358b0aa.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"7b03360003ecd9b3f73a35665504c5c526098e44.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"80a01a350ccea8f837824abb8a78698c5971130d.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"817d69cf89ab760cfd9dfbc25814ba6962145289.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"83a76356a36e400cc41c7877268fef1c940f2214.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"99d6a84f3275b12417299ef742e2824d3ce6e610.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"a75daba84d9bb9b874cc39f466ceed477f12a293.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"a96e5b381fdf9155de48fe91471ffc18f049dae0.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"b44a64a27d99a484a4cf8e757c2d902e147932fa.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"c57d14fa426b54106d5bc5cc344fa166bfb8f51b.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"d71ed2a487872dd50be6d967cedeff0a6b5767b9.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.js"} |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+eventtype]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+eventtype]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
//result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null;
|
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+eventtype]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+eventtype]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
//result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null;
|
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,22 @@ |
|||||||
|
The MIT License (MIT) |
||||||
|
|
||||||
|
Copyright (c) 2015 Rick Beerendonk |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
||||||
|
|
@ -0,0 +1,35 @@ |
|||||||
|
react-mixin-safe-state-change |
||||||
|
============================= |
||||||
|
|
||||||
|
[![npm version](https://img.shields.io/npm/v/react-mixin-safe-state-change.svg)](http://www.npmjs.com/package/react-mixin-safe-state-change) |
||||||
|
[![npm downloads](https://img.shields.io/npm/dm/react-mixin-safe-state-change.svg)](http://www.npmjs.com/package/react-mixin-safe-state-change) |
||||||
|
|
||||||
|
React mixin which sets or replaces state only when it is safe to do so. |
||||||
|
|
||||||
|
## Motivation |
||||||
|
When processing the response of an asynchronous request, it might not be safe to call setState or replaceState, because the component might no longer be mounted. |
||||||
|
|
||||||
|
|
||||||
|
## Install |
||||||
|
`npm install react-mixin-safe-state-change` |
||||||
|
|
||||||
|
## Usage |
||||||
|
```javascript |
||||||
|
var safeStateChangeMixin = require('react-mixin-safe-state-change'); |
||||||
|
|
||||||
|
React.createClass({ |
||||||
|
mixins: [safeStateChangeMixin] |
||||||
|
|
||||||
|
someCallback: function(newValue) { |
||||||
|
if (!this.setStateSafe({value: newValue})) { |
||||||
|
console.warn('Could not set the state.'); |
||||||
|
} |
||||||
|
|
||||||
|
// or |
||||||
|
|
||||||
|
if (!this.replaceStateSafe({value: newValue})) { |
||||||
|
console.warn('Could not replace the state.'); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
``` |
@ -0,0 +1,89 @@ |
|||||||
|
/** |
||||||
|
* react-mixin-safe-state-change |
||||||
|
* React mixin which sets or replaces state only when it is safe to do so. |
||||||
|
* |
||||||
|
* Copyright (c) 2015 Rick Beerendonk, |
||||||
|
* https://github.com/rickbeerendonk/react-mixin-safe-state-change
|
||||||
|
* |
||||||
|
* The MIT License (MIT) |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in |
||||||
|
* all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null; |
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,49 @@ |
|||||||
|
{ |
||||||
|
"name": "react-mixin-safe-state-change", |
||||||
|
"description": "React mixin which sets or replaces state only when it is safe to do so.", |
||||||
|
"version": "0.1.1", |
||||||
|
"author": { |
||||||
|
"name": "Rick Beerendonk", |
||||||
|
"email": "rick@beerendonk.com" |
||||||
|
}, |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/rickbeerendonk/react-mixin-safe-state-change/issues" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/rickbeerendonk/react-mixin-safe-state-change", |
||||||
|
"keywords": [ |
||||||
|
"react", |
||||||
|
"react-mixin" |
||||||
|
], |
||||||
|
"license": "MIT", |
||||||
|
"main": "index.js", |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "git+https://github.com/rickbeerendonk/react-mixin-safe-state-change.git" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||||
|
}, |
||||||
|
"gitHead": "76f0951f959b3549c484884b429f966a40feeb15", |
||||||
|
"_id": "react-mixin-safe-state-change@0.1.1", |
||||||
|
"_shasum": "41d16fa039a54b9cc4c3e930373c7770b5c58705", |
||||||
|
"_from": "react-mixin-safe-state-change@*", |
||||||
|
"_npmVersion": "2.1.17", |
||||||
|
"_nodeVersion": "0.10.35", |
||||||
|
"_npmUser": { |
||||||
|
"name": "rickbeerendonk", |
||||||
|
"email": "rick@beerendonk.com" |
||||||
|
}, |
||||||
|
"maintainers": [ |
||||||
|
{ |
||||||
|
"name": "rickbeerendonk", |
||||||
|
"email": "rick@beerendonk.com" |
||||||
|
} |
||||||
|
], |
||||||
|
"dist": { |
||||||
|
"shasum": "41d16fa039a54b9cc4c3e930373c7770b5c58705", |
||||||
|
"tarball": "http://registry.npmjs.org/react-mixin-safe-state-change/-/react-mixin-safe-state-change-0.1.1.tgz" |
||||||
|
}, |
||||||
|
"directories": {}, |
||||||
|
"_resolved": "https://registry.npmjs.org/react-mixin-safe-state-change/-/react-mixin-safe-state-change-0.1.1.tgz", |
||||||
|
"readme": "ERROR: No README data found!" |
||||||
|
} |
Loading…
Reference in new issue