231 lines
6.2 KiB
Go
231 lines
6.2 KiB
Go
/*
|
|
Pominal
|
|
Copyright (C) 2020 Daniel Anglin
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
type Pominal struct {
|
|
// workSession is a counter for work sessions.
|
|
workSession int
|
|
|
|
// maxWorkSessions sets the number of work sessions to complete
|
|
// before running the countdown timer for each long break.
|
|
maxWorkSessions 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
|
|
|
|
// workNotificationMsg is the desktop notification
|
|
// message when a work session has started.
|
|
workNotificationMsg string
|
|
|
|
// shortBreakNotificationMsg is the desktop notification
|
|
// message when a short break session has started.
|
|
shortBreakNotificationMsg string
|
|
|
|
// longBreakNotificationMsg is the desktop notification
|
|
// message when a long break session has started.
|
|
longBreakNotificationMsg string
|
|
|
|
// currentNotificationMsg is used for desktop notifications.
|
|
// This will be equal to workNotificationMsg, shortBreakNotificationMsg
|
|
// or longBreakNotificationMsg depending on the session in progress.
|
|
currentNotificationMsg string
|
|
|
|
// label labels Pominal based on the session.
|
|
label string
|
|
|
|
// stopChan is a no-data channel used to stop Pominal.
|
|
stopChan chan struct{}
|
|
}
|
|
|
|
// NewPominal creates a new pominal instance
|
|
func newPominal(config PominalConfig) (Pominal, error) {
|
|
initWorkSession := 1
|
|
initCycle := 1
|
|
|
|
maxWorkSessions := setMaxWorkSessions(config.MaxWorkSessions)
|
|
|
|
work, err := setSessionTime(config.Sessions.Work.Duration, defaultWorkTime)
|
|
if err != nil {
|
|
return Pominal{}, err
|
|
}
|
|
|
|
shortBreak, err := setSessionTime(config.Sessions.ShortBreak.Duration, defaultShortBreakTime)
|
|
if err != nil {
|
|
return Pominal{}, err
|
|
}
|
|
|
|
longBreak, err := setSessionTime(config.Sessions.LongBreak.Duration, defaultLongBreakTime)
|
|
if err != nil {
|
|
return Pominal{}, err
|
|
}
|
|
|
|
p := Pominal{
|
|
workSession: initWorkSession,
|
|
maxWorkSessions: maxWorkSessions,
|
|
cycle: initCycle,
|
|
work: work,
|
|
shortBreak: shortBreak,
|
|
longBreak: longBreak,
|
|
workNotificationMsg: config.Sessions.Work.NotificationMessage,
|
|
shortBreakNotificationMsg: config.Sessions.ShortBreak.NotificationMessage,
|
|
longBreakNotificationMsg: config.Sessions.LongBreak.NotificationMessage,
|
|
stopChan: nil,
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
// Run Pominal
|
|
func (p *Pominal) Run(infoUI, timerUI *tview.TextView) {
|
|
p.stopChan = make(chan struct{})
|
|
p.UpdateSession()
|
|
drawInfo(infoUI, p.cycle, p.workSession, p.maxWorkSessions, p.label)
|
|
|
|
// notifier is used for desktop notifications
|
|
notifier := initNotifier()
|
|
|
|
t := time.NewTicker(time.Second)
|
|
|
|
infinite:
|
|
for {
|
|
select {
|
|
case <-p.stopChan:
|
|
t.Stop()
|
|
break infinite
|
|
case <-t.C:
|
|
p.countdown--
|
|
if p.countdown < 0 {
|
|
t.Stop()
|
|
p.UpdateSession()
|
|
drawInfo(
|
|
infoUI,
|
|
p.cycle,
|
|
p.workSession,
|
|
p.maxWorkSessions,
|
|
p.label,
|
|
)
|
|
desktopAlert(p.label, p.currentNotificationMsg, notifier)
|
|
time.Sleep(time.Second)
|
|
t = time.NewTicker(time.Second)
|
|
} else {
|
|
drawTimer(timerUI, p.countdown)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop sends a signal to the Run method to stop the timer.
|
|
// Afterwards the process returns from the Run method.
|
|
func (p *Pominal) Stop() {
|
|
close(p.stopChan)
|
|
}
|
|
|
|
// 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 {
|
|
case workTimerLabel:
|
|
if p.workSession >= p.maxWorkSessions {
|
|
p.countdown = p.longBreak
|
|
p.label = longBreakTimerLabel
|
|
p.currentNotificationMsg = p.longBreakNotificationMsg
|
|
} else {
|
|
p.countdown = p.shortBreak
|
|
p.label = shortBreakTimerLabel
|
|
p.currentNotificationMsg = p.shortBreakNotificationMsg
|
|
}
|
|
case shortBreakTimerLabel:
|
|
p.workSession++
|
|
p.countdown = p.work
|
|
p.label = workTimerLabel
|
|
p.currentNotificationMsg = p.workNotificationMsg
|
|
case longBreakTimerLabel:
|
|
p.cycle++
|
|
p.workSession = 1
|
|
p.countdown = p.work
|
|
p.label = workTimerLabel
|
|
p.currentNotificationMsg = p.workNotificationMsg
|
|
default:
|
|
p.countdown = p.work
|
|
p.label = workTimerLabel
|
|
p.currentNotificationMsg = p.workNotificationMsg
|
|
}
|
|
}
|
|
|
|
// setSessionTime parses the configured session time and
|
|
// returns the session time represented in seconds.
|
|
// If the session time is unconfigured then the default for
|
|
// that particular session is returned.
|
|
// If the value is less than the minimum session time
|
|
// then the minimum is returned instead.
|
|
// An error is returned if the configured session time cannot be parsed.
|
|
func setSessionTime(s, defaultSessionTime string) (float64, error) {
|
|
if len(s) == 0 {
|
|
s = defaultSessionTime
|
|
}
|
|
|
|
sTime, err := time.ParseDuration(s)
|
|
if err != nil {
|
|
return float64(0), err
|
|
}
|
|
|
|
sTimeInSecs := sTime.Seconds()
|
|
if sTimeInSecs < minimumSessionTime {
|
|
sTimeInSecs = minimumSessionTime
|
|
}
|
|
|
|
return sTimeInSecs, nil
|
|
}
|
|
|
|
// setMaxWorkSessions checks and returns the configured
|
|
// number of work session per Pominal cycle.
|
|
// The default value is returned if the argument is 0
|
|
// or less.
|
|
func setMaxWorkSessions(w int) int {
|
|
if w <= 0 {
|
|
w = defaultMaxWorkSessions
|
|
}
|
|
|
|
return w
|
|
}
|