From 94e7b414210581c49e345ab178fb83a748bade4a Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Wed, 13 Nov 2024 17:12:54 +0100 Subject: [PATCH 1/6] fix: preserve client password during reconnection process --- internal/websocket/receive.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/websocket/receive.go b/internal/websocket/receive.go index 4aeb6ad..e4638a6 100644 --- a/internal/websocket/receive.go +++ b/internal/websocket/receive.go @@ -29,8 +29,14 @@ func (c *Client) ProcessServerMessages(channel chan<- GoTomato.ServerMessage) { // Try to reconnect on unexpected disconnect for { channel <- prevMessage // send previous ServerMessage to update view + time.Sleep(time.Second) + + // reconnect while preserving password + pw := c.Password *c = Connect(c.Server) + c.Password = pw + if c.Connected() { break } From 6b90db0d6cbc7ce34703555f0eeaa8d04c84bb76 Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Sun, 10 Nov 2024 20:31:21 +0100 Subject: [PATCH 2/6] feat: add progress bar to timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add progress package for visual feedback - implement calc_percentage function for duration calculation - update TerminalOutput to include progress percentage and view 🤖 --- cmd/client/helper/terminal.go | 26 +++++++++++++++++++++++--- cmd/client/view.go | 2 +- go.mod | 1 + go.sum | 2 ++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/cmd/client/helper/terminal.go b/cmd/client/helper/terminal.go index 06c1713..49049c2 100644 --- a/cmd/client/helper/terminal.go +++ b/cmd/client/helper/terminal.go @@ -4,10 +4,29 @@ import ( "fmt" "github.com/alecthomas/colour" + "github.com/charmbracelet/bubbles/progress" GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" ) +var progressBar = progress.New(progress.WithDefaultGradient(), progress.WithoutPercentage()) + +func calc_percentage(message GoTomato.ServerMessage) float64 { + var duration float64 + + switch message.Mode { + case "Work": + duration = float64(message.Settings.Work) + case "ShortBreak": + duration = float64(message.Settings.ShortBreak) + case "LongBreak", "End": + duration = float64(message.Settings.LongBreak) + } + + timeLeft := float64(message.TimeLeft) + return 1.0 - (timeLeft / duration) +} + // Return terminal output based on the passed ServerMessage func TerminalOutput(pomodoro GoTomato.ServerMessage) string { var ( @@ -37,15 +56,16 @@ func TerminalOutput(pomodoro GoTomato.ServerMessage) string { minutes := pomodoro.TimeLeft / 60 seconds := pomodoro.TimeLeft % 60 - timerOutput = fmt.Sprintf("⏳ %02d:%02d", minutes, seconds) + 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) - output += fmt.Sprintf(timerOutput) + output += fmt.Sprintf("%s %s\n", modePrefix, pomodoro.Mode) + "\n" + output += timerOutput return output } diff --git a/cmd/client/view.go b/cmd/client/view.go index 6189530..c7e9eb7 100644 --- a/cmd/client/view.go +++ b/cmd/client/view.go @@ -25,7 +25,7 @@ func (a app) View() string { } 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 } diff --git a/go.mod b/go.mod index 2b7f407..da5bc03 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/term v0.2.0 // indirect diff --git a/go.sum b/go.sum index c95467b..4f15eb3 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQW github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.2.1 h1:J041h57zculJKEKf/O2pS4edXGIz+V0YvojvfGXePIk= github.com/charmbracelet/bubbletea v1.2.1/go.mod h1:viLoDL7hG4njLJSKU2gw7kB3LSEmWsrM80rO1dBJWBI= +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.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= From 54339dc59f278242a90fb07f898229469d1aacda Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Mon, 11 Nov 2024 23:06:49 +0100 Subject: [PATCH 3/6] feat: replace timer icon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - change timer icon from hourglass to stopwatch 🤖 --- cmd/client/helper/terminal.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/client/helper/terminal.go b/cmd/client/helper/terminal.go index 49049c2..325db78 100644 --- a/cmd/client/helper/terminal.go +++ b/cmd/client/helper/terminal.go @@ -56,7 +56,7 @@ func TerminalOutput(pomodoro GoTomato.ServerMessage) string { minutes := pomodoro.TimeLeft / 60 seconds := pomodoro.TimeLeft % 60 - timerOutput = fmt.Sprintf("⏳ %02d:%02d", minutes, seconds) + "\n" + timerOutput = fmt.Sprintf("⏱ %02d:%02d", minutes, seconds) + "\n" timerOutput += progressBar.ViewAs(calc_percentage(pomodoro)) } From a5afcd2507b548acf1c242cfef1c919842692728 Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Wed, 13 Nov 2024 09:43:58 +0100 Subject: [PATCH 4/6] refactor: merge client/helper package to client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove helper package and move functions to client package - merge `terminalOutput()` to `view.go` - move `desktopNotifications()` to `cmd/client` 🤖 --- cmd/client/helper/terminal.go | 71 ------------------------- cmd/client/{helper => }/notification.go | 4 +- cmd/client/view.go | 68 +++++++++++++++++++++-- 3 files changed, 67 insertions(+), 76 deletions(-) delete mode 100644 cmd/client/helper/terminal.go rename cmd/client/{helper => }/notification.go (93%) diff --git a/cmd/client/helper/terminal.go b/cmd/client/helper/terminal.go deleted file mode 100644 index 325db78..0000000 --- a/cmd/client/helper/terminal.go +++ /dev/null @@ -1,71 +0,0 @@ -package helper - -import ( - "fmt" - - "github.com/alecthomas/colour" - "github.com/charmbracelet/bubbles/progress" - - GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" -) - -var progressBar = progress.New(progress.WithDefaultGradient(), progress.WithoutPercentage()) - -func calc_percentage(message GoTomato.ServerMessage) float64 { - var duration float64 - - switch message.Mode { - case "Work": - duration = float64(message.Settings.Work) - case "ShortBreak": - duration = float64(message.Settings.ShortBreak) - case "LongBreak", "End": - duration = float64(message.Settings.LongBreak) - } - - timeLeft := float64(message.TimeLeft) - return 1.0 - (timeLeft / duration) -} - -// 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) + "\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 -} diff --git a/cmd/client/helper/notification.go b/cmd/client/notification.go similarity index 93% rename from cmd/client/helper/notification.go rename to cmd/client/notification.go index 956e6a2..7e5dfd6 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 diff --git a/cmd/client/view.go b/cmd/client/view.go index c7e9eb7..884ea00 100644 --- a/cmd/client/view.go +++ b/cmd/client/view.go @@ -1,22 +1,25 @@ 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) @@ -29,3 +32,62 @@ func (a app) View() string { 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^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) + "\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 "Work": + duration = float64(message.Settings.Work) + case "ShortBreak": + duration = float64(message.Settings.ShortBreak) + case "LongBreak", "End": + duration = float64(message.Settings.LongBreak) + } + + timeLeft := float64(message.TimeLeft) + return 1.0 - (timeLeft / duration) +} From 9de86fc2175d07fa29a1386dc7eeca52ec47b1ba Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Wed, 13 Nov 2024 17:16:50 +0100 Subject: [PATCH 5/6] feat: add read timeout for server messages --- internal/websocket/receive.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/websocket/receive.go b/internal/websocket/receive.go index e4638a6..7912de3 100644 --- a/internal/websocket/receive.go +++ b/internal/websocket/receive.go @@ -19,6 +19,7 @@ func (c *Client) ProcessServerMessages(channel chan<- GoTomato.ServerMessage) { defer close(Done) for { + c.Conn.SetReadDeadline(time.Now().Add(10 * time.Second)) _, message, err := c.Conn.ReadMessage() if err != nil { // On normal closure exit gracefully @@ -26,6 +27,7 @@ func (c *Client) ProcessServerMessages(channel chan<- GoTomato.ServerMessage) { return } + c.LastErr = err // Try to reconnect on unexpected disconnect for { channel <- prevMessage // send previous ServerMessage to update view From 21edd2d823080771eff13c885f6e1d71d6cfbbe2 Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Wed, 13 Nov 2024 17:17:39 +0100 Subject: [PATCH 6/6] feat: better error display in TUI - always show `client.LasErr` if not `nil` --- cmd/client/view.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/client/view.go b/cmd/client/view.go index 884ea00..0e7d9b9 100644 --- a/cmd/client/view.go +++ b/cmd/client/view.go @@ -22,8 +22,8 @@ func (a app) View() string { 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) }