diff --git a/widgets/msglist.go b/widgets/msglist.go index fef396b..22695ec 100644 --- a/widgets/msglist.go +++ b/widgets/msglist.go @@ -14,6 +14,8 @@ import ( type MessageStore struct { DirInfo types.DirectoryInfo Messages map[uint32]*types.MessageInfo + // Ordered list of known UIDs + Uids []uint32 // Map of uids we've asked the worker to fetch onUpdate func(store *MessageStore) pendingBodies map[uint32]interface{} @@ -67,9 +69,11 @@ func (store *MessageStore) Update(msg types.WorkerMessage) { } } store.Messages = newMap + store.Uids = msg.Uids update = true break case *types.MessageInfo: + // TODO: merge message info into existing record, if applicable store.Messages[msg.Uid] = msg if _, ok := store.pendingHeaders[msg.Uid]; msg.Envelope != nil && ok { delete(store.pendingHeaders, msg.Uid) @@ -90,6 +94,7 @@ type MessageList struct { conf *config.AercConfig logger *log.Logger onInvalidate func(d ui.Drawable) + selected int spinner *Spinner store *MessageStore } @@ -97,8 +102,9 @@ type MessageList struct { // TODO: fish in config func NewMessageList(logger *log.Logger) *MessageList { ml := &MessageList{ - logger: logger, - spinner: NewSpinner(), + logger: logger, + selected: 0, + spinner: NewSpinner(), } ml.spinner.OnInvalidate(func(_ ui.Drawable) { ml.Invalidate() @@ -131,7 +137,10 @@ func (ml *MessageList) Draw(ctx *ui.Context) { row int = 0 ) - for uid, msg := range ml.store.Messages { + for i := len(ml.store.Uids) - 1; i >= 0; i-- { + uid := ml.store.Uids[i] + msg := ml.store.Messages[uid] + if row >= ctx.Height() { break } @@ -139,8 +148,18 @@ func (ml *MessageList) Draw(ctx *ui.Context) { if msg == nil { needsHeaders = append(needsHeaders, uid) ml.spinner.Draw(ctx.Subcontext(0, row, ctx.Width(), 1)) + row += 1 + continue } + style := tcell.StyleDefault + if row == ml.selected { + style = style.Background(tcell.ColorWhite). + Foreground(tcell.ColorBlack) + } + ctx.Fill(0, row, ctx.Width(), 1, ' ', style) + ctx.Printf(0, row, style, "%s", msg.Envelope.Subject) + row += 1 } diff --git a/widgets/spinner.go b/widgets/spinner.go index 812fb2c..bafc712 100644 --- a/widgets/spinner.go +++ b/widgets/spinner.go @@ -73,7 +73,7 @@ func (s *Spinner) IsRunning() bool { func (s *Spinner) Draw(ctx *ui.Context) { if !s.IsRunning() { - return + s.Start() } ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault) diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go new file mode 100644 index 0000000..383a8a8 --- /dev/null +++ b/worker/imap/fetch.go @@ -0,0 +1,46 @@ +package imap + +import ( + "github.com/emersion/go-imap" + + "git.sr.ht/~sircmpwn/aerc2/worker/types" +) + +func (imapw *IMAPWorker) handleFetchMessageHeaders( + msg *types.FetchMessageHeaders) { + + imapw.worker.Logger.Printf("Fetching message headers") + + go func() { + messages := make(chan *imap.Message) + done := make(chan error, 1) + items := []imap.FetchItem{ + imap.FetchEnvelope, + imap.FetchInternalDate, + imap.FetchFlags, + imap.FetchUid, + } + go func() { + done <- imapw.client.UidFetch(&msg.Uids, items, messages) + }() + go func() { + for msg := range messages { + imapw.worker.PostMessage(&types.MessageInfo{ + Envelope: msg.Envelope, + Flags: msg.Flags, + InternalDate: msg.InternalDate, + Uid: msg.Uid, + }, nil) + } + if err := <-done; err != nil { + imapw.worker.PostMessage(&types.Error{ + Message: types.RespondTo(msg), + Error: err, + }, nil) + } else { + imapw.worker.PostMessage( + &types.Done{types.RespondTo(msg)}, nil) + } + }() + }() +} diff --git a/worker/imap/worker.go b/worker/imap/worker.go index 51cb221..1646165 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -154,6 +154,8 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error { w.handleOpenDirectory(msg) case *types.FetchDirectoryContents: w.handleFetchDirectoryContents(msg) + case *types.FetchMessageHeaders: + w.handleFetchMessageHeaders(msg) default: return errUnsupported }