Use react-redux like store connect
parent
eec9c21fa7
commit
5ea8744e83
1
.babelrc
1
.babelrc
|
@ -4,6 +4,7 @@
|
||||||
"transform-es2015-arrow-functions",
|
"transform-es2015-arrow-functions",
|
||||||
"transform-es2015-block-scoping",
|
"transform-es2015-block-scoping",
|
||||||
"transform-es2015-classes",
|
"transform-es2015-classes",
|
||||||
|
"transform-es2015-for-of",
|
||||||
"transform-es2015-computed-properties",
|
"transform-es2015-computed-properties",
|
||||||
"transform-es2015-destructuring",
|
"transform-es2015-destructuring",
|
||||||
"transform-es2015-shorthand-properties",
|
"transform-es2015-shorthand-properties",
|
||||||
|
|
|
@ -6,7 +6,7 @@ var AccountFolders = module.exports = React.createClass({
|
||||||
{
|
{
|
||||||
return <div className="account">
|
return <div className="account">
|
||||||
<div className={"account-header"+(this.state.collapsed ? ' collapsed' : '')} onClick={this.onClick}>
|
<div className={"account-header"+(this.state.collapsed ? ' collapsed' : '')} onClick={this.onClick}>
|
||||||
{this.props.name}
|
{this.props.email || this.props.name}
|
||||||
<div key="n" className="msg-count">{this.props.unreadCount}</div>
|
<div key="n" className="msg-count">{this.props.unreadCount}</div>
|
||||||
{this.props.accountId ? [
|
{this.props.accountId ? [
|
||||||
<div key="load" className="loading icon" style={{display: this.props.loading ? '' : 'none'}}></div>,
|
<div key="load" className="loading icon" style={{display: this.props.loading ? '' : 'none'}}></div>,
|
||||||
|
@ -46,7 +46,7 @@ var AccountFolders = module.exports = React.createClass({
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
var i = DropDownBase.instances.account.state.items;
|
var i = DropDownBase.instances.account.state.items;
|
||||||
i[0].text = 'Read '+this.props.name;
|
i[0].text = 'Read '+(this.props.email||this.props.name);
|
||||||
DropDownBase.instances.account.setState({ items: i });
|
DropDownBase.instances.account.setState({ items: i });
|
||||||
DropDownBase.instances.account.showAt(ev.target, function()
|
DropDownBase.instances.account.showAt(ev.target, function()
|
||||||
{
|
{
|
||||||
|
|
|
@ -134,21 +134,7 @@ var dropdown_list_sort = React.createElement(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
var dropdown_settings = React.createElement(
|
var dropdown_settings = MailSettingsWindow;
|
||||||
MailSettingsWindow, {
|
|
||||||
id: 'settings',
|
|
||||||
window: true,
|
|
||||||
markDelay: -1,
|
|
||||||
defaultSorting: {
|
|
||||||
sort: {
|
|
||||||
sortby: 'sent date',
|
|
||||||
group: 'date',
|
|
||||||
ascending: false,
|
|
||||||
threaded: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
module.exports = function()
|
module.exports = function()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const AttachList = require('./AttachList.js');
|
const AttachList = require('./AttachList.js');
|
||||||
|
const StoreListener = require('./StoreListener.js');
|
||||||
|
|
||||||
var ComposeWindow = module.exports = React.createClass({
|
var ComposeWindow = React.createClass({
|
||||||
getInitialState: function()
|
getInitialState: function()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
@ -58,3 +59,5 @@ var ComposeWindow = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = StoreListener(ComposeWindow, (data) => { return { accounts: data.accounts }; });
|
||||||
|
|
|
@ -2,8 +2,9 @@ const React = require('react');
|
||||||
const DropDownBase = require('./DropDownBase.js');
|
const DropDownBase = require('./DropDownBase.js');
|
||||||
const ListSortSettings = require('./ListSortSettings.js');
|
const ListSortSettings = require('./ListSortSettings.js');
|
||||||
const Store = require('./Store.js');
|
const Store = require('./Store.js');
|
||||||
|
const StoreListener = require('./StoreListener.js');
|
||||||
|
|
||||||
var MailSettingsWindow = module.exports = React.createClass({
|
var MailSettingsWindow = React.createClass({
|
||||||
mixins: [ DropDownBase ],
|
mixins: [ DropDownBase ],
|
||||||
render: function()
|
render: function()
|
||||||
{
|
{
|
||||||
|
@ -12,15 +13,15 @@ var MailSettingsWindow = module.exports = React.createClass({
|
||||||
<div ref="callout" className="callout-top" style={{ left: this.state.calloutLeft }}></div>
|
<div ref="callout" className="callout-top" style={{ left: this.state.calloutLeft }}></div>
|
||||||
<div className="text">Mail Layout</div>
|
<div className="text">Mail Layout</div>
|
||||||
<div className="layouts">
|
<div className="layouts">
|
||||||
<a onClick={this.switchLayout} className={'button mail-message-on-right'+(this.state.layout == 'message-on-right' ? ' selected' : '')} title="List and Message on Right"><span></span></a>
|
<a onClick={this.switchLayout} className={'button mail-message-on-right'+(this.props.layout == 'message-on-right' ? ' selected' : '')} title="List and Message on Right"><span></span></a>
|
||||||
<a onClick={this.switchLayout} className={'button mail-message-on-bottom'+(this.state.layout == 'message-on-bottom' ? ' selected' : '')} title="List and Message Below"><span></span></a>
|
<a onClick={this.switchLayout} className={'button mail-message-on-bottom'+(this.props.layout == 'message-on-bottom' ? ' selected' : '')} title="List and Message Below"><span></span></a>
|
||||||
<a onClick={this.switchLayout} className={'button mail-message-invisible'+(this.state.layout == 'message-invisible' ? ' selected' : '')} title="List Only"><span></span></a>
|
<a onClick={this.switchLayout} className={'button mail-message-invisible'+(this.props.layout == 'message-invisible' ? ' selected' : '')} title="List Only"><span></span></a>
|
||||||
</div>
|
</div>
|
||||||
<div className="split"><i></i></div>
|
<div className="split"><i></i></div>
|
||||||
<div className="text">Default List Sorting</div>
|
<div className="text">Default List Sorting</div>
|
||||||
<ListSortSettings className="fields" sort={this.props.defaultSorting} />
|
<ListSortSettings className="fields" sort={this.props.defaultSorting} />
|
||||||
<div className="fields">
|
<div className="fields">
|
||||||
<label><input type="checkbox" checked={this.state.showQuickReply} onClick={this.showQuickReply} /> Show Quick Reply</label>
|
<label><input type="checkbox" checked={this.props.showQuickReply} onClick={this.showQuickReply} /> Show Quick Reply</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="split"><i></i></div>
|
<div className="split"><i></i></div>
|
||||||
<div className="text">Mark as Read</div>
|
<div className="text">Mark as Read</div>
|
||||||
|
@ -35,36 +36,32 @@ var MailSettingsWindow = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
},
|
},
|
||||||
componentDidMount: function()
|
|
||||||
{
|
|
||||||
Store.on('layout', this.setLayout);
|
|
||||||
Store.on('quickReply', this.setQuickReply);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function()
|
|
||||||
{
|
|
||||||
Store.un('layout', this.setLayout);
|
|
||||||
Store.un('quickReply', this.setQuickReply);
|
|
||||||
},
|
|
||||||
setLayout: function()
|
|
||||||
{
|
|
||||||
this.setState({ layout: Store.layout });
|
|
||||||
},
|
|
||||||
setQuickReply: function()
|
|
||||||
{
|
|
||||||
this.setState({ showQuickReply: Store.quickReply });
|
|
||||||
},
|
|
||||||
switchLayout: function(ev)
|
switchLayout: function(ev)
|
||||||
{
|
{
|
||||||
var t = ev.target.nodeName == 'A' ? ev.target : ev.target.parentNode;
|
var t = ev.target.nodeName == 'A' ? ev.target : ev.target.parentNode;
|
||||||
var l = / mail-(\S+)/.exec(t.className)[1];
|
var l = / mail-(\S+)/.exec(t.className)[1];
|
||||||
Store.set('layout', l);
|
Store.set('layout', l);
|
||||||
},
|
},
|
||||||
getInitialState: function()
|
|
||||||
{
|
|
||||||
return { showQuickReply: Store.quickReply, layout: Store.layout };
|
|
||||||
},
|
|
||||||
showQuickReply: function()
|
showQuickReply: function()
|
||||||
{
|
{
|
||||||
Store.set('quickReply', !this.state.showQuickReply);
|
Store.set('quickReply', !this.state.showQuickReply);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = StoreListener(
|
||||||
|
MailSettingsWindow,
|
||||||
|
(data) => { return { layout: data.layout, showQuickReply: data.showQuickReply }; },
|
||||||
|
{
|
||||||
|
id: 'settings',
|
||||||
|
window: true,
|
||||||
|
markDelay: -1,
|
||||||
|
defaultSorting: {
|
||||||
|
sort: {
|
||||||
|
sortby: 'sent date',
|
||||||
|
group: 'date',
|
||||||
|
ascending: false,
|
||||||
|
threaded: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const DropDownButton = require('./DropDownButton.js');
|
const DropDownButton = require('./DropDownButton.js');
|
||||||
const ListWithSelection = require('./ListWithSelection.js');
|
const ListWithSelection = require('./ListWithSelection.js');
|
||||||
const Store = require('./Store.js');
|
const StoreListener = require('./StoreListener.js');
|
||||||
const Util = require('./Util.js');
|
const Util = require('./Util.js');
|
||||||
|
|
||||||
var MessageInList = React.createClass({
|
var MessageInList = React.createClass({
|
||||||
|
@ -10,10 +10,10 @@ var MessageInList = React.createClass({
|
||||||
{
|
{
|
||||||
var msg = this.props.msg;
|
var msg = this.props.msg;
|
||||||
return <div data-i={this.props.i} className={'message'+(this.msgClasses.map(c => (msg[c] ? ' '+c : '')).join(''))+
|
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.props.onClick}>
|
(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="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>
|
<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}
|
{msg.thread && this.props.threads ? <div className={'expand'+(msg.collapsed ? '' : ' collapse')}></div> : null}
|
||||||
<div className="bullet"></div>
|
<div className="bullet"></div>
|
||||||
<div className="from" style={{ left: (21+10*(msg.level||0)) }}>{(msg.sent || msg.outgoing ? 'To '+msg.to : msg.from)}</div>
|
<div className="from" style={{ left: (21+10*(msg.level||0)) }}>{(msg.sent || msg.outgoing ? 'To '+msg.to : msg.from)}</div>
|
||||||
<div className="size">{Util.formatBytes(msg.size)}</div>
|
<div className="size">{Util.formatBytes(msg.size)}</div>
|
||||||
|
@ -24,17 +24,19 @@ var MessageInList = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: expand/collapse days
|
// TODO: expand/collapse days
|
||||||
var MessageList = module.exports = React.createClass({
|
var MessageList = React.createClass({
|
||||||
mixins: [ ListWithSelection ],
|
mixins: [ ListWithSelection ],
|
||||||
getInitialState: function()
|
getInitialState: function()
|
||||||
{
|
{
|
||||||
return { firstDayTop: 0, firstDay: this.props.groups[0].name, groups: this.props.groups /*FIXME*/ };
|
return { firstDayTop: 0, firstDay: this.props.groups && this.props.groups[0] && this.props.groups[0].name || '', groups: this.props.groups||[] /*FIXME*/ };
|
||||||
},
|
},
|
||||||
changeFirstDay: function(ev)
|
changeFirstDay: function(ev)
|
||||||
{
|
{
|
||||||
|
if (!this.state.groups.length)
|
||||||
|
return;
|
||||||
var scrollTop = ev.target.scrollTop, scrollSize = ev.target.offsetHeight - this.getScrollPaddingTop();
|
var scrollTop = ev.target.scrollTop, scrollSize = ev.target.offsetHeight - this.getScrollPaddingTop();
|
||||||
var top = 0, p, firstVisibleGrp, firstVisible, lastVisibleGrp, lastVisible;
|
var top = 0, p, firstVisibleGrp, firstVisible, lastVisibleGrp, lastVisible;
|
||||||
var itemH = (Store.layout == 'message-on-right' ? 60 : 30);
|
var itemH = (this.props.layout == 'message-on-right' ? 60 : 30);
|
||||||
var i;
|
var i;
|
||||||
for (i = 0; i < this.state.groups.length; i++)
|
for (i = 0; i < this.state.groups.length; i++)
|
||||||
{
|
{
|
||||||
|
@ -104,7 +106,7 @@ var MessageList = module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
getPageSize: function()
|
getPageSize: function()
|
||||||
{
|
{
|
||||||
return Math.round(this.refs.scroll.offsetHeight / (Store.layout == 'message-on-right' ? 60 : 30));
|
return Math.round(this.refs.scroll.offsetHeight / (this.props.layout == 'message-on-right' ? 60 : 30));
|
||||||
},
|
},
|
||||||
getItemOffset: function(index)
|
getItemOffset: function(index)
|
||||||
{
|
{
|
||||||
|
@ -122,12 +124,12 @@ var MessageList = module.exports = React.createClass({
|
||||||
if (index < n)
|
if (index < n)
|
||||||
{
|
{
|
||||||
if (index > p)
|
if (index > p)
|
||||||
top += (i > 0 ? 30 : 0) + (Store.layout == 'message-on-right' ? 60 : 30)*(index-p-1);
|
top += (i > 0 ? 30 : 0) + (this.props.layout == 'message-on-right' ? 60 : 30)*(index-p-1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
top += (i > 0 ? 30 : 0) + (Store.layout == 'message-on-right' ? 60 : 30)*this.state.groups[i].messageCount;
|
top += (i > 0 ? 30 : 0) + (this.props.layout == 'message-on-right' ? 60 : 30)*this.state.groups[i].messageCount;
|
||||||
}
|
}
|
||||||
return [ top, (Store.layout == 'message-on-right' && (index == 0 || index != p) ? 60 : 30) ];
|
return [ top, (this.props.layout == 'message-on-right' && (index == 0 || index != p) ? 60 : 30) ];
|
||||||
},
|
},
|
||||||
getScrollPaddingTop: function()
|
getScrollPaddingTop: function()
|
||||||
{
|
{
|
||||||
|
@ -145,7 +147,7 @@ var MessageList = module.exports = React.createClass({
|
||||||
{
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
var total = 0;
|
var total = 0;
|
||||||
var itemH = (Store.layout == 'message-on-right' ? 60 : 30);
|
var itemH = (this.props.layout == 'message-on-right' ? 60 : 30);
|
||||||
return <div className="message-list">
|
return <div className="message-list">
|
||||||
<div className="top-border-gradient"></div>
|
<div className="top-border-gradient"></div>
|
||||||
<div className="bottom-border-gradient"></div>
|
<div className="bottom-border-gradient"></div>
|
||||||
|
@ -163,7 +165,7 @@ var MessageList = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div ref="scroll" className="listview" tabIndex="1" onScroll={this.changeFirstDay} onKeyDown={this.onListKeyDown}>
|
<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.state.firstDay}</div>
|
<div ref="title" className="day first-day" style={{ top: this.state.firstDayTop }}>{this.state.firstDay}</div>
|
||||||
{this.props.groups.map(function(grp, i) {
|
{(this.props.groups||[]).map(function(grp, i) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
total++;
|
total++;
|
||||||
var start = total+(self.state.firstGrp == i ? self.state.firstMsg : 0);
|
var start = total+(self.state.firstGrp == i ? self.state.firstMsg : 0);
|
||||||
|
@ -181,7 +183,7 @@ var MessageList = module.exports = React.createClass({
|
||||||
self.state.lastGrp == i ? self.state.lastMsg+1 : grp.messageCount
|
self.state.lastGrp == i ? self.state.lastMsg+1 : grp.messageCount
|
||||||
).map((msg, j) => (msg
|
).map((msg, j) => (msg
|
||||||
? [
|
? [
|
||||||
<MessageInList msg={msg} i={start+j} selected={self.isSelected(start+j)} onClick={self.onListItemClick} />,
|
<MessageInList threads={this.props.threads} msg={msg} i={start+j} selected={self.isSelected(start+j)} onClick={self.onListItemClick} />,
|
||||||
/*(msg.thread && Store.threads ?
|
/*(msg.thread && Store.threads ?
|
||||||
<div className="thread">
|
<div className="thread">
|
||||||
{msg.thread.map(reply => <MessageInList msg={reply} i={0} onClick={self.onListItemClick} />)}
|
{msg.thread.map(reply => <MessageInList msg={reply} i={0} onClick={self.onListItemClick} />)}
|
||||||
|
@ -204,3 +206,12 @@ var MessageList = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = StoreListener(MessageList, function(data)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
threads: data.threads,
|
||||||
|
layout: data.layout,
|
||||||
|
groups: data.listGroups
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const DropDownButton = require('./DropDownButton.js');
|
const DropDownButton = require('./DropDownButton.js');
|
||||||
const Store = require('./Store.js');
|
const Store = require('./Store.js');
|
||||||
|
const StoreListener = require('./StoreListener.js');
|
||||||
const Util = require('./Util.js');
|
const Util = require('./Util.js');
|
||||||
|
|
||||||
var MessageView = module.exports = React.createClass({
|
var MessageView = React.createClass({
|
||||||
formatLongDate: function(dt)
|
formatLongDate: function(dt)
|
||||||
{
|
{
|
||||||
if (!(dt instanceof Date))
|
if (!(dt instanceof Date))
|
||||||
|
@ -14,32 +15,10 @@ var MessageView = module.exports = React.createClass({
|
||||||
return Util.WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Util.Months[dt.getMonth()]+' '+dt.getFullYear()+' '+(h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m+':'+(s < 10 ? '0' : '')+s
|
return Util.WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Util.Months[dt.getMonth()]+' '+dt.getFullYear()+' '+(h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m+':'+(s < 10 ? '0' : '')+s
|
||||||
//return dt.toLocaleString();
|
//return dt.toLocaleString();
|
||||||
},
|
},
|
||||||
componentDidMount: function()
|
|
||||||
{
|
|
||||||
Store.on('quickReply', this.setQuickReply);
|
|
||||||
Store.on('msg', this.setMsg);
|
|
||||||
},
|
|
||||||
componentWillUnmount: function()
|
|
||||||
{
|
|
||||||
Store.un('quickReply', this.setQuickReply);
|
|
||||||
Store.un('msg', this.setMsg);
|
|
||||||
},
|
|
||||||
setQuickReply: function()
|
|
||||||
{
|
|
||||||
this.setState({ quickReply: Store.quickReply });
|
|
||||||
},
|
|
||||||
setMsg: function()
|
|
||||||
{
|
|
||||||
this.setState({ msg: Store.msg });
|
|
||||||
},
|
|
||||||
getInitialState: function()
|
|
||||||
{
|
|
||||||
return { quickReply: Store.quickReply };
|
|
||||||
},
|
|
||||||
render: function()
|
render: function()
|
||||||
{
|
{
|
||||||
var msg = this.state.msg;
|
var msg = this.props.msg;
|
||||||
return <div className={'message-view'+(!msg ? ' no-mail-shown' : '')+(!this.state.quickReply ? ' no-quick' : '')}>
|
return <div className={'message-view'+(!msg ? ' no-mail-shown' : '')+(!this.props.quickReply ? ' no-quick' : '')}>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<DropDownButton dropdownId="reply" icon="mail_reply" text="Reply" />
|
<DropDownButton dropdownId="reply" icon="mail_reply" text="Reply" />
|
||||||
<a className="button"><img src="icons/mail_reply_all.png" />Reply All</a>
|
<a className="button"><img src="icons/mail_reply_all.png" />Reply All</a>
|
||||||
|
@ -98,7 +77,7 @@ var MessageView = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
: null),
|
: null),
|
||||||
<div className="text" dangerouslySetInnerHTML={{ __html: msg.body }}></div>,
|
<div className="text" dangerouslySetInnerHTML={{ __html: msg.body }}></div>,
|
||||||
this.state.quickReply ?
|
this.props.quickReply ?
|
||||||
<div className="quick-reply">
|
<div className="quick-reply">
|
||||||
<textarea></textarea>
|
<textarea></textarea>
|
||||||
<div className="btns">
|
<div className="btns">
|
||||||
|
@ -111,3 +90,5 @@ var MessageView = module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = StoreListener(MessageView, (data) => { return { quickReply: data.quickReply, msg: data.msg }; });
|
||||||
|
|
112
Store.js
112
Store.js
|
@ -1,26 +1,102 @@
|
||||||
var Store = module.exports = {
|
const superagent = require('superagent');
|
||||||
layout: 'message-on-right',
|
const _ = require('./I18n.js');
|
||||||
quickReply: true,
|
|
||||||
msg: null,
|
|
||||||
threads: false,
|
|
||||||
|
|
||||||
listeners: {},
|
var Store = module.exports = {
|
||||||
on: function(ev, cb)
|
data: {
|
||||||
{
|
layout: 'message-on-right',
|
||||||
this.listeners[ev] = this.listeners[ev] || [];
|
quickReply: true,
|
||||||
this.listeners[ev].push(cb);
|
msg: null,
|
||||||
|
threads: false,
|
||||||
|
accounts: [],
|
||||||
},
|
},
|
||||||
un: function(ev, cb)
|
|
||||||
|
listeners: [],
|
||||||
|
on: function(cb)
|
||||||
{
|
{
|
||||||
if (!this.listeners[ev])
|
this.listeners.push(cb);
|
||||||
return;
|
},
|
||||||
for (var i = this.listeners[ev].length; i >= 0; i--)
|
un: function(cb)
|
||||||
if (this.listeners[ev] == cb)
|
{
|
||||||
this.listeners[ev].splice(i, 1);
|
for (var i = this.listeners.length; i >= 0; i--)
|
||||||
|
if (this.listeners[i] == cb)
|
||||||
|
this.listeners.splice(i, 1);
|
||||||
|
},
|
||||||
|
get: function(k)
|
||||||
|
{
|
||||||
|
return this.data[k];
|
||||||
},
|
},
|
||||||
set: function(k, v)
|
set: function(k, v)
|
||||||
{
|
{
|
||||||
this[k] = v;
|
this.data[k] = v;
|
||||||
(this.listeners[k] || []).map(i => i());
|
(this.listeners || []).map(i => i());
|
||||||
|
},
|
||||||
|
|
||||||
|
loadAccounts: function()
|
||||||
|
{
|
||||||
|
superagent.get('backend/folders').end(function(err, res)
|
||||||
|
{
|
||||||
|
var ixOfAll = {
|
||||||
|
received: 1,
|
||||||
|
outbox: 3,
|
||||||
|
sent: 4,
|
||||||
|
drafts: 5,
|
||||||
|
spam: 6,
|
||||||
|
trash: 7
|
||||||
|
};
|
||||||
|
var accounts = [ {
|
||||||
|
name: _('All Messages'),
|
||||||
|
accountId: null,
|
||||||
|
unreadCount: 0,
|
||||||
|
folders: [
|
||||||
|
{ name: _('Unread'), icon: 'mail_unread', unreadCount: 0, type: 'unread' },
|
||||||
|
{ name: _('Received'), icon: 'mail_received', unreadCount: 0, type: 'inbox' },
|
||||||
|
{ name: _('Pinned'), icon: 'mail_pinned', unreadCount: 0, type: 'pinned' },
|
||||||
|
{ name: _('Outbox'), icon: 'mail_outbox', unreadCount: 0, type: 'outbox' },
|
||||||
|
{ name: _('Sent'), icon: 'mail_sent', unreadCount: 0, type: 'sent' },
|
||||||
|
{ name: _('Drafts'), icon: 'mail_drafts', unreadCount: 0, type: 'drafts' },
|
||||||
|
{ name: _('Spam'), icon: 'mail_spam', unreadCount: 0, type: 'spam' },
|
||||||
|
{ name: _('Trash'), icon: 'mail_trash', unreadCount: 0, type: 'trash' },
|
||||||
|
],
|
||||||
|
} ];
|
||||||
|
for (let a of res.body.accounts)
|
||||||
|
{
|
||||||
|
let account = {
|
||||||
|
name: a.name,
|
||||||
|
email: a.email,
|
||||||
|
accountId: a.id,
|
||||||
|
unreadCount: 0,
|
||||||
|
warning: false,
|
||||||
|
folders: [
|
||||||
|
{ name: _('Unread'), icon: 'mail_unread', unreadCount: 0, type: 'unread' },
|
||||||
|
{ name: _('Pinned'), icon: 'mail_pinned', unreadCount: a.pinned_unread_count, type: 'pinned' },
|
||||||
|
],
|
||||||
|
folderMap: a.foldermap,
|
||||||
|
folderTypes: {}
|
||||||
|
};
|
||||||
|
if (!account.folderMap.received)
|
||||||
|
{
|
||||||
|
account.folderMap.received = 'INBOX';
|
||||||
|
}
|
||||||
|
for (let f in account.folderMap)
|
||||||
|
{
|
||||||
|
account.folderTypes[account.folderMap[f]] = f;
|
||||||
|
}
|
||||||
|
for (let f of a.folders)
|
||||||
|
{
|
||||||
|
let icon = (account.folderTypes[f.name] ? 'mail_'+account.folderTypes[f.name] : 'folder');
|
||||||
|
account.folders.push({ name: f.name, icon: icon, unreadCount: f.unread_count-0, folderId: f.id });
|
||||||
|
account.folders[0].unreadCount -= -f.unread_count;
|
||||||
|
if (account.folderTypes[f.name])
|
||||||
|
{
|
||||||
|
accounts[0].folders[ixOfAll[account.folderTypes[f.name]]].unreadCount -= -f.unread_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accounts.push(account);
|
||||||
|
accounts[0].unreadCount += account.unreadCount;
|
||||||
|
accounts[0].folders[0].unreadCount += account.unreadCount;
|
||||||
|
accounts[0].folders[2].unreadCount += account.folders[1].unreadCount;
|
||||||
|
}
|
||||||
|
Store.set('accounts', accounts);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
const React = require('react');
|
||||||
|
const Store = require('./Store.js');
|
||||||
|
|
||||||
|
// "react-redux connect()"-like example
|
||||||
|
var StoreListener = React.createClass({
|
||||||
|
componentDidMount: function()
|
||||||
|
{
|
||||||
|
Store.on(this.update);
|
||||||
|
},
|
||||||
|
componentWillUnmount: function()
|
||||||
|
{
|
||||||
|
Store.un(this.update);
|
||||||
|
},
|
||||||
|
update: function()
|
||||||
|
{
|
||||||
|
var newState = this.props.mapStateToProps(Store.data);
|
||||||
|
for (var i in newState)
|
||||||
|
{
|
||||||
|
if (this.state[i] != newState[i])
|
||||||
|
{
|
||||||
|
this.setState(newState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getInitialState: function()
|
||||||
|
{
|
||||||
|
return { ...this.props.initial, ...this.props.mapStateToProps(Store.data) };
|
||||||
|
},
|
||||||
|
render: function()
|
||||||
|
{
|
||||||
|
return React.createElement(this.props.wrappedComponent, this.state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = function(component, map, initial)
|
||||||
|
{
|
||||||
|
return React.createElement(StoreListener, { wrappedComponent: component, mapStateToProps: map, initial: initial||{} });
|
||||||
|
};
|
105
mail.js
105
mail.js
|
@ -6,67 +6,11 @@ const MessageList = require('./MessageList.js');
|
||||||
const MessageView = require('./MessageView.js');
|
const MessageView = require('./MessageView.js');
|
||||||
const TabPanel = require('./TabPanel.js');
|
const TabPanel = require('./TabPanel.js');
|
||||||
const Store = require('./Store.js');
|
const Store = require('./Store.js');
|
||||||
|
const StoreListener = require('./StoreListener.js');
|
||||||
const AllDropdowns = require('./AllDropdowns.js');
|
const AllDropdowns = require('./AllDropdowns.js');
|
||||||
|
|
||||||
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
|
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
|
||||||
|
|
||||||
var accounts = [
|
|
||||||
{
|
|
||||||
name: 'All Messages',
|
|
||||||
accountId: null,
|
|
||||||
unreadCount: 65,
|
|
||||||
folders: [
|
|
||||||
{ name: 'Unread', icon: 'mail_unread', unreadCount: 65 },
|
|
||||||
{ name: 'Received', icon: 'mail_received', unreadCount: 65 },
|
|
||||||
{ name: 'Pinned', icon: 'mail_pinned' },
|
|
||||||
{ name: 'Outbox', icon: 'mail_outbox' },
|
|
||||||
{ name: 'Sent', icon: 'mail_sent' },
|
|
||||||
{ name: 'Drafts', icon: 'mail_drafts', unreadCount: 507 },
|
|
||||||
{ name: 'Spam', icon: 'mail_spam' },
|
|
||||||
{ name: 'Trash', icon: 'mail_trash', unreadCount: 423 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vitalif@mail.ru',
|
|
||||||
accountId: 1,
|
|
||||||
unreadCount: 48,
|
|
||||||
warning: true,
|
|
||||||
folders: [
|
|
||||||
{ name: 'Unread', icon: 'mail_unread', unreadCount: 48 },
|
|
||||||
{ name: 'INBOX', icon: 'folder', unreadCount: 48 },
|
|
||||||
{ name: 'TODO', icon: 'folder' },
|
|
||||||
{ name: 'Архив', icon: 'folder' },
|
|
||||||
{ name: 'Корзина', icon: 'mail_trash' },
|
|
||||||
{ name: 'Отправленные', icon: 'mail_sent' },
|
|
||||||
{ name: 'Спам', icon: 'mail_spam' },
|
|
||||||
{ name: 'Черновики', icon: 'mail_drafts' },
|
|
||||||
{ name: 'Pinned', icon: 'mail_pinned' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vitalif@yourcmc.ru',
|
|
||||||
accountId: 2,
|
|
||||||
unreadCount: 16,
|
|
||||||
loading: true,
|
|
||||||
folders: [
|
|
||||||
{ name: 'Unread', icon: 'mail_unread', unreadCount: 16 },
|
|
||||||
{ name: 'Drafts', icon: 'mail_drafts' },
|
|
||||||
{ name: 'HAM', icon: 'folder' },
|
|
||||||
{ name: 'INBOX', icon: 'folder', unreadCount: 16 },
|
|
||||||
{ name: 'intermedia_su', icon: 'folder' },
|
|
||||||
{ name: 'Sent', icon: 'mail_sent' },
|
|
||||||
{ name: 'SPAM', icon: 'mail_spam' },
|
|
||||||
{ name: 'TRASH', icon: 'mail_trash' },
|
|
||||||
{ name: 'Pinned', icon: 'mail_pinned' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var composeAccounts = [
|
|
||||||
{ name: 'Виталий Филиппов', email: 'vitalif@mail.ru' },
|
|
||||||
{ name: 'Vitaliy Filippov', email: 'vitalif@yourcmc.ru' }
|
|
||||||
];
|
|
||||||
|
|
||||||
var msg2 = [];
|
var msg2 = [];
|
||||||
msg2[5] = {
|
msg2[5] = {
|
||||||
subject: 'кошку хочешь?))',
|
subject: 'кошку хочешь?))',
|
||||||
|
@ -87,7 +31,7 @@ msg2[5] = {
|
||||||
} ]
|
} ]
|
||||||
};
|
};
|
||||||
|
|
||||||
var listGroups = [ {
|
Store.listGroups = [ {
|
||||||
name: 'TODAY',
|
name: 'TODAY',
|
||||||
messageCount: 10,
|
messageCount: 10,
|
||||||
messages: [ {
|
messages: [ {
|
||||||
|
@ -109,7 +53,26 @@ Best regards,<br />\
|
||||||
messages: msg2
|
messages: msg2
|
||||||
} ];
|
} ];
|
||||||
|
|
||||||
var AllTabs = React.createClass({
|
var AllTabs = StoreListener(TabPanel, function(data)
|
||||||
|
{
|
||||||
|
return { tabs: [
|
||||||
|
{
|
||||||
|
className: data.layout,
|
||||||
|
noclose: true,
|
||||||
|
icon: 'mail_unread',
|
||||||
|
title: 'Unread (64)',
|
||||||
|
children: [ MessageList, MessageView ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'mail_drafts',
|
||||||
|
i16: true,
|
||||||
|
title: 'Compose Message',
|
||||||
|
children: [ ComposeWindow ]
|
||||||
|
}
|
||||||
|
] }
|
||||||
|
});
|
||||||
|
|
||||||
|
/*React.createClass({
|
||||||
componentDidMount: function()
|
componentDidMount: function()
|
||||||
{
|
{
|
||||||
Store.on('layout', this.setLayout);
|
Store.on('layout', this.setLayout);
|
||||||
|
@ -150,29 +113,17 @@ var AllTabs = React.createClass({
|
||||||
},
|
},
|
||||||
render: function()
|
render: function()
|
||||||
{
|
{
|
||||||
return React.createElement(TabPanel, { tabs: [
|
return React.createElement();
|
||||||
{
|
|
||||||
className: this.state.layout,
|
|
||||||
noclose: true,
|
|
||||||
icon: 'mail_unread',
|
|
||||||
title: 'Unread (64)',
|
|
||||||
children: [ <MessageList ref="list" groups={listGroups} />, <MessageView ref="view" /> ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: 'mail_drafts',
|
|
||||||
i16: true,
|
|
||||||
title: 'Compose Message',
|
|
||||||
children: <ComposeWindow accounts={composeAccounts} />
|
|
||||||
}
|
|
||||||
] });
|
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<div>
|
<div>
|
||||||
{AllDropdowns()}
|
{AllDropdowns()}
|
||||||
<FolderList accounts={accounts} progress="33" />
|
{StoreListener(FolderList, (data) => { return { accounts: data.accounts }; }, { progress: 33 })}
|
||||||
<AllTabs />
|
{AllTabs}
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
document.body
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Store.loadAccounts();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"babel-plugin-transform-es2015-block-scoping": "latest",
|
"babel-plugin-transform-es2015-block-scoping": "latest",
|
||||||
"babel-plugin-transform-es2015-classes": "latest",
|
"babel-plugin-transform-es2015-classes": "latest",
|
||||||
"babel-plugin-transform-es2015-computed-properties": "latest",
|
"babel-plugin-transform-es2015-computed-properties": "latest",
|
||||||
|
"babel-plugin-transform-es2015-for-of": "latest",
|
||||||
"babel-plugin-transform-es2015-destructuring": "latest",
|
"babel-plugin-transform-es2015-destructuring": "latest",
|
||||||
"babel-plugin-transform-es2015-shorthand-properties": "latest",
|
"babel-plugin-transform-es2015-shorthand-properties": "latest",
|
||||||
"babel-plugin-transform-object-rest-spread": "latest",
|
"babel-plugin-transform-object-rest-spread": "latest",
|
||||||
|
@ -25,10 +26,12 @@
|
||||||
"react-dom": "latest",
|
"react-dom": "latest",
|
||||||
"uglifyjs": "latest",
|
"uglifyjs": "latest",
|
||||||
"uglifyify": "latest",
|
"uglifyify": "latest",
|
||||||
"eslint": "latest"
|
"eslint": "latest",
|
||||||
|
"superagent": "latest"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compile": "browserify -t babelify -t uglifyify mail.js | uglifyjs -cm > mail.c.js",
|
"compile": "browserify -t babelify -t uglifyify mail.js | uglifyjs -cm > mail.c.js",
|
||||||
|
"watch-dev": "watchify -t babelify mail.js -o mail.c.js",
|
||||||
"watch": "watchify -t babelify -t uglifyify mail.js -o 'uglifyjs -cm > mail.c.js'"
|
"watch": "watchify -t babelify -t uglifyify mail.js -o 'uglifyjs -cm > mail.c.js'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue