package views import ( "context" "fmt" "strings" "time" "github.com/derailed/k9s/internal/resource" "github.com/derailed/tview" "github.com/gdamore/tcell" "github.com/rs/zerolog/log" ) const ( flashInfo flashLevel = iota flashWarn flashErr flashFatal flashDelay = 3 emoDoh = "😗" emoRed = "😡" emoDead = "💀" emoHappy = "😎" ) type ( flashLevel int flashView struct { *tview.TextView cancel context.CancelFunc app *appView } ) func newFlashView(app *appView, m string) *flashView { f := flashView{app: app, TextView: tview.NewTextView()} f.SetTextColor(tcell.ColorAqua) f.SetTextAlign(tview.AlignLeft) f.SetBorderPadding(0, 0, 1, 1) f.SetText("") return &f } func (v *flashView) info(msg string) { v.setMessage(flashInfo, msg) } func (v *flashView) infof(fmat string, args ...interface{}) { v.info(fmt.Sprintf(fmat, args...)) } func (v *flashView) warn(msg string) { v.setMessage(flashWarn, msg) } func (v *flashView) warnf(fmat string, args ...interface{}) { v.warn(fmt.Sprintf(fmat, args...)) } func (v *flashView) err(err error) { log.Error().Err(err).Msgf("%v", err) v.setMessage(flashErr, err.Error()) } func (v *flashView) 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...) v.setMessage(flashErr, fmt.Sprintf(fmat, args...)) } func (v *flashView) setMessage(level flashLevel, msg ...string) { if v.cancel != nil { v.cancel() } var ctx1, ctx2 context.Context { var timerCancel context.CancelFunc ctx1, v.cancel = context.WithCancel(context.TODO()) ctx2, timerCancel = context.WithTimeout(context.TODO(), flashDelay*time.Second) go v.refresh(ctx1, ctx2, timerCancel) } _, _, width, _ := v.GetRect() if width <= 15 { width = 100 } m := strings.Join(msg, " ") v.SetTextColor(flashColor(level)) v.SetText(resource.Truncate(flashEmoji(level)+" "+m, width-3)) } func (v *flashView) 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(): v.app.QueueUpdateDraw(func() { v.Clear() v.app.Draw() }) 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 } }