about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2019-07-07 22:43:56 -0400
committerDrew DeVault <sir@cmpwn.com>2019-07-08 16:06:23 -0400
commitcce7cb48081ca090ac2d3a0e781dfbc25d581946 (patch)
tree0709eff3daf75ac975bc9e12f068d7951aeaefe6
parentc79577d37675c8d9ed3355c532a215377e76d3b2 (diff)
downloadaerc-cce7cb48081ca090ac2d3a0e781dfbc25d581946.tar.gz
Factor UI models out of the worker message package
Before, the information needed to display different parts of the UI was
tightly coupled to the specific messages being sent back and forth to
the backend worker. Separating out a models package allows us to be more
specific about exactly what a backend is able to and required to
provide for the UI.
-rw-r--r--lib/indexformat.go4
-rw-r--r--lib/msgstore.go37
-rw-r--r--models/models.go52
-rw-r--r--widgets/account.go9
-rw-r--r--widgets/dirlist.go2
-rw-r--r--widgets/msglist.go4
-rw-r--r--widgets/msgviewer.go16
-rw-r--r--widgets/providesmessage.go4
-rw-r--r--worker/imap/fetch.go41
-rw-r--r--worker/imap/list.go9
-rw-r--r--worker/imap/worker.go29
-rw-r--r--worker/types/messages.go25
12 files changed, 146 insertions, 86 deletions
diff --git a/lib/indexformat.go b/lib/indexformat.go
index 9e7a805..43d2ef8 100644
--- a/lib/indexformat.go
+++ b/lib/indexformat.go
@@ -9,11 +9,11 @@ import (
 	"github.com/emersion/go-imap"
 
 	"git.sr.ht/~sircmpwn/aerc/config"
-	"git.sr.ht/~sircmpwn/aerc/worker/types"
+	"git.sr.ht/~sircmpwn/aerc/models"
 )
 
 func ParseIndexFormat(conf *config.AercConfig, number int,
-	msg *types.MessageInfo) (string, []interface{}, error) {
+	msg *models.MessageInfo) (string, []interface{}, error) {
 
 	format := conf.Ui.IndexFormat
 	retval := make([]byte, 0, len(format))
diff --git a/lib/msgstore.go b/lib/msgstore.go
index 09cf31f..77160ae 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -6,14 +6,15 @@ import (
 
 	"github.com/emersion/go-imap"
 
+	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
 // Accesses to fields must be guarded by MessageStore.Lock/Unlock
 type MessageStore struct {
 	Deleted  map[uint32]interface{}
-	DirInfo  types.DirectoryInfo
-	Messages map[uint32]*types.MessageInfo
+	DirInfo  models.DirectoryInfo
+	Messages map[uint32]*models.MessageInfo
 	// Ordered list of known UIDs
 	Uids []uint32
 
@@ -33,7 +34,7 @@ type MessageStore struct {
 }
 
 func NewMessageStore(worker *types.Worker,
-	dirInfo *types.DirectoryInfo) *MessageStore {
+	dirInfo *models.DirectoryInfo) *MessageStore {
 
 	return &MessageStore{
 		Deleted: make(map[uint32]interface{}),
@@ -106,11 +107,11 @@ func (store *MessageStore) FetchBodyPart(
 		if !ok {
 			return
 		}
-		cb(msg.Reader)
+		cb(msg.Part.Reader)
 	})
 }
 
-func merge(to *types.MessageInfo, from *types.MessageInfo) {
+func merge(to *models.MessageInfo, from *models.MessageInfo) {
 	if from.BodyStructure != nil {
 		to.BodyStructure = from.BodyStructure
 	}
@@ -131,11 +132,11 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
 	update := false
 	switch msg := msg.(type) {
 	case *types.DirectoryInfo:
-		store.DirInfo = *msg
+		store.DirInfo = *msg.Info
 		store.worker.PostAction(&types.FetchDirectoryContents{}, nil)
 		update = true
 	case *types.DirectoryContents:
-		newMap := make(map[uint32]*types.MessageInfo)
+		newMap := make(map[uint32]*models.MessageInfo)
 		for _, uid := range msg.Uids {
 			if msg, ok := store.Messages[uid]; ok {
 				newMap[uid] = msg
@@ -147,14 +148,14 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
 		store.Uids = msg.Uids
 		update = true
 	case *types.MessageInfo:
-		if existing, ok := store.Messages[msg.Uid]; ok && existing != nil {
-			merge(existing, msg)
+		if existing, ok := store.Messages[msg.Info.Uid]; ok && existing != nil {
+			merge(existing, msg.Info)
 		} else {
-			store.Messages[msg.Uid] = msg
+			store.Messages[msg.Info.Uid] = msg.Info
 		}
-		if _, ok := store.pendingHeaders[msg.Uid]; msg.Envelope != nil && ok {
-			delete(store.pendingHeaders, msg.Uid)
-			if cbs, ok := store.headerCallbacks[msg.Uid]; ok {
+		if _, ok := store.pendingHeaders[msg.Info.Uid]; msg.Info.Envelope != nil && ok {
+			delete(store.pendingHeaders, msg.Info.Uid)
+			if cbs, ok := store.headerCallbacks[msg.Info.Uid]; ok {
 				for _, cb := range cbs {
 					cb(msg)
 				}
@@ -162,11 +163,11 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
 		}
 		update = true
 	case *types.FullMessage:
-		if _, ok := store.pendingBodies[msg.Uid]; ok {
-			delete(store.pendingBodies, msg.Uid)
-			if cbs, ok := store.bodyCallbacks[msg.Uid]; ok {
+		if _, ok := store.pendingBodies[msg.Content.Uid]; ok {
+			delete(store.pendingBodies, msg.Content.Uid)
+			if cbs, ok := store.bodyCallbacks[msg.Content.Uid]; ok {
 				for _, cb := range cbs {
-					cb(msg.Reader)
+					cb(msg.Content.Reader)
 				}
 			}
 		}
@@ -283,7 +284,7 @@ func (store *MessageStore) Read(uids []uint32, read bool,
 	}, cb)
 }
 
-func (store *MessageStore) Selected() *types.MessageInfo {
+func (store *MessageStore) Selected() *models.MessageInfo {
 	return store.Messages[store.Uids[len(store.Uids)-store.selected-1]]
 }
 
diff --git a/models/models.go b/models/models.go
new file mode 100644
index 0000000..cff05b1
--- /dev/null
+++ b/models/models.go
@@ -0,0 +1,52 @@
+package models
+
+import (
+	"io"
+	"time"
+
+	"github.com/emersion/go-imap"
+	"github.com/emersion/go-message/mail"
+)
+
+type Directory struct {
+	Name       string
+	Attributes []string
+}
+
+type DirectoryInfo struct {
+	Name     string
+	Flags    []string
+	ReadOnly bool
+
+	// The total number of messages in this mailbox.
+	Exists int
+
+	// The number of messages not seen since the last time the mailbox was opened.
+	Recent int
+
+	// The number of unread messages
+	Unseen int
+}
+
+// A MessageInfo holds information about the structure of a message
+type MessageInfo struct {
+	BodyStructure *imap.BodyStructure
+	Envelope      *imap.Envelope
+	Flags         []string
+	InternalDate  time.Time
+	RFC822Headers *mail.Header
+	Size          uint32
+	Uid           uint32
+}
+
+// A MessageBodyPart can be displayed in the message viewer
+type MessageBodyPart struct {
+	Reader io.Reader
+	Uid    uint32
+}
+
+// A FullMessage is the entire message
+type FullMessage struct {
+	Reader io.Reader
+	Uid    uint32
+}
diff --git a/widgets/account.go b/widgets/account.go
index 0948c5c..e08a253 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -9,6 +9,7 @@ import (
 	"git.sr.ht/~sircmpwn/aerc/config"
 	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/lib/ui"
+	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
@@ -169,7 +170,7 @@ func (acct *AccountView) SelectedAccount() *AccountView {
 	return acct
 }
 
-func (acct *AccountView) SelectedMessage() *types.MessageInfo {
+func (acct *AccountView) SelectedMessage() *models.MessageInfo {
 	return acct.msglist.Selected()
 }
 
@@ -195,11 +196,11 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
 			acct.dirlist.UpdateList(nil)
 		}
 	case *types.DirectoryInfo:
-		if store, ok := acct.msgStores[msg.Name]; ok {
+		if store, ok := acct.msgStores[msg.Info.Name]; ok {
 			store.Update(msg)
 		} else {
-			store = lib.NewMessageStore(acct.worker, msg)
-			acct.msgStores[msg.Name] = store
+			store = lib.NewMessageStore(acct.worker, msg.Info)
+			acct.msgStores[msg.Info.Name] = store
 			store.OnUpdate(func(_ *lib.MessageStore) {
 				store.OnUpdate(nil)
 				acct.msglist.SetStore(store)
diff --git a/widgets/dirlist.go b/widgets/dirlist.go
index 4dc8fd2..c5e4a0c 100644
--- a/widgets/dirlist.go
+++ b/widgets/dirlist.go
@@ -55,7 +55,7 @@ func (dirlist *DirectoryList) UpdateList(done func(dirs []string)) {
 
 			switch msg := msg.(type) {
 			case *types.Directory:
-				dirs = append(dirs, msg.Name)
+				dirs = append(dirs, msg.Dir.Name)
 			case *types.Done:
 				sort.Strings(dirs)
 				dirlist.store.Update(dirs)
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 211cbce..7051478 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -11,7 +11,7 @@ import (
 	"git.sr.ht/~sircmpwn/aerc/config"
 	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/lib/ui"
-	"git.sr.ht/~sircmpwn/aerc/worker/types"
+	"git.sr.ht/~sircmpwn/aerc/models"
 )
 
 type MessageList struct {
@@ -176,7 +176,7 @@ func (ml *MessageList) Empty() bool {
 	return store == nil || len(store.Uids) == 0
 }
 
-func (ml *MessageList) Selected() *types.MessageInfo {
+func (ml *MessageList) Selected() *models.MessageInfo {
 	store := ml.Store()
 	return store.Messages[store.Uids[len(store.Uids)-ml.store.SelectedIndex()-1]]
 }
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index b0ae79e..6a645f9 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -20,7 +20,7 @@ import (
 	"git.sr.ht/~sircmpwn/aerc/config"
 	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/lib/ui"
-	"git.sr.ht/~sircmpwn/aerc/worker/types"
+	"git.sr.ht/~sircmpwn/aerc/models"
 )
 
 var ansi = regexp.MustCompile("^\x1B\\[[0-?]*[ -/]*[@-~]")
@@ -31,7 +31,7 @@ type MessageViewer struct {
 	conf     *config.AercConfig
 	err      error
 	grid     *ui.Grid
-	msg      *types.MessageInfo
+	msg      *models.MessageInfo
 	switcher *PartSwitcher
 	store    *lib.MessageStore
 }
@@ -44,7 +44,7 @@ type PartSwitcher struct {
 }
 
 func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
-	store *lib.MessageStore, msg *types.MessageInfo) *MessageViewer {
+	store *lib.MessageStore, msg *models.MessageInfo) *MessageViewer {
 
 	grid := ui.NewGrid().Rows([]ui.GridSpec{
 		{ui.SIZE_EXACT, 4}, // TODO: Based on number of header rows
@@ -112,7 +112,7 @@ handle_error:
 }
 
 func enumerateParts(conf *config.AercConfig, store *lib.MessageStore,
-	msg *types.MessageInfo, body *imap.BodyStructure,
+	msg *models.MessageInfo, body *imap.BodyStructure,
 	showHeaders bool, index []int) ([]*PartViewer, error) {
 
 	var parts []*PartViewer
@@ -140,7 +140,7 @@ func enumerateParts(conf *config.AercConfig, store *lib.MessageStore,
 }
 
 func createSwitcher(switcher *PartSwitcher, conf *config.AercConfig,
-	store *lib.MessageStore, msg *types.MessageInfo, showHeaders bool) error {
+	store *lib.MessageStore, msg *models.MessageInfo, showHeaders bool) error {
 	var err error
 	switcher.showHeaders = showHeaders
 
@@ -212,7 +212,7 @@ func (mv *MessageViewer) SelectedAccount() *AccountView {
 	return mv.acct
 }
 
-func (mv *MessageViewer) SelectedMessage() *types.MessageInfo {
+func (mv *MessageViewer) SelectedMessage() *models.MessageInfo {
 	return mv.msg
 }
 
@@ -321,7 +321,7 @@ type PartViewer struct {
 	fetched     bool
 	filter      *exec.Cmd
 	index       []int
-	msg         *types.MessageInfo
+	msg         *models.MessageInfo
 	pager       *exec.Cmd
 	pagerin     io.WriteCloser
 	part        *imap.BodyStructure
@@ -333,7 +333,7 @@ type PartViewer struct {
 }
 
 func NewPartViewer(conf *config.AercConfig,
-	store *lib.MessageStore, msg *types.MessageInfo,
+	store *lib.MessageStore, msg *models.MessageInfo,
 	part *imap.BodyStructure, showHeaders bool,
 	index []int) (*PartViewer, error) {
 
diff --git a/widgets/providesmessage.go b/widgets/providesmessage.go
index 4b71637..d8b1e77 100644
--- a/widgets/providesmessage.go
+++ b/widgets/providesmessage.go
@@ -5,7 +5,7 @@ import (
 
 	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/lib/ui"
-	"git.sr.ht/~sircmpwn/aerc/worker/types"
+	"git.sr.ht/~sircmpwn/aerc/models"
 )
 
 type PartInfo struct {
@@ -19,6 +19,6 @@ type ProvidesMessage interface {
 	ui.Drawable
 	Store() *lib.MessageStore
 	SelectedAccount() *AccountView
-	SelectedMessage() *types.MessageInfo
+	SelectedMessage() *models.MessageInfo
 	SelectedMessagePart() *PartInfo
 }
diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go
index 7d1bfcf..d5bb9aa 100644
--- a/worker/imap/fetch.go
+++ b/worker/imap/fetch.go
@@ -8,6 +8,7 @@ import (
 	"github.com/emersion/go-message/mail"
 	"github.com/emersion/go-message/textproto"
 
+	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
@@ -82,39 +83,49 @@ func (imapw *IMAPWorker) handleFetchMessages(
 					header = &mail.Header{message.Header{textprotoHeader}}
 				}
 				imapw.worker.PostMessage(&types.MessageInfo{
-					Message:       types.RespondTo(msg),
-					BodyStructure: _msg.BodyStructure,
-					Envelope:      _msg.Envelope,
-					Flags:         _msg.Flags,
-					InternalDate:  _msg.InternalDate,
-					RFC822Headers: header,
-					Uid:           _msg.Uid,
+					Message: types.RespondTo(msg),
+					Info: &models.MessageInfo{
+						BodyStructure: _msg.BodyStructure,
+						Envelope:      _msg.Envelope,
+						Flags:         _msg.Flags,
+						InternalDate:  _msg.InternalDate,
+						RFC822Headers: header,
+						Uid:           _msg.Uid,
+					},
 				}, nil)
 			case *types.FetchFullMessages:
 				reader := _msg.GetBody(section)
 				imapw.worker.PostMessage(&types.FullMessage{
 					Message: types.RespondTo(msg),
-					Reader:  reader,
-					Uid:     _msg.Uid,
+					Content: &models.FullMessage{
+						Reader: reader,
+						Uid:    _msg.Uid,
+					},
 				}, nil)
 				// Update flags (to mark message as read)
 				imapw.worker.PostMessage(&types.MessageInfo{
 					Message: types.RespondTo(msg),
-					Flags:   _msg.Flags,
-					Uid:     _msg.Uid,
+					Info: &models.MessageInfo{
+						Flags: _msg.Flags,
+						Uid:   _msg.Uid,
+					},
 				}, nil)
 			case *types.FetchMessageBodyPart:
 				reader := _msg.GetBody(section)
 				imapw.worker.PostMessage(&types.MessageBodyPart{
 					Message: types.RespondTo(msg),
-					Reader:  reader,
-					Uid:     _msg.Uid,
+					Part: &models.MessageBodyPart{
+						Reader: reader,
+						Uid:    _msg.Uid,
+					},
 				}, nil)
 				// Update flags (to mark message as read)
 				imapw.worker.PostMessage(&types.MessageInfo{
 					Message: types.RespondTo(msg),
-					Flags:   _msg.Flags,
-					Uid:     _msg.Uid,
+					Info: &models.MessageInfo{
+						Flags: _msg.Flags,
+						Uid:   _msg.Uid,
+					},
 				}, nil)
 			}
 		}
diff --git a/worker/imap/list.go b/worker/imap/list.go
index 708e70f..42be50e 100644
--- a/worker/imap/list.go
+++ b/worker/imap/list.go
@@ -3,6 +3,7 @@ package imap
 import (
 	"github.com/emersion/go-imap"
 
+	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
@@ -18,9 +19,11 @@ func (imapw *IMAPWorker) handleListDirectories(msg *types.ListDirectories) {
 				continue
 			}
 			imapw.worker.PostMessage(&types.Directory{
-				Message:    types.RespondTo(msg),
-				Name:       mbox.Name,
-				Attributes: mbox.Attributes,
+				Message: types.RespondTo(msg),
+				Dir: &models.Directory{
+					Name:       mbox.Name,
+					Attributes: mbox.Attributes,
+				},
 			}, nil)
 		}
 		done <- nil
diff --git a/worker/imap/worker.go b/worker/imap/worker.go
index 5005620..9ddaa47 100644
--- a/worker/imap/worker.go
+++ b/worker/imap/worker.go
@@ -10,6 +10,7 @@ import (
 	idle "github.com/emersion/go-imap-idle"
 	"github.com/emersion/go-imap/client"
 
+	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
@@ -169,13 +170,15 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) {
 			w.selected = *status
 		}
 		w.worker.PostMessage(&types.DirectoryInfo{
-			Flags:    status.Flags,
-			Name:     status.Name,
-			ReadOnly: status.ReadOnly,
-
-			Exists: int(status.Messages),
-			Recent: int(status.Recent),
-			Unseen: int(status.Unseen),
+			Info: &models.DirectoryInfo{
+				Flags:    status.Flags,
+				Name:     status.Name,
+				ReadOnly: status.ReadOnly,
+
+				Exists: int(status.Messages),
+				Recent: int(status.Recent),
+				Unseen: int(status.Unseen),
+			},
 		}, nil)
 	case *client.MessageUpdate:
 		msg := update.Message
@@ -183,11 +186,13 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) {
 			msg.Uid = w.seqMap[msg.SeqNum-1]
 		}
 		w.worker.PostMessage(&types.MessageInfo{
-			BodyStructure: msg.BodyStructure,
-			Envelope:      msg.Envelope,
-			Flags:         msg.Flags,
-			InternalDate:  msg.InternalDate,
-			Uid:           msg.Uid,
+			Info: &models.MessageInfo{
+				BodyStructure: msg.BodyStructure,
+				Envelope:      msg.Envelope,
+				Flags:         msg.Flags,
+				InternalDate:  msg.InternalDate,
+				Uid:           msg.Uid,
+			},
 		}, nil)
 	case *client.ExpungeUpdate:
 		i := update.SeqNum - 1
diff --git a/worker/types/messages.go b/worker/types/messages.go
index d9e911f..bb2505a 100644
--- a/worker/types/messages.go
+++ b/worker/types/messages.go
@@ -5,9 +5,9 @@ import (
 	"time"
 
 	"github.com/emersion/go-imap"
-	"github.com/emersion/go-message/mail"
 
 	"git.sr.ht/~sircmpwn/aerc/config"
+	"git.sr.ht/~sircmpwn/aerc/models"
 )
 
 type WorkerMessage interface {
@@ -139,17 +139,12 @@ type AppendMessage struct {
 
 type Directory struct {
 	Message
-	Attributes []string
-	Name       string
+	Dir *models.Directory
 }
 
 type DirectoryInfo struct {
 	Message
-	Flags    []string
-	Name     string
-	ReadOnly bool
-
-	Exists, Recent, Unseen int
+	Info *models.DirectoryInfo
 }
 
 type DirectoryContents struct {
@@ -164,25 +159,17 @@ type SearchResults struct {
 
 type MessageInfo struct {
 	Message
-	BodyStructure *imap.BodyStructure
-	Envelope      *imap.Envelope
-	Flags         []string
-	InternalDate  time.Time
-	RFC822Headers *mail.Header
-	Size          uint32
-	Uid           uint32
+	Info *models.MessageInfo
 }
 
 type FullMessage struct {
 	Message
-	Reader io.Reader
-	Uid    uint32
+	Content *models.FullMessage
 }
 
 type MessageBodyPart struct {
 	Message
-	Reader io.Reader
-	Uid    uint32
+	Part *models.MessageBodyPart
 }
 
 type MessagesDeleted struct {