k9s/internal/ui/flash.go

166 lines
3.4 KiB
Go

package ui
import (
"context"
"fmt"
"strings"
"time"
"github.com/derailed/k9s/internal/config"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"github.com/rs/zerolog/log"
)
const (
// FlashInfo represents an info message.
FlashInfo FlashLevel = iota
// FlashWarn represents an warning message.
FlashWarn
// FlashErr represents an error message.
FlashErr
// FlashFatal represents an fatal message.
FlashFatal
flashDelay = 3
emoDoh = "😗"
emoRed = "😡"
emoDead = "💀"
emoHappy = "😎"
)
type (
// FlashLevel represents flash message severity.
FlashLevel int
// Flash represents a flash message indicator.
Flash struct {
*tview.TextView
cancel context.CancelFunc
app *App
}
)
// NewFlash returns a new flash view.
func NewFlash(app *App, m string) *Flash {
f := Flash{app: app, TextView: tview.NewTextView()}
f.SetTextColor(tcell.ColorAqua)
f.SetTextAlign(tview.AlignLeft)
f.SetBorderPadding(0, 0, 1, 1)
f.SetText("")
f.app.Styles.AddListener(&f)
return &f
}
// StylesChanged notifies listener the skin changed.
func (f *Flash) StylesChanged(s *config.Styles) {
f.SetBackgroundColor(s.BgColor())
f.SetTextColor(s.FgColor())
}
// Info displays an info flash message.
func (f *Flash) Info(msg string) {
f.SetMessage(FlashInfo, msg)
}
// Infof displays a formatted info flash message.
func (f *Flash) Infof(fmat string, args ...interface{}) {
f.Info(fmt.Sprintf(fmat, args...))
}
// Warn displays a warning flash message.
func (f *Flash) Warn(msg string) {
f.SetMessage(FlashWarn, msg)
}
// Warnf displays a formatted warning flash message.
func (f *Flash) Warnf(fmat string, args ...interface{}) {
f.Warn(fmt.Sprintf(fmat, args...))
}
// Err displays an error flash message.
func (f *Flash) Err(err error) {
log.Error().Err(err).Msgf("%v", err)
f.SetMessage(FlashErr, err.Error())
}
// Errf displays a formatted error flash message.
func (f *Flash) Errf(fmat string, args ...interface{}) {
var err error
for _, a := range args {
switch e := a.(type) {
case error:
err = e
}
}
log.Error().Err(err).Msgf(fmat, args...)
f.SetMessage(FlashErr, fmt.Sprintf(fmat, args...))
}
func (f *Flash) SetMessage(level FlashLevel, msg ...string) {
if f.cancel != nil {
f.cancel()
}
var ctx1, ctx2 context.Context
{
var timerCancel context.CancelFunc
ctx1, f.cancel = context.WithCancel(context.TODO())
ctx2, timerCancel = context.WithTimeout(context.TODO(), flashDelay*time.Second)
go f.refresh(ctx1, ctx2, timerCancel)
}
_, _, width, _ := f.GetRect()
if width <= 15 {
width = 100
}
m := strings.Join(msg, " ")
f.SetTextColor(flashColor(level))
f.SetText(render.Truncate(flashEmoji(level)+" "+m, width-3))
}
func (f *Flash) refresh(ctx1, ctx2 context.Context, cancel context.CancelFunc) {
defer cancel()
for {
select {
// Timer canceled bail now
case <-ctx1.Done():
return
// Timed out clear and bail
case <-ctx2.Done():
f.app.QueueUpdateDraw(func() {
f.Clear()
})
return
}
}
}
func flashEmoji(l FlashLevel) string {
switch l {
case FlashWarn:
return emoDoh
case FlashErr:
return emoRed
case FlashFatal:
return emoDead
default:
return emoHappy
}
}
func flashColor(l FlashLevel) tcell.Color {
switch l {
case FlashWarn:
return tcell.ColorOrange
case FlashErr:
return tcell.ColorOrangeRed
case FlashFatal:
return tcell.ColorFuchsia
default:
return tcell.ColorNavajoWhite
}
}