Files
gspt/ui/notify.go
2023-05-01 13:09:07 +05:30

175 lines
3.6 KiB
Go

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 &notification{
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 &notification{
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
}