Julian Steinwachs
10 years ago
73 changed files with 3716 additions and 334 deletions
@ -0,0 +1,102 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1 @@
|
||||
{".js":"08c714844c29536ebc79f9d30805d704b52fd613.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"5f1e941d8495d0f750d535161ad70ea16a9e4746.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"62b320e94d738a654a51f0072466d00e804e745d.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"6bdf1b84f178854813606fd7d13938769358b0aa.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"7b03360003ecd9b3f73a35665504c5c526098e44.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"80a01a350ccea8f837824abb8a78698c5971130d.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"817d69cf89ab760cfd9dfbc25814ba6962145289.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"83a76356a36e400cc41c7877268fef1c940f2214.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"99d6a84f3275b12417299ef742e2824d3ce6e610.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"a75daba84d9bb9b874cc39f466ceed477f12a293.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"a96e5b381fdf9155de48fe91471ffc18f049dae0.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"b44a64a27d99a484a4cf8e757c2d902e147932fa.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"c57d14fa426b54106d5bc5cc344fa166bfb8f51b.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"d71ed2a487872dd50be6d967cedeff0a6b5767b9.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js"} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{".js":"f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.js"} |
@ -0,0 +1,12 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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