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

View File

@ -283,9 +283,8 @@ class Syncer
} }
else else
{ {
// recent тут уже не будет let flags = this.transformFlags(m.flags);
let flags = m.flags.filter(f => f != 'recent').sort(); if (fetchState.flags[m.uid].join(',') != flags.join(','))
if (fetchState.flags[m.uid].sort().join(',') != flags.join(','))
{ {
fetchState.updateFlags.push({ uid: m.uid, flags: toPgArray(flags) }); fetchState.updateFlags.push({ uid: m.uid, flags: toPgArray(flags) });
} }
@ -293,7 +292,11 @@ class Syncer
} }
} }
fetchState.synced += messages.length; 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); process.stderr.write('\rsynchronizing '+fetchState.synced);
} }
@ -474,6 +477,20 @@ class Syncer
return msg; 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) extractAttachments(struct, attachments)
{ {
attachments = attachments || []; attachments = attachments || [];
@ -547,7 +564,7 @@ class Syncer
msgrow.inreplyto = header.inReplyTo && header.inReplyTo[0] || ''; msgrow.inreplyto = header.inReplyTo && header.inReplyTo[0] || '';
msgrow.time = header.date; msgrow.time = header.date;
msgrow.size = attrs.size; 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); msgrow.refs = toPgArray(header.references);
for (let i in msgrow) for (let i in msgrow)
if (typeof msgrow[i] == 'string') if (typeof msgrow[i] == 'string')
@ -644,7 +661,7 @@ class Syncer
let rows = await SQL.select( let rows = await SQL.select(
this.pg, { m: 'messages', f: 'folders' }, this.pg, { m: 'messages', f: 'folders' },
'f.account_id, m.folder_id, f.name folder_name, m.uid', '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' } { order_by: 'm.folder_id, m.uid' }
); );
if (!rows.length) if (!rows.length)
@ -658,41 +675,63 @@ class Syncer
} }
for (let i = 0; i < rows.length; i++) 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) 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); 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); this.imap.releaseConnection(rows[i].account_id);
uids = []; uids = [];
} }
else
{
uids.push(rows[i].uid);
}
} }
let upd = 'flags', bind = []; let upd = 'flags', bind = [];
if (action == 'add') if (action == 'add')
{ {
for (let flag of flags) for (let flag of flags)
{ {
upd = upd + ' || (case when flags @> array[?] then \'{}\' else array[?] end)'; if (flag == 'seen')
bind.push(flag, flag); {
// 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') else if (action == 'del')
{ {
for (let flag of flags) for (let flag of flags)
{ {
upd = 'array_remove('+upd+', ?)'; if (flag == 'seen')
bind.push(flag); {
// 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 else
{ {
flags = flags.filter(f => f == 'seen').length > 0
? flags.filter(f => f != 'seen')
: [ ...flags, 'unread' ];
upd = 'array[' + flags.map(f => '?').join(', ') + ']::text[]'; upd = 'array[' + flags.map(f => '?').join(', ') + ']::text[]';
bind = [ ...flags ]; 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 id, name, email, settings->\'folders\' folderMap,'+
' (select count(*) from messages m, folders f'+ ' (select count(*) from messages m, folders f'+
' where m.folder_id=f.id and f.account_id=a.id'+ ' 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' ' from accounts a'
)).rows; )).rows;
const folders = (await this.pg.query( 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) total_count,'+
' (select count(*) from messages m where m.folder_id=f.id and (flags @> array[\'unread\'])) unread_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' ' from folders f order by account_id, name'
@ -106,15 +109,11 @@ class SyncerWeb
{ {
p['m.folder_id'] = query.folderId; p['m.folder_id'] = query.folderId;
} }
else if (query.folderType == 'unread')
{
p['(flags @> array[\'unread\'])'] = [];
}
else if (query.folderType == 'pinned') else if (query.folderType == 'pinned')
{ {
p['(flags @> array[\'flagged\'])'] = []; p['(flags @> array[\'flagged\'])'] = [];
} }
else if (query.folderType == 'inbox') else if (query.folderType == 'inbox' || query.folderType == 'unread')
{ {
let folders = Object.keys(this.syncer.accounts) let folders = Object.keys(this.syncer.accounts)
.map(id => [ id, this.syncer.accounts[id].settings.folders.spam ]) .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(', ')+')'] = p['(f.account_id, f.name) NOT IN ('+folders.map(f => '(?, ?)').join(', ')+')'] =
[].concat.apply([], folders); [].concat.apply([], folders);
p['f.kind NOT IN (?, ?, ?)'] = [ 'sent', 'drafts', 'trash' ]; p['f.kind NOT IN (?, ?, ?)'] = [ 'sent', 'drafts', 'trash' ];
if (query.folderType == 'unread')
{
p['(flags @> array[\'unread\'])'] = [];
}
} }
else if (query.folderType == 'sent') else if (query.folderType == 'sent')
{ {