1 | package main
|
2 |
|
3 | import (
|
4 | "time"
|
5 | )
|
6 |
|
7 | type TaskRunner struct {
|
8 | count int
|
9 | taskID int
|
10 | taskMessage string
|
11 | nPomodoros int
|
12 | origDuration time.Duration
|
13 | state State
|
14 | store *Store
|
15 | started time.Time
|
16 | pause chan bool
|
17 | toggle chan bool
|
18 | notifier Notifier
|
19 | duration time.Duration
|
20 | }
|
21 |
|
22 | func NewTaskRunner(task *Task, store *Store, notifier Notifier) (*TaskRunner, error) {
|
23 | taskID, err := store.CreateTask(*task)
|
24 | if err != nil {
|
25 | return nil, err
|
26 | }
|
27 | tr := &TaskRunner{
|
28 | taskID: taskID,
|
29 | taskMessage: task.Message,
|
30 | nPomodoros: task.NPomodoros,
|
31 | origDuration: task.Duration,
|
32 | store: store,
|
33 | state: State(0),
|
34 | pause: make(chan bool),
|
35 | toggle: make(chan bool),
|
36 | notifier: notifier,
|
37 | duration: task.Duration,
|
38 | }
|
39 | return tr, nil
|
40 | }
|
41 |
|
42 | func (t *TaskRunner) Start() {
|
43 | go t.run()
|
44 | }
|
45 |
|
46 | func (t *TaskRunner) TimeRemaining() time.Duration {
|
47 | return (t.duration - time.Since(t.started)).Truncate(time.Second)
|
48 | }
|
49 |
|
50 | func (t *TaskRunner) run() error {
|
51 | for t.count < t.nPomodoros {
|
52 | // Create a new pomodoro where we
|
53 | // track the start / end time of
|
54 | // of this session.
|
55 | pomodoro := &Pomodoro{}
|
56 | // Start this pomodoro
|
57 | pomodoro.Start = time.Now()
|
58 | // Set state to RUNNING
|
59 | t.state = RUNNING
|
60 | // Create a new timer
|
61 | timer := time.NewTimer(t.duration)
|
62 | // Record our started time
|
63 | t.started = pomodoro.Start
|
64 | loop:
|
65 | select {
|
66 | case <-timer.C:
|
67 | t.state = BREAKING
|
68 | t.count++
|
69 | case <-t.toggle:
|
70 | // Catch any toggles when we
|
71 | // are not expecting them
|
72 | goto loop
|
73 | case <-t.pause:
|
74 | timer.Stop()
|
75 | // Record the remaining time of the current pomodoro
|
76 | remaining := t.TimeRemaining()
|
77 | // Change state to PAUSED
|
78 | t.state = PAUSED
|
79 | // Wait for the user to press [p]
|
80 | <-t.pause
|
81 | // Resume the timer with previous
|
82 | // remaining time
|
83 | timer.Reset(remaining)
|
84 | // Change duration
|
85 | t.started = time.Now()
|
86 | t.duration = remaining
|
87 | // Restore state to RUNNING
|
88 | t.state = RUNNING
|
89 | goto loop
|
90 | }
|
91 | pomodoro.End = time.Now()
|
92 | err := t.store.CreatePomodoro(t.taskID, *pomodoro)
|
93 | if err != nil {
|
94 | return err
|
95 | }
|
96 | // All pomodoros completed
|
97 | if t.count == t.nPomodoros {
|
98 | break
|
99 | }
|
100 |
|
101 | t.notifier.Notify("Pomo", "It is time to take a break!")
|
102 | // Reset the duration incase it
|
103 | // was paused.
|
104 | t.duration = t.origDuration
|
105 | // User concludes the break
|
106 | <-t.toggle
|
107 |
|
108 | }
|
109 | t.notifier.Notify("Pomo", "Pomo session has completed!")
|
110 | t.state = COMPLETE
|
111 | return nil
|
112 | }
|
113 |
|
114 | func (t *TaskRunner) Toggle() {
|
115 | t.toggle <- true
|
116 | }
|
117 |
|
118 | func (t *TaskRunner) Pause() {
|
119 | t.pause <- true
|
120 | }
|
121 |
|
122 | func (t *TaskRunner) Status() *Status {
|
123 | return &Status{
|
124 | State: t.state,
|
125 | Count: t.count,
|
126 | NPomodoros: t.nPomodoros,
|
127 | Remaining: t.TimeRemaining(),
|
128 | }
|
129 | }
|