Clean up some mess with flags, fix read/unread switching

master
Vitaliy Filippov 2019-05-20 19:35:28 +03:00
parent 54f872e264
commit 0058d37543
3 changed files with 68 additions and 29 deletions

View File

@ -40,7 +40,7 @@ class ImapManager
if (boxName && this.selected[connKey] != boxName)
{
// select different box
await new Promise((r, e) => this.connections[connKey].openBox(boxName, true, r));
await new Promise((r, e) => this.connections[connKey].openBox(boxName, false, r));
this.selected[connKey] = boxName;
}
this.busy[connKey] = true;
@ -79,7 +79,7 @@ class ImapManager
if (boxName)
{
await new Promise((r, e) => srv.openBox(boxName, true, r));
await new Promise((r, e) => srv.openBox(boxName, false, r));
this.selected[connKey] = boxName;
}
@ -253,10 +253,7 @@ class ImapManager
});
await new Promise((r, e) => msg.once('end', r));
msgrow.uid = attrs.uid;
msgrow.flags = attrs.flags.map(f => f[0] == '\\' ? f.toLowerCase().replace(/^\\/, '') : f.replace(/^\$*/, '$'));
let nf = msgrow.flags.filter(f => f != 'seen');
nf = nf.length == msgrow.flags.length ? nf.concat(['unread']) : nf;
msgrow.flags = nf.sort();
msgrow.flags = attrs.flags.map(f => f[0] == '\\' ? f.toLowerCase().replace(/^\\/, '') : f.replace(/^\$*/, '$')).sort();
return [ msgrow, attrs ];
}
}

View File

@ -283,9 +283,8 @@ class Syncer
}
else
{
// recent тут уже не будет
let flags = m.flags.filter(f => f != 'recent').sort();
if (fetchState.flags[m.uid].sort().join(',') != flags.join(','))
let flags = this.transformFlags(m.flags);
if (fetchState.flags[m.uid].join(',') != flags.join(','))
{
fetchState.updateFlags.push({ uid: m.uid, flags: toPgArray(flags) });
}
@ -293,7 +292,11 @@ class Syncer
}
}
fetchState.synced += messages.length;
//this.events.emit('sync', { state: 'progress', done: fetchState.synced, total: fetchState.total });
if (fetchState.synced-(fetchState.prevSynced||0) >= fetchState.total/100)
{
this.events.emit('sync', { state: 'progress', done: fetchState.synced, total: fetchState.total });
fetchState.prevSynced = fetchState.synced;
}
process.stderr.write('\rsynchronizing '+fetchState.synced);
}
@ -474,6 +477,20 @@ class Syncer
return msg;
}
transformFlags(flags)
{
// the absence of 'seen' is transformed into the added 'unread' flag
// 'unseen' is something that mail.ru adds by itself
// 'unread' is removed to not mess up with our 'unread'
flags = flags.filter(f => f != 'unseen' && f != 'recent' && f != 'unread');
if (!flags.filter(f => f == 'seen').length)
flags = [ ...flags, 'unread' ];
else
flags = flags.filter(f => f != 'seen');
flags = flags.sort();
return flags;
}
extractAttachments(struct, attachments)
{
attachments = attachments || [];
@ -547,7 +564,7 @@ class Syncer
msgrow.inreplyto = header.inReplyTo && header.inReplyTo[0] || '';
msgrow.time = header.date;
msgrow.size = attrs.size;
msgrow.flags = toPgArray(msgrow.flags.filter(f => f != 'recent'));
msgrow.flags = toPgArray(this.transformFlags(msgrow.flags));
msgrow.refs = toPgArray(header.references);
for (let i in msgrow)
if (typeof msgrow[i] == 'string')
@ -644,7 +661,7 @@ class Syncer
let rows = await SQL.select(
this.pg, { m: 'messages', f: 'folders' },
'f.account_id, m.folder_id, f.name folder_name, m.uid',
{ id: msgIds, 'f.id=m.folder_id': [] },
{ 'm.id': msgIds, 'f.id=m.folder_id': [] },
{ order_by: 'm.folder_id, m.uid' }
);
if (!rows.length)
@ -658,41 +675,63 @@ class Syncer
}
for (let i = 0; i < rows.length; i++)
{
uids.push(rows[i].uid);
if (i == rows.length-1 || i > 0 && rows[i].folder_id != rows[i-1].folder_id)
{
let srv = await this.imap.getConnection(rows[i].account_id, rows[i].folder_name);
await new Promise((r, j) => srv[action+'Flags'](uids, flags.map(f => '\\'+f.substr(0, 1).toUpperCase()+f.substr(1)), r));
await new Promise((ok, no) => srv[action+'Flags'](
uids,
flags.map(f => '\\'+f.substr(0, 1).toUpperCase()+f.substr(1)),
(err, res) => err ? no(err) : ok(res)
));
this.imap.releaseConnection(rows[i].account_id);
uids = [];
}
else
{
uids.push(rows[i].uid);
}
}
let upd = 'flags', bind = [];
if (action == 'add')
{
for (let flag of flags)
{
upd = upd + ' || (case when flags @> array[?] then \'{}\' else array[?] end)';
bind.push(flag, flag);
if (flag == 'seen')
{
// instead of the 'seen' flag we store the absence of 'unread'
upd = 'array_remove(' + upd + ', ?)';
bind.push('unread');
}
else
{
upd = upd + ' || (case when flags @> array[?] then \'{}\' else array[?] end)';
bind.push(flag, flag);
}
}
}
else if (action == 'del')
{
for (let flag of flags)
{
upd = 'array_remove('+upd+', ?)';
bind.push(flag);
if (flag == 'seen')
{
// instead of the absence of 'seen' flag we store 'unread'
upd = upd + ' || (case when flags @> array[?] then \'{}\' else array[?] end)';
bind.push('unread', 'unread');
}
else
{
upd = 'array_remove('+upd+', ?)';
bind.push(flag);
}
}
}
else
{
flags = flags.filter(f => f == 'seen').length > 0
? flags.filter(f => f != 'seen')
: [ ...flags, 'unread' ];
upd = 'array[' + flags.map(f => '?').join(', ') + ']::text[]';
bind = [ ...flags ];
}
await SQL.update(this.pg, 'messages', { ['flags = '+upd]: bind }, { id: msgIds });
await SQL.update(this.pg, 'messages m', { ['flags = '+upd]: bind }, { 'm.id': msgIds });
}
}

View File

@ -77,11 +77,14 @@ class SyncerWeb
'select id, name, email, settings->\'folders\' folderMap,'+
' (select count(*) from messages m, folders f'+
' where m.folder_id=f.id and f.account_id=a.id'+
' and (flags @> array[\'pinned\',\'unread\'])) pinned_unread_count'+
' and (flags @> array[\'flagged\',\'unread\'])) pinned_unread_count, '+
' (select count(*) from messages m, folders f'+
' where m.folder_id=f.id and f.account_id=a.id'+
' and (flags @> array[\'flagged\'])) pinned_count'+
' from accounts a'
)).rows;
const folders = (await this.pg.query(
'select id, account_id, name,'+
'select id, account_id, name, kind,'+
' (select count(*) from messages m where m.folder_id=f.id) total_count,'+
' (select count(*) from messages m where m.folder_id=f.id and (flags @> array[\'unread\'])) unread_count'+
' from folders f order by account_id, name'
@ -106,15 +109,11 @@ class SyncerWeb
{
p['m.folder_id'] = query.folderId;
}
else if (query.folderType == 'unread')
{
p['(flags @> array[\'unread\'])'] = [];
}
else if (query.folderType == 'pinned')
{
p['(flags @> array[\'flagged\'])'] = [];
}
else if (query.folderType == 'inbox')
else if (query.folderType == 'inbox' || query.folderType == 'unread')
{
let folders = Object.keys(this.syncer.accounts)
.map(id => [ id, this.syncer.accounts[id].settings.folders.spam ])
@ -122,6 +121,10 @@ class SyncerWeb
p['(f.account_id, f.name) NOT IN ('+folders.map(f => '(?, ?)').join(', ')+')'] =
[].concat.apply([], folders);
p['f.kind NOT IN (?, ?, ?)'] = [ 'sent', 'drafts', 'trash' ];
if (query.folderType == 'unread')
{
p['(flags @> array[\'unread\'])'] = [];
}
}
else if (query.folderType == 'sent')
{