~sircmpwn/aerc

a782b709d1312bfe80dda7c864de96ba1c854bc2 — Drew DeVault 2 years ago f87fe50
Add loading spinner
3 files changed, 190 insertions(+), 1 deletions(-)

M widgets/directories.go
A widgets/spinner.go
A worker/messages.go
M widgets/directories.go => widgets/directories.go +19 -1
@@ 17,13 17,24 @@ type DirectoryList struct {
	logger       *log.Logger
	onInvalidate func(d ui.Drawable)
	selected     string
	spinner      *Spinner
	worker       *types.Worker
}

func NewDirectoryList(conf *config.AccountConfig,
	logger *log.Logger, worker *types.Worker) *DirectoryList {

	return &DirectoryList{conf: conf, logger: logger, worker: worker}
	dirlist := &DirectoryList{
		conf:    conf,
		logger:  logger,
		spinner: NewSpinner(),
		worker:  worker,
	}
	dirlist.spinner.OnInvalidate(func(_ ui.Drawable) {
		dirlist.Invalidate()
	})
	dirlist.spinner.Start()
	return dirlist
}

func (dirlist *DirectoryList) UpdateList(done func(dirs []string)) {


@@ 37,6 48,7 @@ func (dirlist *DirectoryList) UpdateList(done func(dirs []string)) {
			case *types.Done:
				sort.Strings(dirs)
				dirlist.dirs = dirs
				dirlist.spinner.Stop()
				dirlist.Invalidate()
				if done != nil {
					done(dirs)


@@ 63,6 75,12 @@ func (dirlist *DirectoryList) Invalidate() {

func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)

	if dirlist.spinner.IsRunning() {
		dirlist.spinner.Draw(ctx)
		return
	}

	row := 0
	for _, name := range dirlist.dirs {
		if row >= ctx.Height() {

A widgets/spinner.go => widgets/spinner.go +80 -0
@@ 0,0 1,80 @@
package widgets

import (
	"time"

	"github.com/gdamore/tcell"

	"git.sr.ht/~sircmpwn/aerc2/lib/ui"
)

var (
	frames = []string{
		"[..]    ",
		" [..]   ",
		"  [..]  ",
		"   [..] ",
		"    [..]",
		"   [..] ",
		"  [..]  ",
		" [..]   ",
	}
)

type Spinner struct {
	frame        int
	onInvalidate func(d ui.Drawable)
	stop         chan interface{}
}

func NewSpinner() *Spinner {
	spinner := Spinner{
		stop:  make(chan interface{}),
		frame: -1,
	}
	return &spinner
}

func (s *Spinner) Start() {
	s.frame = 0
	go func() {
		for {
			select {
			case <-s.stop:
				return
			case <-time.After(200 * time.Millisecond):
				s.frame++
				if s.frame >= len(frames) {
					s.frame = 0
				}
				s.Invalidate()
			}
		}
	}()
}

func (s *Spinner) Stop() {
	s.stop <- nil
	s.frame = -1
	s.Invalidate()
}

func (s *Spinner) IsRunning() bool {
	return s.frame != -1
}

func (s *Spinner) Draw(ctx *ui.Context) {
	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
	col := ctx.Width()/2 - len(frames[0])/2 + 1
	ctx.Printf(col, 0, tcell.StyleDefault, "%s", frames[s.frame])
}

func (s *Spinner) OnInvalidate(onInvalidate func(d ui.Drawable)) {
	s.onInvalidate = onInvalidate
}

func (s *Spinner) Invalidate() {
	if s.onInvalidate != nil {
		s.onInvalidate(s)
	}
}

A worker/messages.go => worker/messages.go +91 -0
@@ 0,0 1,91 @@
package worker

import (
	"crypto/x509"

	"git.sr.ht/~sircmpwn/aerc2/config"
)

type WorkerMessage interface {
	InResponseTo() WorkerMessage
}

type Message struct {
	inResponseTo WorkerMessage
}

func RespondTo(msg WorkerMessage) Message {
	return Message{
		inResponseTo: msg,
	}
}

func (m Message) InResponseTo() WorkerMessage {
	return m.inResponseTo
}

// Meta-messages

type Done struct {
	Message
}

type Error struct {
	Message
	Error error
}

type Unsupported struct {
	Message
}

// Actions

type ApproveCertificate struct {
	Message
	Approved bool
}

type Configure struct {
	Message
	Config *config.AccountConfig
}

type Connect struct {
	Message
}

type Disconnect struct {
	Message
}

type ListDirectories struct {
	Message
}

type OpenDirectory struct {
	Message
	Directory string
}

// Messages

type CertificateApprovalRequest struct {
	Message
	CertPool *x509.CertPool
}

type Directory struct {
	Message
	Attributes []string
	Name       string
}

type DirectoryInfo struct {
	Message
	Flags    []string
	Name     string
	ReadOnly bool

	Exists, Recent, Unseen int
}