Browse Source

endless scrolling

master
Julian Steinwachs 10 years ago
parent
commit
5d08929e04
  1. 659
      build/app-bundle.js
  2. 23
      build/twister-lib.js
  3. 13
      css/main.css
  4. 101
      js/.module-cache/08c714844c29536ebc79f9d30805d704b52fd613.js
  5. 102
      js/.module-cache/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js
  6. 160
      js/.module-cache/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js
  7. 12
      js/.module-cache/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.js
  8. 101
      js/.module-cache/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.js
  9. 101
      js/.module-cache/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js
  10. 172
      js/.module-cache/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js
  11. 60
      js/.module-cache/5f1e941d8495d0f750d535161ad70ea16a9e4746.js
  12. 101
      js/.module-cache/62b320e94d738a654a51f0072466d00e804e745d.js
  13. 162
      js/.module-cache/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js
  14. 101
      js/.module-cache/6bdf1b84f178854813606fd7d13938769358b0aa.js
  15. 162
      js/.module-cache/7b03360003ecd9b3f73a35665504c5c526098e44.js
  16. 60
      js/.module-cache/80a01a350ccea8f837824abb8a78698c5971130d.js
  17. 101
      js/.module-cache/817d69cf89ab760cfd9dfbc25814ba6962145289.js
  18. 101
      js/.module-cache/83a76356a36e400cc41c7877268fef1c940f2214.js
  19. 101
      js/.module-cache/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js
  20. 102
      js/.module-cache/99d6a84f3275b12417299ef742e2824d3ce6e610.js
  21. 12
      js/.module-cache/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js
  22. 101
      js/.module-cache/a75daba84d9bb9b874cc39f466ceed477f12a293.js
  23. 160
      js/.module-cache/a96e5b381fdf9155de48fe91471ffc18f049dae0.js
  24. 12
      js/.module-cache/b44a64a27d99a484a4cf8e757c2d902e147932fa.js
  25. 172
      js/.module-cache/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.js
  26. 166
      js/.module-cache/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.js
  27. 62
      js/.module-cache/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.js
  28. 101
      js/.module-cache/d71ed2a487872dd50be6d967cedeff0a6b5767b9.js
  29. 172
      js/.module-cache/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js
  30. 101
      js/.module-cache/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.js
  31. 1
      js/.module-cache/manifest/08c714844c29536ebc79f9d30805d704b52fd613.json
  32. 1
      js/.module-cache/manifest/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.json
  33. 1
      js/.module-cache/manifest/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.json
  34. 1
      js/.module-cache/manifest/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.json
  35. 1
      js/.module-cache/manifest/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.json
  36. 1
      js/.module-cache/manifest/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.json
  37. 1
      js/.module-cache/manifest/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.json
  38. 1
      js/.module-cache/manifest/5f1e941d8495d0f750d535161ad70ea16a9e4746.json
  39. 1
      js/.module-cache/manifest/62b320e94d738a654a51f0072466d00e804e745d.json
  40. 1
      js/.module-cache/manifest/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.json
  41. 1
      js/.module-cache/manifest/6bdf1b84f178854813606fd7d13938769358b0aa.json
  42. 1
      js/.module-cache/manifest/7b03360003ecd9b3f73a35665504c5c526098e44.json
  43. 1
      js/.module-cache/manifest/80a01a350ccea8f837824abb8a78698c5971130d.json
  44. 1
      js/.module-cache/manifest/817d69cf89ab760cfd9dfbc25814ba6962145289.json
  45. 1
      js/.module-cache/manifest/83a76356a36e400cc41c7877268fef1c940f2214.json
  46. 1
      js/.module-cache/manifest/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.json
  47. 1
      js/.module-cache/manifest/99d6a84f3275b12417299ef742e2824d3ce6e610.json
  48. 1
      js/.module-cache/manifest/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.json
  49. 1
      js/.module-cache/manifest/a75daba84d9bb9b874cc39f466ceed477f12a293.json
  50. 1
      js/.module-cache/manifest/a96e5b381fdf9155de48fe91471ffc18f049dae0.json
  51. 1
      js/.module-cache/manifest/b44a64a27d99a484a4cf8e757c2d902e147932fa.json
  52. 1
      js/.module-cache/manifest/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.json
  53. 1
      js/.module-cache/manifest/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.json
  54. 1
      js/.module-cache/manifest/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.json
  55. 1
      js/.module-cache/manifest/d71ed2a487872dd50be6d967cedeff0a6b5767b9.json
  56. 1
      js/.module-cache/manifest/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.json
  57. 1
      js/.module-cache/manifest/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.json
  58. 13
      js/App.js
  59. 12
      js/EventListenerMixin.js
  60. 21
      js/Post.js
  61. 62
      js/SafeStateChangeMixin.js
  62. 2
      js/StreamMixin.js
  63. 27
      js/Timeline.js
  64. 13
      jsx/App.js
  65. 12
      jsx/EventListenerMixin.js
  66. 21
      jsx/Post.js
  67. 62
      jsx/SafeStateChangeMixin.js
  68. 2
      jsx/StreamMixin.js
  69. 27
      jsx/Timeline.js
  70. 22
      node_modules/react-mixin-safe-state-change/LICENSE
  71. 35
      node_modules/react-mixin-safe-state-change/README.md
  72. 89
      node_modules/react-mixin-safe-state-change/index.js
  73. 49
      node_modules/react-mixin-safe-state-change/package.json

659
build/app-bundle.js

File diff suppressed because it is too large Load Diff

23
build/twister-lib.js

@ -31261,7 +31261,7 @@ TwisterPubKey.prototype.verifySignature = function (message_ori, signature_ori,
Twister._signatureVerificationsInProgress++; Twister._signatureVerificationsInProgress++;
var timeout=Twister._signatureVerificationsInProgress*Twister._averageSignatureCompTime*2; var timeout=Twister._signatureVerificationsInProgress*Twister._averageSignatureCompTime*4;
setTimeout(function(){ setTimeout(function(){
@ -31577,7 +31577,7 @@ TwisterResource.prototype._handleError = function (error) {
TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc) { TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc) {
console.log(method,params); //console.log(method,params);
var thisResource = this; var thisResource = this;
@ -31607,12 +31607,14 @@ TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc)
var request = require('request'); var request = require('request');
request({ request({
uri: this.getQuerySetting("host"), uri: this.getQuerySetting("host"),
method: "POST", method: "POST",
timeout: this.getQuerySetting("timeout"), timeout: this.getQuerySetting("timeout"),
followRedirect: true, followRedirect: true,
maxRedirects: 10, maxRedirects: 10,
body: '{"jsonrpc": "2.0", "method": "'+method+'", "params": '+JSON.stringify(params)+', "id": 0}' body: '{"jsonrpc": "2.0", "method": "'+method+'", "params": '+JSON.stringify(params)+', "id": 0}'
}, function(error, response, body) { }, function(error, response, body) {
if (error) { if (error) {
@ -31639,14 +31641,11 @@ TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc)
TwisterResource.prototype.dhtget = function (args,cbfunc) { TwisterResource.prototype.dhtget = function (args,cbfunc) {
var Twister = this._scope; var Twister = this._scope;
var thisResource = this; var thisResource = this;
console.log(thisResource._name+" "+thisResource._type) //console.log(thisResource._name+" "+thisResource._type)
if ( Twister._activeDHTQueries < Twister._maxDHTQueries ) { if ( Twister._activeDHTQueries < Twister._maxDHTQueries ) {
@ -32008,7 +32007,7 @@ TwisterStream.prototype._doPost = function (id,cbfunc) {
} else { } else {
console.log("post "+id+" not in cache"); //console.log("post "+id+" not in cache");
var thisResource = this; var thisResource = this;
@ -32295,7 +32294,7 @@ TwisterTorrent.prototype._fillCacheUsingGetposts = function (count,usernames,max
} }
console.log("maxId = "+maxId+" "+usernames) //console.log("maxId = "+maxId+" "+usernames)
if ( !maxId || maxId==-1 ) { if ( !maxId || maxId==-1 ) {
@ -32438,17 +32437,17 @@ TwisterTorrent.prototype.updateCache = function (cbfunc) {
var thisTorrent = this; var thisTorrent = this;
var thisStream = Twister.getUser(this._name)._stream; var thisStream = Twister.getUser(this._name)._stream;
console.log("update cache "+thisTorrent._name) //console.log("update cache "+thisTorrent._name)
thisTorrent._checkForUpdatesUsingGetLastHave(function(uptodate){ thisTorrent._checkForUpdatesUsingGetLastHave(function(uptodate){
if (uptodate) { if (uptodate) {
console.log("lasthaves "+thisTorrent._name+" worked") //console.log("lasthaves "+thisTorrent._name+" worked")
cbfunc(true); cbfunc(true);
} else { } else {
console.log("lasthaves "+thisTorrent._name+" failed") //console.log("lasthaves "+thisTorrent._name+" failed")
thisTorrent._fillCacheUsingGetposts(30,[thisTorrent._name],-1,-1,cbfunc); thisTorrent._fillCacheUsingGetposts(30,[thisTorrent._name],-1,-1,cbfunc);
@ -32466,7 +32465,7 @@ TwisterTorrent.prototype.fillCache = function (id,cbfunc) {
var thisTorrent = this; var thisTorrent = this;
var thisUser = Twister.getUser(this._name); var thisUser = Twister.getUser(this._name);
console.log("fill cache "+thisTorrent._name) //console.log("fill cache "+thisTorrent._name)
thisTorrent._fillCacheUsingGetposts(30,[thisTorrent._name],id,-1,cbfunc); thisTorrent._fillCacheUsingGetposts(30,[thisTorrent._name],id,-1,cbfunc);

13
css/main.css

@ -1,8 +1,16 @@
.container {
.tight {
padding-right:0.4em; padding-right:0.4em;
padding-left:0.4em; padding-left:0.4em;
} }
.fullytight {
padding-right:0;
padding-left:0;
}
/*
.col-xs-2 { .col-xs-2 {
padding-right:0.4em; padding-right:0.4em;
padding-left:0.4em; padding-left:0.4em;
@ -38,7 +46,8 @@
padding-right:0.4em; padding-right:0.4em;
padding-left:0.4em; padding-left:0.4em;
} }
*/
.img-responsive { .img-responsive {
width: 100%; width: 100%;
} }

101
js/.module-cache/08c714844c29536ebc79f9d30805d704b52fd613.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, className: "fullytight"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

102
js/.module-cache/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js vendored

@ -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);
}
};

160
js/.module-cache/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js vendored

@ -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})
)
);
}
});

12
js/.module-cache/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.js vendored

@ -0,0 +1,12 @@
module.exports = EventListenerMixin = function (eventtype) {
return {
componentDidMount: function() {
window.addEventListener(eventtype, this.handleResize);
},
componentWillUnmount: function() {
window.removeEventListener(eventtype, this.handleResize);
}
}
}

101
js/.module-cache/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, className: "fullytight"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1, className: "fullytight"}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 6},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 6}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

101
js/.module-cache/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('react-mixin-safe-state-change');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, null,
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

172
js/.module-cache/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js vendored

@ -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})
)
);
}
});

60
js/.module-cache/5f1e941d8495d0f750d535161ad70ea16a9e4746.js vendored

@ -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 {
}
}
}

101
js/.module-cache/62b320e94d738a654a51f0072466d00e804e745d.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, className: "tight"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

162
js/.module-cache/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js vendored

@ -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})
)
);
}
});

101
js/.module-cache/6bdf1b84f178854813606fd7d13938769358b0aa.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, null,
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

162
js/.module-cache/7b03360003ecd9b3f73a35665504c5c526098e44.js vendored

@ -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})
)
);
}
});

60
js/.module-cache/80a01a350ccea8f837824abb8a78698c5971130d.js vendored

@ -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 {
}
}
}

101
js/.module-cache/817d69cf89ab760cfd9dfbc25814ba6962145289.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, className: "fullytight"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1, className: "fullytight"}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

101
js/.module-cache/83a76356a36e400cc41c7877268fef1c940f2214.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, className: "nopadding"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

101
js/.module-cache/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, nopadding: true}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

102
js/.module-cache/99d6a84f3275b12417299ef742e2824d3ce6e610.js vendored

@ -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);
}
};

12
js/.module-cache/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js vendored

@ -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]);
}
}
}

101
js/.module-cache/a75daba84d9bb9b874cc39f466ceed477f12a293.js vendored

@ -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");
}
};

160
js/.module-cache/a96e5b381fdf9155de48fe91471ffc18f049dae0.js vendored

@ -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})
)
);
}
});

12
js/.module-cache/b44a64a27d99a484a4cf8e757c2d902e147932fa.js vendored

@ -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]);
}
}
}

172
js/.module-cache/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.js vendored

@ -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})
)
);
}
});

166
js/.module-cache/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.js vendored

@ -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})
)
);
}
});

62
js/.module-cache/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.js vendored

@ -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;

101
js/.module-cache/d71ed2a487872dd50be6d967cedeff0a6b5767b9.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2, className: "fullytight"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1, className: "tight"}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

172
js/.module-cache/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js vendored

@ -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})
)
);
}
});

101
js/.module-cache/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.js vendored

@ -0,0 +1,101 @@
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
, ListGroupItem = ReactBootstrap.ListGroupItem
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
fullname: "",
retwistingUser: this.props.post.retwistingUser,
timeAgo: ""
};
},
updateTimeAgo: function() {
var secondsAgo = Date.now()/1000-this.props.post.timestamp;
var newTimeAgo = "";
if (secondsAgo<45) {newTimeAgo="1m"}
else if (secondsAgo<45*60) {newTimeAgo=Math.round(secondsAgo/60)+"m"}
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
var thisComponent = this;
//console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setStateSafe({avatar: avatar.getUrl()});
});
Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
this.updateTimeAgo();
this.setInterval(this.updateTimeAgo,60000);
},
render: function() {
var post = this.props.post;
return (
React.createElement(ListGroupItem, null,
React.createElement(Grid, {fill: true},
React.createElement(Row, null,
React.createElement(Col, {xs: 2}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ",
post.content
),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
React.createElement(Row, null,
React.createElement(Col, {xs: 4},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
)
)
)
);
}
});
/*
<div className="post-avatar">
<img src={this.state.avatar}/>
</div>
<div className="post-bulk">
<div className="post-username">
<span className="post-fullname">{this.state.fullname} </span>
@{post.username} - {post.id}
</div>
<div className="post-timestamp">{post.timestamp}</div>
<div className="post-content">{post.content}</div>
</div>
<hr/>
*/

1
js/.module-cache/manifest/08c714844c29536ebc79f9d30805d704b52fd613.json vendored

@ -0,0 +1 @@
{".js":"08c714844c29536ebc79f9d30805d704b52fd613.js"}

1
js/.module-cache/manifest/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.json vendored

@ -0,0 +1 @@
{".js":"1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js"}

1
js/.module-cache/manifest/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.json vendored

@ -0,0 +1 @@
{".js":"1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js"}

1
js/.module-cache/manifest/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.json vendored

@ -0,0 +1 @@
{".js":"37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.js"}

1
js/.module-cache/manifest/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.json vendored

@ -0,0 +1 @@
{".js":"3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.js"}

1
js/.module-cache/manifest/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.json vendored

@ -0,0 +1 @@
{".js":"3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js"}

1
js/.module-cache/manifest/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.json vendored

@ -0,0 +1 @@
{".js":"5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js"}

1
js/.module-cache/manifest/5f1e941d8495d0f750d535161ad70ea16a9e4746.json vendored

@ -0,0 +1 @@
{".js":"5f1e941d8495d0f750d535161ad70ea16a9e4746.js"}

1
js/.module-cache/manifest/62b320e94d738a654a51f0072466d00e804e745d.json vendored

@ -0,0 +1 @@
{".js":"62b320e94d738a654a51f0072466d00e804e745d.js"}

1
js/.module-cache/manifest/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.json vendored

@ -0,0 +1 @@
{".js":"6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js"}

1
js/.module-cache/manifest/6bdf1b84f178854813606fd7d13938769358b0aa.json vendored

@ -0,0 +1 @@
{".js":"6bdf1b84f178854813606fd7d13938769358b0aa.js"}

1
js/.module-cache/manifest/7b03360003ecd9b3f73a35665504c5c526098e44.json vendored

@ -0,0 +1 @@
{".js":"7b03360003ecd9b3f73a35665504c5c526098e44.js"}

1
js/.module-cache/manifest/80a01a350ccea8f837824abb8a78698c5971130d.json vendored

@ -0,0 +1 @@
{".js":"80a01a350ccea8f837824abb8a78698c5971130d.js"}

1
js/.module-cache/manifest/817d69cf89ab760cfd9dfbc25814ba6962145289.json vendored

@ -0,0 +1 @@
{".js":"817d69cf89ab760cfd9dfbc25814ba6962145289.js"}

1
js/.module-cache/manifest/83a76356a36e400cc41c7877268fef1c940f2214.json vendored

@ -0,0 +1 @@
{".js":"83a76356a36e400cc41c7877268fef1c940f2214.js"}

1
js/.module-cache/manifest/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.json vendored

@ -0,0 +1 @@
{".js":"95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js"}

1
js/.module-cache/manifest/99d6a84f3275b12417299ef742e2824d3ce6e610.json vendored

@ -0,0 +1 @@
{".js":"99d6a84f3275b12417299ef742e2824d3ce6e610.js"}

1
js/.module-cache/manifest/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.json vendored

@ -0,0 +1 @@
{".js":"9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js"}

1
js/.module-cache/manifest/a75daba84d9bb9b874cc39f466ceed477f12a293.json vendored

@ -0,0 +1 @@
{".js":"a75daba84d9bb9b874cc39f466ceed477f12a293.js"}

1
js/.module-cache/manifest/a96e5b381fdf9155de48fe91471ffc18f049dae0.json vendored

@ -0,0 +1 @@
{".js":"a96e5b381fdf9155de48fe91471ffc18f049dae0.js"}

1
js/.module-cache/manifest/b44a64a27d99a484a4cf8e757c2d902e147932fa.json vendored

@ -0,0 +1 @@
{".js":"b44a64a27d99a484a4cf8e757c2d902e147932fa.js"}

1
js/.module-cache/manifest/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.json vendored

@ -0,0 +1 @@
{".js":"bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.js"}

1
js/.module-cache/manifest/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.json vendored

@ -0,0 +1 @@
{".js":"c57d14fa426b54106d5bc5cc344fa166bfb8f51b.js"}

1
js/.module-cache/manifest/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.json vendored

@ -0,0 +1 @@
{".js":"c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.js"}

1
js/.module-cache/manifest/d71ed2a487872dd50be6d967cedeff0a6b5767b9.json vendored

@ -0,0 +1 @@
{".js":"d71ed2a487872dd50be6d967cedeff0a6b5767b9.js"}

1
js/.module-cache/manifest/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.json vendored

@ -0,0 +1 @@
{".js":"f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js"}

1
js/.module-cache/manifest/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.json vendored

@ -0,0 +1 @@
{".js":"f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.js"}

13
js/App.js

@ -56,7 +56,7 @@ App = React.createClass({displayName: "App",
"pampalulu" "pampalulu"
) )
), ),
React.createElement(RouteHandler, {pollInterval: "60000", key: this.getHandlerKey()}) React.createElement(RouteHandler, {pollInterval: "60", key: this.getHandlerKey()})
) )
); );
} }
@ -90,4 +90,13 @@ loadCache();
setInterval(saveCache,300000); setInterval(saveCache,300000);
Twister.loadServerAccounts(intitializeApp); 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);
}
};

12
js/EventListenerMixin.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]);
}
}
}

21
js/Post.js

@ -8,9 +8,10 @@ var ReactBootstrap = require('react-bootstrap')
var React = require('react'); var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js"); var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post", module.exports = Post = React.createClass({displayName: "Post",
mixins: [SetIntervalMixin], mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() { getInitialState: function() {
return { return {
avatar: "img/genericPerson.png", avatar: "img/genericPerson.png",
@ -29,7 +30,7 @@ module.exports = Post = React.createClass({displayName: "Post",
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"} else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"} else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setState({timeAgo: newTimeAgo}); this.setStateSafe({timeAgo: newTimeAgo});
}, },
componentDidMount: function () { componentDidMount: function () {
@ -37,16 +38,16 @@ module.exports = Post = React.createClass({displayName: "Post",
//console.log(this.props.post.username+":post"+this.props.post.id); //console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){ Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setState({avatar: avatar.getUrl()}); thisComponent.setStateSafe({avatar: avatar.getUrl()});
}); });
Twister.getUser(this.props.post.username).doProfile(function(profile){ Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setState({fullname: profile.getField("fullname")}); thisComponent.setStateSafe({fullname: profile.getField("fullname")});
}); });
if (this.props.post.isRetwist) { if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){ Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setState({retwistingUser: profile.getField("fullname")}); thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
}); });
} }
@ -58,22 +59,22 @@ module.exports = Post = React.createClass({displayName: "Post",
render: function() { render: function() {
var post = this.props.post; var post = this.props.post;
return ( return (
React.createElement(ListGroupItem, null, React.createElement(ListGroupItem, {fill: true},
React.createElement(Grid, {fill: true}, React.createElement(Grid, {fill: true},
React.createElement(Row, null, React.createElement(Row, null,
React.createElement(Col, {xs: 2}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})), React.createElement(Col, {xs: 2, className: "fullytight"}, React.createElement("img", {className: "img-responsive", src: this.state.avatar})),
React.createElement(Col, {xs: 9}, React.createElement(Col, {xs: 9},
React.createElement("strong", null, this.state.fullname), " ", React.createElement("strong", null, this.state.fullname), " ",
post.content post.content
), ),
React.createElement(Col, {xs: 1}, React.createElement("p", {className: "text-right"}, this.state.timeAgo)) React.createElement(Col, {xs: 1, className: "fullytight"}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
), ),
React.createElement(Row, null, React.createElement(Row, null,
React.createElement(Col, {xs: 4}, React.createElement(Col, {xs: 6},
post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser)) post.isRetwist && React.createElement("small", null, React.createElement("span", {className: "glyphicon glyphicon-retweet", "aria-hidden": "true"}), " ", React.createElement("em", null, "  retwisted by ", this.state.retwistingUser))
), ),
React.createElement(Col, {xs: 8}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test")))) React.createElement(Col, {xs: 6}, React.createElement("p", {className: "text-right"}, React.createElement("small", null, React.createElement("em", null, "test"))))
) )
) )

62
js/SafeStateChangeMixin.js

@ -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;

2
js/StreamMixin.js

@ -6,7 +6,7 @@ module.exports = StreamMixin = {
if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) { if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) {
this.setState(function(previousState, currentProps) { this.setStateSafe(function(previousState, currentProps) {
previousState.postIdentifiers[postid] = true; previousState.postIdentifiers[postid] = true;

27
js/Timeline.js

@ -11,10 +11,13 @@ var React = require('react');
var Postboard = require("./Postboard.js"); var Postboard = require("./Postboard.js");
var SetIntervalMixin = require("./SetIntervalMixin.js"); var SetIntervalMixin = require("./SetIntervalMixin.js");
var StreamMixin = require("./StreamMixin.js"); var StreamMixin = require("./StreamMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
var EventListenerMixin = require('./EventListenerMixin.js')
module.exports = Timeline = React.createClass({displayName: "Timeline", module.exports = Timeline = React.createClass({displayName: "Timeline",
mixins: [StreamMixin,SetIntervalMixin], mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')],
contextTypes: { contextTypes: {
router: React.PropTypes.func router: React.PropTypes.func
}, },
@ -37,7 +40,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
postIdentifiers: {}, postIdentifiers: {},
usernames: [], usernames: [],
timelineUser: [], timelineUser: [],
postrange: ( Date.now()/1000 - 24*60*60 ), postrange: ( Date.now()/1000 - 12*60*60 ),
min_posts: 30 min_posts: 30
}; };
}, },
@ -46,7 +49,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
var thisComponent = this; var thisComponent = this;
this.setState(function(previousState, currentProps){ this.setStateSafe(function(previousState, currentProps){
previousState.usernames.push(username); previousState.usernames.push(username);
@ -67,7 +70,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
}, },
removeUser: function(username) { removeUser: function(username) {
this.setState(function(previousState, currentProps){ this.setStateSafe(function(previousState, currentProps){
var newusers = []; var newusers = [];
@ -97,7 +100,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
}, },
updatePosts: function(outdatedLimit) { updatePosts: function(outdatedLimit) {
if (!outdatedLimit) {outdatedLimit=30;} if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;}
for (var i = 0; i<this.state.usernames.length; i++) { for (var i = 0; i<this.state.usernames.length; i++) {
@ -139,14 +142,24 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
} }
thisComponent.updatePosts(60); thisComponent.updatePosts(thisComponent.props.pollInterval);
}); });
}); });
this.setInterval(this.updatePosts, this.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() { render: function() {
return ( return (

13
jsx/App.js

@ -56,7 +56,7 @@ App = React.createClass({
pampalulu pampalulu
</NavItem> </NavItem>
</Nav> </Nav>
<RouteHandler pollInterval="60000" key={this.getHandlerKey()}/> <RouteHandler pollInterval="60" key={this.getHandlerKey()}/>
</div> </div>
); );
} }
@ -90,4 +90,13 @@ loadCache();
setInterval(saveCache,300000); setInterval(saveCache,300000);
Twister.loadServerAccounts(intitializeApp); 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);
}
};

12
jsx/EventListenerMixin.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]);
}
}
}

21
jsx/Post.js

@ -8,9 +8,10 @@ var ReactBootstrap = require('react-bootstrap')
var React = require('react'); var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js"); var SetIntervalMixin = require("./SetIntervalMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({ module.exports = Post = React.createClass({
mixins: [SetIntervalMixin], mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() { getInitialState: function() {
return { return {
avatar: "img/genericPerson.png", avatar: "img/genericPerson.png",
@ -29,7 +30,7 @@ module.exports = Post = React.createClass({
else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"} else if (secondsAgo<45*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"}
else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"} else if (secondsAgo<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
this.setState({timeAgo: newTimeAgo}); this.setStateSafe({timeAgo: newTimeAgo});
}, },
componentDidMount: function () { componentDidMount: function () {
@ -37,16 +38,16 @@ module.exports = Post = React.createClass({
//console.log(this.props.post.username+":post"+this.props.post.id); //console.log(this.props.post.username+":post"+this.props.post.id);
Twister.getUser(this.props.post.username).doAvatar(function(avatar){ Twister.getUser(this.props.post.username).doAvatar(function(avatar){
thisComponent.setState({avatar: avatar.getUrl()}); thisComponent.setStateSafe({avatar: avatar.getUrl()});
}); });
Twister.getUser(this.props.post.username).doProfile(function(profile){ Twister.getUser(this.props.post.username).doProfile(function(profile){
thisComponent.setState({fullname: profile.getField("fullname")}); thisComponent.setStateSafe({fullname: profile.getField("fullname")});
}); });
if (this.props.post.isRetwist) { if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){ Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
thisComponent.setState({retwistingUser: profile.getField("fullname")}); thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
}); });
} }
@ -58,22 +59,22 @@ module.exports = Post = React.createClass({
render: function() { render: function() {
var post = this.props.post; var post = this.props.post;
return ( return (
<ListGroupItem> <ListGroupItem fill>
<Grid fill> <Grid fill>
<Row> <Row>
<Col xs={2}><img className="img-responsive" src={this.state.avatar}/></Col> <Col xs={2} className="fullytight"><img className="img-responsive" src={this.state.avatar}/></Col>
<Col xs={9}> <Col xs={9}>
<strong>{this.state.fullname}</strong>&nbsp; <strong>{this.state.fullname}</strong>&nbsp;
{post.content} {post.content}
</Col> </Col>
<Col xs={1}><p className="text-right">{this.state.timeAgo}</p></Col> <Col xs={1} className="fullytight"><p className="text-right">{this.state.timeAgo}</p></Col>
</Row> </Row>
<Row> <Row>
<Col xs={4}> <Col xs={6}>
{post.isRetwist && <small><span className="glyphicon glyphicon-retweet" aria-hidden="true"></span> <em> &nbsp;retwisted by {this.state.retwistingUser}</em></small> {post.isRetwist && <small><span className="glyphicon glyphicon-retweet" aria-hidden="true"></span> <em> &nbsp;retwisted by {this.state.retwistingUser}</em></small>
} }
</Col> </Col>
<Col xs={8}><p className="text-right"><small><em>test</em></small></p></Col> <Col xs={6}><p className="text-right"><small><em>test</em></small></p></Col>
</Row> </Row>
</Grid> </Grid>

62
jsx/SafeStateChangeMixin.js

@ -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;

2
jsx/StreamMixin.js

@ -6,7 +6,7 @@ module.exports = StreamMixin = {
if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) { if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) {
this.setState(function(previousState, currentProps) { this.setStateSafe(function(previousState, currentProps) {
previousState.postIdentifiers[postid] = true; previousState.postIdentifiers[postid] = true;

27
jsx/Timeline.js

@ -11,10 +11,13 @@ var React = require('react');
var Postboard = require("./Postboard.js"); var Postboard = require("./Postboard.js");
var SetIntervalMixin = require("./SetIntervalMixin.js"); var SetIntervalMixin = require("./SetIntervalMixin.js");
var StreamMixin = require("./StreamMixin.js"); var StreamMixin = require("./StreamMixin.js");
var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
var EventListenerMixin = require('./EventListenerMixin.js')
module.exports = Timeline = React.createClass({ module.exports = Timeline = React.createClass({
mixins: [StreamMixin,SetIntervalMixin], mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')],
contextTypes: { contextTypes: {
router: React.PropTypes.func router: React.PropTypes.func
}, },
@ -37,7 +40,7 @@ module.exports = Timeline = React.createClass({
postIdentifiers: {}, postIdentifiers: {},
usernames: [], usernames: [],
timelineUser: [], timelineUser: [],
postrange: ( Date.now()/1000 - 24*60*60 ), postrange: ( Date.now()/1000 - 12*60*60 ),
min_posts: 30 min_posts: 30
}; };
}, },
@ -46,7 +49,7 @@ module.exports = Timeline = React.createClass({
var thisComponent = this; var thisComponent = this;
this.setState(function(previousState, currentProps){ this.setStateSafe(function(previousState, currentProps){
previousState.usernames.push(username); previousState.usernames.push(username);
@ -67,7 +70,7 @@ module.exports = Timeline = React.createClass({
}, },
removeUser: function(username) { removeUser: function(username) {
this.setState(function(previousState, currentProps){ this.setStateSafe(function(previousState, currentProps){
var newusers = []; var newusers = [];
@ -97,7 +100,7 @@ module.exports = Timeline = React.createClass({
}, },
updatePosts: function(outdatedLimit) { updatePosts: function(outdatedLimit) {
if (!outdatedLimit) {outdatedLimit=30;} if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;}
for (var i = 0; i<this.state.usernames.length; i++) { for (var i = 0; i<this.state.usernames.length; i++) {
@ -139,14 +142,24 @@ module.exports = Timeline = React.createClass({
} }
thisComponent.updatePosts(60); thisComponent.updatePosts(thisComponent.props.pollInterval);
}); });
}); });
this.setInterval(this.updatePosts, this.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() { render: function() {
return ( return (

22
node_modules/react-mixin-safe-state-change/LICENSE generated vendored

@ -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.

35
node_modules/react-mixin-safe-state-change/README.md generated vendored

@ -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.');
}
}
});
```

89
node_modules/react-mixin-safe-state-change/index.js generated vendored

@ -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;

49
node_modules/react-mixin-safe-state-change/package.json generated vendored

@ -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…
Cancel
Save