likeopera-frontend/mail.js

423 lines
14 KiB
JavaScript

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 _ from './I18n.js';
import Util from './Util.js';
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame;
class MainWindow extends React.PureComponent
{
state = {
layout: 'message-on-right',
quickReply: true,
msg: null,
threads: false,
accounts: [],
listGroups: [],
messages: []
}
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 });
});
}
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' }, // FIXME
{ 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_count-0, 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)
{
if (f.kind)
{
account.folderTypes[f.name] = f.kind;
}
}
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 });
if (f.kind != 'sent' && f.kind != 'trash' && f.kind != 'drafts')
{
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);
// unread total
accounts[0].unreadCount += account.folders[0].unreadCount;
accounts[0].folders[0].unreadCount += account.folders[0].unreadCount;
// pinned total (not unread)
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);
});
}
toggleRead = (is_read) =>
{
// FIXME: Support marking multiple messages as read
let msg = this.state.msg;
if (!msg)
{
return;
}
let flags = [ ...msg.flags.filter(f => f != 'unread') ];
if (!is_read)
{
flags.push('unread');
}
superagent.post('backend/mark').query({ msgId: msg.id, flag: 'seen', del: is_read ? '' : '1' }).end((err, res) =>
{
if (this.state.msg == msg)
{
let newMsg = { ...msg, flags };
this.setState({
msg: newMsg,
messages: this.state.messages.map(m => m == msg ? newMsg : m),
});
}
});
}
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 (<div>
<DropDownMenu
id="account"
items={[ {
icon: 'mail_unread',
i16: true,
text: 'Read vitalif@mail.ru'
}, {
icon: 'folder',
text: 'IMAP Folders',
}, {
icon: 'properties',
text: 'Properties...'
} ]}
/>
<DropDownMenu
id="reply"
items={[ {
hotkey: 'R',
icon: 'mail_reply',
text: 'Reply'
}, {
icon: 'mail_reply',
text: 'Reply to Sender',
}, {
disabled: true,
icon: 'mail_reply_all',
text: 'Reply to List'
} ]}
/>
<DropDownMenu
id="forward"
items={[ {
hotkey: 'F',
icon: 'mail_forward',
text: 'Reply'
}, {
hotkey: 'D',
text: 'Redirect'
} ]}
/>
<DropDownMenu
id="delete"
items={[ {
text: 'Move to Trash'
}, {
icon: 'delete',
text: 'Delete Permanently'
} ]}
/>
<DropDownMenu
id="check-send"
items={[ {
hotkey: 'Ctrl-K',
icon: 'mail_check',
text: 'Check All'
}, {
hotkey: 'Ctrl-Shift-K',
icon: 'mail_send',
text: 'Send Queued'
}, { split: true }, {
icon: 'mail_check',
text: 'vitalif@mail.ru'
}, {
icon: 'mail_check',
text: 'vitalif@yourcmc.ru'
}, { split: true }, {
text: 'Resynchronize All Messages'
} ]}
/>
<DropDownMenu
id="threads"
items={[ {
icon: 'thread',
text: 'Show Message Thread'
}, {
text: 'Follow Thread'
}, {
text: 'Ignore Thread'
}, { split: true }, {
hotkey: 'M',
icon: 'read',
text: 'Mark Thread as Read'
}, { split: true }, {
hotkey: 'N',
text: 'Mark Thread and Go to Next Unread'
} ]}
/>
<ListSortSettingsWindow
id="list-sort"
window={true}
folder="INBOX"
override={false}
markDelay={-1}
sorting={{}}
defaultSorting={{
sort: {
sortby: 'sent date',
group: 'date',
ascending: false,
threaded: false
}
}}
show={{
read: true,
trash: false,
spam: false,
lists: true,
sent: true,
dups: true
}}
/>
<MailSettingsWindow
id="settings"
defaultSorting={{
sort: {
sortby: 'sent date',
group: 'date',
ascending: false,
threaded: false
}
}}
layout={this.state.layout}
quickReply={this.state.quickReply}
setLayout={this.setLayout}
toggleQuickReply={this.toggleQuickReply}
/>
<FolderList
accounts={this.state.accounts}
progressText={this.state.progressText}
progressPercent={this.state.progressPercent}
startResync={this.startResync}
loadFolder={this.loadFolder}
/>
<TabPanel
tabs={[
{
className: this.state.layout,
noclose: true,
icon: 'mail_unread',
title: 'Unread (64)',
children: [
<MessageList
key="1"
loadMessages={this.loadMessages}
loadMessage={this.loadMessage}
setMessage={this.setMessage}
search={this.search}
threads={this.state.threads}
layout={this.state.layout}
groups={this.state.listGroups}
messages={this.state.messages}
/>,
<MessageView
key="2"
layout={this.state.layout}
quickReply={this.state.quickReply}
msg={this.state.msg}
onToggleRead={this.toggleRead}
/>,
]
},
{
icon: 'mail_drafts',
i16: true,
title: 'Compose Message',
children: [
<ComposeWindow
key="1"
accounts={this.state.accounts}
/>
]
}
]}
/>
</div>);
}
componentDidMount()
{
this.loadAccounts();
this.startIo();
}
}
ReactDOM.render(<MainWindow />, document.getElementById('app'));