diff --git a/build/app-bundle.js b/build/app-bundle.js
index bf7aa02..6d3baf4 100644
--- a/build/app-bundle.js
+++ b/build/app-bundle.js
@@ -57,7 +57,7 @@ App = React.createClass({displayName: "App",
"pampalulu"
)
),
- React.createElement(RouteHandler, {pollInterval: "60000", key: this.getHandlerKey()})
+ React.createElement(RouteHandler, {pollInterval: "60", key: this.getHandlerKey()})
)
);
}
@@ -92,7 +92,29 @@ loadCache();
setInterval(saveCache,300000);
Twister.loadServerAccounts(intitializeApp);
-},{"./Timeline.js":6,"react":261,"react-bootstrap":58,"react-router":91}],2:[function(require,module,exports){
+
+////// INIT EVENTLISTENERS ON WINDOW
+
+window.onscroll = function(ev) {
+ if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
+ var event = new Event('scrolledtobottom');
+ window.dispatchEvent(event);
+ }
+};
+},{"./Timeline.js":8,"react":263,"react-bootstrap":60,"react-router":93}],2:[function(require,module,exports){
+module.exports = EventListenerMixin = function (eventtype) {
+
+ return {
+ componentDidMount: function() {
+ window.addEventListener(eventtype, this["on"+eventtype]);
+ },
+ componentWillUnmount: function() {
+ window.removeEventListener(eventtype, this["on"+eventtype]);
+ }
+ }
+
+}
+},{}],3:[function(require,module,exports){
var ReactBootstrap = require('react-bootstrap')
, Grid = ReactBootstrap.Grid
@@ -103,9 +125,10 @@ var ReactBootstrap = require('react-bootstrap')
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
+var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
- mixins: [SetIntervalMixin],
+ mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
avatar: "img/genericPerson.png",
@@ -124,7 +147,7 @@ module.exports = Post = React.createClass({displayName: "Post",
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.setState({timeAgo: newTimeAgo});
+ this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
@@ -132,16 +155,16 @@ module.exports = Post = React.createClass({displayName: "Post",
//console.log(this.props.post.username+":post"+this.props.post.id);
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){
- thisComponent.setState({fullname: profile.getField("fullname")});
+ thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
Twister.getUser(this.props.post.retwistingUser).doProfile(function(profile){
- thisComponent.setState({retwistingUser: profile.getField("fullname")});
+ thisComponent.setStateSafe({retwistingUser: profile.getField("fullname")});
});
}
@@ -153,22 +176,22 @@ module.exports = Post = React.createClass({displayName: "Post",
render: function() {
var post = this.props.post;
return (
- React.createElement(ListGroupItem, null,
+ React.createElement(ListGroupItem, {fill: true},
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: 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(Col, {xs: 1, className: "fullytight"}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
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))
),
- 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"))))
)
)
@@ -193,7 +216,7 @@ module.exports = Post = React.createClass({displayName: "Post",
*/
-},{"./SetIntervalMixin.js":4,"react":261,"react-bootstrap":58}],3:[function(require,module,exports){
+},{"./SafeStateChangeMixin.js":5,"./SetIntervalMixin.js":6,"react":263,"react-bootstrap":60}],4:[function(require,module,exports){
var ReactBootstrap = require('react-bootstrap')
, NavItem = ReactBootstrap.NavItem
@@ -219,7 +242,70 @@ module.exports = Postboard = React.createClass({displayName: "Postboard",
);
}
});
-},{"./Post.js":2,"react":261,"react-bootstrap":58}],4:[function(require,module,exports){
+},{"./Post.js":3,"react":263,"react-bootstrap":60}],5:[function(require,module,exports){
+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;
+},{}],6:[function(require,module,exports){
module.exports = SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
@@ -231,7 +317,7 @@ module.exports = SetIntervalMixin = {
this.intervals.map(clearInterval);
}
};
-},{}],5:[function(require,module,exports){
+},{}],7:[function(require,module,exports){
module.exports = StreamMixin = {
addPost: function(post) {
@@ -240,7 +326,7 @@ module.exports = StreamMixin = {
if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) {
- this.setState(function(previousState, currentProps) {
+ this.setStateSafe(function(previousState, currentProps) {
previousState.postIdentifiers[postid] = true;
@@ -292,7 +378,7 @@ module.exports = StreamMixin = {
}
}
}
-},{}],6:[function(require,module,exports){
+},{}],8:[function(require,module,exports){
var ReactBootstrap = require('react-bootstrap')
, NavItem = ReactBootstrap.NavItem
@@ -306,10 +392,13 @@ 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],
+ mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')],
contextTypes: {
router: React.PropTypes.func
},
@@ -332,7 +421,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
postIdentifiers: {},
usernames: [],
timelineUser: [],
- postrange: ( Date.now()/1000 - 24*60*60 ),
+ postrange: ( Date.now()/1000 - 12*60*60 ),
min_posts: 30
};
},
@@ -341,7 +430,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
var thisComponent = this;
- this.setState(function(previousState, currentProps){
+ this.setStateSafe(function(previousState, currentProps){
previousState.usernames.push(username);
@@ -362,7 +451,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
},
removeUser: function(username) {
- this.setState(function(previousState, currentProps){
+ this.setStateSafe(function(previousState, currentProps){
var newusers = [];
@@ -392,7 +481,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
},
updatePosts: function(outdatedLimit) {
- if (!outdatedLimit) {outdatedLimit=30;}
+ if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;}
for (var i = 0; i
+
+
+
+
+ {this.state.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js b/js/.module-cache/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js
new file mode 100644
index 0000000..03d393e
--- /dev/null
+++ b/js/.module-cache/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js
@@ -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);
+ }
+};
\ No newline at end of file
diff --git a/js/.module-cache/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js b/js/.module-cache/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js
new file mode 100644
index 0000000..d0c2336
--- /dev/null
+++ b/js/.module-cache/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js
@@ -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.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js b/js/.module-cache/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js
new file mode 100644
index 0000000..11ef710
--- /dev/null
+++ b/js/.module-cache/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js
@@ -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"))))
+ )
+ )
+
+ )
+ );
+ }
+});
+
+/*
+
+
+
+
+
+ {this.state.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js b/js/.module-cache/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js
new file mode 100644
index 0000000..af3e075
--- /dev/null
+++ b/js/.module-cache/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js
@@ -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 b.timestamp)
+ return -1;
+ return 0;
+ }
+
+ previousState.data.sort(compare);
+
+ return {data: previousState.data, postIdentifiers: previousState.postIdentifiers };
+ });
+
+ } else {
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/js/.module-cache/62b320e94d738a654a51f0072466d00e804e745d.js b/js/.module-cache/62b320e94d738a654a51f0072466d00e804e745d.js
new file mode 100644
index 0000000..af71ea1
--- /dev/null
+++ b/js/.module-cache/62b320e94d738a654a51f0072466d00e804e745d.js
@@ -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"))))
+ )
+ )
+
+ )
+ );
+ }
+});
+
+/*
+
+
+
+
+
+ {this.state.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js b/js/.module-cache/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js
new file mode 100644
index 0000000..0ce178d
--- /dev/null
+++ b/js/.module-cache/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js
@@ -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.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/7b03360003ecd9b3f73a35665504c5c526098e44.js b/js/.module-cache/7b03360003ecd9b3f73a35665504c5c526098e44.js
new file mode 100644
index 0000000..090c83f
--- /dev/null
+++ b/js/.module-cache/7b03360003ecd9b3f73a35665504c5c526098e44.js
@@ -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 b.timestamp)
+ return -1;
+ return 0;
+ }
+
+ previousState.data.sort(compare);
+
+ return {data: previousState.data, postIdentifiers: previousState.postIdentifiers };
+ });
+
+ } else {
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/js/.module-cache/817d69cf89ab760cfd9dfbc25814ba6962145289.js b/js/.module-cache/817d69cf89ab760cfd9dfbc25814ba6962145289.js
new file mode 100644
index 0000000..25cd7a8
--- /dev/null
+++ b/js/.module-cache/817d69cf89ab760cfd9dfbc25814ba6962145289.js
@@ -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"))))
+ )
+ )
+
+ )
+ );
+ }
+});
+
+/*
+
+
+
+
+
+ {this.state.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/83a76356a36e400cc41c7877268fef1c940f2214.js b/js/.module-cache/83a76356a36e400cc41c7877268fef1c940f2214.js
new file mode 100644
index 0000000..71d7b35
--- /dev/null
+++ b/js/.module-cache/83a76356a36e400cc41c7877268fef1c940f2214.js
@@ -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"))))
+ )
+ )
+
+ )
+ );
+ }
+});
+
+/*
+
+
+
+
+
+ {this.state.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js b/js/.module-cache/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js
new file mode 100644
index 0000000..6b0a099
--- /dev/null
+++ b/js/.module-cache/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js
@@ -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"))))
+ )
+ )
+
+ )
+ );
+ }
+});
+
+/*
+
+
+
+
+
+ {this.state.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/99d6a84f3275b12417299ef742e2824d3ce6e610.js b/js/.module-cache/99d6a84f3275b12417299ef742e2824d3ce6e610.js
new file mode 100644
index 0000000..ba6f769
--- /dev/null
+++ b/js/.module-cache/99d6a84f3275b12417299ef742e2824d3ce6e610.js
@@ -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);
+ }
+};
\ No newline at end of file
diff --git a/js/.module-cache/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js b/js/.module-cache/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js
new file mode 100644
index 0000000..48ffe8a
--- /dev/null
+++ b/js/.module-cache/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.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]);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/js/.module-cache/a75daba84d9bb9b874cc39f466ceed477f12a293.js b/js/.module-cache/a75daba84d9bb9b874cc39f466ceed477f12a293.js
new file mode 100644
index 0000000..6446867
--- /dev/null
+++ b/js/.module-cache/a75daba84d9bb9b874cc39f466ceed477f12a293.js
@@ -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");
+ }
+};
\ No newline at end of file
diff --git a/js/.module-cache/a96e5b381fdf9155de48fe91471ffc18f049dae0.js b/js/.module-cache/a96e5b381fdf9155de48fe91471ffc18f049dae0.js
new file mode 100644
index 0000000..bfcfc11
--- /dev/null
+++ b/js/.module-cache/a96e5b381fdf9155de48fe91471ffc18f049dae0.js
@@ -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.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js b/js/.module-cache/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js
new file mode 100644
index 0000000..08a7e8c
--- /dev/null
+++ b/js/.module-cache/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js
@@ -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.fullname}
+ @{post.username} - {post.id}
+
+
+
{post.timestamp}
+
{post.content}
+
+
+
+ */
\ No newline at end of file
diff --git a/js/.module-cache/manifest/08c714844c29536ebc79f9d30805d704b52fd613.json b/js/.module-cache/manifest/08c714844c29536ebc79f9d30805d704b52fd613.json
new file mode 100644
index 0000000..75e9f90
--- /dev/null
+++ b/js/.module-cache/manifest/08c714844c29536ebc79f9d30805d704b52fd613.json
@@ -0,0 +1 @@
+{".js":"08c714844c29536ebc79f9d30805d704b52fd613.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.json b/js/.module-cache/manifest/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.json
new file mode 100644
index 0000000..6a5a234
--- /dev/null
+++ b/js/.module-cache/manifest/1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.json
@@ -0,0 +1 @@
+{".js":"1790c1dcc8106c5ece39d81ebfaa7b9ffa396496.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.json b/js/.module-cache/manifest/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.json
new file mode 100644
index 0000000..bbbb89b
--- /dev/null
+++ b/js/.module-cache/manifest/1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.json
@@ -0,0 +1 @@
+{".js":"1dc4fe12ecab4f1f54667b86d2cfa2071aa1e5bc.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.json b/js/.module-cache/manifest/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.json
new file mode 100644
index 0000000..78b4cf2
--- /dev/null
+++ b/js/.module-cache/manifest/37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.json
@@ -0,0 +1 @@
+{".js":"37e111cc9b81f1cdb7a5eaa6ce00f435348bd48a.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.json b/js/.module-cache/manifest/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.json
new file mode 100644
index 0000000..4240642
--- /dev/null
+++ b/js/.module-cache/manifest/3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.json
@@ -0,0 +1 @@
+{".js":"3c5a8a3726ee4db1fc5375c1f0b9ff889948e240.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.json b/js/.module-cache/manifest/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.json
new file mode 100644
index 0000000..d64d2b1
--- /dev/null
+++ b/js/.module-cache/manifest/3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.json
@@ -0,0 +1 @@
+{".js":"3cb5ee718a67a61dc4cbdfb8e03165b978a0e71a.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.json b/js/.module-cache/manifest/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.json
new file mode 100644
index 0000000..d4c363c
--- /dev/null
+++ b/js/.module-cache/manifest/5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.json
@@ -0,0 +1 @@
+{".js":"5d1f97ef0ac61cdf58e8fa2bb34cc7fadc08f04c.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/5f1e941d8495d0f750d535161ad70ea16a9e4746.json b/js/.module-cache/manifest/5f1e941d8495d0f750d535161ad70ea16a9e4746.json
new file mode 100644
index 0000000..9c41213
--- /dev/null
+++ b/js/.module-cache/manifest/5f1e941d8495d0f750d535161ad70ea16a9e4746.json
@@ -0,0 +1 @@
+{".js":"5f1e941d8495d0f750d535161ad70ea16a9e4746.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/62b320e94d738a654a51f0072466d00e804e745d.json b/js/.module-cache/manifest/62b320e94d738a654a51f0072466d00e804e745d.json
new file mode 100644
index 0000000..3ffa706
--- /dev/null
+++ b/js/.module-cache/manifest/62b320e94d738a654a51f0072466d00e804e745d.json
@@ -0,0 +1 @@
+{".js":"62b320e94d738a654a51f0072466d00e804e745d.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.json b/js/.module-cache/manifest/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.json
new file mode 100644
index 0000000..6527807
--- /dev/null
+++ b/js/.module-cache/manifest/6963c41578eaddc39957b6cb561cf7b19a2dc5ce.json
@@ -0,0 +1 @@
+{".js":"6963c41578eaddc39957b6cb561cf7b19a2dc5ce.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/6bdf1b84f178854813606fd7d13938769358b0aa.json b/js/.module-cache/manifest/6bdf1b84f178854813606fd7d13938769358b0aa.json
new file mode 100644
index 0000000..674d197
--- /dev/null
+++ b/js/.module-cache/manifest/6bdf1b84f178854813606fd7d13938769358b0aa.json
@@ -0,0 +1 @@
+{".js":"6bdf1b84f178854813606fd7d13938769358b0aa.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/7b03360003ecd9b3f73a35665504c5c526098e44.json b/js/.module-cache/manifest/7b03360003ecd9b3f73a35665504c5c526098e44.json
new file mode 100644
index 0000000..da7a8ba
--- /dev/null
+++ b/js/.module-cache/manifest/7b03360003ecd9b3f73a35665504c5c526098e44.json
@@ -0,0 +1 @@
+{".js":"7b03360003ecd9b3f73a35665504c5c526098e44.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/80a01a350ccea8f837824abb8a78698c5971130d.json b/js/.module-cache/manifest/80a01a350ccea8f837824abb8a78698c5971130d.json
new file mode 100644
index 0000000..1bf38a8
--- /dev/null
+++ b/js/.module-cache/manifest/80a01a350ccea8f837824abb8a78698c5971130d.json
@@ -0,0 +1 @@
+{".js":"80a01a350ccea8f837824abb8a78698c5971130d.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/817d69cf89ab760cfd9dfbc25814ba6962145289.json b/js/.module-cache/manifest/817d69cf89ab760cfd9dfbc25814ba6962145289.json
new file mode 100644
index 0000000..dcaa5a6
--- /dev/null
+++ b/js/.module-cache/manifest/817d69cf89ab760cfd9dfbc25814ba6962145289.json
@@ -0,0 +1 @@
+{".js":"817d69cf89ab760cfd9dfbc25814ba6962145289.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/83a76356a36e400cc41c7877268fef1c940f2214.json b/js/.module-cache/manifest/83a76356a36e400cc41c7877268fef1c940f2214.json
new file mode 100644
index 0000000..4aeb77b
--- /dev/null
+++ b/js/.module-cache/manifest/83a76356a36e400cc41c7877268fef1c940f2214.json
@@ -0,0 +1 @@
+{".js":"83a76356a36e400cc41c7877268fef1c940f2214.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.json b/js/.module-cache/manifest/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.json
new file mode 100644
index 0000000..6347c57
--- /dev/null
+++ b/js/.module-cache/manifest/95bd287b14edcd74662d28b1aaf141b7e86eb5a4.json
@@ -0,0 +1 @@
+{".js":"95bd287b14edcd74662d28b1aaf141b7e86eb5a4.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/99d6a84f3275b12417299ef742e2824d3ce6e610.json b/js/.module-cache/manifest/99d6a84f3275b12417299ef742e2824d3ce6e610.json
new file mode 100644
index 0000000..e40c74f
--- /dev/null
+++ b/js/.module-cache/manifest/99d6a84f3275b12417299ef742e2824d3ce6e610.json
@@ -0,0 +1 @@
+{".js":"99d6a84f3275b12417299ef742e2824d3ce6e610.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.json b/js/.module-cache/manifest/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.json
new file mode 100644
index 0000000..9777d15
--- /dev/null
+++ b/js/.module-cache/manifest/9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.json
@@ -0,0 +1 @@
+{".js":"9a94dd6850590563ad4a1c5bfe8d9538c625f7cc.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/a75daba84d9bb9b874cc39f466ceed477f12a293.json b/js/.module-cache/manifest/a75daba84d9bb9b874cc39f466ceed477f12a293.json
new file mode 100644
index 0000000..53110ee
--- /dev/null
+++ b/js/.module-cache/manifest/a75daba84d9bb9b874cc39f466ceed477f12a293.json
@@ -0,0 +1 @@
+{".js":"a75daba84d9bb9b874cc39f466ceed477f12a293.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/a96e5b381fdf9155de48fe91471ffc18f049dae0.json b/js/.module-cache/manifest/a96e5b381fdf9155de48fe91471ffc18f049dae0.json
new file mode 100644
index 0000000..0a00451
--- /dev/null
+++ b/js/.module-cache/manifest/a96e5b381fdf9155de48fe91471ffc18f049dae0.json
@@ -0,0 +1 @@
+{".js":"a96e5b381fdf9155de48fe91471ffc18f049dae0.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/b44a64a27d99a484a4cf8e757c2d902e147932fa.json b/js/.module-cache/manifest/b44a64a27d99a484a4cf8e757c2d902e147932fa.json
new file mode 100644
index 0000000..118942e
--- /dev/null
+++ b/js/.module-cache/manifest/b44a64a27d99a484a4cf8e757c2d902e147932fa.json
@@ -0,0 +1 @@
+{".js":"b44a64a27d99a484a4cf8e757c2d902e147932fa.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.json b/js/.module-cache/manifest/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.json
new file mode 100644
index 0000000..f47a18f
--- /dev/null
+++ b/js/.module-cache/manifest/bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.json
@@ -0,0 +1 @@
+{".js":"bb2cde08d4b5a26c66a5c56c75e288b0d0476c48.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.json b/js/.module-cache/manifest/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.json
new file mode 100644
index 0000000..37e46e6
--- /dev/null
+++ b/js/.module-cache/manifest/c57d14fa426b54106d5bc5cc344fa166bfb8f51b.json
@@ -0,0 +1 @@
+{".js":"c57d14fa426b54106d5bc5cc344fa166bfb8f51b.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.json b/js/.module-cache/manifest/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.json
new file mode 100644
index 0000000..9b9fa72
--- /dev/null
+++ b/js/.module-cache/manifest/c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.json
@@ -0,0 +1 @@
+{".js":"c7f08b920603d73fac97d6007dbf6b5bc7f57ba5.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/d71ed2a487872dd50be6d967cedeff0a6b5767b9.json b/js/.module-cache/manifest/d71ed2a487872dd50be6d967cedeff0a6b5767b9.json
new file mode 100644
index 0000000..3e7c50b
--- /dev/null
+++ b/js/.module-cache/manifest/d71ed2a487872dd50be6d967cedeff0a6b5767b9.json
@@ -0,0 +1 @@
+{".js":"d71ed2a487872dd50be6d967cedeff0a6b5767b9.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.json b/js/.module-cache/manifest/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.json
new file mode 100644
index 0000000..ec45ed2
--- /dev/null
+++ b/js/.module-cache/manifest/f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.json
@@ -0,0 +1 @@
+{".js":"f4da31f5826a7a96ab3cc20d1d12e0b754e4085a.js"}
\ No newline at end of file
diff --git a/js/.module-cache/manifest/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.json b/js/.module-cache/manifest/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.json
new file mode 100644
index 0000000..419be15
--- /dev/null
+++ b/js/.module-cache/manifest/f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.json
@@ -0,0 +1 @@
+{".js":"f837fa85d2697fe09e9b8a879a4ced37d7c6d3c0.js"}
\ No newline at end of file
diff --git a/js/App.js b/js/App.js
index 0139d8b..03d393e 100644
--- a/js/App.js
+++ b/js/App.js
@@ -56,7 +56,7 @@ App = React.createClass({displayName: "App",
"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);
-Twister.loadServerAccounts(intitializeApp);
\ No newline at end of file
+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);
+ }
+};
\ No newline at end of file
diff --git a/js/EventListenerMixin.js b/js/EventListenerMixin.js
new file mode 100644
index 0000000..48ffe8a
--- /dev/null
+++ b/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]);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/js/Post.js b/js/Post.js
index c089445..66b295c 100644
--- a/js/Post.js
+++ b/js/Post.js
@@ -8,9 +8,10 @@ var ReactBootstrap = require('react-bootstrap')
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
+var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({displayName: "Post",
- mixins: [SetIntervalMixin],
+ mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
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<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
- this.setState({timeAgo: newTimeAgo});
+ this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
@@ -37,16 +38,16 @@ module.exports = Post = React.createClass({displayName: "Post",
//console.log(this.props.post.username+":post"+this.props.post.id);
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){
- thisComponent.setState({fullname: profile.getField("fullname")});
+ thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
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() {
var post = this.props.post;
return (
- React.createElement(ListGroupItem, null,
+ React.createElement(ListGroupItem, {fill: true},
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: 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(Col, {xs: 1, className: "fullytight"}, React.createElement("p", {className: "text-right"}, this.state.timeAgo))
),
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))
),
- 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"))))
)
)
diff --git a/js/SafeStateChangeMixin.js b/js/SafeStateChangeMixin.js
new file mode 100644
index 0000000..2dddc93
--- /dev/null
+++ b/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;
\ No newline at end of file
diff --git a/js/StreamMixin.js b/js/StreamMixin.js
index aa6da63..502958f 100644
--- a/js/StreamMixin.js
+++ b/js/StreamMixin.js
@@ -6,7 +6,7 @@ module.exports = StreamMixin = {
if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) {
- this.setState(function(previousState, currentProps) {
+ this.setStateSafe(function(previousState, currentProps) {
previousState.postIdentifiers[postid] = true;
diff --git a/js/Timeline.js b/js/Timeline.js
index 13e6b1b..af3e075 100644
--- a/js/Timeline.js
+++ b/js/Timeline.js
@@ -11,10 +11,13 @@ 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],
+ mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')],
contextTypes: {
router: React.PropTypes.func
},
@@ -37,7 +40,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
postIdentifiers: {},
usernames: [],
timelineUser: [],
- postrange: ( Date.now()/1000 - 24*60*60 ),
+ postrange: ( Date.now()/1000 - 12*60*60 ),
min_posts: 30
};
},
@@ -46,7 +49,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
var thisComponent = this;
- this.setState(function(previousState, currentProps){
+ this.setStateSafe(function(previousState, currentProps){
previousState.usernames.push(username);
@@ -67,7 +70,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
},
removeUser: function(username) {
- this.setState(function(previousState, currentProps){
+ this.setStateSafe(function(previousState, currentProps){
var newusers = [];
@@ -97,7 +100,7 @@ module.exports = Timeline = React.createClass({displayName: "Timeline",
},
updatePosts: function(outdatedLimit) {
- if (!outdatedLimit) {outdatedLimit=30;}
+ if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;}
for (var i = 0; i
-
+
);
}
@@ -90,4 +90,13 @@ loadCache();
setInterval(saveCache,300000);
-Twister.loadServerAccounts(intitializeApp);
\ No newline at end of file
+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);
+ }
+};
\ No newline at end of file
diff --git a/jsx/EventListenerMixin.js b/jsx/EventListenerMixin.js
new file mode 100644
index 0000000..48ffe8a
--- /dev/null
+++ b/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]);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/jsx/Post.js b/jsx/Post.js
index f57e389..c548c3e 100644
--- a/jsx/Post.js
+++ b/jsx/Post.js
@@ -8,9 +8,10 @@ var ReactBootstrap = require('react-bootstrap')
var React = require('react');
var SetIntervalMixin = require("./SetIntervalMixin.js");
+var SafeStateChangeMixin = require('./SafeStateChangeMixin.js');
module.exports = Post = React.createClass({
- mixins: [SetIntervalMixin],
+ mixins: [SetIntervalMixin,SafeStateChangeMixin],
getInitialState: function() {
return {
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<60*60*60*18) {newTimeAgo=Math.round(secondsAgo/60/60/60)+"d"}
- this.setState({timeAgo: newTimeAgo});
+ this.setStateSafe({timeAgo: newTimeAgo});
},
componentDidMount: function () {
@@ -37,16 +38,16 @@ module.exports = Post = React.createClass({
//console.log(this.props.post.username+":post"+this.props.post.id);
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){
- thisComponent.setState({fullname: profile.getField("fullname")});
+ thisComponent.setStateSafe({fullname: profile.getField("fullname")});
});
if (this.props.post.isRetwist) {
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() {
var post = this.props.post;
return (
-
+
-
+
{this.state.fullname}
{post.content}
- {this.state.timeAgo}
+ {this.state.timeAgo}
-
+
{post.isRetwist && retwisted by {this.state.retwistingUser}
}
- test
+ test
diff --git a/jsx/SafeStateChangeMixin.js b/jsx/SafeStateChangeMixin.js
new file mode 100644
index 0000000..2dddc93
--- /dev/null
+++ b/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;
\ No newline at end of file
diff --git a/jsx/StreamMixin.js b/jsx/StreamMixin.js
index aa6da63..502958f 100644
--- a/jsx/StreamMixin.js
+++ b/jsx/StreamMixin.js
@@ -6,7 +6,7 @@ module.exports = StreamMixin = {
if (!this.state.postIdentifiers[postid] && this.verifyPost(post)) {
- this.setState(function(previousState, currentProps) {
+ this.setStateSafe(function(previousState, currentProps) {
previousState.postIdentifiers[postid] = true;
diff --git a/jsx/Timeline.js b/jsx/Timeline.js
index 859d281..4aebda3 100644
--- a/jsx/Timeline.js
+++ b/jsx/Timeline.js
@@ -11,10 +11,13 @@ 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({
- mixins: [StreamMixin,SetIntervalMixin],
+ mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin,EventListenerMixin('scrolledtobottom')],
contextTypes: {
router: React.PropTypes.func
},
@@ -37,7 +40,7 @@ module.exports = Timeline = React.createClass({
postIdentifiers: {},
usernames: [],
timelineUser: [],
- postrange: ( Date.now()/1000 - 24*60*60 ),
+ postrange: ( Date.now()/1000 - 12*60*60 ),
min_posts: 30
};
},
@@ -46,7 +49,7 @@ module.exports = Timeline = React.createClass({
var thisComponent = this;
- this.setState(function(previousState, currentProps){
+ this.setStateSafe(function(previousState, currentProps){
previousState.usernames.push(username);
@@ -67,7 +70,7 @@ module.exports = Timeline = React.createClass({
},
removeUser: function(username) {
- this.setState(function(previousState, currentProps){
+ this.setStateSafe(function(previousState, currentProps){
var newusers = [];
@@ -97,7 +100,7 @@ module.exports = Timeline = React.createClass({
},
updatePosts: function(outdatedLimit) {
- if (!outdatedLimit) {outdatedLimit=30;}
+ if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;}
for (var i = 0; i