timefiles/tui/day.go

275 lines
6.7 KiB
Go

package tui
import (
"time"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"suruatoel.xyz/timefiles/lib"
)
const (
PageNameTimer = "set"
)
type DayUI struct {
*tview.Pages
app *TUI
summaryList *tview.Table
totalsList *tview.Table
trackButtons *tview.Flex
startButton *tview.Button
stopButton *tview.Button
setButton *tview.Button
timerModal *ProActModal
currentTimer *lib.Time
summaryCount int
}
func NewDayUI(app *TUI) *DayUI {
ui := DayUI{
Pages: tview.NewPages(),
app: app,
}
main := tview.NewFlex().SetDirection(tview.FlexRow)
ui.AddPage("main", main, true, true)
summary := tview.NewFlex().SetDirection(tview.FlexColumn)
main.AddItem(summary, 0, 1, false)
ui.summaryList = tview.NewTable()
ui.summaryList.SetBorder(true).SetTitle("List")
ui.summaryList.SetSelectable(true, false)
summary.AddItem(ui.summaryList, 0, 1, false)
ui.totalsList = tview.NewTable()
ui.totalsList.SetBorder(true).SetTitle(" Totals ")
summary.AddItem(ui.totalsList, 0, 1, false)
track := tview.NewFlex().SetDirection(tview.FlexRow)
track.SetBorder(true).SetTitle(" Track ")
main.AddItem(track, 3, 0, true)
ui.trackButtons = tview.NewFlex().SetDirection(tview.FlexColumn)
track.AddItem(ui.trackButtons, 1, 0, true)
ui.startButton = tview.NewButton("start (n)")
ui.trackButtons.AddItem(ui.startButton, 0, 1, false)
ui.trackButtons.AddItem(nil, 1, 0, false)
ui.stopButton = tview.NewButton("stop (r)")
ui.trackButtons.AddItem(ui.stopButton, 0, 1, false)
ui.trackButtons.AddItem(nil, 1, 0, false)
ui.setButton = tview.NewButton("set (s)")
ui.trackButtons.AddItem(ui.setButton, 0, 1, false)
ui.timerModal = NewProActModal(app, "Timer")
ui.AddPage(PageNameTimer, ui.timerModal, true, false)
ui.assignHandlers()
return &ui
}
func (ui *DayUI) SetTimer(timer *lib.Time) {
if ui.currentTimer != nil {
ui.summaryList.RemoveRow(ui.summaryCount)
}
ui.currentTimer = timer
ui.setInputStates()
if timer == nil {
ui.app.Focus(ui.startButton)
ui.timerModal.SetValues(nil, nil)
} else {
ui.timerModal.SetValues(ui.resolveTimerValues(timer.ProjectID, timer.ActivityID))
if timer.ProjectID == "" || timer.ActivityID == "" {
ui.openTimerModal()
} else {
ui.app.Focus(ui.stopButton)
}
ui.includeTimerInSummary()
}
}
func (ui *DayUI) SetSummary(summary []*lib.Time) {
ui.summaryCount = len(summary)
ui.summaryList.Clear()
for i, entry := range summary {
ui.setSummaryLine(entry, i)
}
ui.includeTimerInSummary()
}
func (ui *DayUI) SetTotals(totalTime lib.TotalTime) {
ui.totalsList.Clear()
for i, entry := range totalTime.Totals {
projectLabel := entry.Project
var projectAttrs tcell.AttrMask
activityLabel := entry.Activity
var activityAttrs tcell.AttrMask
if entry.Project == "" {
projectLabel = "not set"
projectAttrs = tcell.AttrItalic
}
if entry.Activity == "" {
activityLabel = "not set"
activityAttrs = tcell.AttrItalic
}
ui.totalsList.SetCell(i, 0, tview.NewTableCell(projectLabel).SetExpansion(1).SetAttributes(projectAttrs))
ui.totalsList.SetCell(i, 1, tview.NewTableCell(activityLabel).SetExpansion(1))
ui.totalsList.SetCell(i, 2, tview.NewTableCell(entry.Duration.Round(time.Second).String()).SetExpansion(0).SetAttributes(activityAttrs))
}
ui.totalsList.SetCell(len(totalTime.Totals), 0, tview.NewTableCell("Total").SetExpansion(1).SetAttributes(tcell.AttrBold))
ui.totalsList.SetCell(len(totalTime.Totals), 2, tview.NewTableCell(totalTime.TotalSum.Round(time.Second).String()).SetExpansion(0).SetAttributes(tcell.AttrBold))
}
func (ui *DayUI) setSummaryLine(entry *lib.Time, line int) {
projectAttrs := tcell.AttrItalic
activityAttrs := tcell.AttrItalic
projectLabel := "not set"
activityLabel := "not set"
project, activity := ui.resolveTimerValues(entry.ProjectID, entry.ActivityID)
if project != nil {
projectLabel = project.Title
projectAttrs = tcell.AttrNone
if activity != nil {
activityLabel = activity.Title
activityAttrs = tcell.AttrNone
}
}
ui.summaryList.SetCell(line, 0, tview.NewTableCell(projectLabel).SetExpansion(1).SetAttributes(projectAttrs))
ui.summaryList.SetCell(line, 1, tview.NewTableCell(activityLabel).SetExpansion(1).SetAttributes(activityAttrs))
if !entry.Start.IsZero() {
ui.summaryList.SetCell(line, 2, tview.NewTableCell(formatTime(entry.Start)).SetExpansion(0))
}
if !entry.End.IsZero() {
ui.summaryList.SetCell(line, 3, tview.NewTableCell(formatTime(entry.End)).SetExpansion(0))
}
}
func (ui *DayUI) includeTimerInSummary() {
if ui.currentTimer == nil {
return
}
ui.setSummaryLine(ui.currentTimer, ui.summaryCount)
}
func (ui *DayUI) assignHandlers() {
ui.startButton.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyTAB:
ui.app.Focus(ui.stopButton)
return nil
}
return event
})
ui.startButton.SetSelectedFunc(func() {
ui.start()
})
ui.stopButton.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyTAB:
ui.app.Focus(ui.setButton)
return nil
}
return event
})
ui.stopButton.SetSelectedFunc(func() {
ui.stop()
})
ui.setButton.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyTAB:
ui.app.Focus(ui.summaryList)
return nil
}
return event
})
ui.setButton.SetSelectedFunc(func() {
ui.openTimerModal()
})
ui.timerModal.SetCloseFunc(func(project string, activity string, success bool) {
ui.HidePage(PageNameTimer)
if success {
ui.setDetails(project, activity)
ui.app.Focus(ui.stopButton)
} else {
ui.app.Focus(ui.setButton)
}
})
ui.trackButtons.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Rune() {
case 'n':
ui.start()
return nil
case 'r':
ui.stop()
return nil
case 's':
ui.openTimerModal()
return nil
}
return event
})
}
func (ui *DayUI) setInputStates() {
ui.stopButton.SetDisabled(ui.currentTimer == nil)
ui.setButton.SetDisabled(ui.currentTimer == nil)
}
func (ui *DayUI) start() {
ui.app.timeApp.StartTimer()
}
func (ui *DayUI) stop() {
if ui.currentTimer != nil {
ui.app.timeApp.StopTimer()
}
}
func (ui *DayUI) openTimerModal() {
if ui.currentTimer != nil {
ui.ShowPage(PageNameTimer)
ui.app.Focus(ui.timerModal)
}
}
func (ui *DayUI) setDetails(project, activity string) {
if ui.currentTimer == nil {
return
}
ui.app.timeApp.UpdateTimer(project, activity)
}
func (ui *DayUI) resolveTimerValues(projectID, activityID string) (*lib.Project, *lib.Activity) {
project := ui.app.timeApp.GetProject(projectID)
if project != nil {
activity := ui.app.timeApp.GetActivity(project, activityID)
return project, activity
}
return project, nil
}
func formatTime(t time.Time) string {
return t.Format("15:04:05")
}