refactor: code refactoring to improve stability.

This commit removes the need to calculate the time remaining in a
session and instead introduces a countdown counter which decrements at
each one second 'tick'. This stabilizes the interface where it appeared
that he timer was randomly pausing or skipping time.

A summary of changes made in this commit includes:

- Updated the command line flags so that they are easier to remember.
- The work, short break and long break session times are now parsed and
converted to type float64. These times represent the total amount of
seconds.
- Removal of timer and finish from Pominal.
- The method that calculates the time remaining is removed.
- A countdown counter is added to the Pominal type.
- Updated alert message for the desktop notification.
- Simplified the Run method by moving the logic to chosse the next session
in a separate method.
This commit is contained in:
Dan Anglin 2019-09-18 09:08:12 +01:00
parent 1ccf3b6302
commit f0b46ed8b7
No known key found for this signature in database
GPG key ID: 7AC2B18EC1D09F27
4 changed files with 101 additions and 101 deletions

View file

@ -7,10 +7,10 @@ BIN_FILE := $(BIN_DIR)/$(NAME)
all: test_unit build all: test_unit build
test_unit: test_unit:
go test -v -cover ./... @go test -v -cover ./...
build: build:
go build -a -v -o $(BIN_FILE) @go build -a -v -o $(BIN_FILE)
clean: clean:
go clean @go clean

View file

@ -54,9 +54,9 @@ func getIconPath() string {
return result return result
} }
// alert pushes a desktop notification // alert creates a new desktop notification.
func alert(oldLabel, newLabel string) { func alert(l string) {
title := oldLabel + " timer has stopped" title := "Pominal notification."
text := newLabel + " timer has started" text := l + " session has started."
notifier.Push(title, text, "", notificator.UR_NORMAL) notifier.Push(title, text, "", notificator.UR_NORMAL)
} }

41
main.go
View file

@ -21,7 +21,6 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"math"
"os" "os"
"os/exec" "os/exec"
"time" "time"
@ -34,18 +33,18 @@ const (
) )
var ( var (
workTimer string workTime string
shortBreakTimer string shortBreakTime string
longBreakTimer string longBreakTime string
maxWorkCycles int maxWorkSessions int
printVersion bool printVersion bool
) )
func init() { func init() {
flag.StringVar(&workTimer, "work-timer", "25m", "sets the timer for your work session.") flag.StringVar(&workTime, "work", "25m", "sets the timer for your work session.")
flag.StringVar(&shortBreakTimer, "short-break-timer", "5m", "sets the timer for your short break.") flag.StringVar(&shortBreakTime, "short-break", "5m", "sets the timer for your short break.")
flag.StringVar(&longBreakTimer, "long-break-timer", "20m", "sets the timer for your long break.") flag.StringVar(&longBreakTime, "long-break", "20m", "sets the timer for your long break.")
flag.IntVar(&maxWorkCycles, "max-work-cycles", 4, "sets the maximum number of work cycles to complete before taking a long break.") flag.IntVar(&maxWorkSessions, "max-work-sessions", 4, "sets the maximum number of work cycles to complete before taking a long break.")
flag.BoolVar(&printVersion, "version", false, "print version and exit.") flag.BoolVar(&printVersion, "version", false, "print version and exit.")
flag.Parse() flag.Parse()
} }
@ -59,43 +58,41 @@ func main() {
initNotifier() initNotifier()
workTimeDuration, err := time.ParseDuration(workTimer) w, err := time.ParseDuration(workTime)
if err != nil { if err != nil {
fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error())
os.Exit(1) os.Exit(1)
} }
shortBreakTimeDuration, err := time.ParseDuration(shortBreakTimer) s, err := time.ParseDuration(shortBreakTime)
if err != nil { if err != nil {
fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error())
os.Exit(1) os.Exit(1)
} }
longBreakTimeDuration, err := time.ParseDuration(longBreakTimer) l, err := time.ParseDuration(longBreakTime)
if err != nil { if err != nil {
fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error())
os.Exit(1) os.Exit(1)
} }
pominal := NewPominal( pominal := NewPominal(
workTimeDuration, w.Seconds(),
shortBreakTimeDuration, s.Seconds(),
longBreakTimeDuration, l.Seconds(),
maxWorkCycles, maxWorkSessions,
) )
pominal.Run() pominal.Run()
} }
// printScreen prints the details of the Pominal session on screen including // printScreen prints the details of the Pominal session on screen including
// the current work cycle number, the timer's current label and the time remaining // the current work session number, the timer's current label and the current countdoen.
// on the timer.
// TODO: To be removed when TUI is implemented. // TODO: To be removed when TUI is implemented.
func printScreen(remaining float64, pominalCount, workCycle, maxWorkCycle int, label string) { func printScreen(countdown float64, pominalCount, workSessionCount, maxWorkSession int, label string) {
clearScreen() clearScreen()
remainingSecs := int(math.Ceil(remaining)) fmt.Printf("Pominal cycle: %d\nWork session: %d of %d\n\n", pominalCount, workSessionCount, maxWorkSession)
fmt.Printf("Pominal session: %d\nWork cycle: %d of %d\n\n", pominalCount, workCycle, maxWorkCycle) fmt.Printf("Session: %s\nTime remaining: %02d:%02d", label, int(countdown)/60, int(countdown)%60)
fmt.Printf("Timer: %s\nTime remaining: %02d:%02d", label, (remainingSecs/60)%60, remainingSecs%60)
} }
// clearScreen clears the terminal screen // clearScreen clears the terminal screen

View file

@ -27,106 +27,109 @@ import (
) )
type Pominal struct { type Pominal struct {
timer *time.Timer // workSession is a counter for work sessions.
finish time.Time workSession int
work time.Duration
shortBreak time.Duration // maxWorkSessions sets the number of work sessions to complete
longBreak time.Duration // before running the countdown timer for each long break.
maxWorkCycles int maxWorkSessions int
session int
// cycle represents a Pominal cycle. A Pominal cycle increments after
// every long break.
cycle int
// countdown is the decrementing counter for Pominal.
countdown float64
// work is the time (represented in seconds) for the work sessions.
work float64
// shortBreak is the time (represented in seconds) for the short break sessions.
shortBreak float64
// longBreak is the time (represented in seconds) for the long break sessions.
longBreak float64
// label labels Pominal based on the session.
label string label string
} }
// NewPominal creates a new pominal instance // NewPominal creates a new pominal instance
func NewPominal(w, s, l time.Duration, m int) Pominal { func NewPominal(w, s, l float64, m int) Pominal {
return Pominal{ return Pominal{
workSession: 1,
maxWorkSessions: m,
cycle: 1,
work: w, work: w,
shortBreak: s, shortBreak: s,
longBreak: l, longBreak: l,
maxWorkCycles: m,
session: 1,
} }
} }
// Run Pominal // Run Pominal
func (p *Pominal) Run() { func (p *Pominal) Run() {
workCycleCount := 1
t := time.NewTicker(1 * time.Second)
s := make(chan os.Signal, 1) s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGINT, syscall.SIGTERM) signal.Notify(s, syscall.SIGINT, syscall.SIGTERM)
p.Start(workTimerLabel, workCycleCount)
p.UpdateSession()
t := time.NewTicker(1 * time.Second)
infinite: infinite:
for { for {
select { select {
case sig := <-s: case sig := <-s:
fmt.Printf("\n\nReceived signal '%s'. Closing Pominal.\n", sig) fmt.Printf("\n\nReceived signal '%s'. Closing Pominal.\n", sig)
p.timer.Stop()
t.Stop() t.Stop()
break infinite break infinite
case <-t.C: case <-t.C:
printScreen(p.TimeRemaining(), p.session, workCycleCount, p.maxWorkCycles, p.label) p.countdown--
case <-p.timer.C: if p.countdown < 0 {
fmt.Printf("\n%s timer has finished\n", p.label) fmt.Printf("\n%s session has finished.\n", p.label)
p.timer.Stop()
t.Stop() t.Stop()
p.UpdateSession()
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
oldLabel := p.label t = time.NewTicker(1 * time.Second)
} else {
printScreen(p.countdown,
p.cycle,
p.workSession,
p.maxWorkSessions,
p.label,
)
}
}
}
}
// UpdateSession resets Pominal's countdown
// based on the next session. If a 'short break'
// session is over the work session counter is incremented.
// If a 'long break' session is over then the Pominal cycle
// is incremented and the work session counter is reset
// to 1.
func (p *Pominal) UpdateSession() {
switch p.label { switch p.label {
case workTimerLabel: case workTimerLabel:
if workCycleCount >= p.maxWorkCycles { if p.workSession >= p.maxWorkSessions {
p.Start(longBreakTimerLabel, workCycleCount) p.countdown = p.longBreak
p.label = longBreakTimerLabel
} else { } else {
p.Start(shortBreakTimerLabel, workCycleCount) p.countdown = p.shortBreak
p.label = shortBreakTimerLabel
} }
case shortBreakTimerLabel: case shortBreakTimerLabel:
workCycleCount++ p.workSession++
p.Start(workTimerLabel, workCycleCount) p.countdown = p.work
p.label = workTimerLabel
case longBreakTimerLabel: case longBreakTimerLabel:
workCycleCount = 1 p.cycle++
p.IncrementCount() p.workSession = 1
p.Start(workTimerLabel, workCycleCount) p.countdown = p.work
p.label = workTimerLabel
default:
p.countdown = p.work
p.label = workTimerLabel
} }
t = time.NewTicker(1 * time.Second) alert(p.label)
newLabel := p.label
alert(oldLabel, newLabel)
}
}
}
// Start begins the timer specified by the
// label argument.
func (p *Pominal) Start(label string, workCycleCount int) {
var d time.Duration
switch label {
case workTimerLabel:
d = p.work
case shortBreakTimerLabel:
d = p.shortBreak
case longBreakTimerLabel:
d = p.longBreak
}
p.label = label
if p.timer == nil {
p.timer = time.NewTimer(d)
} else {
p.timer.Reset(d)
}
p.finish = time.Now().Add(d)
}
// TimeRemaining returns the remaining time left
// on the timer
func (p *Pominal) TimeRemaining() float64 {
return p.finish.Sub(time.Now()).Seconds()
}
// IncrementCount increments the pominal session count
func (p *Pominal) IncrementCount() {
p.session++
} }