Gotypes.go -rw-r--r-- 3.7 KiB
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "github.com/fatih/color"
7 "io/ioutil"
8 "time"
9
10 "github.com/kevinschoon/pomo/libnotify"
11)
12
13// RefreshInterval is the frequency at which
14// the display is updated.
15const RefreshInterval = 800 * time.Millisecond
16
17// Message is used internally for updating
18// the display.
19type Message struct {
20 Start time.Time
21 Duration time.Duration
22 Pomodoros int
23 CurrentPomodoro int
24 Wheel *Wheel
25}
26
27// Wheel keeps track of an ASCII spinner
28type Wheel struct {
29 state int
30}
31
32func (w *Wheel) String() string {
33 switch w.state {
34 case 0:
35 w.state++
36 return "|"
37 case 1:
38 w.state++
39 return "/"
40 case 2:
41 w.state++
42 return "-"
43 case 3:
44 w.state = 0
45 return "\\"
46 }
47 return ""
48}
49
50// Config represents user preferences
51type Config struct {
52 Colors map[string]*color.Color
53}
54
55var colorMap = map[string]*color.Color{
56 "red": color.New(color.FgRed),
57 "blue": color.New(color.FgBlue),
58 "green": color.New(color.FgGreen),
59 "white": color.New(color.FgWhite),
60}
61
62func (c *Config) UnmarshalJSON(raw []byte) error {
63 config := &struct {
64 Colors map[string]string `json:"colors"`
65 }{}
66 err := json.Unmarshal(raw, config)
67 if err != nil {
68 return err
69 }
70 for key, name := range config.Colors {
71 if color, ok := colorMap[name]; ok {
72 c.Colors[key] = color
73 } else {
74 return fmt.Errorf("bad color choice: %s", name)
75 }
76 }
77 return nil
78}
79
80func NewConfig(path string) (*Config, error) {
81 raw, err := ioutil.ReadFile(path)
82 if err != nil {
83 return nil, err
84 }
85 config := &Config{
86 Colors: map[string]*color.Color{},
87 }
88 return config, json.Unmarshal(raw, config)
89}
90
91// Task describes some activity
92type Task struct {
93 ID int `json:"id"`
94 Message string `json:"message"`
95 // Array of completed pomodoros
96 Pomodoros []*Pomodoro `json:"pomodoros"`
97 // Free-form tags associated with this task
98 Tags []string `json:"tags"`
99 // Number of pomodoros for this task
100 NPomodoros int `json:"n_pomodoros"`
101 // Duration of each pomodoro
102 Duration time.Duration `json:"duration"`
103}
104
105// ByID is a sortable array of tasks
106type ByID []*Task
107
108func (b ByID) Len() int { return len(b) }
109func (b ByID) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
110func (b ByID) Less(i, j int) bool { return b[i].ID < b[j].ID }
111
112// Pomodoro is a unit of time to spend working
113// on a single task.
114type Pomodoro struct {
115 Start time.Time `json:"start"`
116 End time.Time `json:"end"`
117}
118
119// Notifier implements a system specific
120// notification. On Linux this libnotify.
121// TODO: OSX, Windows(?)
122type Notifier interface {
123 Begin(int, Task) error
124 Break(Task) error
125 Finish(Task) error
126}
127
128// LibNotifier implements a Linux
129// notifier client.
130type LibNotifier struct {
131 client *libnotify.Client
132 iconPath string
133}
134
135func NewLibNotifier() Notifier {
136 ln := &LibNotifier{
137 client: libnotify.NewClient(),
138 }
139 // Write the tomato icon to a temp path
140 raw := MustAsset("tomato-icon.png")
141 fp, _ := ioutil.TempFile("", "pomo")
142 fp.Write(raw)
143 ln.iconPath = fp.Name()
144 fp.Close()
145 return ln
146}
147
148func (ln LibNotifier) Begin(count int, t Task) error {
149 return ln.client.Notify(libnotify.Notification{
150 Title: t.Message,
151 Body: fmt.Sprintf("Task is starting (%d/%d pomodoros)", count, t.NPomodoros),
152 Icon: ln.iconPath,
153 })
154}
155
156func (ln LibNotifier) Break(t Task) error {
157 return ln.client.Notify(libnotify.Notification{
158 Title: t.Message,
159 Urgency: "critical",
160 Body: fmt.Sprintf("Time to take a break!\nPress enter at the console to initiate the break."),
161 Icon: ln.iconPath,
162 })
163}
164
165func (ln LibNotifier) Finish(t Task) error {
166 return ln.client.Notify(libnotify.Notification{
167 Title: t.Message,
168 Urgency: "critical",
169 Body: fmt.Sprintf("This task session is complete!"),
170 Icon: ln.iconPath,
171 })
172}