- {(self.state.firstDay||'').toUpperCase()}
+ style={{ top: this.state.firstDayTop, display: this.state.firstDay ? '' : 'none' }}>
+ {(this.state.firstDay||'').toUpperCase()}
- {(self.props.groups||[]).map(function(grp, i) {
+ {(this.props.groups||[]).map((grp, i) =>
+ {
if (i > 0)
total++;
- var start = total+(self.state.firstGrp == i ? self.state.firstMsg : 0);
- var r = [
+ let start = total+(this.state.firstGrp == i ? this.state.firstMsg : 0);
+ let r = [
i > 0 ?
{grp.name.toUpperCase()}
: null,
- {(self.state.firstGrp > i || self.state.lastGrp < i
+ {(this.state.firstGrp > i || this.state.lastGrp < i
?
: [
- (self.state.firstGrp == i
- ?
+ (this.state.firstGrp == i
+ ?
: 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
+ this.getMessages(grp,
+ this.state.firstGrp == i ? this.state.firstMsg : 0,
+ this.state.lastGrp == i ? this.state.lastMsg+1 : grp.messageCount
+ ).map((msg, j) => (msg
? [
-
,
- /*(msg.thread && Store.threads ?
+
,
+ /*(msg.thread && this.props.threads ?
- {msg.thread.map(reply => )}
+ {msg.thread.map(reply => )}
: null)*/
]
- :
- ); }),
- (self.state.lastGrp == i
- ?
+ :
+ )),
+ (this.state.lastGrp == i
+ ?
: null)
]
)}
@@ -294,13 +295,3 @@ class MessageList extends ListWithSelection
window.removeEventListener('resize', this.changeFirstDay);
}
}
-
-export default StoreListener(MessageList, function(data)
-{
- return {
- threads: data.threads,
- layout: data.layout,
- groups: data.listGroups,
- messages: data.messages
- };
-});
diff --git a/MessageView.js b/MessageView.js
index 36a0dda..3beac5e 100644
--- a/MessageView.js
+++ b/MessageView.js
@@ -1,10 +1,8 @@
import React from 'react';
import DropDownButton from './DropDownButton.js';
-import Store from './Store.js';
-import StoreListener from './StoreListener.js';
import Util from './Util.js';
-class MessageView extends React.PureComponent
+export default class MessageView extends React.PureComponent
{
state = { showImages: false }
@@ -151,5 +149,3 @@ class MessageView extends React.PureComponent
}
}
-
-export default StoreListener(MessageView, (data) => { return { layout: data.layout, quickReply: data.quickReply, msg: data.msg }; });
diff --git a/Store.js b/Store.js
deleted file mode 100644
index d61d6b7..0000000
--- a/Store.js
+++ /dev/null
@@ -1,199 +0,0 @@
-import superagent from 'superagent';
-import socket_io from 'socket.io-client';
-
-import _ from './I18n.js';
-import Util from './Util.js';
-
-const Store = {
- data: {
- layout: 'message-on-right',
- quickReply: true,
- msg: null,
- threads: false,
- accounts: [],
- listGroups: [],
- messages: []
- },
-
- listeners: [],
- on: function(cb)
- {
- this.listeners.push(cb);
- },
- un: function(cb)
- {
- 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)
- {
- this.data[k] = v;
- (this.listeners || []).map(i => i());
- },
- setAll: function(obj)
- {
- for (var k in obj)
- this.data[k] = obj[k];
- (this.listeners || []).map(i => i());
- },
-
- startIo: function()
- {
- var self = this;
- this.io = socket_io('', { path: window.location.pathname.replace(/[^\/]+$/, 'backend/socket.io') });
- this.io.on('sync', function(params)
- {
- if (params.state == 'start')
- {
- self.setAll({ progressText: 'Syncing '+params.email+' / '+params.folder, progressPercent: 0 });
- }
- else if (params.state == 'progress')
- {
- self.setAll({ progressPercent: Math.round(100*params.done/(params.total||1)) });
- }
- else if (params.state == 'finish-box')
- {
- self.setAll({ progressPercent: 100 });
- }
- else if (params.state == 'complete')
- {
- self.setAll({ progressText: '', progressPercent: 0 });
- }
- self.set('sync', params.progress);
- });
- },
-
- 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-0);
- if (account.folderTypes[f.name])
- {
- accounts[0].folders[ixOfAll[account.folderTypes[f.name]]].unreadCount += (f.unread_count-0);
- }
- account.unreadCount += (f.unread_count-0);
- }
- 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);
- });
- },
-
- loadFolder: function(folderParams)
- {
- superagent.get('backend/groups').query(folderParams).end(function(err, res)
- {
- 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({
- folderParams: folderParams,
- listGroups: groups,
- messages: []
- });
- });
- },
-
- search: function(text)
- {
- Store.loadFolder({ ...Store.get('folderParams'), search: text });
- },
-
- loadMessages: function(start, count)
- {
- var p = { ...Store.get('folderParams') };
- p.offset = start;
- p.limit = count;
- superagent.get('backend/messages').query(p).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);
- });
- },
-
- loadMessage: function(msgId, callback)
- {
- superagent.get('backend/message').query({ msgId: msgId }).end(function(err, res)
- {
- callback(res.body.msg);
- });
- },
-
- startResync: function()
- {
- superagent.post('backend/sync').send().end(function(err, res)
- {
- });
- }
-};
-
-Store.startIo();
-
-export default Store;
diff --git a/StoreListener.js b/StoreListener.js
deleted file mode 100644
index 990b943..0000000
--- a/StoreListener.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-
-import Store from './Store.js';
-
-// "react-redux connect()"-like example
-class StoreListener extends React.PureComponent
-{
- componentDidMount()
- {
- Store.on(this.update);
- }
-
- componentWillUnmount()
- {
- Store.un(this.update);
- }
-
- update = () =>
- {
- var newState = this.mapStateToProps(Store.data);
- for (var i in newState)
- {
- if (this.state[i] != newState[i])
- {
- this.setState(newState);
- return;
- }
- }
- }
-
- render()
- {
- var props = { ...this.initial, ...this.props, ...this.state };
- return React.createElement(this.wrappedComponent, props);
- }
-}
-
-export default function(component, map, initial)
-{
- var cl = class extends StoreListener
- {
- constructor(props, context, updater)
- {
- super(props, context, updater);
- this.wrappedComponent = component;
- this.mapStateToProps = map;
- this.initial = initial;
- this.state = map(Store.data);
- this.update = this.update.bind(this);
- }
- };
- return cl;
-};
diff --git a/TabPanel.js b/TabPanel.js
index dc8b833..24dce2b 100644
--- a/TabPanel.js
+++ b/TabPanel.js
@@ -6,11 +6,11 @@ export default class TabPanel extends React.PureComponent
render()
{
- var bar = [];
- var body = [];
- for (var i = 0; i < this.state.tabs.length; i++)
+ let bar = [];
+ let body = [];
+ for (let i = 0; i < this.state.tabs.length; i++)
{
- var t = this.state.tabs[i];
+ let t = this.state.tabs[i];
bar.push(
@@ -44,19 +44,18 @@ export default class TabPanel extends React.PureComponent
closeTab = (ev) =>
{
- var self = this;
- var tab = ev.target.parentNode;
+ let tab = ev.target.parentNode;
//this.setState({ closing: tab.id.substr(5) });
tab.style.width = '1px';
tab.style.padding = '0';
tab.style.opacity = '0';
- setTimeout(function()
+ setTimeout(() =>
{
- var t = self.state.tabs;
+ let t = this.state.tabs;
t.splice(tab.id.substr(5), 1);
- var s = self.state.selected;
+ let s = this.state.selected;
if ('t-tab'+s == tab.id)
- s = self.state.selected-1;
+ s = this.state.selected-1;
this.setState({ tabs: t, selected: s });
}, 200);
ev.stopPropagation();
diff --git a/Util.js b/Util.js
index 10fd67c..2926ed3 100644
--- a/Util.js
+++ b/Util.js
@@ -16,25 +16,25 @@ export default class Util
{
if (!(dt instanceof Date))
dt = new Date(dt.replace(' ', 'T'));
- var tod = new Date();
+ let tod = new Date();
tod.setHours(0);
tod.setMinutes(0);
tod.setSeconds(0);
tod.setMilliseconds(0);
- var prevweek = tod;
+ let prevweek = tod;
prevweek = prevweek.getTime() - (7 + (prevweek.getDay()+6)%7)*86400000;
if (dt.getTime() < prevweek)
{
- var d = dt.getDate();
- var m = dt.getMonth()+1;
+ let d = dt.getDate();
+ let m = dt.getMonth()+1;
return (d < 10 ? '0' : '')+d+'.'+(m < 10 ? '0' : '')+m+'.'+dt.getFullYear();
}
else if (dt.getTime() < tod.getTime())
{
return Util.WeekDays[dt.getDay()]+' '+dt.getDate()+' '+Util.Months[dt.getMonth()];
}
- var h = dt.getHours();
- var m = dt.getMinutes();
+ let h = dt.getHours();
+ let m = dt.getMinutes();
return (h < 10 ? '0' : '')+h+':'+(m < 10 ? '0' : '')+m;
}
diff --git a/mail.js b/mail.js
index db1c8fb..75c8bdb 100644
--- a/mail.js
+++ b/mail.js
@@ -1,42 +1,381 @@
import React from 'react';
import ReactDOM from 'react-dom';
+
+import superagent from 'superagent';
+import socket_io from 'socket.io-client';
+
import ComposeWindow from './ComposeWindow.js';
import FolderList from './FolderList.js';
import MessageList from './MessageList.js';
import MessageView from './MessageView.js';
+import DropDownMenu from './DropDownMenu.js';
+import ListSortSettingsWindow from './ListSortSettingsWindow.js';
+import MailSettingsWindow from './MailSettingsWindow.js';
import TabPanel from './TabPanel.js';
-import Store from './Store.js';
-import StoreListener from './StoreListener.js';
-import AllDropdowns from './AllDropdowns.js';
+import _ from './I18n.js';
+import Util from './Util.js';
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
-var AllTabs = StoreListener(TabPanel, function(data)
+class MainWindow extends React.PureComponent
{
- return { tabs: [
- {
- className: data.layout,
- noclose: true,
- icon: 'mail_unread',
- title: 'Unread (64)',
- children: [
,
]
- },
- {
- icon: 'mail_drafts',
- i16: true,
- title: 'Compose Message',
- children: [
]
- }
- ] }
-});
+ state = {
+ layout: 'message-on-right',
+ quickReply: true,
+ msg: null,
+ threads: false,
+ accounts: [],
+ listGroups: [],
+ messages: []
+ }
-ReactDOM.render(
-
,
- document.getElementById('app')
-);
+ startIo = () =>
+ {
+ this.io = socket_io('', { path: window.location.pathname.replace(/[^\/]+$/, 'backend/socket.io') });
+ this.io.on('sync', (params) =>
+ {
+ if (params.state == 'start')
+ {
+ this.setState({ progressText: 'Syncing '+params.email+' / '+params.folder, progressPercent: 0 });
+ }
+ else if (params.state == 'progress')
+ {
+ this.setState({ progressPercent: Math.round(100*params.done/(params.total||1)) });
+ }
+ else if (params.state == 'finish-box')
+ {
+ this.setState({ progressPercent: 100 });
+ }
+ else if (params.state == 'complete')
+ {
+ this.setState({ progressText: '', progressPercent: 0 });
+ }
+ this.setState({ sync: params.progress });
+ });
+ }
-Store.loadAccounts();
+ loadAccounts()
+ {
+ superagent.get('backend/folders').end((err, res) =>
+ {
+ let ixOfAll = {
+ received: 1,
+ outbox: 3,
+ sent: 4,
+ drafts: 5,
+ spam: 6,
+ trash: 7
+ };
+ let 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-0);
+ if (account.folderTypes[f.name])
+ {
+ accounts[0].folders[ixOfAll[account.folderTypes[f.name]]].unreadCount += (f.unread_count-0);
+ }
+ account.unreadCount += (f.unread_count-0);
+ }
+ accounts.push(account);
+ accounts[0].unreadCount += account.unreadCount;
+ accounts[0].folders[0].unreadCount += account.unreadCount;
+ accounts[0].folders[2].unreadCount += account.folders[1].unreadCount;
+ }
+ this.setState({ accounts });
+ });
+ }
+
+ loadFolder = (folderParams) =>
+ {
+ superagent.get('backend/groups').query(folderParams).end((err, res) =>
+ {
+ let groups = res.body.groups.map(g => ({ name: Util.getGroupName(g.name), messageCount: g.count-0, start: 0 }));
+ let start = 0;
+ for (let i = 0; i < groups.length; i++)
+ {
+ groups[i].start = start;
+ start += groups[i].messageCount;
+ }
+ this.setState({
+ folderParams: folderParams,
+ listGroups: groups,
+ messages: []
+ });
+ });
+ }
+
+ search = (text) =>
+ {
+ this.loadFolder({ ...this.state.folderParams, search: text });
+ }
+
+ loadMessages = (start, count) =>
+ {
+ let p = { ...this.state.folderParams };
+ p.offset = start;
+ p.limit = count;
+ superagent.get('backend/messages').query(p).end((err, res) =>
+ {
+ let msgs = [ ...this.state.messages ];
+ let par = res.body.messages;
+ par.unshift(par.length);
+ par.unshift(start);
+ msgs.splice.apply(msgs, par);
+ this.setState({ messages: msgs });
+ });
+ }
+
+ loadMessage = (msgId, callback) =>
+ {
+ superagent.get('backend/message').query({ msgId: msgId }).end((err, res) =>
+ {
+ callback(res.body.msg);
+ });
+ }
+
+ startResync = () =>
+ {
+ superagent.post('backend/sync').send().end((err, res) =>
+ {
+ });
+ }
+
+ setLayout = (l) =>
+ {
+ this.setState({ layout: l });
+ }
+
+ toggleQuickReply = () =>
+ {
+ this.setState({ quickReply: !this.state.quickReply });
+ }
+
+ setMessage = (msg) =>
+ {
+ this.setState({ msg });
+ }
+
+ render()
+ {
+ return (
+
+
+
+
+
+
+
+
+
+ ,
+ ,
+ ]
+ },
+ {
+ icon: 'mail_drafts',
+ i16: true,
+ title: 'Compose Message',
+ children: [
+
+ ]
+ }
+ ]}
+ />
+
);
+ }
+
+ componentDidMount()
+ {
+ this.loadAccounts();
+ this.startIo();
+ }
+}
+
+ReactDOM.render(
, document.getElementById('app'));