Implement message store side of message fetching

This commit is contained in:
Drew DeVault 2019-03-14 21:51:29 -04:00
parent 0f8b7a1203
commit 11f0a7267f
4 changed files with 58 additions and 21 deletions

View File

@ -58,7 +58,7 @@ func NewAccountView(conf *config.AccountConfig,
dirlist := NewDirectoryList(conf, logger, worker) dirlist := NewDirectoryList(conf, logger, worker)
grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT)).Span(2, 1) grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT)).Span(2, 1)
msglist := NewMessageList(logger, worker) msglist := NewMessageList(logger)
grid.AddChild(msglist).At(0, 1) grid.AddChild(msglist).At(0, 1)
acct := &AccountView{ acct := &AccountView{
@ -183,7 +183,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
if store, ok := acct.msgStores[msg.Name]; ok { if store, ok := acct.msgStores[msg.Name]; ok {
store.Update(msg) store.Update(msg)
} else { } else {
acct.msgStores[msg.Name] = NewMessageStore(msg) acct.msgStores[msg.Name] = NewMessageStore(acct.worker, msg)
} }
case *types.DirectoryContents: case *types.DirectoryContents:
store := acct.msgStores[acct.dirlist.selected] store := acct.msgStores[acct.dirlist.selected]

View File

@ -3,6 +3,7 @@ package widgets
import ( import (
"log" "log"
"github.com/emersion/go-imap"
"github.com/gdamore/tcell" "github.com/gdamore/tcell"
"git.sr.ht/~sircmpwn/aerc2/config" "git.sr.ht/~sircmpwn/aerc2/config"
@ -12,20 +13,52 @@ import (
type MessageStore struct { type MessageStore struct {
DirInfo types.DirectoryInfo DirInfo types.DirectoryInfo
Messages map[uint64]*types.MessageInfo Messages map[uint32]*types.MessageInfo
// Map of uids we've asked the worker to fetch
onUpdate func(store *MessageStore)
pendingBodies map[uint32]interface{}
pendingHeaders map[uint32]interface{}
worker *types.Worker
} }
func NewMessageStore(dirInfo *types.DirectoryInfo) *MessageStore { func NewMessageStore(worker *types.Worker,
return &MessageStore{DirInfo: *dirInfo} dirInfo *types.DirectoryInfo) *MessageStore {
return &MessageStore{
DirInfo: *dirInfo,
pendingBodies: make(map[uint32]interface{}),
pendingHeaders: make(map[uint32]interface{}),
worker: worker,
}
}
func (store *MessageStore) FetchHeaders(uids []uint32) {
// TODO: this could be optimized by pre-allocating toFetch and trimming it
// at the end. In practice we expect to get most messages back in one frame.
var toFetch imap.SeqSet
for _, uid := range uids {
if _, ok := store.pendingHeaders[uid]; !ok {
toFetch.AddNum(uint32(uid))
store.pendingHeaders[uid] = nil
}
}
if !toFetch.Empty() {
store.worker.PostAction(&types.FetchMessageHeaders{
Uids: toFetch,
}, nil)
}
} }
func (store *MessageStore) Update(msg types.WorkerMessage) { func (store *MessageStore) Update(msg types.WorkerMessage) {
update := false
switch msg := msg.(type) { switch msg := msg.(type) {
case *types.DirectoryInfo: case *types.DirectoryInfo:
store.DirInfo = *msg store.DirInfo = *msg
update = true
break break
case *types.DirectoryContents: case *types.DirectoryContents:
newMap := make(map[uint64]*types.MessageInfo) newMap := make(map[uint32]*types.MessageInfo)
for _, uid := range msg.Uids { for _, uid := range msg.Uids {
if msg, ok := store.Messages[uid]; ok { if msg, ok := store.Messages[uid]; ok {
newMap[uid] = msg newMap[uid] = msg
@ -34,11 +67,23 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
} }
} }
store.Messages = newMap store.Messages = newMap
update = true
break break
case *types.MessageInfo: case *types.MessageInfo:
store.Messages[msg.Uid] = msg store.Messages[msg.Uid] = msg
if _, ok := store.pendingHeaders[msg.Uid]; msg.Envelope != nil && ok {
delete(store.pendingHeaders, msg.Uid)
}
update = true
break break
} }
if update && store.onUpdate != nil {
store.onUpdate(store)
}
}
func (store *MessageStore) OnUpdate(fn func(store *MessageStore)) {
store.onUpdate = fn
} }
type MessageList struct { type MessageList struct {
@ -47,15 +92,13 @@ type MessageList struct {
onInvalidate func(d ui.Drawable) onInvalidate func(d ui.Drawable)
spinner *Spinner spinner *Spinner
store *MessageStore store *MessageStore
worker *types.Worker
} }
// TODO: fish in config // TODO: fish in config
func NewMessageList(logger *log.Logger, worker *types.Worker) *MessageList { func NewMessageList(logger *log.Logger) *MessageList {
ml := &MessageList{ ml := &MessageList{
logger: logger, logger: logger,
spinner: NewSpinner(), spinner: NewSpinner(),
worker: worker,
} }
ml.spinner.OnInvalidate(func(_ ui.Drawable) { ml.spinner.OnInvalidate(func(_ ui.Drawable) {
ml.Invalidate() ml.Invalidate()
@ -84,7 +127,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
} }
var ( var (
needsHeaders []uint64 needsHeaders []uint32
row int = 0 row int = 0
) )
@ -102,12 +145,11 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
} }
if len(needsHeaders) != 0 { if len(needsHeaders) != 0 {
ml.store.FetchHeaders(needsHeaders)
ml.spinner.Start() ml.spinner.Start()
} else { } else {
ml.spinner.Stop() ml.spinner.Stop()
} }
// TODO: Fetch these messages
} }
func (ml *MessageList) SetStore(store *MessageStore) { func (ml *MessageList) SetStore(store *MessageStore) {

View File

@ -29,7 +29,7 @@ func (imapw *IMAPWorker) handleFetchDirectoryContents(
go func() { go func() {
seqSet := &imap.SeqSet{} seqSet := &imap.SeqSet{}
seqSet.AddRange(1, imapw.selected.Messages) seqSet.AddRange(1, imapw.selected.Messages)
uid32, err := imapw.client.UidSearch(&imap.SearchCriteria{ uids, err := imapw.client.UidSearch(&imap.SearchCriteria{
SeqNum: seqSet, SeqNum: seqSet,
}) })
if err != nil { if err != nil {
@ -38,12 +38,7 @@ func (imapw *IMAPWorker) handleFetchDirectoryContents(
Error: err, Error: err,
}, nil) }, nil)
} else { } else {
imapw.worker.Logger.Printf("Found %d UIDs", len(uid32)) imapw.worker.Logger.Printf("Found %d UIDs", len(uids))
var uids []uint64
for _, uid := range uid32 {
uids = append(uids,
(uint64(imapw.selected.UidValidity)<<32)|uint64(uid))
}
imapw.worker.PostMessage(&types.DirectoryContents{ imapw.worker.PostMessage(&types.DirectoryContents{
Message: types.RespondTo(msg), Message: types.RespondTo(msg),
Uids: uids, Uids: uids,

View File

@ -110,7 +110,7 @@ type DirectoryInfo struct {
type DirectoryContents struct { type DirectoryContents struct {
Message Message
Uids []uint64 Uids []uint32
} }
type MessageInfo struct { type MessageInfo struct {
@ -120,5 +120,5 @@ type MessageInfo struct {
InternalDate time.Time InternalDate time.Time
Mail *mail.Message Mail *mail.Message
Size uint32 Size uint32
Uid uint64 Uid uint32
} }