Implement virtual scrolling
parent
4ea0bdcb85
commit
31da61a9ac
|
@ -34,17 +34,24 @@ var MessageInList = React.createClass({
|
|||
// TODO: expand/collapse days
|
||||
var MessageList = React.createClass({
|
||||
mixins: [ ListWithSelection ],
|
||||
_preloadSize: 20,
|
||||
_pageSize: 50,
|
||||
getInitialState: function()
|
||||
{
|
||||
return { firstDayTop: 0, firstDay: this.props.groups && this.props.groups[0] && this.props.groups[0].name || null, groups: this.props.groups||[] /*FIXME*/ };
|
||||
return {
|
||||
firstDayTop: 0,
|
||||
firstDay: this.props.groups && this.props.groups[0] && this.props.groups[0].name || null
|
||||
};
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps)
|
||||
{
|
||||
this.setFirstDayFromProps(nextProps);
|
||||
},
|
||||
// Main virtual scroll detector method
|
||||
setFirstDayFromProps: function(props)
|
||||
{
|
||||
var groups = props.groups;
|
||||
var messages = props.messages;
|
||||
if (!groups || !groups.length)
|
||||
return;
|
||||
var scrollTop = this.refs.scroll.scrollTop, scrollSize = this.refs.scroll.offsetHeight - this.getScrollPaddingTop();
|
||||
|
@ -76,6 +83,11 @@ var MessageList = React.createClass({
|
|||
if (firstVisibleGrp !== undefined && lastVisibleGrp !== undefined)
|
||||
break;
|
||||
}
|
||||
if (firstVisibleGrp === undefined || firstVisibleGrp >= groups.length)
|
||||
{
|
||||
this.refs.scroll.scrollTop = 0;
|
||||
return;
|
||||
}
|
||||
if (lastVisibleGrp === undefined)
|
||||
{
|
||||
lastVisibleGrp = groups.length-1;
|
||||
|
@ -89,6 +101,23 @@ var MessageList = React.createClass({
|
|||
lastGrp: lastVisibleGrp,
|
||||
lastMsg: lastVisible
|
||||
});
|
||||
var loadFirst = groups[firstVisibleGrp].start+firstVisible-this._preloadSize;
|
||||
var loadLast = groups[lastVisibleGrp].start+lastVisible+1+this._preloadSize;
|
||||
var total = groups[groups.length-1].messageCount+groups[groups.length-1].start;
|
||||
loadLast = Math.floor((loadLast + this._pageSize - 1) / this._pageSize) * this._pageSize;
|
||||
loadLast = loadLast < total ? loadLast : total;
|
||||
loadFirst = loadFirst < 0 ? 0 : loadFirst;
|
||||
loadFirst = loadFirst - (loadFirst % this._pageSize);
|
||||
var loadFirstEnd;
|
||||
for (loadFirstEnd = loadFirst; loadFirstEnd < loadLast && messages[loadFirstEnd] === undefined; loadFirstEnd++)
|
||||
messages[loadFirstEnd] = null;
|
||||
var loadLastStart;
|
||||
for (loadLastStart = loadLast; loadLastStart > loadFirst && messages[loadLastStart-1] === undefined; loadLastStart--)
|
||||
messages[loadLastStart-1] = null;
|
||||
if (loadFirstEnd > loadFirst)
|
||||
Store.loadMessages(loadFirst, loadFirstEnd-loadFirst);
|
||||
if (loadFirstEnd < loadLastStart && loadLastStart < loadLast)
|
||||
Store.loadMessages(loadLastStart, loadLast-loadLastStart);
|
||||
},
|
||||
changeFirstDay: function(ev)
|
||||
{
|
||||
|
@ -108,16 +137,16 @@ var MessageList = React.createClass({
|
|||
total += (i > 0 ? 1 : 0)+self.props.groups[i].messageCount;
|
||||
if (index < total)
|
||||
{
|
||||
idx = index-p-(i > 0 ? 1 : 0);
|
||||
msg = self.props.groups[i].messages[idx];
|
||||
if (!msg.body_text && !msg.body_html)
|
||||
idx = self.props.groups[i].start+index-p-(i > 0 ? 1 : 0);
|
||||
msg = self.props.messages[idx];
|
||||
if (msg && !msg.body_text && !msg.body_html)
|
||||
{
|
||||
Store.loadMessage(msg.id, function(newMsg)
|
||||
{
|
||||
Store.set('msg', newMsg);
|
||||
if (self.props.groups[i] && self.props.groups[i].messages[idx] == msg)
|
||||
if (self.props.messages[idx] == msg)
|
||||
{
|
||||
self.props.groups[i].messages[idx] = newMsg;
|
||||
self.props.messages[idx] = newMsg;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -163,7 +192,7 @@ var MessageList = React.createClass({
|
|||
},
|
||||
getMessages: function(grp, start, end)
|
||||
{
|
||||
var a = grp.messages.slice(start, end);
|
||||
var a = this.props.messages.slice(grp.start+start, grp.start+end);
|
||||
for (var i = 0; i < end-start; i++)
|
||||
if (!a[i])
|
||||
a[i] = null;
|
||||
|
@ -190,13 +219,16 @@ var MessageList = React.createClass({
|
|||
<div className="clear"></div>
|
||||
</div>
|
||||
<div ref="scroll" className="listview" tabIndex="1" onScroll={self.changeFirstDay} onKeyDown={self.onListKeyDown}>
|
||||
<div ref="title" className="day first-day" style={{ top: self.state.firstDayTop, display: self.state.firstDay ? '' : 'none' }}>{self.state.firstDay||''}</div>
|
||||
<div ref="title" className="day first-day"
|
||||
style={{ top: self.state.firstDayTop, display: self.state.firstDay ? '' : 'none' }}>
|
||||
{(self.state.firstDay||'').toUpperCase()}
|
||||
</div>
|
||||
{(self.props.groups||[]).map(function(grp, i) {
|
||||
if (i > 0)
|
||||
total++;
|
||||
var start = total+(self.state.firstGrp == i ? self.state.firstMsg : 0);
|
||||
var r = [
|
||||
i > 0 ? <div className="day" data-i={total-1}>{grp.name}</div> : null,
|
||||
i > 0 ? <div className="day" data-i={total-1}>{grp.name.toUpperCase()}</div> : null,
|
||||
<div className="day-list">
|
||||
{(self.state.firstGrp > i || self.state.lastGrp < i
|
||||
? <div className="placeholder" style={{ height: itemH*grp.messageCount }}></div>
|
||||
|
@ -238,6 +270,7 @@ module.exports = StoreListener(MessageList, function(data)
|
|||
return {
|
||||
threads: data.threads,
|
||||
layout: data.layout,
|
||||
groups: data.listGroups
|
||||
groups: data.listGroups,
|
||||
messages: data.messages
|
||||
};
|
||||
});
|
||||
|
|
37
Store.js
37
Store.js
|
@ -1,6 +1,8 @@
|
|||
const superagent = require('superagent');
|
||||
const socket_io = require('socket.io-client');
|
||||
|
||||
const _ = require('./I18n.js');
|
||||
const Util = require('./Util.js');
|
||||
|
||||
var Store = module.exports = {
|
||||
data: {
|
||||
|
@ -9,6 +11,7 @@ var Store = module.exports = {
|
|||
msg: null,
|
||||
threads: false,
|
||||
accounts: [],
|
||||
listGroups: [],
|
||||
messages: []
|
||||
},
|
||||
|
||||
|
@ -136,14 +139,34 @@ var Store = module.exports = {
|
|||
|
||||
loadFolder: function(folderId)
|
||||
{
|
||||
superagent.get('backend/messages').query({ folderId: folderId, limit: 50, offset: 0 }).end(function(err, res)
|
||||
superagent.get('backend/groups').query({ folderId: folderId }).end(function(err, res)
|
||||
{
|
||||
var msgs = res.body.messages;
|
||||
Store.set('listGroups', [ {
|
||||
name: 'TODAY',
|
||||
messageCount: msgs.length,
|
||||
messages: msgs
|
||||
} ]);
|
||||
var groups = res.body.groups.map(g => { return { name: Util.getGroupName(g.name), messageCount: g.count-0, start: 0 } });
|
||||
var start = 0;
|
||||
for (var i = 0; i < groups.length; i++)
|
||||
{
|
||||
groups[i].start = start;
|
||||
start += groups[i].messageCount;
|
||||
}
|
||||
Store.setAll({
|
||||
folderId: folderId,
|
||||
listGroups: groups,
|
||||
messages: []
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
loadMessages: function(start, count)
|
||||
{
|
||||
console.log('load '+start+'..'+count);
|
||||
superagent.get('backend/messages').query({ folderId: Store.get('folderId'), limit: count, offset: start }).end(function(err, res)
|
||||
{
|
||||
var msgs = Store.get('messages').slice(0);
|
||||
var par = res.body.messages;
|
||||
par.unshift(par.length);
|
||||
par.unshift(start);
|
||||
msgs.splice.apply(msgs, par);
|
||||
Store.set('messages', msgs);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
21
Util.js
21
Util.js
|
@ -35,3 +35,24 @@ module.exports.formatDate = function(dt)
|
|||
var m = dt.getMinutes();
|
||||
return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m;
|
||||
}
|
||||
|
||||
module.exports.getGroupName = function(k)
|
||||
{
|
||||
if (k == 't')
|
||||
{
|
||||
return 'Today';
|
||||
}
|
||||
else if (k[0] == 'd')
|
||||
{
|
||||
return WeekDays[k.substr(1)%7];
|
||||
}
|
||||
else if (k == 'pw')
|
||||
{
|
||||
return 'Last Week';
|
||||
}
|
||||
else if (k[0] == 'm')
|
||||
{
|
||||
return Months[k.substr(1)-1];
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue