237 lines
10 KiB
JavaScript
237 lines
10 KiB
JavaScript
const React = require('react');
|
|
const DropDownButton = require('./DropDownButton.js');
|
|
const ListWithSelection = require('./ListWithSelection.js');
|
|
const Store = require('./Store.js');
|
|
const StoreListener = require('./StoreListener.js');
|
|
const Util = require('./Util.js');
|
|
|
|
var MessageInList = React.createClass({
|
|
msgClasses: [ 'unread', 'unseen', 'replied', 'pinned', 'sent', 'unloaded' ],
|
|
render: function()
|
|
{
|
|
var msg = this.props.msg;
|
|
return <div data-i={this.props.i} className={'message'+(this.msgClasses.map(c => (msg[c] ? ' '+c : '')).join(''))+
|
|
(this.props.selected ? ' selected' : '')+(msg.thread && this.props.threads ? ' thread0' : '')} onMouseDown={this.props.onClick}>
|
|
<div className="icon" style={{ width: (20+10*(msg.level||0)), backgroundPosition: (10*(msg.level||0))+'px 7px' }}></div>
|
|
<div className="subject" style={{ paddingLeft: (20+10*(msg.level||0)) }}>{msg.subject}</div>
|
|
{msg.thread && this.props.threads ? <div className={'expand'+(msg.collapsed ? '' : ' collapse')}></div> : null}
|
|
<div className="bullet"></div>
|
|
<div className="from" style={{ left: (21+10*(msg.level||0)) }}>{(msg.sent || msg.outgoing ? 'To '+(msg.to_name||msg.to_email) : (msg.from_name||msg.from_email))}</div>
|
|
<div className="size">{Util.formatBytes(msg.size||0)}</div>
|
|
<div className="attach" style={msg.attach ? null : { display: 'none' }}></div>
|
|
<div className="time">{Util.formatDate(msg.time)}</div>
|
|
</div>
|
|
}
|
|
});
|
|
|
|
// TODO: expand/collapse days
|
|
var MessageList = React.createClass({
|
|
mixins: [ ListWithSelection ],
|
|
getInitialState: function()
|
|
{
|
|
return { firstDayTop: 0, firstDay: this.props.groups && this.props.groups[0] && this.props.groups[0].name || '', groups: this.props.groups||[] /*FIXME*/ };
|
|
},
|
|
componentWillReceiveProps: function(nextProps)
|
|
{
|
|
this.setFirstDayFromProps(nextProps);
|
|
},
|
|
setFirstDayFromProps: function(props)
|
|
{
|
|
var groups = props.groups;
|
|
if (!groups.length)
|
|
return;
|
|
var scrollTop = this.refs.scroll.scrollTop, scrollSize = this.refs.scroll.offsetHeight - this.getScrollPaddingTop();
|
|
var top = 0, p, firstVisibleGrp, firstVisible, lastVisibleGrp, lastVisible;
|
|
var itemH = (this.props.layout == 'message-on-right' ? 60 : 30);
|
|
var i;
|
|
for (i = 0; i < groups.length; i++)
|
|
{
|
|
p = top;
|
|
top += (i > 0 ? 30 : 0) + itemH*groups[i].messageCount;
|
|
if (firstVisibleGrp === undefined && scrollTop < top)
|
|
{
|
|
firstVisibleGrp = i;
|
|
if (i > 0 && scrollTop < p+30)
|
|
firstVisible = 0;
|
|
else
|
|
firstVisible = Math.floor((scrollTop - p - (i > 0 ? 30 : 0))/itemH);
|
|
}
|
|
if (lastVisibleGrp === undefined && scrollTop+scrollSize < top)
|
|
{
|
|
lastVisibleGrp = i;
|
|
if (i > 0 && scrollTop+scrollSize < p+30)
|
|
lastVisible = 0;
|
|
else
|
|
lastVisible = Math.floor((scrollTop+scrollSize - p - (i > 0 ? 30 : 0))/itemH);
|
|
if (lastVisible >= groups[i].messageCount)
|
|
lastVisible = groups[i].messageCount-1;
|
|
}
|
|
if (firstVisibleGrp !== undefined && lastVisibleGrp !== undefined)
|
|
break;
|
|
}
|
|
if (lastVisibleGrp === undefined)
|
|
{
|
|
lastVisibleGrp = groups.length-1;
|
|
lastVisible = groups[lastVisibleGrp].messageCount-1;
|
|
}
|
|
this.setState({
|
|
firstDayTop: scrollTop,
|
|
firstDay: groups[firstVisibleGrp].name,
|
|
firstGrp: firstVisibleGrp,
|
|
firstMsg: firstVisible,
|
|
lastGrp: lastVisibleGrp,
|
|
lastMsg: lastVisible
|
|
});
|
|
},
|
|
changeFirstDay: function(ev)
|
|
{
|
|
this.setFirstDayFromProps(this.props);
|
|
},
|
|
deleteSelected: function()
|
|
{
|
|
|
|
},
|
|
onSelectCurrent: function(index)
|
|
{
|
|
var self = this;
|
|
var total = 0, p, msg, idx;
|
|
for (var i = 0; i < self.props.groups.length; i++)
|
|
{
|
|
p = total;
|
|
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)
|
|
{
|
|
Store.loadMessage(msg.id, function(newMsg)
|
|
{
|
|
Store.set('msg', newMsg);
|
|
if (self.props.groups[i] && self.props.groups[i].messages[idx] == msg)
|
|
{
|
|
self.props.groups[i].messages[idx] = newMsg;
|
|
}
|
|
});
|
|
}
|
|
else
|
|
Store.set('msg', msg);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
getTotalItems: function()
|
|
{
|
|
var total = -1; // do not count first-day as item
|
|
for (var i = 0; i < this.props.groups.length; i++)
|
|
{
|
|
total += 1+this.props.groups[i].messageCount;
|
|
}
|
|
return total;
|
|
},
|
|
getPageSize: function()
|
|
{
|
|
return Math.round(this.refs.scroll.offsetHeight / (this.props.layout == 'message-on-right' ? 60 : 30));
|
|
},
|
|
getItemOffset: function(index)
|
|
{
|
|
var n = 0, top = this.refs.title.offsetHeight, p;
|
|
for (var i = 0; i < this.props.groups.length; i++)
|
|
{
|
|
p = n;
|
|
n += (i > 0 ? 1 : 0)+this.props.groups[i].messageCount;
|
|
if (index < n)
|
|
{
|
|
if (index > p)
|
|
top += (i > 0 ? 30 : 0) + (this.props.layout == 'message-on-right' ? 60 : 30)*(index-p-1);
|
|
break;
|
|
}
|
|
top += (i > 0 ? 30 : 0) + (this.props.layout == 'message-on-right' ? 60 : 30)*this.props.groups[i].messageCount;
|
|
}
|
|
return [ top, (this.props.layout == 'message-on-right' && (index == 0 || index != p) ? 60 : 30) ];
|
|
},
|
|
getScrollPaddingTop: function()
|
|
{
|
|
return this.refs.title.offsetHeight;
|
|
},
|
|
getMessages: function(grp, start, end)
|
|
{
|
|
var a = grp.messages.slice(start, end);
|
|
for (var i = 0; i < end-start; i++)
|
|
if (!a[i])
|
|
a[i] = null;
|
|
return a;
|
|
},
|
|
render: function()
|
|
{
|
|
var self = this;
|
|
var total = 0;
|
|
var itemH = (this.props.layout == 'message-on-right' ? 60 : 30);
|
|
return <div className="message-list">
|
|
<div className="top-border-gradient"></div>
|
|
<div className="bottom-border-gradient"></div>
|
|
<div className="actions">
|
|
<div className="searchbox">
|
|
<input type="text" placeholder="Search mail" />
|
|
</div>
|
|
<DropDownButton dropdownId="threads" className="threads"
|
|
title="Show Message Thread" checkedTitle="Hide Message Thread" icon="thread" checkedIcon="thread_selected" checkable="1" />
|
|
<DropDownButton dropdownId="settings" className="settings" whole="1" hidden={self.props.layout == 'message-on-right'}
|
|
title="Settings for self folder" icon="config" />
|
|
<DropDownButton dropdownId="list-sort" className="list-sort" whole="1"
|
|
title="Sorting settings" icon="list_sort" />
|
|
<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 }}>{self.state.firstDay}</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,
|
|
<div className="day-list">
|
|
{(self.state.firstGrp > i || self.state.lastGrp < i
|
|
? <div className="placeholder" style={{ height: itemH*grp.messageCount }}></div>
|
|
: [
|
|
(self.state.firstGrp == i
|
|
? <div className="placeholder" style={{ height: itemH*self.state.firstMsg }}></div>
|
|
: null),
|
|
self.getMessages(grp,
|
|
self.state.firstGrp == i ? self.state.firstMsg : 0,
|
|
self.state.lastGrp == i ? self.state.lastMsg+1 : grp.messageCount
|
|
).map(function(msg, j) { return (msg
|
|
? [
|
|
<MessageInList threads={self.props.threads} msg={msg} i={start+j} selected={self.isSelected(start+j)} onClick={self.onListItemClick} />,
|
|
/*(msg.thread && Store.threads ?
|
|
<div className="thread">
|
|
{msg.thread.map(reply => <MessageInList msg={reply} i={0} onClick={self.onListItemClick} />)}
|
|
</div>
|
|
: null)*/
|
|
]
|
|
: <div data-i={start+j} className={'message'+(self.isSelected(start+j) ? ' selected' : '')} onMouseDown={self.onListItemClick}></div>
|
|
); }),
|
|
(self.state.lastGrp == i
|
|
? <div className="placeholder" style={{ height: itemH*(grp.messageCount-self.state.lastMsg-1) }}></div>
|
|
: null)
|
|
]
|
|
)}
|
|
</div>
|
|
];
|
|
total += grp.messageCount;
|
|
return r;
|
|
})}
|
|
</div>
|
|
</div>
|
|
}
|
|
});
|
|
|
|
module.exports = StoreListener(MessageList, function(data)
|
|
{
|
|
return {
|
|
threads: data.threads,
|
|
layout: data.layout,
|
|
groups: data.listGroups
|
|
};
|
|
});
|