about summary refs log tree commit diff stats
path: root/widgets
diff options
context:
space:
mode:
authorReto Brunner <reto@labrat.space>2019-07-04 11:17:21 +0200
committerDrew DeVault <sir@cmpwn.com>2019-07-04 12:24:16 -0400
commitd7fecf7740655e9d772963fd7e2a6d2af81458df (patch)
tree84237464c4d77bbfad39fefe79b9cb875956283c /widgets
parentf9d26eef58ce2b9fa1e2443032c17c2ccc6afa36 (diff)
downloadaerc-d7fecf7740655e9d772963fd7e2a6d2af81458df.tar.gz
dirlist: sync dirstore in filterDirsByFoldersConfig
Also sets the public List() method to return the unfiltered
list of directories directly from the store.
Diffstat (limited to 'widgets')
-rw-r--r--widgets/dirlist.go16
1 files changed, 7 insertions, 9 deletions
diff --git a/widgets/dirlist.go b/widgets/dirlist.go
index 1cbc055..2e7dd06 100644
--- a/widgets/dirlist.go
+++ b/widgets/dirlist.go
@@ -43,10 +43,6 @@ func NewDirectoryList(acctConf *config.AccountConfig, uiConf *config.UIConfig,
 	return dirlist
 }
 
-func (dirlist *DirectoryList) FilteredList() []string {
-	return dirlist.dirs
-}
-
 func (dirlist *DirectoryList) List() []string {
 	return dirlist.store.List()
 }
@@ -62,6 +58,7 @@ func (dirlist *DirectoryList) UpdateList(done func(dirs []string)) {
 			case *types.Done:
 				sort.Strings(dirs)
 				dirlist.store.Update(dirs)
+				dirlist.filterDirsByFoldersConfig()
 				dirlist.spinner.Stop()
 				dirlist.Invalidate()
 				if done != nil {
@@ -114,14 +111,14 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
 		return
 	}
 
-	if len(dirlist.store.List()) == 0 {
+	if len(dirlist.dirs) == 0 {
 		style := tcell.StyleDefault
 		ctx.Printf(0, 0, style, dirlist.uiConf.EmptyDirlist)
 		return
 	}
 
 	row := 0
-	for _, name := range dirlist.store.List() {
+	for _, name := range dirlist.dirs {
 		if row >= ctx.Height() {
 			break
 		}
@@ -179,15 +176,16 @@ func (dirlist *DirectoryList) Prev() {
 	dirlist.nextPrev(-1)
 }
 
-// filterDirsByFoldersConfig filters a folders list to only contain folders
-// present in the account.folders config option
+// filterDirsByFoldersConfig sets dirlist.dirs to the filtered subset of the
+// dirstore, based on the AccountConfig.Folders option
 func (dirlist *DirectoryList) filterDirsByFoldersConfig() {
+	dirlist.dirs = dirlist.store.List()
 	// config option defaults to show all if unset
 	if len(dirlist.acctConf.Folders) == 0 {
 		return
 	}
 	var filtered []string
-	for _, folder := range dirlist.store.List() {
+	for _, folder := range dirlist.dirs {
 		for _, cfgfolder := range dirlist.acctConf.Folders {
 			if folder == cfgfolder {
 				filtered = append(filtered, folder)
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
package widgets

import (
	"errors"
	"fmt"
	"log"

	"github.com/gdamore/tcell"

	"git.sr.ht/~sircmpwn/aerc/config"
	"git.sr.ht/~sircmpwn/aerc/lib"
	"git.sr.ht/~sircmpwn/aerc/lib/sort"
	"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"
)

var _ ProvidesMessages = (*AccountView)(nil)

type AccountView struct {
	acct    *config.AccountConfig
	aerc    *Aerc
	conf    *config.AercConfig
	dirlist *DirectoryList
	labels  []string
	grid    *ui.Grid
	host    TabHost
	logger  *log.Logger
	msglist *MessageList
	worker  *types.Worker
}

func (acct *AccountView) UiConfig() config.UIConfig {
	return acct.conf.GetUiConfig(map[config.ContextType]string{
		config.UI_CONTEXT_ACCOUNT: acct.AccountConfig().Name,
		config.UI_CONTEXT_FOLDER:  acct.Directories().Selected(),
	})
}

func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountConfig,
	logger *log.Logger, host TabHost) *AccountView {

	acctUiConf := conf.GetUiConfig(map[config.ContextType]string{
		config.UI_CONTEXT_ACCOUNT: acct.Name,
	})

	grid := ui.NewGrid().Rows([]ui.GridSpec{
		{ui.SIZE_WEIGHT, 1},
	}).Columns([]ui.GridSpec{
		{ui.SIZE_EXACT, acctUiConf.SidebarWidth},
		{ui.SIZE_WEIGHT, 1},
	})

	worker, err := worker.NewWorker(acct.Source, logger)
	if err != nil {
		host.SetStatus(fmt.Sprintf("%s: %s", acct.Name, err)).
			Color(tcell.ColorDefault, tcell.ColorRed)
		return &AccountView{
			acct:   acct,
			aerc:   aerc,
			grid:   grid,
			host:   host,
			logger: logger,
		}
	}

	dirlist := NewDirectoryList(conf, acct, logger, worker)
	if acctUiConf.SidebarWidth > 0 {
		grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT))
	}

	msglist := NewMessageList(conf, logger, aerc)
	grid.AddChild(msglist).At(0, 1)

	view := &AccountView{
		acct:    acct,
		aerc:    aerc,
		conf:    conf,
		dirlist: dirlist,
		grid:    grid,
		host:    host,
		logger:  logger,
		msglist: msglist,
		worker:  worker,
	}

	go worker.Backend.Run()

	worker.PostAction(&types.Configure{Config: acct}, nil)
	worker.PostAction(&types.Connect{}, view.connected)
	host.SetStatus("Connecting...")

	return view
}

func (acct *AccountView) Tick() bool {
	if acct.worker == nil {
		return false
	}
	select {
	case msg := <-acct.worker.Messages:
		msg = acct.worker.ProcessMessage(msg)
		acct.onMessage(msg)
		return true
	default:
		return false
	}
}

func (acct *AccountView) AccountConfig() *config.AccountConfig {
	return acct.acct
}

func (acct *AccountView) Worker() *types.Worker {
	return acct.worker
}

func (acct *AccountView) Logger() *log.Logger {
	return acct.logger
}

func (acct *AccountView) Name() string {
	return acct.acct.Name
}

func (acct *AccountView) Children() []ui.Drawable {
	return acct.grid.Children()
}

func (acct *AccountView) OnInvalidate(onInvalidate func(d ui.Drawable)) {
	acct.grid.OnInvalidate(func(_ ui.Drawable) {
		onInvalidate(acct)
	})
}

func (acct *AccountView) Invalidate() {
	acct.grid.Invalidate()
}

func (acct *AccountView) Draw(ctx *ui.Context) {
	acct.grid.Draw(ctx)
}

func (acct *AccountView) MouseEvent(localX int, localY int, event tcell.Event) {
	acct.grid.MouseEvent(localX, localY, event)
}

func (acct *AccountView) Focus(focus bool) {
	// TODO: Unfocus children I guess
}

func (acct *AccountView) connected(msg types.WorkerMessage) {
	switch msg.(type) {
	case *types.Done:
		acct.host.SetStatus("Listing mailboxes...")
		acct.logger.Println("Listing mailboxes...")
		acct.dirlist.UpdateList(func(dirs []string) {
			var dir string
			for _, _dir := range dirs {
				if _dir == acct.acct.Default {
					dir = _dir
					break
				}
			}
			if dir == "" && len(dirs) > 0 {
				dir = dirs[0]
			}
			if dir != "" {
				acct.dirlist.Select(dir)
			}

			acct.msglist.SetInitDone()
			acct.logger.Println("Connected.")
			acct.host.SetStatus("Connected.")
		})
	}
}

func (acct *AccountView) Directories() *DirectoryList {
	return acct.dirlist
}

func (acct *AccountView) Labels() []string {
	return acct.labels
}

func (acct *AccountView) Messages() *MessageList {
	return acct.msglist
}

func (acct *AccountView) Store() *lib.MessageStore {
	if acct.msglist == nil {
		return nil
	}
	return acct.msglist.Store()
}

func (acct *AccountView) SelectedAccount() *AccountView {
	return acct
}

func (acct *AccountView) SelectedDirectory() string {
	return acct.dirlist.Selected()
}

func (acct *AccountView) SelectedMessage() (*models.MessageInfo, error) {
	if len(acct.msglist.Store().Uids()) == 0 {
		return nil, errors.New("no message selected")
	}
	msg := acct.msglist.Selected()
	if msg == nil {
		return nil, errors.New("message not loaded")
	}
	return msg, nil
}

func (acct *AccountView) MarkedMessages() ([]uint32, error) {
	store := acct.Store()
	return store.Marked(), nil
}

func (acct *AccountView) SelectedMessagePart() *PartInfo {
	return nil
}

func (acct *AccountView) onMessage(msg types.WorkerMessage) {
	switch msg := msg.(type) {
	case *types.Done:
		switch msg.InResponseTo().(type) {
		case *types.OpenDirectory:
			if store, ok := acct.dirlist.SelectedMsgStore(); ok {
				// If we've opened this dir before, we can re-render it from
				// memory while we wait for the update and the UI feels
				// snappier. If not, we'll unset the store and show the spinner
				// while we download the UID list.
				acct.msglist.SetStore(store)
			} else {
				acct.msglist.SetStore(nil)
			}
		case *types.CreateDirectory:
			acct.dirlist.UpdateList(nil)
		}
	case *types.DirectoryInfo:
		if store, ok := acct.dirlist.MsgStore(msg.Info.Name); ok {
			store.Update(msg)
		} else {
			store = lib.NewMessageStore(acct.worker, msg.Info,
				acct.getSortCriteria(),
				func(msg *models.MessageInfo) {
					acct.conf.Triggers.ExecNewEmail(acct.acct,
						acct.conf, msg)
				}, func() {
					if acct.UiConfig().NewMessageBell {
						acct.host.Beep()
					}
				})
			acct.dirlist.SetMsgStore(msg.Info.Name, store)
		}
	case *types.DirectoryContents:
		if store, ok := acct.dirlist.SelectedMsgStore(); ok {
			if acct.msglist.Store() == nil {
				acct.msglist.SetStore(store)
			}
			store.Update(msg)
		}
	case *types.FullMessage:
		if store, ok := acct.dirlist.SelectedMsgStore(); ok {
			store.Update(msg)
		}
	case *types.MessageInfo:
		if store, ok := acct.dirlist.SelectedMsgStore(); ok {
			store.Update(msg)
		}
	case *types.MessagesDeleted:
		if store, ok := acct.dirlist.SelectedMsgStore(); ok {
			store.Update(msg)
		}
	case *types.LabelList:
		acct.labels = msg.Labels
	case *types.Error:
		acct.logger.Printf("%v", msg.Error)
		acct.host.SetStatus(fmt.Sprintf("%v", msg.Error)).
			Color(tcell.ColorDefault, tcell.ColorRed)
	}
}

func (acct *AccountView) getSortCriteria() []*types.SortCriterion {
	if len(acct.UiConfig().Sort) == 0 {
		return nil
	}
	criteria, err := sort.GetSortCriteria(acct.UiConfig().Sort)
	if err != nil {
		acct.aerc.PushError(" ui.sort: " + err.Error())
		return nil
	}
	return criteria
}