package ui import ( "fmt" "sync" "time" "github.com/aditya-K2/tview" "github.com/gdamore/tcell/v2" ) var ( maxNotifications = 3 pm sync.Mutex notAvailable = -1 posArr = positionArray{} c chan *notification ) // Start Notification Service func InitNotifier() { for _m := maxNotifications; _m != 0; _m-- { posArr = append(posArr, true) } c = make(chan *notification, maxNotifications) routine() } // notification Primitive type notification struct { *tview.Box text string position int msg chan string timer time.Duration } // Array for all available positions where the notification can be displayed. type positionArray []bool // Check If there is a position available. func (p *positionArray) Available() bool { var t = false pm.Lock() for _, v := range *p { t = t || v } pm.Unlock() return t } func (p *positionArray) GetNextPosition() int { pm.Lock() v := *p for k := range v { if v[k] { v[k] = false pm.Unlock() return k } } pm.Unlock() return notAvailable } // Free a position func (p *positionArray) Free(i int) { pm.Lock() v := *p v[i] = true pm.Unlock() } // Get A Pointer to A Notification Struct func newNotificationWithTimer(s string, t time.Duration) *notification { return ¬ification{ Box: tview.NewBox(), text: s, timer: t, msg: nil, } } // Get A Pointer to A Notification Struct with a close channel func newNotificationWithChan(s string, c chan string) *notification { return ¬ification{ Box: tview.NewBox(), text: s, timer: time.Second, msg: c, } } // Draw Function for the Notification Primitive func (self *notification) Draw(screen tcell.Screen) { pos := (self.position*3 + self.position + 1) var ( _, _, COL, _ int = Flex.GetRect() TEXTLENGTH int = len(self.text) HEIGHT int = 3 TextPosition int = 1 ) self.Box.SetBackgroundColor(tcell.ColorBlack) self.SetRect(COL-(TEXTLENGTH+7), pos, TEXTLENGTH+4, HEIGHT) self.DrawForSubclass(screen, self.Box) tview.Print(screen, self.text, COL-(TEXTLENGTH+5), pos+TextPosition, TEXTLENGTH, tview.AlignCenter, tcell.ColorWhite) } // this routine checks for available position and sends notification if // position is available. func routine() { go func() { for { val := <-c // Wait until a new position isn't available for !posArr.Available() { continue } notify(val) } }() } func notify(n *notification) { go func() { currentTime := time.Now().String() npos := posArr.GetNextPosition() // Ensure a position is available. if npos == notAvailable { for !posArr.Available() { } npos = posArr.GetNextPosition() } n.position = npos root.AddPage(currentTime, n, false, true) App.Draw() App.SetFocus(Main) if n.msg != nil { n.text = <-n.msg App.Draw() } time.Sleep(n.timer) root.RemovePage(currentTime) posArr.Free(npos) App.SetFocus(Main) App.Draw() }() } func SendNotification(format string, args ...any) { SendNotificationWithTimer(time.Second, fmt.Sprintf(format, args...)) } func SendNotificationWithTimer(t time.Duration, format string, args ...any) { go func() { c <- newNotificationWithTimer(fmt.Sprintf(format, args...), t) }() } // SendNotificationWithChan sends a notification that won't be closed unless // an update message isn't sent over the channel that it returns. The message // string received over the channel is then displayed for half a second and the // notification is then removed. func SendNotificationWithChan(format string, args ...any) chan string { close := make(chan string) go func() { c <- newNotificationWithChan(fmt.Sprintf(format, args...), close) }() return close }