diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-25 23:10:38 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-25 23:10:38 +0300 |
| commit | 2e74f80bdb12ac5cb9c9e30e9878bb594c03e5df (patch) | |
| tree | 6c324110869b35e0e51e46a3ba3b421cd6bb78ff /internal | |
| parent | 7e7a0ce7996d877984085180395694495c02b830 (diff) | |
feat: Implement live timer view
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/live/live.go | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/internal/live/live.go b/internal/live/live.go new file mode 100644 index 0000000..300e0dc --- /dev/null +++ b/internal/live/live.go @@ -0,0 +1,120 @@ +package live + +import ( + "fmt" + "time" + + "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + timrTimer "timr/internal/timer" +) + +type tickMsg time.Time + +func tick() tea.Cmd { + return tea.Tick(time.Second, func(t time.Time) tea.Msg { + return tickMsg(t) + }) +} + +// Model is the Bubble Tea model for the live timer view. +type Model struct { + state timrTimer.State + quitting bool + helpStyle lipgloss.Style + timerStyle lipgloss.Style + statusStyle lipgloss.Style +} + +// NewModel creates a new Model. +func NewModel() Model { + state, err := timrTimer.LoadState() + if err != nil { + panic(err) // Or handle more gracefully + } + + return Model{ + state: state, + helpStyle: lipgloss.NewStyle().Faint(true), + timerStyle: lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#00BFFF")), + statusStyle: lipgloss.NewStyle().Italic(true), + } +} + +// Init is the first function that will be called. +func (m Model) Init() tea.Cmd { + if m.state.Running { + return tick() + } + return nil +} + +// Update is called when a message is received. +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tickMsg: + if !m.state.Running { + return m, nil + } + return m, tick() + + case tea.KeyMsg: + switch msg.String() { + case "q", "ctrl+c": + m.quitting = true + if err := m.state.Save(); err != nil { + // handle error + } + return m, tea.Quit + + case "s": + if m.state.Running { + // Stop the timer + m.state.ElapsedTime += time.Since(m.state.StartTime) + m.state.Running = false + return m, nil // Stop ticking + } else { + // Start the timer + m.state.Running = true + m.state.StartTime = time.Now() + return m, tick() // Start ticking + } + + case "r": + // Reset the timer + m.state = timrTimer.State{} + if _, err := timrTimer.ResetTimer(); err != nil { + // handle error + } + return m, nil // Stop ticking + } + } + + return m, nil +} + +// View renders the model's state to the terminal. +func (m Model) View() string { + if m.quitting { + return "" + } + + var currentSession time.Duration + if m.state.Running { + currentSession = time.Since(m.state.StartTime) + } + + totalElapsed := (m.state.ElapsedTime + currentSession).Round(time.Second) + + status := "Paused" + if m.state.Running { + status = "Running" + } + + return fmt.Sprintf( + "%s\n%s\n\n%s", + m.timerStyle.Render(totalElapsed.String()), + m.statusStyle.Render(status), + m.helpStyle.Render("q: quit, s: start/stop, r: reset"), + ) +} |
