mblaze-ui

A minimal TUI for the mblaze email client

git clone https://git.8pit.net/mblaze-ui.git

  1package main
  2
  3import (
  4	"github.com/gdamore/tcell/v2"
  5	"log"
  6	"os"
  7)
  8
  9func initScreen() (tcell.Screen, error) {
 10	s, err := tcell.NewScreen()
 11	if err != nil {
 12		return nil, err
 13	}
 14	if err := s.Init(); err != nil {
 15		return nil, err
 16	}
 17	s.SetStyle(defStyle)
 18	s.EnablePaste()
 19	s.Clear()
 20	return s, nil
 21}
 22
 23func fatal(ui *UserInterface, v ...any) {
 24	cleanup(ui)
 25	log.Fatal(v...)
 26}
 27
 28func handleEventKey(ui *UserInterface, ev *tcell.EventKey) {
 29	switch ev.Key() {
 30	case tcell.KeyEnter:
 31		err := ui.withoutScreen(func() error {
 32			mail := ui.SelectedMail()
 33			return mail.Show()
 34		})
 35		if err != nil {
 36			fatal(ui, err)
 37		}
 38	case tcell.KeyRune:
 39		mail := ui.SelectedMail()
 40		switch ev.Rune() {
 41		case 'r':
 42			err := ui.withoutScreen(func() error {
 43				mail := ui.SelectedMail()
 44				return mail.Reply()
 45			})
 46			if err != nil {
 47				fatal(ui, err)
 48			}
 49		case 'd':
 50			err := os.Remove(mail.Path)
 51			if err != nil {
 52				fatal(ui, err)
 53			}
 54		case 'f':
 55			mail.Flag(Flagged)
 56		case 'F':
 57			mail.Flag(Unflagged)
 58		case 's':
 59			mail.Flag(Unseen)
 60		case 'S':
 61			mail.Flag(Seen)
 62		case 't':
 63			mail.Flag(Untrashed)
 64		case 'T':
 65			mail.Flag(Trashed)
 66		default:
 67			return
 68		}
 69
 70		// TODO: Consider using `mflag -v` above to determine the
 71		// new file names and thereby keep the sequence in tact.
 72
 73		err := ui.Refresh()
 74		if err != nil {
 75			fatal(ui, err)
 76		}
 77
 78		ui.Screen.Clear()
 79		ui.Draw()
 80	case tcell.KeyDown:
 81		ui.NextMail()
 82	case tcell.KeyUp:
 83		ui.PrevMail()
 84	case tcell.KeyPgDn:
 85		ui.NextPage()
 86	case tcell.KeyPgUp:
 87		ui.PrevPage()
 88	case tcell.KeyCtrlL:
 89		ui.Screen.Sync()
 90	}
 91}
 92
 93func eventLoop(ui *UserInterface) {
 94	for {
 95		ui.Screen.Show()
 96		ev := ui.Screen.PollEvent()
 97
 98		switch ev := ev.(type) {
 99		case *tcell.EventResize:
100			ui.Draw()
101			ui.Screen.Sync()
102		case *tcell.EventKey:
103			if ev.Key() == tcell.KeyEscape ||
104				ev.Key() == tcell.KeyCtrlC ||
105				(ev.Key() == tcell.KeyRune && ev.Rune() == 'q') {
106				return
107			}
108
109			handleEventKey(ui, ev)
110		}
111	}
112}
113
114func cleanup(ui *UserInterface) {
115	// You have to catch panics in a defer, clean up, and
116	// re-raise them - otherwise your application can
117	// die without leaving any diagnostic trace.
118	maybePanic := recover()
119
120	ui.Screen.Fini()
121	if maybePanic != nil {
122		panic(maybePanic)
123	}
124}
125
126func main() {
127	mails, err := mscan()
128	if err != nil {
129		log.Fatal(err)
130	}
131
132	screen, err := initScreen()
133	if err != nil {
134		log.Fatal(err)
135	}
136
137	ui := NewUI(mails, screen)
138	defer cleanup(ui)
139
140	ui.Draw()
141	eventLoop(ui)
142}