diff --git a/.gitignore b/.gitignore index dd9e974..c835627 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ ChronoTomato + +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..644912b --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,40 @@ +# vim: set ts=2 sw=2 tw=0 fo=cnqoj +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json + +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - goos: + - linux + goarch: + - amd64 + - arm64 + env: + - CGO_ENABLED=0 + ldflags: + - -s -w -X {{.ModulePath}}/internal/metadata.ChronoTomatoVersion={{.Version}} + +upx: + - enabled: true + compress: best + lzma: true + +changelog: + use: gitea + +archives: + - format: binary + +release: + gitea: + owner: pomodoro + name: ChronoTomato + mode: replace + +gitea_urls: + download: http://git.smsvc.net + api: http://git.smsvc.net/api/v1 diff --git a/.renovaterc.json b/.renovaterc.json index ff27ef5..aceb001 100644 --- a/.renovaterc.json +++ b/.renovaterc.json @@ -2,5 +2,11 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "local>infrastructure/renovate-config" + ], + "packageRules": [ + { + "matchManagers": ["gomod"], + "semanticCommitType": "chore" + } ] } diff --git a/README.md b/README.md index 71b0918..c03c07c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The configuration is loaded from `~/config/ChronoTomato.yml`, here is an example url: ws://localhost:8080/ws password: "" config: - work: 1500 + focus: 1500 shortbreak: 300 longbreak: 600 sessions: 4 diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..b5b59f1 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,41 @@ +# yaml-language-server: $schema=https://taskfile.dev/schema.json +# +# github.com/go-task/task/v3/cmd/task@latest +# Requirements: +# github.com/caarlos0/svu@latest +# github.com/goreleaser/goreleaser/v2@latest + +version: '3' + +tasks: + + release: + desc: Create and publish an new release + vars: + RELEASE: + sh: svu next + BRANCH: + sh: git branch --show-current + COMMIT: + sh: git rev-parse --short --verify {{.BRANCH}} + preconditions: + - sh: test "{{.BRANCH}}" == "main" + msg: "You must be on the main branch to release" + - sh: test -z "$(git status --porcelain)" + msg: "You must have a clean working tree to release" + prompt: Create new release {{.RELEASE}} from {{.COMMIT}}@{{.BRANCH}}? + cmds: + - git tag {{.RELEASE}} + - git push + - git push origin tag {{.RELEASE}} + - goreleaser release --clean + + snapshot: + desc: Create a local snapshot release + cmds: + - goreleaser release --clean --snapshot + + install: + desc: Install the latest published version locally + cmds: + - go install git.smsvc.net/pomodoro/ChronoTomato@latest diff --git a/cmd/client/app.go b/cmd/client/app.go index 6a87b53..b0d4ccf 100644 --- a/cmd/client/app.go +++ b/cmd/client/app.go @@ -2,8 +2,11 @@ package client import ( "flag" + "fmt" + "os" "git.smsvc.net/pomodoro/ChronoTomato/internal/helper" + "git.smsvc.net/pomodoro/ChronoTomato/internal/metadata" "git.smsvc.net/pomodoro/ChronoTomato/internal/websocket" ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" @@ -68,9 +71,15 @@ func Start() { parameter_url = flag.String("url", "", "GoTomato Server URL (eg ws://localhost:8080/ws)") parameter_password = flag.String("password", "", "Control password for pomodoro session") parameter_configfile = flag.String("config", defaultConfigFile, "Path to config file") + showVersion = flag.Bool("version", false, "Show Version") ) flag.Parse() + if *showVersion { + fmt.Printf("ChronoTomato v%v\n", metadata.ChronoTomatoVersion) + os.Exit(0) + } + // read passed config file or try to use default config if *parameter_configfile != defaultConfigFile { config = helper.ParseConfig(*parameter_configfile) diff --git a/cmd/client/helper/terminal.go b/cmd/client/helper/terminal.go deleted file mode 100644 index 06c1713..0000000 --- a/cmd/client/helper/terminal.go +++ /dev/null @@ -1,51 +0,0 @@ -package helper - -import ( - "fmt" - - "github.com/alecthomas/colour" - - GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" -) - -// Return terminal output based on the passed ServerMessage -func TerminalOutput(pomodoro GoTomato.ServerMessage) string { - var ( - output string - - modePrefix string - timerOutput string - ) - - // header - output += colour.Sprintf("^D^4Work: %d ◊ Break: %d ◊ Longbreak: %d^R\n\n", - pomodoro.Settings.Work/60, - pomodoro.Settings.ShortBreak/60, - pomodoro.Settings.LongBreak/60, - ) - - //body - switch pomodoro.Mode { - case "Idle": - modePrefix = " " - timerOutput = "" - default: - modePrefix = " " - if pomodoro.Paused { - modePrefix = " " - } - minutes := pomodoro.TimeLeft / 60 - seconds := pomodoro.TimeLeft % 60 - - timerOutput = fmt.Sprintf("⏳ %02d:%02d", minutes, seconds) - } - - output += fmt.Sprintf("Session: %d/%d\n", - pomodoro.Session, - pomodoro.Settings.Sessions, - ) - output += fmt.Sprintf("%s %s\n", modePrefix, pomodoro.Mode) - output += fmt.Sprintf(timerOutput) - - return output -} diff --git a/cmd/client/helper/notification.go b/cmd/client/notification.go similarity index 88% rename from cmd/client/helper/notification.go rename to cmd/client/notification.go index 956e6a2..5199c82 100644 --- a/cmd/client/helper/notification.go +++ b/cmd/client/notification.go @@ -1,4 +1,4 @@ -package helper +package client import ( "fmt" @@ -8,7 +8,7 @@ import ( ) // Send desktop notifications on the start of each segment -func DesktopNotifications(pomodoro GoTomato.ServerMessage) { +func desktopNotifications(pomodoro GoTomato.ServerMessage) { var ( duration int notification string @@ -19,8 +19,8 @@ func DesktopNotifications(pomodoro GoTomato.ServerMessage) { sessions := pomodoro.Settings.Sessions switch mode { - case "Work": - duration = pomodoro.Settings.Work + case "Focus": + duration = pomodoro.Settings.Focus notification = fmt.Sprintf("Session %d/%d: %s %0.f minutes", session, sessions, mode, float32(duration)/60) case "ShortBreak": duration = pomodoro.Settings.ShortBreak diff --git a/cmd/client/view.go b/cmd/client/view.go index 6189530..e11763c 100644 --- a/cmd/client/view.go +++ b/cmd/client/view.go @@ -1,31 +1,93 @@ package client import ( + "fmt" "strings" "github.com/alecthomas/colour" - "git.smsvc.net/pomodoro/ChronoTomato/cmd/client/helper" GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" + "github.com/charmbracelet/bubbles/progress" ) +var progressBar = progress.New(progress.WithDefaultGradient(), progress.WithoutPercentage()) + func (a app) View() string { var body string var serverStatus string body = "Waiting for server message..." if a.pomodoro != (GoTomato.ServerMessage{}) { - body = helper.TerminalOutput(a.pomodoro) - helper.DesktopNotifications(a.pomodoro) + body = terminalOutput(a.pomodoro) + desktopNotifications(a.pomodoro) } - serverStatus = colour.Sprintf("^D^1disconnected: %v^R\n", client.LastErr) - if client.Connected() { + serverStatus = colour.Sprintf("^D^1%v^R\n", client.LastErr) + if client.Connected() && client.LastErr == nil { serverStatus = colour.Sprintf("^D^2connected to %s^R\n", client.Server) } helpView := a.help.View(a.keys) - height := 8 - strings.Count(body, "\n") - strings.Count(serverStatus, "\n") - strings.Count(helpView, "\n") + height := 10 - strings.Count(body, "\n") - strings.Count(serverStatus, "\n") - strings.Count(helpView, "\n") return body + strings.Repeat("\n", height) + serverStatus + helpView } + +// Return terminal output based on the passed ServerMessage +func terminalOutput(pomodoro GoTomato.ServerMessage) string { + var ( + output string + + modePrefix string + timerOutput string + ) + + // header + output += colour.Sprintf("^D^4Focus: %d ◊ Break: %d ◊ Longbreak: %d^R\n\n", + pomodoro.Settings.Focus/60, + pomodoro.Settings.ShortBreak/60, + pomodoro.Settings.LongBreak/60, + ) + + //body + switch pomodoro.Mode { + case "Idle": + modePrefix = " " + timerOutput = "" + default: + modePrefix = " " + if pomodoro.Paused { + modePrefix = " " + } + minutes := pomodoro.TimeLeft / 60 + seconds := pomodoro.TimeLeft % 60 + + timerOutput = fmt.Sprintf("⏱ %02d:%02d", minutes, seconds) + "\n" + timerOutput += progressBar.ViewAs(calc_percentage(pomodoro)) + } + + output += fmt.Sprintf("Session: %d/%d\n", + pomodoro.Session, + pomodoro.Settings.Sessions, + ) + output += fmt.Sprintf("%s %s\n", modePrefix, pomodoro.Mode) + "\n" + output += timerOutput + + return output +} + +func calc_percentage(message GoTomato.ServerMessage) float64 { + var duration float64 + + switch message.Mode { + case "Focus": + duration = float64(message.Settings.Focus) + case "ShortBreak": + duration = float64(message.Settings.ShortBreak) + case "LongBreak", "End": + duration = float64(message.Settings.LongBreak) + } + + timeLeft := float64(message.TimeLeft) + return 1.0 - (timeLeft / duration) +} diff --git a/go.mod b/go.mod index a590ff3..0759daf 100644 --- a/go.mod +++ b/go.mod @@ -3,25 +3,31 @@ module git.smsvc.net/pomodoro/ChronoTomato go 1.23.1 require ( - git.smsvc.net/pomodoro/GoTomato v0.0.8 + git.smsvc.net/pomodoro/GoTomato v0.5.0 github.com/alecthomas/colour v0.1.0 - github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/bubbletea v1.1.2 - github.com/charmbracelet/log v0.4.0 - github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 + github.com/charmbracelet/bubbles v0.21.0 + github.com/charmbracelet/bubbletea v1.3.5 + github.com/charmbracelet/log v0.4.2 + github.com/gen2brain/beeep v0.11.1 github.com/gorilla/websocket v1.5.3 gopkg.in/yaml.v3 v3.0.1 ) require ( + git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/lipgloss v0.13.0 // indirect - github.com/charmbracelet/x/ansi v0.4.0 // indirect - github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/harmonica v0.2.0 // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/esiqveland/notify v0.13.3 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/jackmordaunt/icns/v3 v3.0.1 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -29,13 +35,16 @@ require ( github.com/mattn/go-runewidth v0.0.16 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.15.2 // indirect - github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/sergeymakinen/go-bmp v1.0.0 // indirect + github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.3.8 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/go.sum b/go.sum index 2498813..3a1f369 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,52 @@ -git.smsvc.net/pomodoro/GoTomato v0.0.8 h1:ePD6T14pFtrtXybHypabpDs4MURdOnPkm8nQzzEteVA= -git.smsvc.net/pomodoro/GoTomato v0.0.8/go.mod h1:LaKrPdnFB5v4RpltExKasW67TmjJlmhDDvmoEYWv0P4= +git.smsvc.net/pomodoro/GoTomato v0.5.0 h1:ni6v5iNnSac/Os3skmGobAwjyXP3uS76N+fhZDubqkU= +git.smsvc.net/pomodoro/GoTomato v0.5.0/go.mod h1:LaKrPdnFB5v4RpltExKasW67TmjJlmhDDvmoEYWv0P4= +git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE= +git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo= github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk= github.com/alecthomas/colour v0.1.0/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= -github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.1.2 h1:naQXF2laRxyLyil/i7fxdpiz1/k06IKquhm4vBfHsIc= -github.com/charmbracelet/bubbletea v1.1.2/go.mod h1:9HIU/hBV24qKjlehyj8z1r/tR9TYTQEag+cWZnuXo8E= -github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= -github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= -github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= -github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= -github.com/charmbracelet/x/ansi v0.4.0 h1:NqwHA4B23VwsDn4H3VcNX1W1tOmgnvY1NDx5tOXdnOU= -github.com/charmbracelet/x/ansi v0.4.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= -github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= +github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc= +github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= +github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig= +github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 h1:ygs9POGDQpQGLJPlq4+0LBUmMBNox1N4JSpw+OETcvI= -github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4= +github.com/esiqveland/notify v0.13.3 h1:QCMw6o1n+6rl+oLUfg8P1IIDSFsDEb2WlXvVvIJbI/o= +github.com/esiqveland/notify v0.13.3/go.mod h1:hesw/IRYTO0x99u1JPweAl4+5mwXJibQVUcP0Iu5ORE= +github.com/gen2brain/beeep v0.11.1 h1:EbSIhrQZFDj1K2fzlMpAYlFOzV8YuNe721A58XcCTYI= +github.com/gen2brain/beeep v0.11.1/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= -github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4/go.mod h1:kW3HQ4UdaAyrUCSSDR4xUzBKW6O2iA4uHhk7AtyYp10= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jackmordaunt/icns/v3 v3.0.1 h1:xxot6aNuGrU+lNgxz5I5H0qSeCjNKp8uTXB1j8D4S3o= +github.com/jackmordaunt/icns/v3 v3.0.1/go.mod h1:5sHL59nqTd2ynTnowxB/MDQFhKNqkK8X687uKNygaSQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -47,31 +64,45 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= -github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= -github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M= +github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY= +github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ= +github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/metadata/version.go b/internal/metadata/version.go new file mode 100644 index 0000000..c1f994e --- /dev/null +++ b/internal/metadata/version.go @@ -0,0 +1,33 @@ +package metadata + +import ( + "os/exec" + "runtime/debug" + "strings" +) + +var ChronoTomatoVersion = "" // The GoTomato version + +func stripVersionPrefix(version string) string { + return strings.TrimLeft(version, "v") +} + +func getLatestTag() string { + bytes, _ := exec.Command("git", "describe", "--tags").Output() + output := strings.TrimSpace(string(bytes)) + return stripVersionPrefix(output) +} + +// set GoTomatoVersion from runtime/debug.Main.Version +// use latest git tag as fallback +// can be overwritten via ldflags (e,g. by goreleaser) +func init() { + if ChronoTomatoVersion == "" { + info, _ := debug.ReadBuildInfo() + if info.Main.Version != "(devel)" { + ChronoTomatoVersion = stripVersionPrefix(info.Main.Version) + } else { + ChronoTomatoVersion = getLatestTag() + } + } +} diff --git a/internal/websocket/connect.go b/internal/websocket/connect.go index 1f35b7b..7dd7b57 100644 --- a/internal/websocket/connect.go +++ b/internal/websocket/connect.go @@ -1,13 +1,15 @@ package websocket import ( - "github.com/gorilla/websocket" "time" + "github.com/gorilla/websocket" + ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" ) -type Client ChronoTomato.GoTomatoClient // New websocket client +// New websocket client +type Client ChronoTomato.GoTomatoClient // Connects to websocket func Connect(url string) Client { @@ -16,10 +18,6 @@ func Connect(url string) Client { return Client{Conn: conn, Server: url, LastErr: err} } -func (c Client) Connected() bool { - return c.Conn != nil -} - // Disconnects from websocket func (c Client) Disconnect() { // Cleanly close the connection by sending a close message and then @@ -33,3 +31,7 @@ func (c Client) Disconnect() { } return } + +func (c Client) Connected() bool { + return c.Conn != nil +} diff --git a/internal/websocket/receive.go b/internal/websocket/receive.go index 4aeb6ad..bd3e91e 100644 --- a/internal/websocket/receive.go +++ b/internal/websocket/receive.go @@ -9,32 +9,33 @@ import ( GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" ) -var Done = make(chan struct{}) - // Receives websocket messages and writes them to a channel. // Closes the channel if websocket closes. func (c *Client) ProcessServerMessages(channel chan<- GoTomato.ServerMessage) { - var serverMessage, prevMessage GoTomato.ServerMessage + var serverMessage GoTomato.ServerMessage defer close(Done) for { + c.Conn.SetReadDeadline(time.Now().Add(10 * time.Second)) _, message, err := c.Conn.ReadMessage() if err != nil { + c.LastErr = err + // On normal closure exit gracefully if websocket.IsCloseError(err, websocket.CloseNormalClosure) { return } - // Try to reconnect on unexpected disconnect - for { - channel <- prevMessage // send previous ServerMessage to update view + // reset connection and reconnect + c.Conn = nil + pw := c.Password + for !c.Connected() { + channel <- serverMessage // send last known ServerMessage to update view time.Sleep(time.Second) *c = Connect(c.Server) - if c.Connected() { - break - } } + c.Password = pw continue } @@ -42,6 +43,5 @@ func (c *Client) ProcessServerMessages(channel chan<- GoTomato.ServerMessage) { c.LastErr = err channel <- serverMessage - prevMessage = serverMessage } } diff --git a/internal/websocket/vars.go b/internal/websocket/vars.go new file mode 100644 index 0000000..a9896e2 --- /dev/null +++ b/internal/websocket/vars.go @@ -0,0 +1,4 @@ +package websocket + +// Websocket closure +var Done = make(chan struct{})