Julian Steinwachs
10 years ago
59 changed files with 3384 additions and 0 deletions
@ -0,0 +1,231 @@ |
|||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
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') |
||||||
|
, DropdownButton = ReactBootstrap.DropdownButton |
||||||
|
, MenuItem = ReactBootstrap.MenuItem |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, OverlayTrigger = ReactBootstrap.OverlayTrigger |
||||||
|
, Popover = ReactBootstrap.Popover |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Grid = ReactBootstrap.Grid |
||||||
|
, Col = ReactBootstrap.Col |
||||||
|
, Row = ReactBootstrap.Row |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
var Router = require('react-router'); |
||||||
|
var { Route, DefaultRoute, RouteHandler, Link } = Router; |
||||||
|
|
||||||
|
var Home = require("./home/Home.js"); |
||||||
|
var Profile = require("./profile/Profile.js"); |
||||||
|
var SetIntervalMixin = require("./common/SetIntervalMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./common/SafeStateChangeMixin.js'); |
||||||
|
var Timeline = require('./profile/Timeline.js'); |
||||||
|
var Followings = require('./profile/Followings.js'); |
||||||
|
var Mentions = require('./profile/Mentions.js'); |
||||||
|
|
||||||
|
|
||||||
|
App = React.createClass({displayName: "App", |
||||||
|
|
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
|
||||||
|
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; |
||||||
|
if (key=="home" || key=="profile-active" || key=="accountProfileMore") {key=key+"/"+this.state.activeAccount;} |
||||||
|
var id = JSON.stringify(router.getCurrentParams()); |
||||||
|
if (id) { key += id; } |
||||||
|
console.log(key); |
||||||
|
return key; |
||||||
|
} else {return "none"} |
||||||
|
}, |
||||||
|
|
||||||
|
clearCache: function () { |
||||||
|
localStorage.setItem("twister-cache", null); |
||||||
|
}, |
||||||
|
|
||||||
|
saveCache: function () { |
||||||
|
localStorage.setItem("twister-cache", JSON.stringify(Twister.serializeCache())) |
||||||
|
}, |
||||||
|
|
||||||
|
switchAccount: function (newaccoutname) { |
||||||
|
|
||||||
|
console.log(newaccoutname); |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
Twister.getAccount(newaccoutname).activateTorrents(function(){ |
||||||
|
thisComponent.setStateSafe({activeAccount: newaccoutname},function(){ |
||||||
|
localStorage.setItem("twister-react-activeAccount", newaccoutname); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
getInitialState: function () { |
||||||
|
|
||||||
|
Twister.deserializeCache(JSON.parse(localStorage.getItem("twister-cache"))); |
||||||
|
|
||||||
|
//this.clearCache();
|
||||||
|
|
||||||
|
var state={}; |
||||||
|
|
||||||
|
state.activeAccount = localStorage.getItem("twister-react-activeAccount") |
||||||
|
|
||||||
|
state.pollInterval = 60; |
||||||
|
state.pollIntervalProfile = 60*60; |
||||||
|
|
||||||
|
state.accounts = Twister.getAccounts(); |
||||||
|
|
||||||
|
//console.log(state);
|
||||||
|
|
||||||
|
return state; |
||||||
|
}, |
||||||
|
|
||||||
|
componentDidMount: function () { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
if (this.state.accounts.length==0) { |
||||||
|
|
||||||
|
Twister.init({ |
||||||
|
host: 'http://user:pwd@localhost:28332', |
||||||
|
logfunc: function(log){console.log(log)} |
||||||
|
}); |
||||||
|
Twister.loadServerAccounts(function(){ |
||||||
|
|
||||||
|
thisComponent.setStateSafe(function(state){ |
||||||
|
|
||||||
|
state.accounts = Twister.getAccounts(); |
||||||
|
//console.log(state.accounts);
|
||||||
|
state.activeAccount = state.accounts[0]; |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
thisComponent.switchAccount(thisComponent.state.activeAccount); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} else { |
||||||
|
this.switchAccount(this.state.activeAccount); |
||||||
|
} |
||||||
|
|
||||||
|
this.setInterval(this.saveCache,300000); |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
render: function() { |
||||||
|
|
||||||
|
var firstroute = this.context.router.getCurrentRoutes()[1].name; |
||||||
|
|
||||||
|
//console.log(firstroute);
|
||||||
|
|
||||||
|
var userbuttons = []; |
||||||
|
for (var i in this.state.accounts) { |
||||||
|
userbuttons.push( |
||||||
|
React.createElement(MenuItem, { |
||||||
|
key: this.state.accounts[i], |
||||||
|
bsStyle: this.state.accounts[i]==this.state.activeAccount ? 'primary' : 'default', |
||||||
|
onClick: this.switchAccount.bind(this,this.state.accounts[i]), |
||||||
|
href: "javascript:void(0);" |
||||||
|
}, this.state.accounts[i]) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
React.createElement(Grid, null, |
||||||
|
React.createElement(Row, null, |
||||||
|
React.createElement(Col, {xs: 12, sm: 10, smOffset: 1, md: 8, mdOffset: 2, lg: 6, lgOffset: 3}, |
||||||
|
React.createElement(ButtonGroup, {justified: true}, |
||||||
|
React.createElement(Button, { |
||||||
|
href: "#", |
||||||
|
bsStyle: firstroute=="home" ? 'primary' : 'default' |
||||||
|
}, React.createElement(Glyphicon, {glyph: "home"})), |
||||||
|
React.createElement(Button, { |
||||||
|
href: "#/profile", |
||||||
|
bsStyle: firstroute=="profile-active" ? 'primary' : 'default' |
||||||
|
}, React.createElement(Glyphicon, {glyph: "user"})), |
||||||
|
React.createElement(Button, {href: "#/directmessages"}, React.createElement(Glyphicon, {glyph: "transfer"})), |
||||||
|
React.createElement(DropdownButton, {title: this.state.activeAccount}, |
||||||
|
userbuttons |
||||||
|
), |
||||||
|
React.createElement(DropdownButton, {title: React.createElement(Glyphicon, {glyph: "menu-hamburger"})}, |
||||||
|
React.createElement(MenuItem, { |
||||||
|
onClick: this.clearCache, |
||||||
|
href: "javascript:void(0);" |
||||||
|
}, "Clear Cache"), |
||||||
|
React.createElement(MenuItem, {href: "#/search"}, "Search"), |
||||||
|
React.createElement(MenuItem, {href: "#/settings"}, "Settings"), |
||||||
|
React.createElement(MenuItem, {href: "#/howtofollow"}, "How to Follow"), |
||||||
|
React.createElement(MenuItem, {href: "#/trendinghashtags"}, "Trending Hashtags") |
||||||
|
) |
||||||
|
), |
||||||
|
React.createElement("br", null), |
||||||
|
React.createElement(RouteHandler, { |
||||||
|
pollInterval: this.state.pollInterval, |
||||||
|
pollIntervalProfile: this.state.pollIntervalProfile, |
||||||
|
activeAccount: this.state.activeAccount, |
||||||
|
key: this.getHandlerKey()} |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
var routes = ( |
||||||
|
React.createElement(Route, {handler: App, path: "/"}, |
||||||
|
React.createElement(Route, {name: "profile-active", path: "/profile", handler: Profile}, |
||||||
|
React.createElement(Route, {name: "profile-active-timeline", path: "timeline", handler: Timeline}), |
||||||
|
React.createElement(Route, {name: "profile-active-followings", path: "followings", handler: Followings}), |
||||||
|
React.createElement(Route, {name: "profile-active-mentions", path: "mentions", handler: Mentions}), |
||||||
|
React.createElement(DefaultRoute, {name: "profile-active-timeline-default", handler: Timeline}) |
||||||
|
), |
||||||
|
React.createElement(Route, {name: "profile", path: "/profile/:username", handler: Profile}, |
||||||
|
React.createElement(Route, {name: "profile-timeline", path: "timeline", handler: Timeline}), |
||||||
|
React.createElement(Route, {name: "profile-followings", path: "followings", handler: Followings}), |
||||||
|
React.createElement(Route, {name: "profile-mentions", path: "mentions", handler: Mentions}), |
||||||
|
React.createElement(DefaultRoute, {name: "profile-timeline-default", handler: Timeline}) |
||||||
|
), |
||||||
|
React.createElement(DefaultRoute, {name: "home", handler: Home}) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Router.run(routes, function (Handler) { |
||||||
|
React.render(React.createElement(Handler, null), document.getElementById('content')); |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////// INIT EVENTLISTENERS ON WINDOW
|
||||||
|
|
||||||
|
window.onscroll = function(ev) { |
||||||
|
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 50) { |
||||||
|
var event = new Event('scrolledtobottom'); |
||||||
|
//alert("scrolled to bottom")
|
||||||
|
window.dispatchEvent(event); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,71 @@ |
|||||||
|
module.exports = ProfileMixin = { |
||||||
|
getInitialState: function() { |
||||||
|
|
||||||
|
var username = this.props.username; |
||||||
|
|
||||||
|
if (!username) { |
||||||
|
|
||||||
|
username = (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var state = { |
||||||
|
username: username, |
||||||
|
avatar: "img/genericPerson.png", |
||||||
|
fullname: "", |
||||||
|
bio: "", |
||||||
|
location: "", |
||||||
|
url: "" |
||||||
|
}; |
||||||
|
|
||||||
|
var profile = Twister.getUser(username).getProfile(); |
||||||
|
|
||||||
|
if (profile.inCache()) { |
||||||
|
|
||||||
|
state.fullname = profile.getField("fullname"); |
||||||
|
state.bio = profile.getField("bio"); |
||||||
|
state.location = profile.getField("location"); |
||||||
|
state.url = profile.getField("url"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var avatar = Twister.getUser(username).getAvatar(); |
||||||
|
|
||||||
|
if (avatar.inCache()) { |
||||||
|
|
||||||
|
state.avatar = avatar.getUrl(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
updateProfile: function () { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doAvatar(function(avatar){ |
||||||
|
if (avatar.getUrl()) { |
||||||
|
thisComponent.setStateSafe({avatar: avatar.getUrl()}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doProfile(function(profile){ |
||||||
|
thisComponent.setStateSafe({ |
||||||
|
fullname: profile.getField("fullname"), |
||||||
|
bio: profile.getField("bio"), |
||||||
|
location: profile.getField("location"), |
||||||
|
url: profile.getField("url"), |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function () { |
||||||
|
|
||||||
|
this.updateProfile(); |
||||||
|
|
||||||
|
this.setInterval(this.updateProfile,this.props.pollIntervalProfile*1000); |
||||||
|
|
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,60 @@ |
|||||||
|
module.exports = StreamMixin = { |
||||||
|
|
||||||
|
addPost: function(post) { |
||||||
|
|
||||||
|
var postid = post.getUsername() + ":post" + post.getId(); |
||||||
|
|
||||||
|
if (!this.state.postIdentifiers[postid]) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps) { |
||||||
|
|
||||||
|
previousState.postIdentifiers[postid] = true; |
||||||
|
|
||||||
|
if (post.isRetwist()){ |
||||||
|
|
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getRetwistedUser(), |
||||||
|
retwistingUser: post.getUsername(), |
||||||
|
content: post.getRetwistedContent(), |
||||||
|
id: post.getRetwistedId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: true |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getUsername(), |
||||||
|
content: post.getContent(), |
||||||
|
id: post.getId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: false |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.push(postdata) |
||||||
|
|
||||||
|
var compare = function (a,b) { |
||||||
|
if (a.timestamp < b.timestamp) |
||||||
|
return 1; |
||||||
|
if (a.timestamp > b.timestamp) |
||||||
|
return -1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.sort(compare); |
||||||
|
|
||||||
|
return {data: previousState.data, postIdentifiers: previousState.postIdentifiers }; |
||||||
|
}); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var NewPostModalButton = require("../home/NewPostModalButton.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Home = React.createClass({displayName: "Home", |
||||||
|
|
||||||
|
mixins: [ |
||||||
|
StreamMixin, |
||||||
|
SetIntervalMixin, |
||||||
|
SafeStateChangeMixin, |
||||||
|
EventListenerMixin('scrolledtobottom'), |
||||||
|
EventListenerMixin('newpostbyuser') |
||||||
|
], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
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!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
//console.log("adding post",post.getUsername(),post.getId())
|
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: 2*thisComponent.props.pollInterval}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
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); |
||||||
|
//console.log("adding post",post.getUsername(),post.getId())
|
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
if (this.props.activeAccount) { |
||||||
|
|
||||||
|
console.log("active account is "+this.props.activeAccount) |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.props.activeAccount; |
||||||
|
|
||||||
|
thisComponent.addUser(username); |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
//console.log(followings[i].getUsername())
|
||||||
|
} |
||||||
|
|
||||||
|
//thisComponent.updatePosts(thisComponent.props.pollInterval);
|
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
} else {console.log("active account is null")} |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange -= 6*60*60; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
onnewpostbyuser: function (event) { |
||||||
|
|
||||||
|
for (var i in this.state.usernames) { |
||||||
|
if(this.state.usernames[i]==event.detail.getUsername()) { |
||||||
|
this.addPost(event.detail); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Postboard, {data: this.state.data, header: |
||||||
|
React.createElement(ListGroupItem, null, |
||||||
|
"Home", |
||||||
|
React.createElement(NewPostModalButton, {activeAccount: this.props.activeAccount}) |
||||||
|
) |
||||||
|
}) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,77 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, OverlayMixin = ReactBootstrap.OverlayMixin |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Modal = ReactBootstrap.Modal |
||||||
|
, Input = ReactBootstrap.Input |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
|
||||||
|
module.exports = NewPostModalButton = React.createClass({displayName: "NewPostModalButton", |
||||||
|
mixins: [OverlayMixin], |
||||||
|
getInitialState: function () { |
||||||
|
return { |
||||||
|
isModalOpen: false |
||||||
|
}; |
||||||
|
}, |
||||||
|
handleToggle: function () { |
||||||
|
this.setState({ |
||||||
|
isModalOpen: !this.state.isModalOpen |
||||||
|
}); |
||||||
|
}, |
||||||
|
handleNewPost: function (e) { |
||||||
|
e.preventDefault(); |
||||||
|
//console.log(e)
|
||||||
|
var msg = JSON.parse(JSON.stringify(e.target[0].value)); |
||||||
|
if (!msg) { |
||||||
|
console.log("empty post was passed as new post") |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Twister.getAccount(this.props.activeAccount).post(msg,function(post){ |
||||||
|
|
||||||
|
var event = new CustomEvent('newpostbyuser',{detail: post}); |
||||||
|
//alert("scrolled to bottom")
|
||||||
|
window.dispatchEvent(event); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
e.target[0].value = ""; |
||||||
|
|
||||||
|
|
||||||
|
this.handleToggle(); |
||||||
|
|
||||||
|
//React.findDOMNode(this.refs.msg).value = '';
|
||||||
|
return; |
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Button, {onClick: this.handleToggle, className: "link-button-gray pull-right fullytight_all", bsStyle: "link"}, |
||||||
|
React.createElement(Glyphicon, {glyph: "pencil"}) |
||||||
|
) |
||||||
|
); |
||||||
|
}, |
||||||
|
renderOverlay: function() { |
||||||
|
|
||||||
|
if (!this.state.isModalOpen) { |
||||||
|
return React.createElement("span", null); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
React.createElement(Modal, {bsStyle: "primary", title: React.createElement(Glyphicon, {glyph: "pencil"}), onRequestHide: this.handleToggle}, |
||||||
|
React.createElement("div", {className: "modal-body"}, |
||||||
|
React.createElement("form", {onSubmit: this.handleNewPost}, |
||||||
|
React.createElement(Input, {type: "textarea", label: "Text Area", placeholder: "textarea"}), |
||||||
|
React.createElement(Input, {type: "submit", value: "Submit button", "data-dismiss": "modal"}) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,84 @@ |
|||||||
|
|
||||||
|
var React = require('react/addons'); |
||||||
|
|
||||||
|
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; |
||||||
|
|
||||||
|
var MiniProfile = require("../common/MiniProfile.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Home = React.createClass({displayName: "Home", |
||||||
|
|
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
followings: [] |
||||||
|
}; |
||||||
|
}, |
||||||
|
updateFollowings: function(outdatedLimit) { |
||||||
|
|
||||||
|
thisComponent=this; |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
thisComponent.setStateSafe(function(state){ |
||||||
|
|
||||||
|
var newfollowings = []; |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
newfollowings.push(followings[i].getUsername()); |
||||||
|
} |
||||||
|
|
||||||
|
state.followings = newfollowings; |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updateFollowings(this.props.pollInterval*2); |
||||||
|
|
||||||
|
this.setInterval(this.updateFollowings, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var profiles = this.state.followings.map(function(username, index) { |
||||||
|
return ( |
||||||
|
React.createElement(MiniProfile, {username: username, key: "miniprofile:"+username, pollIntervalProfile: thisComponent.props.pollIntervalProfile}) |
||||||
|
); |
||||||
|
}); |
||||||
|
return ( |
||||||
|
React.createElement(ListGroup, {fill: true}, |
||||||
|
React.createElement(ReactCSSTransitionGroup, {transitionName: "item"}, |
||||||
|
profiles |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+eventtype]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+eventtype]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins:[ |
||||||
|
StreamMixin, |
||||||
|
SetIntervalMixin, |
||||||
|
SafeStateChangeMixin, |
||||||
|
EventListenerMixin('scrolledtobottom'), |
||||||
|
EventListenerMixin('newpostbyuser') |
||||||
|
], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
postCount: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.username; |
||||||
|
|
||||||
|
var count = 0; |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
//console.log(count)
|
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(count++>=thisComponent.state.postCount) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
console.log(this.props.pollInterval) |
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange += 10; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
onnewpostbyuser: function (event) { |
||||||
|
|
||||||
|
//alert("got event")
|
||||||
|
|
||||||
|
if(this.state.username==event.post.getUsername()) { |
||||||
|
this.addPost(event.post); |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Postboard, {data: this.state.data, header: ""}) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,51 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var MiniProfile = require("../common/MiniProfile.js"); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
module.exports = Home = React.createClass({displayName: "Home", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
data: [], |
||||||
|
postIdentifiers: {} |
||||||
|
}; |
||||||
|
}, |
||||||
|
updateMentions: function(outdatedLimit) { |
||||||
|
|
||||||
|
thisComponent=this; |
||||||
|
|
||||||
|
if (outdatedLimit===undefined) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doMentions(function(mentions){ |
||||||
|
|
||||||
|
for(var i in mentions){ |
||||||
|
thisComponent.addPost(mentions[i]); |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updateMentions(this.props.pollInterval*2); |
||||||
|
|
||||||
|
this.setInterval(this.updateMentions, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Postboard, {data: this.state.data, header: ""}) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,11 @@ |
|||||||
|
module.exports = SetIntervalMixin = { |
||||||
|
componentWillMount: function() { |
||||||
|
this.intervals = []; |
||||||
|
}, |
||||||
|
setInterval: function() { |
||||||
|
this.intervals.push(setInterval.apply(null, arguments)); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
this.intervals.map(clearInterval); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,62 @@ |
|||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
//result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null;
|
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,34 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
var React = require('react/addons'); |
||||||
|
|
||||||
|
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; |
||||||
|
|
||||||
|
var Post = require("../common/Post.js"); |
||||||
|
|
||||||
|
module.exports = Postboard = React.createClass({displayName: "Postboard", |
||||||
|
render: function() { |
||||||
|
var posts = this.props.data.map(function(post, index) { |
||||||
|
return ( |
||||||
|
React.createElement(Post, {post: post, key: post.postid}) |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
return ( |
||||||
|
React.createElement(ListGroup, {fill: true}, |
||||||
|
this.props.header, |
||||||
|
React.createElement(ReactCSSTransitionGroup, {transitionName: "item"}, |
||||||
|
posts |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1 @@ |
|||||||
|
{".js":"1481637775089844184af71820625492ebd04146.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"20db71076a525ed87d3d9dfbd98c8b31be8c6896.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"242ec183241b4b53f4ddae69b8afb6b60426ec42.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"299f979899a857c45bf2f5de63a5ca750da60433.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"38e3cc72af24ddb96884506416d1a140d84277fe.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"7b5f4f98c79393354f44412127d1b1b4768c7ae2.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"9dff733c4585512f6ed2deca306255214765bbe7.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"9fc14ade32e04296d9f74634da69458d5595110f.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"bb5e20b0dfe1177cc608549060d0883ef58e156b.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"bd19abac2e2e24498b933499d1eff7ae0825111d.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"d33e863ad34154f54a43fa2e43e161c04d397c4c.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"d7e8646c084253b7cfd0f981f734a2b391aa60a5.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"dea17f9cee8acb42aaea8c4b65723338c5d1feb2.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"e2700f3eddcf47a0235b40f1879c236d04edfb9f.js"} |
@ -0,0 +1 @@ |
|||||||
|
{".js":"e394633e8498d5a50e1a34cd73f803421446d4f3.js"} |
@ -0,0 +1,231 @@ |
|||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
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') |
||||||
|
, DropdownButton = ReactBootstrap.DropdownButton |
||||||
|
, MenuItem = ReactBootstrap.MenuItem |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, OverlayTrigger = ReactBootstrap.OverlayTrigger |
||||||
|
, Popover = ReactBootstrap.Popover |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Grid = ReactBootstrap.Grid |
||||||
|
, Col = ReactBootstrap.Col |
||||||
|
, Row = ReactBootstrap.Row |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
var Router = require('react-router'); |
||||||
|
var { Route, DefaultRoute, RouteHandler, Link } = Router; |
||||||
|
|
||||||
|
var Home = require("./home/Home.js"); |
||||||
|
var Profile = require("./profile/Profile.js"); |
||||||
|
var SetIntervalMixin = require("./common/SetIntervalMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('./common/SafeStateChangeMixin.js'); |
||||||
|
var Timeline = require('./profile/Timeline.js'); |
||||||
|
var Followings = require('./profile/Followings.js'); |
||||||
|
var Mentions = require('./profile/Mentions.js'); |
||||||
|
|
||||||
|
|
||||||
|
App = React.createClass({displayName: "App", |
||||||
|
|
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
|
||||||
|
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; |
||||||
|
if (key=="home" || key=="profile-active" || key=="accountProfileMore") {key=key+"/"+this.state.activeAccount;} |
||||||
|
var id = JSON.stringify(router.getCurrentParams()); |
||||||
|
if (id) { key += id; } |
||||||
|
console.log(key); |
||||||
|
return key; |
||||||
|
} else {return "none"} |
||||||
|
}, |
||||||
|
|
||||||
|
clearCache: function () { |
||||||
|
localStorage.setItem("twister-cache", null); |
||||||
|
}, |
||||||
|
|
||||||
|
saveCache: function () { |
||||||
|
localStorage.setItem("twister-cache", JSON.stringify(Twister.serializeCache())) |
||||||
|
}, |
||||||
|
|
||||||
|
switchAccount: function (newaccoutname) { |
||||||
|
|
||||||
|
console.log(newaccoutname); |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
Twister.getAccount(newaccoutname).activateTorrents(function(){ |
||||||
|
thisComponent.setStateSafe({activeAccount: newaccoutname},function(){ |
||||||
|
localStorage.setItem("twister-react-activeAccount", newaccoutname); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
getInitialState: function () { |
||||||
|
|
||||||
|
Twister.deserializeCache(JSON.parse(localStorage.getItem("twister-cache"))); |
||||||
|
|
||||||
|
//this.clearCache();
|
||||||
|
|
||||||
|
var state={}; |
||||||
|
|
||||||
|
state.activeAccount = localStorage.getItem("twister-react-activeAccount") |
||||||
|
|
||||||
|
state.pollInterval = 60; |
||||||
|
state.pollIntervalProfile = 60*60; |
||||||
|
|
||||||
|
state.accounts = Twister.getAccounts(); |
||||||
|
|
||||||
|
//console.log(state);
|
||||||
|
|
||||||
|
return state; |
||||||
|
}, |
||||||
|
|
||||||
|
componentDidMount: function () { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
if (this.state.accounts.length==0) { |
||||||
|
|
||||||
|
Twister.init({ |
||||||
|
host: 'http://user:pwd@localhost:28332', |
||||||
|
logfunc: function(log){console.log(log)} |
||||||
|
}); |
||||||
|
Twister.loadServerAccounts(function(){ |
||||||
|
|
||||||
|
thisComponent.setStateSafe(function(state){ |
||||||
|
|
||||||
|
state.accounts = Twister.getAccounts(); |
||||||
|
//console.log(state.accounts);
|
||||||
|
state.activeAccount = state.accounts[0]; |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
},function(){ |
||||||
|
thisComponent.switchAccount(thisComponent.state.activeAccount); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} else { |
||||||
|
this.switchAccount(this.state.activeAccount); |
||||||
|
} |
||||||
|
|
||||||
|
this.setInterval(this.saveCache,300000); |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
render: function() { |
||||||
|
|
||||||
|
var firstroute = this.context.router.getCurrentRoutes()[1].name; |
||||||
|
|
||||||
|
//console.log(firstroute);
|
||||||
|
|
||||||
|
var userbuttons = []; |
||||||
|
for (var i in this.state.accounts) { |
||||||
|
userbuttons.push( |
||||||
|
React.createElement(MenuItem, { |
||||||
|
key: this.state.accounts[i], |
||||||
|
bsStyle: this.state.accounts[i]==this.state.activeAccount ? 'primary' : 'default', |
||||||
|
onClick: this.switchAccount.bind(this,this.state.accounts[i]), |
||||||
|
href: "javascript:void(0);" |
||||||
|
}, this.state.accounts[i]) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
React.createElement(Grid, null, |
||||||
|
React.createElement(Row, null, |
||||||
|
React.createElement(Col, {xs: 12, sm: 10, smOffset: 1, md: 8, mdOffset: 2, lg: 6, lgOffset: 3}, |
||||||
|
React.createElement(ButtonGroup, {justified: true}, |
||||||
|
React.createElement(Button, { |
||||||
|
href: "#", |
||||||
|
bsStyle: firstroute=="home" ? 'primary' : 'default' |
||||||
|
}, React.createElement(Glyphicon, {glyph: "home"})), |
||||||
|
React.createElement(Button, { |
||||||
|
href: "#/profile", |
||||||
|
bsStyle: firstroute=="profile-active" ? 'primary' : 'default' |
||||||
|
}, React.createElement(Glyphicon, {glyph: "user"})), |
||||||
|
React.createElement(Button, {href: "#/directmessages"}, React.createElement(Glyphicon, {glyph: "transfer"})), |
||||||
|
React.createElement(DropdownButton, {title: this.state.activeAccount}, |
||||||
|
userbuttons |
||||||
|
), |
||||||
|
React.createElement(DropdownButton, {title: React.createElement(Glyphicon, {glyph: "menu-hamburger"})}, |
||||||
|
React.createElement(MenuItem, { |
||||||
|
onClick: this.clearCache, |
||||||
|
href: "javascript:void(0);" |
||||||
|
}, "Clear Cache"), |
||||||
|
React.createElement(MenuItem, {href: "#/search"}, "Search"), |
||||||
|
React.createElement(MenuItem, {href: "#/settings"}, "Settings"), |
||||||
|
React.createElement(MenuItem, {href: "#/howtofollow"}, "How to Follow"), |
||||||
|
React.createElement(MenuItem, {href: "#/trendinghashtags"}, "Trending Hashtags") |
||||||
|
) |
||||||
|
), |
||||||
|
React.createElement("br", null), |
||||||
|
React.createElement(RouteHandler, { |
||||||
|
pollInterval: this.state.pollInterval, |
||||||
|
pollIntervalProfile: this.state.pollIntervalProfile, |
||||||
|
activeAccount: this.state.activeAccount, |
||||||
|
key: this.getHandlerKey()} |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
var routes = ( |
||||||
|
React.createElement(Route, {handler: App, path: "/"}, |
||||||
|
React.createElement(Route, {name: "profile-active", path: "/profile", handler: Profile}, |
||||||
|
React.createElement(Route, {name: "profile-active-timeline", path: "timeline", handler: Timeline}), |
||||||
|
React.createElement(Route, {name: "profile-active-followings", path: "followings", handler: Followings}), |
||||||
|
React.createElement(Route, {name: "profile-active-mentions", path: "mentions", handler: Mentions}), |
||||||
|
React.createElement(DefaultRoute, {name: "profile-active-timeline-default", handler: Timeline}) |
||||||
|
), |
||||||
|
React.createElement(Route, {name: "profile", path: "/profile/:username", handler: Profile}, |
||||||
|
React.createElement(Route, {name: "profile-timeline", path: "timeline", handler: Timeline}), |
||||||
|
React.createElement(Route, {name: "profile-followings", path: "followings", handler: Followings}), |
||||||
|
React.createElement(Route, {name: "profile-mentions", path: "mentions", handler: Mentions}), |
||||||
|
React.createElement(DefaultRoute, {name: "profile-timeline-default", handler: Timeline}) |
||||||
|
), |
||||||
|
React.createElement(DefaultRoute, {name: "home", handler: Home}) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Router.run(routes, function (Handler) { |
||||||
|
React.render(React.createElement(Handler, null), document.getElementById('content')); |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////// INIT EVENTLISTENERS ON WINDOW
|
||||||
|
|
||||||
|
window.onscroll = function(ev) { |
||||||
|
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 50) { |
||||||
|
var event = new Event('scrolledtobottom'); |
||||||
|
//alert("scrolled to bottom")
|
||||||
|
window.dispatchEvent(event); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+eventtype]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+eventtype]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
var React = require('react/addons'); |
||||||
|
|
||||||
|
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; |
||||||
|
|
||||||
|
var Post = require("../common/Post.js"); |
||||||
|
|
||||||
|
module.exports = Postboard = React.createClass({displayName: "Postboard", |
||||||
|
render: function() { |
||||||
|
var posts = this.props.data.map(function(post, index) { |
||||||
|
return ( |
||||||
|
React.createElement(Post, {post: post, key: post.postid}) |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
return ( |
||||||
|
React.createElement(ListGroup, {fill: true}, |
||||||
|
this.props.header, |
||||||
|
React.createElement(ReactCSSTransitionGroup, {transitionName: "item"}, |
||||||
|
posts |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,71 @@ |
|||||||
|
module.exports = ProfileMixin = { |
||||||
|
getInitialState: function() { |
||||||
|
|
||||||
|
var username = this.props.username; |
||||||
|
|
||||||
|
if (!username) { |
||||||
|
|
||||||
|
username = (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var state = { |
||||||
|
username: username, |
||||||
|
avatar: "img/genericPerson.png", |
||||||
|
fullname: "", |
||||||
|
bio: "", |
||||||
|
location: "", |
||||||
|
url: "" |
||||||
|
}; |
||||||
|
|
||||||
|
var profile = Twister.getUser(username).getProfile(); |
||||||
|
|
||||||
|
if (profile.inCache()) { |
||||||
|
|
||||||
|
state.fullname = profile.getField("fullname"); |
||||||
|
state.bio = profile.getField("bio"); |
||||||
|
state.location = profile.getField("location"); |
||||||
|
state.url = profile.getField("url"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var avatar = Twister.getUser(username).getAvatar(); |
||||||
|
|
||||||
|
if (avatar.inCache()) { |
||||||
|
|
||||||
|
state.avatar = avatar.getUrl(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
updateProfile: function () { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doAvatar(function(avatar){ |
||||||
|
if (avatar.getUrl()) { |
||||||
|
thisComponent.setStateSafe({avatar: avatar.getUrl()}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doProfile(function(profile){ |
||||||
|
thisComponent.setStateSafe({ |
||||||
|
fullname: profile.getField("fullname"), |
||||||
|
bio: profile.getField("bio"), |
||||||
|
location: profile.getField("location"), |
||||||
|
url: profile.getField("url"), |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function () { |
||||||
|
|
||||||
|
this.updateProfile(); |
||||||
|
|
||||||
|
this.setInterval(this.updateProfile,this.props.pollIntervalProfile*1000); |
||||||
|
|
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,62 @@ |
|||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
//result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null;
|
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,11 @@ |
|||||||
|
module.exports = SetIntervalMixin = { |
||||||
|
componentWillMount: function() { |
||||||
|
this.intervals = []; |
||||||
|
}, |
||||||
|
setInterval: function() { |
||||||
|
this.intervals.push(setInterval.apply(null, arguments)); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
this.intervals.map(clearInterval); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,60 @@ |
|||||||
|
module.exports = StreamMixin = { |
||||||
|
|
||||||
|
addPost: function(post) { |
||||||
|
|
||||||
|
var postid = post.getUsername() + ":post" + post.getId(); |
||||||
|
|
||||||
|
if (!this.state.postIdentifiers[postid]) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps) { |
||||||
|
|
||||||
|
previousState.postIdentifiers[postid] = true; |
||||||
|
|
||||||
|
if (post.isRetwist()){ |
||||||
|
|
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getRetwistedUser(), |
||||||
|
retwistingUser: post.getUsername(), |
||||||
|
content: post.getRetwistedContent(), |
||||||
|
id: post.getRetwistedId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: true |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getUsername(), |
||||||
|
content: post.getContent(), |
||||||
|
id: post.getId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: false |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.push(postdata) |
||||||
|
|
||||||
|
var compare = function (a,b) { |
||||||
|
if (a.timestamp < b.timestamp) |
||||||
|
return 1; |
||||||
|
if (a.timestamp > b.timestamp) |
||||||
|
return -1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.sort(compare); |
||||||
|
|
||||||
|
return {data: previousState.data, postIdentifiers: previousState.postIdentifiers }; |
||||||
|
}); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var NewPostModalButton = require("../home/NewPostModalButton.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Home = React.createClass({displayName: "Home", |
||||||
|
|
||||||
|
mixins: [ |
||||||
|
StreamMixin, |
||||||
|
SetIntervalMixin, |
||||||
|
SafeStateChangeMixin, |
||||||
|
EventListenerMixin('scrolledtobottom'), |
||||||
|
EventListenerMixin('newpostbyuser') |
||||||
|
], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
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!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
//console.log("adding post",post.getUsername(),post.getId())
|
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: 2*thisComponent.props.pollInterval}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
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); |
||||||
|
//console.log("adding post",post.getUsername(),post.getId())
|
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
if (this.props.activeAccount) { |
||||||
|
|
||||||
|
console.log("active account is "+this.props.activeAccount) |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.props.activeAccount; |
||||||
|
|
||||||
|
thisComponent.addUser(username); |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
//console.log(followings[i].getUsername())
|
||||||
|
} |
||||||
|
|
||||||
|
//thisComponent.updatePosts(thisComponent.props.pollInterval);
|
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
} else {console.log("active account is null")} |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange -= 6*60*60; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
onnewpostbyuser: function (event) { |
||||||
|
|
||||||
|
for (var i in this.state.usernames) { |
||||||
|
if(this.state.usernames[i]==event.detail.getUsername()) { |
||||||
|
this.addPost(event.detail); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Postboard, {data: this.state.data, header: |
||||||
|
React.createElement(ListGroupItem, null, |
||||||
|
"Home", |
||||||
|
React.createElement(NewPostModalButton, {activeAccount: this.props.activeAccount}) |
||||||
|
) |
||||||
|
}) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,77 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, OverlayMixin = ReactBootstrap.OverlayMixin |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Modal = ReactBootstrap.Modal |
||||||
|
, Input = ReactBootstrap.Input |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
|
||||||
|
module.exports = NewPostModalButton = React.createClass({displayName: "NewPostModalButton", |
||||||
|
mixins: [OverlayMixin], |
||||||
|
getInitialState: function () { |
||||||
|
return { |
||||||
|
isModalOpen: false |
||||||
|
}; |
||||||
|
}, |
||||||
|
handleToggle: function () { |
||||||
|
this.setState({ |
||||||
|
isModalOpen: !this.state.isModalOpen |
||||||
|
}); |
||||||
|
}, |
||||||
|
handleNewPost: function (e) { |
||||||
|
e.preventDefault(); |
||||||
|
//console.log(e)
|
||||||
|
var msg = JSON.parse(JSON.stringify(e.target[0].value)); |
||||||
|
if (!msg) { |
||||||
|
console.log("empty post was passed as new post") |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Twister.getAccount(this.props.activeAccount).post(msg,function(post){ |
||||||
|
|
||||||
|
var event = new CustomEvent('newpostbyuser',{detail: post}); |
||||||
|
//alert("scrolled to bottom")
|
||||||
|
window.dispatchEvent(event); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
e.target[0].value = ""; |
||||||
|
|
||||||
|
|
||||||
|
this.handleToggle(); |
||||||
|
|
||||||
|
//React.findDOMNode(this.refs.msg).value = '';
|
||||||
|
return; |
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Button, {onClick: this.handleToggle, className: "link-button-gray pull-right fullytight_all", bsStyle: "link"}, |
||||||
|
React.createElement(Glyphicon, {glyph: "pencil"}) |
||||||
|
) |
||||||
|
); |
||||||
|
}, |
||||||
|
renderOverlay: function() { |
||||||
|
|
||||||
|
if (!this.state.isModalOpen) { |
||||||
|
return React.createElement("span", null); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
React.createElement(Modal, {bsStyle: "primary", title: React.createElement(Glyphicon, {glyph: "pencil"}), onRequestHide: this.handleToggle}, |
||||||
|
React.createElement("div", {className: "modal-body"}, |
||||||
|
React.createElement("form", {onSubmit: this.handleNewPost}, |
||||||
|
React.createElement(Input, {type: "textarea", label: "Text Area", placeholder: "textarea"}), |
||||||
|
React.createElement(Input, {type: "submit", value: "Submit button", "data-dismiss": "modal"}) |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,84 @@ |
|||||||
|
|
||||||
|
var React = require('react/addons'); |
||||||
|
|
||||||
|
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; |
||||||
|
|
||||||
|
var MiniProfile = require("../common/MiniProfile.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Home = React.createClass({displayName: "Home", |
||||||
|
|
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
followings: [] |
||||||
|
}; |
||||||
|
}, |
||||||
|
updateFollowings: function(outdatedLimit) { |
||||||
|
|
||||||
|
thisComponent=this; |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
thisComponent.setStateSafe(function(state){ |
||||||
|
|
||||||
|
var newfollowings = []; |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
newfollowings.push(followings[i].getUsername()); |
||||||
|
} |
||||||
|
|
||||||
|
state.followings = newfollowings; |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updateFollowings(this.props.pollInterval*2); |
||||||
|
|
||||||
|
this.setInterval(this.updateFollowings, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var profiles = this.state.followings.map(function(username, index) { |
||||||
|
return ( |
||||||
|
React.createElement(MiniProfile, {username: username, key: "miniprofile:"+username, pollIntervalProfile: thisComponent.props.pollIntervalProfile}) |
||||||
|
); |
||||||
|
}); |
||||||
|
return ( |
||||||
|
React.createElement(ListGroup, {fill: true}, |
||||||
|
React.createElement(ReactCSSTransitionGroup, {transitionName: "item"}, |
||||||
|
profiles |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,51 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var MiniProfile = require("../common/MiniProfile.js"); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
module.exports = Home = React.createClass({displayName: "Home", |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
data: [], |
||||||
|
postIdentifiers: {} |
||||||
|
}; |
||||||
|
}, |
||||||
|
updateMentions: function(outdatedLimit) { |
||||||
|
|
||||||
|
thisComponent=this; |
||||||
|
|
||||||
|
if (outdatedLimit===undefined) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doMentions(function(mentions){ |
||||||
|
|
||||||
|
for(var i in mentions){ |
||||||
|
thisComponent.addPost(mentions[i]); |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updateMentions(this.props.pollInterval*2); |
||||||
|
|
||||||
|
this.setInterval(this.updateMentions, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Postboard, {data: this.state.data, header: ""}) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,95 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({displayName: "Timeline", |
||||||
|
|
||||||
|
mixins:[ |
||||||
|
StreamMixin, |
||||||
|
SetIntervalMixin, |
||||||
|
SafeStateChangeMixin, |
||||||
|
EventListenerMixin('scrolledtobottom'), |
||||||
|
EventListenerMixin('newpostbyuser') |
||||||
|
], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
postCount: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.username; |
||||||
|
|
||||||
|
var count = 0; |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
//console.log(count)
|
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(count++>=thisComponent.state.postCount) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
console.log(this.props.pollInterval) |
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange += 10; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
onnewpostbyuser: function (event) { |
||||||
|
|
||||||
|
//alert("got event")
|
||||||
|
|
||||||
|
if(this.state.username==event.post.getUsername()) { |
||||||
|
this.addPost(event.post); |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
React.createElement(Postboard, {data: this.state.data, header: ""}) |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,12 @@ |
|||||||
|
module.exports = EventListenerMixin = function (eventtype) { |
||||||
|
|
||||||
|
return { |
||||||
|
componentDidMount: function() { |
||||||
|
window.addEventListener(eventtype, this["on"+eventtype]); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
window.removeEventListener(eventtype, this["on"+eventtype]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, Grid = ReactBootstrap.Grid |
||||||
|
, Col = ReactBootstrap.Col |
||||||
|
, Row = ReactBootstrap.Row |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var ProfileMixin = require('../common/ProfileMixin.js'); |
||||||
|
|
||||||
|
module.exports = MiniProfile = React.createClass({ |
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin,ProfileMixin], |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
<ListGroupItem> |
||||||
|
<Row className="nomargin"> |
||||||
|
<Col xs={2} md={2} className="fullytight"> |
||||||
|
<a href={"#/profile/"+this.props.username}> |
||||||
|
<img className="img-responsive" src={this.state.avatar}/> |
||||||
|
</a> |
||||||
|
</Col> |
||||||
|
<Col xs={9} md={9}> |
||||||
|
<h5 className="nomargin-top"> |
||||||
|
{this.state.fullname}<small> {'@'+this.props.username}</small> |
||||||
|
</h5> |
||||||
|
<p>{this.state.bio}</p> |
||||||
|
</Col> |
||||||
|
<Col xs={1} md={1} className="fullytight text-align-right"></Col> |
||||||
|
</Row> |
||||||
|
</ListGroupItem> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,107 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, Grid = ReactBootstrap.Grid |
||||||
|
, Col = ReactBootstrap.Col |
||||||
|
, Row = ReactBootstrap.Row |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
|
||||||
|
module.exports = Post = React.createClass({ |
||||||
|
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<18*60*60) {newTimeAgo=Math.round(secondsAgo/60/60)+"h"} |
||||||
|
else if (secondsAgo<26*24*60*60) {newTimeAgo=Math.round(secondsAgo/24/60/60)+"d"} |
||||||
|
else if (secondsAgo<9*30.5*24*60*60) {newTimeAgo=Math.round(secondsAgo/30.5/24/60/60)+"mo"} |
||||||
|
else {newTimeAgo=Math.round(secondsAgo/365/24/60/60)+"y"} |
||||||
|
|
||||||
|
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){ |
||||||
|
if (avatar.getUrl()) { |
||||||
|
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 ( |
||||||
|
<ListGroupItem> |
||||||
|
<Row className="nomargin"> |
||||||
|
<Col xs={2} md={2} className="fullytight"> |
||||||
|
<a href={"#/profile/"+this.props.post.username}> |
||||||
|
<img className="img-responsive" src={this.state.avatar}/> |
||||||
|
</a> |
||||||
|
</Col> |
||||||
|
<Col xs={9} md={9}> |
||||||
|
<strong>{this.state.fullname}</strong> |
||||||
|
{post.content} |
||||||
|
</Col> |
||||||
|
<Col xs={1} md={1} className="fullytight text-align-right">{this.state.timeAgo}</Col> |
||||||
|
</Row> |
||||||
|
<Row className="nomargin"> |
||||||
|
<Col xs={6} md={6} className="fullytight"> |
||||||
|
{post.isRetwist && <small><span className="glyphicon glyphicon-retweet" aria-hidden="true"></span> <em> retwisted by {this.state.retwistingUser}</em></small> |
||||||
|
} |
||||||
|
</Col> |
||||||
|
<Col xs={6} md={6} className="fullytight text-align-right"><small><em>test</em></small></Col> |
||||||
|
</Row> |
||||||
|
|
||||||
|
</ListGroupItem> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
/* |
||||||
|
<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/> |
||||||
|
|
||||||
|
*/ |
@ -0,0 +1,34 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
var React = require('react/addons'); |
||||||
|
|
||||||
|
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; |
||||||
|
|
||||||
|
var Post = require("../common/Post.js"); |
||||||
|
|
||||||
|
module.exports = Postboard = React.createClass({ |
||||||
|
render: function() { |
||||||
|
var posts = this.props.data.map(function(post, index) { |
||||||
|
return ( |
||||||
|
<Post post={post} key={post.postid} /> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
return ( |
||||||
|
<ListGroup fill> |
||||||
|
{this.props.header} |
||||||
|
<ReactCSSTransitionGroup transitionName="item"> |
||||||
|
{posts} |
||||||
|
</ReactCSSTransitionGroup> |
||||||
|
</ListGroup> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,71 @@ |
|||||||
|
module.exports = ProfileMixin = { |
||||||
|
getInitialState: function() { |
||||||
|
|
||||||
|
var username = this.props.username; |
||||||
|
|
||||||
|
if (!username) { |
||||||
|
|
||||||
|
username = (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var state = { |
||||||
|
username: username, |
||||||
|
avatar: "img/genericPerson.png", |
||||||
|
fullname: "", |
||||||
|
bio: "", |
||||||
|
location: "", |
||||||
|
url: "" |
||||||
|
}; |
||||||
|
|
||||||
|
var profile = Twister.getUser(username).getProfile(); |
||||||
|
|
||||||
|
if (profile.inCache()) { |
||||||
|
|
||||||
|
state.fullname = profile.getField("fullname"); |
||||||
|
state.bio = profile.getField("bio"); |
||||||
|
state.location = profile.getField("location"); |
||||||
|
state.url = profile.getField("url"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
var avatar = Twister.getUser(username).getAvatar(); |
||||||
|
|
||||||
|
if (avatar.inCache()) { |
||||||
|
|
||||||
|
state.avatar = avatar.getUrl(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
updateProfile: function () { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doAvatar(function(avatar){ |
||||||
|
if (avatar.getUrl()) { |
||||||
|
thisComponent.setStateSafe({avatar: avatar.getUrl()}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doProfile(function(profile){ |
||||||
|
thisComponent.setStateSafe({ |
||||||
|
fullname: profile.getField("fullname"), |
||||||
|
bio: profile.getField("bio"), |
||||||
|
location: profile.getField("location"), |
||||||
|
url: profile.getField("url"), |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function () { |
||||||
|
|
||||||
|
this.updateProfile(); |
||||||
|
|
||||||
|
this.setInterval(this.updateProfile,this.props.pollIntervalProfile*1000); |
||||||
|
|
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,62 @@ |
|||||||
|
function isValidLifeCycleForReplaceState(instance) { |
||||||
|
// See function validateLifeCycleOnReplaceState(instance) in
|
||||||
|
// ReactCompositeComponent.js
|
||||||
|
var result = true; |
||||||
|
|
||||||
|
//result &= ReactCurrentOwner.current == null;
|
||||||
|
//result &= __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || __REACT_DEVTOOLS_GLOBAL_HOOK__._reactRuntime.CurrentOwner.current == null;
|
||||||
|
|
||||||
|
result &= instance.isMounted(); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
var safeStateChangeMixin = { |
||||||
|
/** |
||||||
|
* Calls setState with the provided parameters if it is safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as setState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} partialState Next partial state to be merged with state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
setStateSafe: function (partialState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.setState(partialState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* Calls replaceState with the provided parameters if it safe to do so. |
||||||
|
* |
||||||
|
* Safe means it will try to do the same checks as replaceState does |
||||||
|
* without throwing an exception. |
||||||
|
* See function validateLifeCycleOnReplaceState(instance) in |
||||||
|
* ReactCompositeComponent.js |
||||||
|
* |
||||||
|
* @param {object} completeState Next state. |
||||||
|
* @param {?function} callback Called after state is updated. |
||||||
|
* @return {boolean} Whether or not setState is called. |
||||||
|
* @final |
||||||
|
* @protected |
||||||
|
*/ |
||||||
|
replaceStateSafe: function(completeState, callback) { |
||||||
|
if (isValidLifeCycleForReplaceState(this)) { |
||||||
|
this.replaceState(completeState, callback); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = safeStateChangeMixin; |
@ -0,0 +1,11 @@ |
|||||||
|
module.exports = SetIntervalMixin = { |
||||||
|
componentWillMount: function() { |
||||||
|
this.intervals = []; |
||||||
|
}, |
||||||
|
setInterval: function() { |
||||||
|
this.intervals.push(setInterval.apply(null, arguments)); |
||||||
|
}, |
||||||
|
componentWillUnmount: function() { |
||||||
|
this.intervals.map(clearInterval); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,60 @@ |
|||||||
|
module.exports = StreamMixin = { |
||||||
|
|
||||||
|
addPost: function(post) { |
||||||
|
|
||||||
|
var postid = post.getUsername() + ":post" + post.getId(); |
||||||
|
|
||||||
|
if (!this.state.postIdentifiers[postid]) { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps) { |
||||||
|
|
||||||
|
previousState.postIdentifiers[postid] = true; |
||||||
|
|
||||||
|
if (post.isRetwist()){ |
||||||
|
|
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getRetwistedUser(), |
||||||
|
retwistingUser: post.getUsername(), |
||||||
|
content: post.getRetwistedContent(), |
||||||
|
id: post.getRetwistedId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: true |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
var postdata = { |
||||||
|
username: post.getUsername(), |
||||||
|
content: post.getContent(), |
||||||
|
id: post.getId(), |
||||||
|
timestamp: post.getTimestamp(), |
||||||
|
postid: postid, |
||||||
|
isRetwist: false |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.push(postdata) |
||||||
|
|
||||||
|
var compare = function (a,b) { |
||||||
|
if (a.timestamp < b.timestamp) |
||||||
|
return 1; |
||||||
|
if (a.timestamp > b.timestamp) |
||||||
|
return -1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
previousState.data.sort(compare); |
||||||
|
|
||||||
|
return {data: previousState.data, postIdentifiers: previousState.postIdentifiers }; |
||||||
|
}); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var NewPostModalButton = require("../home/NewPostModalButton.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Home = React.createClass({ |
||||||
|
|
||||||
|
mixins: [ |
||||||
|
StreamMixin, |
||||||
|
SetIntervalMixin, |
||||||
|
SafeStateChangeMixin, |
||||||
|
EventListenerMixin('scrolledtobottom'), |
||||||
|
EventListenerMixin('newpostbyuser') |
||||||
|
], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
usernames: [], |
||||||
|
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!==null) { |
||||||
|
if(post.getTimestamp()<thisComponent.state.postrange) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
//console.log("adding post",post.getUsername(),post.getId())
|
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: 2*thisComponent.props.pollInterval}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
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); |
||||||
|
//console.log("adding post",post.getUsername(),post.getId())
|
||||||
|
} |
||||||
|
} else { |
||||||
|
thisComponent.removeUser(thisUsername); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
if (this.props.activeAccount) { |
||||||
|
|
||||||
|
console.log("active account is "+this.props.activeAccount) |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var username=this.props.activeAccount; |
||||||
|
|
||||||
|
thisComponent.addUser(username); |
||||||
|
|
||||||
|
Twister.getUser(username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
thisComponent.addUser(followings[i].getUsername()); |
||||||
|
//console.log(followings[i].getUsername())
|
||||||
|
} |
||||||
|
|
||||||
|
//thisComponent.updatePosts(thisComponent.props.pollInterval);
|
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
} else {console.log("active account is null")} |
||||||
|
|
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange -= 6*60*60; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
onnewpostbyuser: function (event) { |
||||||
|
|
||||||
|
for (var i in this.state.usernames) { |
||||||
|
if(this.state.usernames[i]==event.detail.getUsername()) { |
||||||
|
this.addPost(event.detail); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
<Postboard data={this.state.data} header={ |
||||||
|
<ListGroupItem> |
||||||
|
Home |
||||||
|
<NewPostModalButton activeAccount={this.props.activeAccount}/> |
||||||
|
</ListGroupItem> |
||||||
|
}/> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,77 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, OverlayMixin = ReactBootstrap.OverlayMixin |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Modal = ReactBootstrap.Modal |
||||||
|
, Input = ReactBootstrap.Input |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
|
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
|
||||||
|
module.exports = NewPostModalButton = React.createClass({ |
||||||
|
mixins: [OverlayMixin], |
||||||
|
getInitialState: function () { |
||||||
|
return { |
||||||
|
isModalOpen: false |
||||||
|
}; |
||||||
|
}, |
||||||
|
handleToggle: function () { |
||||||
|
this.setState({ |
||||||
|
isModalOpen: !this.state.isModalOpen |
||||||
|
}); |
||||||
|
}, |
||||||
|
handleNewPost: function (e) { |
||||||
|
e.preventDefault(); |
||||||
|
//console.log(e)
|
||||||
|
var msg = JSON.parse(JSON.stringify(e.target[0].value)); |
||||||
|
if (!msg) { |
||||||
|
console.log("empty post was passed as new post") |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Twister.getAccount(this.props.activeAccount).post(msg,function(post){ |
||||||
|
|
||||||
|
var event = new CustomEvent('newpostbyuser',{detail: post}); |
||||||
|
//alert("scrolled to bottom")
|
||||||
|
window.dispatchEvent(event); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
e.target[0].value = ""; |
||||||
|
|
||||||
|
|
||||||
|
this.handleToggle(); |
||||||
|
|
||||||
|
//React.findDOMNode(this.refs.msg).value = '';
|
||||||
|
return; |
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
<Button onClick={this.handleToggle} className="link-button-gray pull-right fullytight_all" bsStyle="link"> |
||||||
|
<Glyphicon glyph='pencil' /> |
||||||
|
</Button> |
||||||
|
); |
||||||
|
}, |
||||||
|
renderOverlay: function() { |
||||||
|
|
||||||
|
if (!this.state.isModalOpen) { |
||||||
|
return <span/>; |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<Modal bsStyle='primary' title={<Glyphicon glyph='pencil'/>} onRequestHide={this.handleToggle}> |
||||||
|
<div className='modal-body'> |
||||||
|
<form onSubmit={this.handleNewPost}> |
||||||
|
<Input type='textarea' label='Text Area' placeholder='textarea'/> |
||||||
|
<Input type='submit' value='Submit button' data-dismiss="modal" /> |
||||||
|
</form> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
); |
||||||
|
|
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,84 @@ |
|||||||
|
|
||||||
|
var React = require('react/addons'); |
||||||
|
|
||||||
|
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; |
||||||
|
|
||||||
|
var MiniProfile = require("../common/MiniProfile.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Home = React.createClass({ |
||||||
|
|
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
followings: [] |
||||||
|
}; |
||||||
|
}, |
||||||
|
updateFollowings: function(outdatedLimit) { |
||||||
|
|
||||||
|
thisComponent=this; |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doFollowings(function(followings){ |
||||||
|
|
||||||
|
thisComponent.setStateSafe(function(state){ |
||||||
|
|
||||||
|
var newfollowings = []; |
||||||
|
|
||||||
|
for(var i in followings){ |
||||||
|
newfollowings.push(followings[i].getUsername()); |
||||||
|
} |
||||||
|
|
||||||
|
state.followings = newfollowings; |
||||||
|
|
||||||
|
return state; |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updateFollowings(this.props.pollInterval*2); |
||||||
|
|
||||||
|
this.setInterval(this.updateFollowings, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
|
||||||
|
var profiles = this.state.followings.map(function(username, index) { |
||||||
|
return ( |
||||||
|
<MiniProfile username={username} key={"miniprofile:"+username} pollIntervalProfile={thisComponent.props.pollIntervalProfile}/> |
||||||
|
); |
||||||
|
}); |
||||||
|
return ( |
||||||
|
<ListGroup fill> |
||||||
|
<ReactCSSTransitionGroup transitionName="item"> |
||||||
|
{profiles} |
||||||
|
</ReactCSSTransitionGroup> |
||||||
|
</ListGroup> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,51 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var MiniProfile = require("../common/MiniProfile.js"); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
module.exports = Home = React.createClass({ |
||||||
|
|
||||||
|
mixins: [StreamMixin,SetIntervalMixin,SafeStateChangeMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
data: [], |
||||||
|
postIdentifiers: {} |
||||||
|
}; |
||||||
|
}, |
||||||
|
updateMentions: function(outdatedLimit) { |
||||||
|
|
||||||
|
thisComponent=this; |
||||||
|
|
||||||
|
if (outdatedLimit===undefined) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doMentions(function(mentions){ |
||||||
|
|
||||||
|
for(var i in mentions){ |
||||||
|
thisComponent.addPost(mentions[i]); |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updateMentions(this.props.pollInterval*2); |
||||||
|
|
||||||
|
this.setInterval(this.updateMentions, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
<Postboard data={this.state.data} header=""/> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,80 @@ |
|||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, Grid = ReactBootstrap.Grid |
||||||
|
, Col = ReactBootstrap.Col |
||||||
|
, Row = ReactBootstrap.Row |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
, ButtonGroup = ReactBootstrap.ButtonGroup |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
|
||||||
|
var React = require('react'); |
||||||
|
var Router = require('react-router'); |
||||||
|
var { Route, DefaultRoute, RouteHandler, Link } = Router; |
||||||
|
|
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var ProfileMixin = require('../common/ProfileMixin.js'); |
||||||
|
|
||||||
|
module.exports = Post = React.createClass({ |
||||||
|
mixins: [SetIntervalMixin,SafeStateChangeMixin,ProfileMixin], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getHandlerKey: function () { |
||||||
|
var childDepth = 2; // assuming App is top-level route
|
||||||
|
var { router } = this.context; |
||||||
|
//console.log(router.getCurrentParams())
|
||||||
|
if ( router.getCurrentRoutes()[childDepth] ) { |
||||||
|
var key = router.getCurrentRoutes()[childDepth].name; |
||||||
|
if (key.indexOf("active")>-1) {key+="/"+this.props.activeAccount;} |
||||||
|
var id = JSON.stringify(router.getCurrentParams()); |
||||||
|
if (id) { key += id; } |
||||||
|
console.log(key); |
||||||
|
return key; |
||||||
|
} else {return "none"} |
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
|
||||||
|
var routeprefix = "#/profile/"+(this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username+"/" : "") |
||||||
|
|
||||||
|
var subroute = this.context.router.getCurrentRoutes()[2].name |
||||||
|
|
||||||
|
console.log(this.context.router.getCurrentRoutes()); |
||||||
|
|
||||||
|
return ( |
||||||
|
<ListGroup fill> |
||||||
|
<ListGroupItem> |
||||||
|
<Row className="nomargin"> |
||||||
|
<Col xs={3} md={3} className="fullytight"> |
||||||
|
<img className="img-responsive" src={this.state.avatar}/> |
||||||
|
</Col> |
||||||
|
<Col xs={8} md={8}> |
||||||
|
<h4 className="nomargin-top">{this.state.fullname}<small> {'@'+this.state.username}</small></h4> |
||||||
|
<p className="text-center">{this.state.location}</p> |
||||||
|
<p className="text-center">{this.state.bio}</p> |
||||||
|
<p className="text-center"><a href={this.state.url}>{this.state.url}</a></p> |
||||||
|
</Col> |
||||||
|
<Col xs={1} md={1} className="fullytight text-align-right"></Col> |
||||||
|
</Row> |
||||||
|
</ListGroupItem> |
||||||
|
<ListGroupItem className="fullytight_all"> |
||||||
|
<ButtonGroup justified> |
||||||
|
<Button href={routeprefix+"timeline"} bsStyle={subroute.indexOf("timeline")>-1 ? "primary" : "default"}><Glyphicon glyph="list"/></Button> |
||||||
|
<Button href={routeprefix+"followings"} bsStyle={subroute.indexOf("followings")>-1 ? "primary" : "default"}><Glyphicon glyph="eye-open"/></Button> |
||||||
|
<Button href={routeprefix+"mentions"} bsStyle={subroute.indexOf("mentions")>-1 ? "primary" : "default"}><Glyphicon glyph="comment"/></Button> |
||||||
|
</ButtonGroup> |
||||||
|
</ListGroupItem> |
||||||
|
<RouteHandler |
||||||
|
pollInterval={this.props.pollInterval} |
||||||
|
pollIntervalProfile={this.props.pollIntervalProfile} |
||||||
|
activeAccount={this.props.activeAccount} |
||||||
|
key={this.getHandlerKey()} |
||||||
|
/> |
||||||
|
</ListGroup> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,95 @@ |
|||||||
|
var React = require('react'); |
||||||
|
var Postboard = require("../common/Postboard.js"); |
||||||
|
var SetIntervalMixin = require("../common/SetIntervalMixin.js"); |
||||||
|
var StreamMixin = require("../common/StreamMixin.js"); |
||||||
|
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js'); |
||||||
|
var EventListenerMixin = require('../common/EventListenerMixin.js'); |
||||||
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap') |
||||||
|
, NavItem = ReactBootstrap.NavItem |
||||||
|
, Nav = ReactBootstrap.Nav |
||||||
|
, ListGroup = ReactBootstrap.ListGroup |
||||||
|
, ListGroupItem = ReactBootstrap.ListGroupItem |
||||||
|
, Panel = ReactBootstrap.Panel |
||||||
|
, Glyphicon = ReactBootstrap.Glyphicon |
||||||
|
, Button = ReactBootstrap.Button |
||||||
|
|
||||||
|
module.exports = Timeline = React.createClass({ |
||||||
|
|
||||||
|
mixins:[ |
||||||
|
StreamMixin, |
||||||
|
SetIntervalMixin, |
||||||
|
SafeStateChangeMixin, |
||||||
|
EventListenerMixin('scrolledtobottom'), |
||||||
|
EventListenerMixin('newpostbyuser') |
||||||
|
], |
||||||
|
contextTypes: { |
||||||
|
router: React.PropTypes.func |
||||||
|
}, |
||||||
|
getInitialState: function() { |
||||||
|
return { |
||||||
|
username: (this.context.router.getCurrentParams().username ? this.context.router.getCurrentParams().username : this.props.activeAccount), |
||||||
|
data: [], |
||||||
|
postIdentifiers: {}, |
||||||
|
postCount: 30 |
||||||
|
}; |
||||||
|
}, |
||||||
|
updatePosts: function(outdatedLimit) { |
||||||
|
|
||||||
|
if (!outdatedLimit) {outdatedLimit=this.props.pollInterval/2;} |
||||||
|
|
||||||
|
var thisComponent = this; |
||||||
|
var thisUsername = this.state.username; |
||||||
|
|
||||||
|
var count = 0; |
||||||
|
|
||||||
|
Twister.getUser(this.state.username).doLatestPostsUntil(function(post){ |
||||||
|
|
||||||
|
//console.log(count)
|
||||||
|
|
||||||
|
if (post!==null) { |
||||||
|
if(count++>=thisComponent.state.postCount) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
thisComponent.addPost(post); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
},{outdatedLimit: outdatedLimit}); |
||||||
|
|
||||||
|
}, |
||||||
|
componentDidMount: function() { |
||||||
|
|
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
this.setInterval(this.updatePosts, this.props.pollInterval*1000); |
||||||
|
|
||||||
|
console.log(this.props.pollInterval) |
||||||
|
}, |
||||||
|
onscrolledtobottom: function () { |
||||||
|
|
||||||
|
this.setStateSafe(function(previousState, currentProps){ |
||||||
|
previousState.postrange += 10; |
||||||
|
return previousState; |
||||||
|
},function(){ |
||||||
|
this.updatePosts(2*this.props.pollInterval); |
||||||
|
}); |
||||||
|
|
||||||
|
}, |
||||||
|
onnewpostbyuser: function (event) { |
||||||
|
|
||||||
|
//alert("got event")
|
||||||
|
|
||||||
|
if(this.state.username==event.post.getUsername()) { |
||||||
|
this.addPost(event.post); |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
render: function() { |
||||||
|
return ( |
||||||
|
<Postboard data={this.state.data} header=""/> |
||||||
|
); |
||||||
|
} |
||||||
|
}); |
Loading…
Reference in new issue