Use selection mixin in message list

master
Vitaliy Filippov 2016-06-23 22:36:21 +03:00
parent 617d90757e
commit 27552f167d
2 changed files with 81 additions and 36 deletions

View File

@ -872,6 +872,11 @@ div
border-bottom: 1px solid #dadce0;
}
.message-list .listview .day-list .message:last-child
{
border-bottom: 0;
}
.message-list .listview .day-list .thread:last-child .message:last-child
{
border-bottom: 0;

112
mail.js
View File

@ -769,11 +769,11 @@ var ListWithSelection = {
dir = (ev.keyCode == 40 ? 1 : -1);
if (sel !== null)
{
var nsel = sel+dir;
var nsel = sel+dir, n = this.getTotalItems();
if (nsel < 0)
nsel = 0;
if (nsel >= this.getTotalItems())
nsel = this.getTotalItems()-1;
if (nsel >= n)
nsel = n-1;
if (sel != nsel)
{
if (ev.shiftKey)
@ -818,8 +818,9 @@ var ListWithSelection = {
if (this.lastSel === undefined)
return this.selectOne(ns);
var sel = {};
if (this.lastSel >= this.getTotalItems())
this.lastSel = this.getTotalItems()-1;
var n = this.getTotalItems();
if (this.lastSel >= n)
this.lastSel = n-1;
if (ns < this.lastSel)
sel = { begin: ns, end: this.lastSel };
else if (ns > this.lastSel)
@ -994,21 +995,16 @@ var ComposeWindow = React.createClass({
var MessageInList = React.createClass({
msgClasses: [ 'unread', 'unseen', 'replied', 'pinned', 'sent', 'unloaded' ],
onClick: function()
onClick: function(ev)
{
Store.set('msg', this.props.msg);
this.setState({ selected: true });
this.props.onClick(this);
},
getInitialState: function()
{
return { selected: false };
this.props.onClick(ev);
},
render: function()
{
var msg = this.props.msg;
return <div className={'message'+(this.msgClasses.map(c => (msg[c] ? ' '+c : '')).join(''))+
(this.state.selected ? ' selected' : '')+(msg.thread && Store.threads ? ' thread0' : '')} onMouseDown={this.onClick}>
return <div data-i={this.props.i} className={'message'+(this.msgClasses.map(c => (msg[c] ? ' '+c : '')).join(''))+
(this.props.selected ? ' selected' : '')+(msg.thread && Store.threads ? ' thread0' : '')} onMouseDown={this.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 && Store.threads ? <div className={'expand'+(msg.collapsed ? '' : ' collapse')}></div> : null}
@ -1023,16 +1019,63 @@ var MessageInList = React.createClass({
// TODO: full keyboard navigation in message list, expand/collapse days, virtual scroll...
var MessageList = React.createClass({
mixins: [ ListWithSelection ],
getInitialState: function()
{
return { firstDayTop: 0 };
return { firstDayTop: 0, groups: this.props.groups /*FIXME*/ };
},
changeFirstDay: function(ev)
{
this.setState({ firstDayTop: ev.target.scrollTop });
},
deleteSelected: function()
{
},
getTotalItems: function()
{
var total = 0;
for (var i = 0; i < this.state.groups.length; i++)
{
total += 1+this.state.groups[i].messages.length;
}
return total;
},
getPageSize: function()
{
return Math.round(this.refs.scroll.offsetHeight / (Store.layout == 'message-on-right' ? 60 : 30));
},
getItemOffset: function(index)
{
/*var c, gmin = 0, gmax = this.state.groups.length;
while (gmin != gmax-1)
{
c = Math.floor((gmax+gmin)/2);
if (this.state.groups[c]
}*/
var n = 0, top = 0, p;
for (var i = 0; i < this.state.groups.length; i++)
{
p = n;
n += 1+this.state.groups[i].messages.length;
if (index < n)
{
if (index > p)
top += 30 + (Store.layout == 'message-on-right' ? 60 : 30)*(index-p-1);
break;
}
top += 30 + (Store.layout == 'message-on-right' ? 60 : 30)*this.state.groups[i].messages.length;
}
return [ top, (Store.layout == 'message-on-right' && index != p ? 60 : 30) ];
},
getScrollPaddingTop: function()
{
return this.refs.title.offsetHeight;
},
render: function()
{
var self = this;
var total = 0;
return <div className="message-list">
<div className="top-border-gradient"></div>
<div className="bottom-border-gradient"></div>
@ -1048,31 +1091,28 @@ var MessageList = React.createClass({
title="Sorting settings" icon="list_sort" />
<div className="clear"></div>
</div>
<div className="listview" tabIndex="1" onScroll={this.changeFirstDay}>
<div className="day first-day" style={{ top: this.state.firstDayTop }}>{this.props.groups[0].name}</div>
{this.props.groups.map((grp, i) => [
i > 0 ? <div className="day">{grp.name}</div> : null,
<div className="day-list">
{grp.messages.map(msg => [
<MessageInList msg={msg} onClick={this.onMsgClick} />,
(msg.thread ?
(Store.threads ?
<div ref="scroll" className="listview" tabIndex="1" onScroll={this.changeFirstDay} onKeyDown={this.onListKeyDown}>
<div ref="title" className="day first-day" style={{ top: this.state.firstDayTop }}>{this.props.groups[0].name}</div>
{this.props.groups.map(function(grp, i) {
total++;
var r = [
i > 0 ? <div className="day" data-i={total-1}>{grp.name}</div> : null,
<div className="day-list">
{grp.messages.map((msg, j) => [
<MessageInList msg={msg} i={total+j} selected={self.isSelected(total+j)} onClick={self.onListItemClick} />,
(msg.thread && Store.threads ?
<div className="thread">
{msg.thread.map(reply => <MessageInList msg={reply} onClick={this.onMsgClick} />)}
{msg.thread.map(reply => <MessageInList msg={reply} i={0} onClick={self.onListItemClick} />)}
</div>
: msg.thread.map(reply => <MessageInList msg={reply} onClick={this.onMsgClick} />))
: null)
])}
</div>
])}
: null)
])}
</div>
];
total += grp.messages.length;
return r;
})}
</div>
</div>
},
onMsgClick: function(msg)
{
if (this.prevSelected && this.prevSelected != msg)
this.prevSelected.setState({ selected: false });
this.prevSelected = msg;
}
});