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") }