2015-06-14 23:53:32 -08:00
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var PostStore = require ( '../stores/post_store.jsx' ) ;
var ChannelStore = require ( '../stores/channel_store.jsx' ) ;
var UserStore = require ( '../stores/user_store.jsx' ) ;
var UserProfile = require ( './user_profile.jsx' ) ;
var AsyncClient = require ( '../utils/async_client.jsx' ) ;
var Post = require ( './post.jsx' ) ;
2015-07-11 08:32:02 -07:00
var LoadingScreen = require ( './loading_screen.jsx' ) ;
2015-06-14 23:53:32 -08:00
var SocketStore = require ( '../stores/socket_store.jsx' ) ;
var utils = require ( '../utils/utils.jsx' ) ;
var Client = require ( '../utils/client.jsx' ) ;
var AppDispatcher = require ( '../dispatcher/app_dispatcher.jsx' ) ;
var Constants = require ( '../utils/constants.jsx' ) ;
var ActionTypes = Constants . ActionTypes ;
function getStateFromStores ( ) {
var channel = ChannelStore . getCurrent ( ) ;
if ( channel == null ) channel = { } ;
return {
post _list : PostStore . getCurrentPosts ( ) ,
2015-07-16 15:25:28 -04:00
channel : channel ,
activeThreadRootId : ""
2015-06-14 23:53:32 -08:00
} ;
}
module . exports = React . createClass ( {
2015-07-11 08:32:02 -07:00
displayName : "PostList" ,
2015-06-14 23:53:32 -08:00
scrollPosition : 0 ,
preventScrollTrigger : false ,
gotMorePosts : false ,
oldScrollHeight : 0 ,
oldZoom : 0 ,
scrolledToNew : false ,
componentDidMount : function ( ) {
var user = UserStore . getCurrentUser ( ) ;
if ( user . props && user . props . theme ) {
utils . changeCss ( 'a.theme' , 'color:' + user . props . theme + '; fill:' + user . props . theme + '!important;' ) ;
utils . changeCss ( 'div.theme' , 'background-color:' + user . props . theme + ';' ) ;
utils . changeCss ( '.btn.btn-primary' , 'background: ' + user . props . theme + ';' ) ;
2015-07-11 08:32:02 -07:00
utils . changeCss ( '.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus' , 'background: ' + utils . changeColor ( user . props . theme , - 10 ) + ';' ) ;
2015-06-14 23:53:32 -08:00
utils . changeCss ( '.modal .modal-header' , 'background: ' + user . props . theme + ';' ) ;
utils . changeCss ( '.mention' , 'background: ' + user . props . theme + ';' ) ;
utils . changeCss ( '.mention-link' , 'color: ' + user . props . theme + ';' ) ;
utils . changeCss ( '@media(max-width: 768px){.search-bar__container' , 'background: ' + user . props . theme + ';}' ) ;
}
PostStore . addChangeListener ( this . _onChange ) ;
ChannelStore . addChangeListener ( this . _onChange ) ;
2015-07-20 08:58:18 -04:00
UserStore . addStatusesChangeListener ( this . _onTimeChange ) ;
2015-06-14 23:53:32 -08:00
SocketStore . addChangeListener ( this . _onSocketChange ) ;
2015-07-16 15:25:28 -04:00
PostStore . addActiveThreadChangedListener ( this . _onActiveThreadChanged ) ;
2015-06-14 23:53:32 -08:00
$ ( ".post-list-holder-by-time" ) . perfectScrollbar ( ) ;
this . resize ( ) ;
var post _holder = $ ( ".post-list-holder-by-time" ) [ 0 ] ;
this . scrollPosition = $ ( post _holder ) . scrollTop ( ) + $ ( post _holder ) . innerHeight ( ) ;
this . oldScrollHeight = post _holder . scrollHeight ;
this . oldZoom = ( window . outerWidth - 8 ) / window . innerWidth ;
var self = this ;
$ ( window ) . resize ( function ( ) {
$ ( post _holder ) . perfectScrollbar ( 'update' ) ;
// this only kind of works, detecting zoom in browsers is a nightmare
var newZoom = ( window . outerWidth - 8 ) / window . innerWidth ;
if ( self . scrollPosition >= post _holder . scrollHeight || ( self . oldScrollHeight != post _holder . scrollHeight && self . scrollPosition >= self . oldScrollHeight ) || self . oldZoom != newZoom ) self . resize ( ) ;
self . oldZoom = newZoom ;
if ( $ ( '#create_post' ) . length > 0 ) {
var height = $ ( window ) . height ( ) - $ ( '#create_post' ) . height ( ) - $ ( '#error_bar' ) . outerHeight ( ) - 50 ;
$ ( ".post-list-holder-by-time" ) . css ( "height" , height + "px" ) ;
}
} ) ;
$ ( post _holder ) . scroll ( function ( e ) {
if ( ! self . preventScrollTrigger ) {
self . scrollPosition = $ ( post _holder ) . scrollTop ( ) + $ ( post _holder ) . innerHeight ( ) ;
}
self . preventScrollTrigger = false ;
} ) ;
$ ( 'body' ) . on ( 'click.userpopover' , function ( e ) {
if ( $ ( e . target ) . attr ( 'data-toggle' ) !== 'popover'
&& $ ( e . target ) . parents ( '.popover.in' ) . length === 0 ) {
$ ( '.user-popover' ) . popover ( 'hide' ) ;
}
} ) ;
$ ( '.post-list__content div .post' ) . removeClass ( 'post--last' ) ;
$ ( '.post-list__content div:last-child .post' ) . addClass ( 'post--last' ) ;
2015-07-09 21:11:47 +05:00
$ ( 'body' ) . on ( 'mouseenter mouseleave' , '.post' , function ( ev ) {
2015-06-14 23:53:32 -08:00
if ( ev . type === 'mouseenter' ) {
2015-06-28 20:53:26 +05:00
$ ( this ) . parent ( 'div' ) . prev ( '.date-separator, .new-separator' ) . addClass ( 'hovered--after' ) ;
$ ( this ) . parent ( 'div' ) . next ( '.date-separator, .new-separator' ) . addClass ( 'hovered--before' ) ;
2015-06-14 23:53:32 -08:00
}
else {
2015-06-28 20:53:26 +05:00
$ ( this ) . parent ( 'div' ) . prev ( '.date-separator, .new-separator' ) . removeClass ( 'hovered--after' ) ;
$ ( this ) . parent ( 'div' ) . next ( '.date-separator, .new-separator' ) . removeClass ( 'hovered--before' ) ;
2015-06-14 23:53:32 -08:00
}
} ) ;
2015-07-09 21:11:47 +05:00
$ ( 'body' ) . on ( 'mouseenter mouseleave' , '.post.post--comment.same--root' , function ( ev ) {
if ( ev . type === 'mouseenter' ) {
$ ( this ) . parent ( 'div' ) . prev ( '.date-separator, .new-separator' ) . addClass ( 'hovered--comment' ) ;
$ ( this ) . parent ( 'div' ) . next ( '.date-separator, .new-separator' ) . addClass ( 'hovered--comment' ) ;
}
else {
$ ( this ) . parent ( 'div' ) . prev ( '.date-separator, .new-separator' ) . removeClass ( 'hovered--comment' ) ;
$ ( this ) . parent ( 'div' ) . next ( '.date-separator, .new-separator' ) . removeClass ( 'hovered--comment' ) ;
}
} ) ;
2015-06-14 23:53:32 -08:00
} ,
componentDidUpdate : function ( ) {
this . resize ( ) ;
var post _holder = $ ( ".post-list-holder-by-time" ) [ 0 ] ;
this . scrollPosition = $ ( post _holder ) . scrollTop ( ) + $ ( post _holder ) . innerHeight ( ) ;
this . oldScrollHeight = post _holder . scrollHeight ;
$ ( '.post-list__content div .post' ) . removeClass ( 'post--last' ) ;
$ ( '.post-list__content div:last-child .post' ) . addClass ( 'post--last' ) ;
} ,
componentWillUnmount : function ( ) {
PostStore . removeChangeListener ( this . _onChange ) ;
ChannelStore . removeChangeListener ( this . _onChange ) ;
2015-07-20 08:58:18 -04:00
UserStore . removeStatusesChangeListener ( this . _onTimeChange ) ;
2015-06-14 23:53:32 -08:00
SocketStore . removeChangeListener ( this . _onSocketChange ) ;
2015-07-16 15:25:28 -04:00
PostStore . removeActiveThreadChangedListener ( this . _onActiveThreadChanged ) ;
2015-06-14 23:53:32 -08:00
$ ( 'body' ) . off ( 'click.userpopover' ) ;
} ,
resize : function ( ) {
2015-07-11 08:32:02 -07:00
var post _holder = $ ( ".post-list-holder-by-time" ) [ 0 ] ;
this . preventScrollTrigger = true ;
2015-06-14 23:53:32 -08:00
if ( this . gotMorePosts ) {
this . gotMorePosts = false ;
$ ( post _holder ) . scrollTop ( $ ( post _holder ) . scrollTop ( ) + ( post _holder . scrollHeight - this . oldScrollHeight ) ) ;
} else {
if ( $ ( "#new_message" ) [ 0 ] && ! this . scrolledToNew ) {
$ ( post _holder ) . scrollTop ( $ ( post _holder ) . scrollTop ( ) + $ ( "#new_message" ) . offset ( ) . top - 63 ) ;
this . scrolledToNew = true ;
} else {
$ ( post _holder ) . scrollTop ( post _holder . scrollHeight ) ;
}
}
2015-07-11 08:32:02 -07:00
$ ( post _holder ) . perfectScrollbar ( 'update' ) ;
2015-06-14 23:53:32 -08:00
} ,
_onChange : function ( ) {
var newState = getStateFromStores ( ) ;
if ( ! utils . areStatesEqual ( newState , this . state ) ) {
if ( this . state . post _list && this . state . post _list . order ) {
if ( this . state . channel . id === newState . channel . id && this . state . post _list . order . length != newState . post _list . order . length && newState . post _list . order . length > Constants . POST _CHUNK _SIZE ) {
this . gotMorePosts = true ;
}
}
if ( this . state . channel . id !== newState . channel . id ) {
this . scrolledToNew = false ;
}
this . setState ( newState ) ;
2015-07-14 08:07:01 -04:00
}
2015-06-14 23:53:32 -08:00
} ,
_onSocketChange : function ( msg ) {
if ( msg . action == "posted" ) {
var post = JSON . parse ( msg . props . post ) ;
var post _list = PostStore . getPosts ( msg . channel _id ) ;
if ( ! post _list ) return ;
post _list . posts [ post . id ] = post ;
if ( post _list . order . indexOf ( post . id ) === - 1 ) {
post _list . order . unshift ( post . id ) ;
}
if ( this . state . channel . id === msg . channel _id ) {
this . setState ( { post _list : post _list } ) ;
} ;
PostStore . storePosts ( post . channel _id , post _list ) ;
} else if ( msg . action == "post_edited" ) {
if ( this . state . channel . id == msg . channel _id ) {
var post _list = this . state . post _list ;
if ( ! ( msg . props . post _id in post _list . posts ) ) return ;
var post = post _list . posts [ msg . props . post _id ] ;
post . message = msg . props . message ;
post _list . posts [ post . id ] = post ;
this . setState ( { post _list : post _list } ) ;
PostStore . storePosts ( msg . channel _id , post _list ) ;
} else {
AsyncClient . getPosts ( true , msg . channel _id ) ;
}
} else if ( msg . action == "post_deleted" ) {
var activeRoot = $ ( document . activeElement ) . closest ( '.comment-create-body' ) [ 0 ] ;
var activeRootPostId = activeRoot && activeRoot . id . length > 0 ? activeRoot . id : "" ;
if ( this . state . channel . id == msg . channel _id ) {
var post _list = this . state . post _list ;
if ( ! ( msg . props . post _id in this . state . post _list . posts ) ) return ;
delete post _list . posts [ msg . props . post _id ] ;
var index = post _list . order . indexOf ( msg . props . post _id ) ;
if ( index > - 1 ) post _list . order . splice ( index , 1 ) ;
this . setState ( { post _list : post _list } ) ;
PostStore . storePosts ( msg . channel _id , post _list ) ;
} else {
AsyncClient . getPosts ( true , msg . channel _id ) ;
}
if ( activeRootPostId === msg . props . post _id && UserStore . getCurrentId ( ) != msg . user _id ) {
$ ( '#post_deleted' ) . modal ( 'show' ) ;
}
2015-07-20 08:58:18 -04:00
} else if ( msg . action == "new_user" ) {
2015-06-14 23:53:32 -08:00
AsyncClient . getProfiles ( ) ;
}
} ,
2015-07-20 08:58:18 -04:00
_onTimeChange : function ( ) {
2015-07-22 09:51:42 -04:00
if ( ! this . state . post _list ) return ;
2015-07-20 08:58:18 -04:00
for ( var id in this . state . post _list . posts ) {
if ( ! this . refs [ id ] ) continue ;
this . refs [ id ] . forceUpdateInfo ( ) ;
}
} ,
2015-07-16 15:25:28 -04:00
_onActiveThreadChanged : function ( rootId , parentId ) {
this . setState ( { "activeThreadRootId" : rootId } ) ;
} ,
2015-06-14 23:53:32 -08:00
getMorePosts : function ( e ) {
e . preventDefault ( ) ;
if ( ! this . state . post _list ) return ;
var posts = this . state . post _list . posts ;
var order = this . state . post _list . order ;
var channel _id = this . state . channel . id ;
$ ( this . refs . loadmore . getDOMNode ( ) ) . text ( "Retrieving more messages..." ) ;
var self = this ;
var currentPos = $ ( ".post-list" ) . scrollTop ;
Client . getPosts (
channel _id ,
order . length ,
Constants . POST _CHUNK _SIZE ,
function ( data ) {
$ ( self . refs . loadmore . getDOMNode ( ) ) . text ( "Load more messages" ) ;
if ( ! data ) return ;
if ( data . order . length === 0 ) return ;
var post _list = { }
post _list . posts = $ . extend ( posts , data . posts ) ;
post _list . order = order . concat ( data . order ) ;
AppDispatcher . handleServerAction ( {
type : ActionTypes . RECIEVED _POSTS ,
id : channel _id ,
post _list : post _list
} ) ;
Client . getProfiles ( ) ;
$ ( ".post-list" ) . scrollTop ( currentPos ) ;
} ,
function ( err ) {
$ ( self . refs . loadmore . getDOMNode ( ) ) . text ( "Load more messages" ) ;
2015-07-13 04:23:24 -07:00
AsyncClient . dispatchError ( err , "getPosts" ) ;
2015-06-14 23:53:32 -08:00
}
) ;
} ,
getInitialState : function ( ) {
return getStateFromStores ( ) ;
} ,
render : function ( ) {
var order = [ ] ;
2015-07-01 16:51:26 -07:00
var posts ;
2015-06-14 23:53:32 -08:00
var last _viewed = Number . MAX _VALUE ;
if ( ChannelStore . getCurrentMember ( ) != null )
last _viewed = ChannelStore . getCurrentMember ( ) . last _viewed _at ;
if ( this . state . post _list != null ) {
posts = this . state . post _list . posts ;
order = this . state . post _list . order ;
}
var rendered _last _viewed = false ;
var user _id = "" ;
if ( UserStore . getCurrentId ( ) ) {
user _id = UserStore . getCurrentId ( ) ;
} else {
return < div / > ;
}
var channel = this . state . channel ;
var more _messages = < p className = "beginning-messages-text" > Beginning of Channel < / p > ;
if ( channel != null ) {
if ( order . length > 0 && order . length % Constants . POST _CHUNK _SIZE === 0 ) {
more _messages = < a ref = "loadmore" className = "more-messages-text theme" href = "#" onClick = { this . getMorePosts } > Load more messages < / a > ;
} else if ( channel . type === 'D' ) {
2015-07-01 12:41:36 -07:00
var teammate = utils . getDirectTeammate ( channel . id )
2015-06-14 23:53:32 -08:00
if ( teammate ) {
2015-07-09 15:49:23 -04:00
var teammate _name = teammate . nickname . length > 0 ? teammate . nickname : teammate . username ;
2015-06-14 23:53:32 -08:00
more _messages = (
< div className = "channel-intro" >
< div className = "post-profile-img__container channel-intro-img" >
2015-07-09 16:37:03 -07:00
< img className = "post-profile-img" src = { "/api/v1/users/" + teammate . id + "/image?time=" + teammate . update _at } height = "50" width = "50" / >
2015-06-14 23:53:32 -08:00
< / div >
< div className = "channel-intro-profile" >
< strong > < UserProfile userId = { teammate . id } / > < / strong >
< / div >
2015-07-11 08:32:02 -07:00
< p className = "channel-intro-text" >
{ "This is the start of your private message history with " + teammate _name + "." } < br / >
{ "Private messages and files shared here are not shown to people outside this area." }
< / p >
2015-06-14 23:53:32 -08:00
< / div >
) ;
} else {
more _messages = (
< div className = "channel-intro" >
2015-07-01 09:48:39 -07:00
< p className = "channel-intro-text" > { "This is the start of your private message history with this " + strings . Team + "mate. Private messages and files shared here are not shown to people outside this area." } < / p >
2015-06-14 23:53:32 -08:00
< / div >
) ;
}
} else if ( channel . type === 'P' || channel . type === 'O' ) {
var ui _name = channel . display _name
var members = ChannelStore . getCurrentExtraInfo ( ) . members ;
var creator _name = "" ;
2015-06-29 10:31:56 -04:00
var userStyle = { color : UserStore . getCurrentUser ( ) . props . theme }
2015-06-14 23:53:32 -08:00
for ( var i = 0 ; i < members . length ; i ++ ) {
if ( members [ i ] . roles . indexOf ( 'admin' ) > - 1 ) {
creator _name = members [ i ] . username ;
break ;
}
}
2015-07-15 09:03:28 -07:00
if ( ChannelStore . isDefault ( channel ) ) {
2015-06-14 23:53:32 -08:00
more _messages = (
< div className = "channel-intro" >
2015-07-22 22:25:27 +05:00
< h4 className = "channel-intro__title" > Beginning of { ui _name } < / h4 >
< p className = "channel-intro__content" >
2015-06-14 23:53:32 -08:00
Welcome to { ui _name } !
< br / > < br / >
{ "This is the first channel " + strings . Team + "mates see when they" }
< br / >
sign up - use it for posting updates everyone needs to know .
< br / > < br / >
To create a new channel or join an existing one , go to
< br / >
the Left Hand Sidebar under “ Channels ” and click “ More … ” .
< br / >
< / p >
< / div >
) ;
2015-06-29 10:31:56 -04:00
} else if ( channel . name === Constants . OFFTOPIC _CHANNEL ) {
more _messages = (
< div className = "channel-intro" >
2015-07-22 22:25:27 +05:00
< h4 className = "channel-intro__title" > Beginning of { ui _name } < / h4 >
< p className = "channel-intro__content" >
2015-06-29 10:31:56 -04:00
{ "This is the start of " + ui _name + ", a channel for conversations you’ d prefer out of more focused channels." }
< br / >
< / p >
2015-07-22 22:25:27 +05:00
< a className = "intro-links" href = "#" style = { userStyle } data - toggle = "modal" data - target = "#edit_channel" data - desc = { channel . description } data - title = { ui _name } data - channelid = { channel . id } > < i className = "fa fa-pencil" > < / i > Set a description < / a >
2015-06-29 10:31:56 -04:00
< / div >
) ;
2015-06-14 23:53:32 -08:00
} else {
var ui _type = channel . type === 'P' ? "private group" : "channel" ;
more _messages = (
< div className = "channel-intro" >
2015-07-22 22:25:27 +05:00
< h4 className = "channel-intro__title" > Beginning of { ui _name } < / h4 >
< p className = "channel-intro__content" >
2015-06-28 20:53:26 +05:00
{ creator _name != "" ? "This is the start of the " + ui _name + " " + ui _type + ", created by " + creator _name + " on " + utils . displayDate ( channel . create _at ) + "."
2015-06-14 23:53:32 -08:00
: "This is the start of the " + ui _name + " " + ui _type + ", created on " + utils . displayDate ( channel . create _at ) + "." }
{ channel . type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
< br / >
< / p >
2015-07-22 22:25:27 +05:00
< a className = "intro-links" href = "#" style = { userStyle } data - toggle = "modal" data - target = "#edit_channel" data - desc = { channel . description } data - title = { channel . display _name } data - channelid = { channel . id } > < i className = "fa fa-pencil" > < / i > Set a description < / a >
< a className = "intro-links" href = "#" style = { userStyle } data - toggle = "modal" data - target = "#channel_invite" > < i className = "fa fa-user-plus" > < / i > Invite others to this { ui _type } < / a >
2015-06-14 23:53:32 -08:00
< / div >
) ;
}
}
}
var postCtls = [ ] ;
2015-07-11 08:32:02 -07:00
if ( posts ) {
2015-07-02 16:38:34 -07:00
var previousPostDay = posts [ order [ order . length - 1 ] ] ? utils . getDateForUnixTicks ( posts [ order [ order . length - 1 ] ] . create _at ) : new Date ( ) ;
2015-07-11 08:32:02 -07:00
var currentPostDay ;
2015-06-14 23:53:32 -08:00
2015-07-02 16:38:34 -07:00
for ( var i = order . length - 1 ; i >= 0 ; i -- ) {
var post = posts [ order [ i ] ] ;
2015-07-11 08:32:02 -07:00
var parentPost = post . parent _id ? posts [ post . parent _id ] : null ;
2015-06-14 23:53:32 -08:00
2015-07-11 08:32:02 -07:00
var sameUser = '' ;
var sameRoot = false ;
var hideProfilePic = false ;
var prevPost = ( i < order . length - 1 ) ? posts [ order [ i + 1 ] ] : null ;
2015-06-14 23:53:32 -08:00
2015-07-11 08:32:02 -07:00
if ( prevPost ) {
sameUser = ( prevPost . user _id === post . user _id ) && ( post . create _at - prevPost . create _at <= 1000 * 60 * 5 ) ? "same--user" : "" ;
sameRoot = utils . isComment ( post ) && ( prevPost . id === post . root _id || prevPost . root _id === post . root _id ) ;
2015-06-14 23:53:32 -08:00
2015-07-11 08:32:02 -07:00
// we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post
hideProfilePic = ( prevPost . user _id === post . user _id ) && ! utils . isComment ( prevPost ) && ! utils . isComment ( post ) ;
}
2015-06-14 23:53:32 -08:00
2015-07-02 16:38:34 -07:00
// check if it's the last comment in a consecutive string of comments on the same post
2015-07-11 08:32:02 -07:00
// it is the last comment if it is last post in the channel or the next post has a different root post
var isLastComment = utils . isComment ( post ) && ( i === 0 || posts [ order [ i - 1 ] ] . root _id != post . root _id ) ;
2015-06-14 23:53:32 -08:00
2015-07-16 15:25:28 -04:00
// check if this is part of the thread that we're currently replying to
var isActiveThread = this . state . activeThreadRootId && ( post . id === this . state . activeThreadRootId || post . root _id === this . state . activeThreadRootId ) ;
var postCtl = (
< Post ref = { post . id } sameUser = { sameUser } sameRoot = { sameRoot } post = { post } parentPost = { parentPost } key = { post . id }
posts = { posts } hideProfilePic = { hideProfilePic } isLastComment = { isLastComment } isActiveThread = { isActiveThread }
/ >
) ;
2015-06-14 23:53:32 -08:00
2015-07-02 16:38:34 -07:00
currentPostDay = utils . getDateForUnixTicks ( post . create _at ) ;
2015-07-11 08:32:02 -07:00
if ( currentPostDay . toDateString ( ) != previousPostDay . toDateString ( ) ) {
2015-07-02 16:38:34 -07:00
postCtls . push (
< div className = "date-separator" >
< hr className = "separator__hr" / >
< div className = "separator__text" > { currentPostDay . toDateString ( ) } < / div >
< / div >
) ;
}
if ( post . create _at > last _viewed && ! rendered _last _viewed ) {
rendered _last _viewed = true ;
postCtls . push (
< div className = "new-separator" >
< hr id = "new_message" className = "separator__hr" / >
< div className = "separator__text" > New Messages < / div >
< / div >
) ;
}
postCtls . push ( postCtl ) ;
2015-07-11 08:32:02 -07:00
previousPostDay = currentPostDay ;
2015-06-14 23:53:32 -08:00
}
2015-07-11 08:32:02 -07:00
} else {
postCtls . push ( < LoadingScreen position = "absolute" / > ) ;
2015-07-01 16:51:26 -07:00
}
2015-06-14 23:53:32 -08:00
return (
< div ref = "postlist" className = "post-list-holder-by-time" >
< div className = "post-list__table" >
< div className = "post-list__content" >
{ more _messages }
{ postCtls }
< / div >
< / div >
< / div >
) ;
}
} ) ;