From 8a0ef32c9178879ad001dbafde3d345d3ab11f4a Mon Sep 17 00:00:00 2001 From: Sebastian Mark Date: Thu, 31 Oct 2024 07:37:50 +0100 Subject: [PATCH] feat: use bubbletea framework - implement TUI in bubbletea - split components into separate files - remove now unused functions - restructure files --- cmd/client/app.go | 92 +++++++++++++++++++ .../client/helper}/notification.go | 4 +- .../client/helper}/terminal.go | 27 +++--- cmd/client/keys.go | 44 +++++++++ cmd/client/main.go | 60 ------------ cmd/client/update.go | 45 +++++++++ cmd/client/view.go | 24 +++++ go.mod | 22 +++-- go.sum | 49 ++++++---- internal/frontend/keyhandler.go | 40 -------- internal/frontend/main.go | 36 -------- internal/helper/files.go | 2 +- internal/websocket/connect.go | 5 +- internal/websocket/receive.go | 6 +- 14 files changed, 269 insertions(+), 187 deletions(-) create mode 100644 cmd/client/app.go rename {internal/frontend => cmd/client/helper}/notification.go (92%) rename {internal/frontend => cmd/client/helper}/terminal.go (53%) create mode 100644 cmd/client/keys.go delete mode 100644 cmd/client/main.go create mode 100644 cmd/client/update.go create mode 100644 cmd/client/view.go delete mode 100644 internal/frontend/keyhandler.go delete mode 100644 internal/frontend/main.go diff --git a/cmd/client/app.go b/cmd/client/app.go new file mode 100644 index 0000000..f4652ab --- /dev/null +++ b/cmd/client/app.go @@ -0,0 +1,92 @@ +package client + +import ( + "flag" + "github.com/charmbracelet/log" + + "git.smsvc.net/pomodoro/ChronoTomato/internal/helper" + "git.smsvc.net/pomodoro/ChronoTomato/internal/websocket" + ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" + GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" + + "github.com/charmbracelet/bubbles/help" + tea "github.com/charmbracelet/bubbletea" +) + +var ( + config ChronoTomato.Config + client websocket.Client +) + +type app struct { + keys keyMap + help help.Model + channel chan GoTomato.ServerMessage + pomodoro GoTomato.ServerMessage +} + +func myApp() app { + return app{ + keys: keys, + help: help.New(), + channel: make(chan GoTomato.ServerMessage), + pomodoro: GoTomato.ServerMessage{}, + } +} + +// Wait for channel signal and return the ServerMessage as a tea.Msg. +// Return tea.Quit() if the channel has been closed. +// Excapsulate all this in a tea.Cmd() because bubbletea demands it. +func (a app) waitForChannelSignal() tea.Cmd { + return func() tea.Msg { + content, open := <-a.channel + if !open { + return tea.Quit() + } + return content + } +} + +func (a app) Init() tea.Cmd { + client = websocket.Connect(config.URL) + client.Password = config.Password + go client.ProcessServerMessages(a.channel) + + return tea.Batch( + tea.ClearScreen, + a.waitForChannelSignal(), + ) +} + +func Start() { + var ( + defaultConfigFile = "~/.config/ChronoTomato.yml" + + parameter_url = flag.String("url", "", "GoTomato Server URL (eg ws://localhost:8080/ws)") + parameter_password = flag.String("password", "", "Control password for pomodoro session") + configfile = flag.String("config", "", "Path to config file") + ) + flag.Parse() + + // read passed config file or try to use default config + if *configfile != "" { + config = helper.ParseConfig(*configfile) + } else { + if helper.FileExists(defaultConfigFile) { + config = helper.ParseConfig(defaultConfigFile) + } + } + + // cli parameters always supersede config file + if *parameter_url != "" { + config.URL = *parameter_url + } + if *parameter_password != "" { + config.Password = *parameter_password + } + + _, err := tea.NewProgram(myApp()).Run() + if err != nil { + log.Fatal("Could not start program:", "msg", err) + } +} diff --git a/internal/frontend/notification.go b/cmd/client/helper/notification.go similarity index 92% rename from internal/frontend/notification.go rename to cmd/client/helper/notification.go index 7895f30..956e6a2 100644 --- a/internal/frontend/notification.go +++ b/cmd/client/helper/notification.go @@ -1,4 +1,4 @@ -package frontend +package helper 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/internal/frontend/terminal.go b/cmd/client/helper/terminal.go similarity index 53% rename from internal/frontend/terminal.go rename to cmd/client/helper/terminal.go index 1935721..06c1713 100644 --- a/internal/frontend/terminal.go +++ b/cmd/client/helper/terminal.go @@ -1,25 +1,24 @@ -package frontend +package helper import ( "fmt" - "github.com/fatih/color" - "strings" + + "github.com/alecthomas/colour" GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" ) -// Update the terminal output based on the passed ServerMessage -func terminalOutput(pomodoro GoTomato.ServerMessage) { +// Return terminal output based on the passed ServerMessage +func TerminalOutput(pomodoro GoTomato.ServerMessage) string { var ( + output string + modePrefix string timerOutput string ) - // Clear the screen - fmt.Print("\033[H\033[2J") - // header - color.Blue("Work: %d ◊ Break: %d ◊ Longbreak: %d\n\n", + 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, @@ -41,14 +40,12 @@ func terminalOutput(pomodoro GoTomato.ServerMessage) { timerOutput = fmt.Sprintf("⏳ %02d:%02d", minutes, seconds) } - fmt.Printf("Session: %d/%d\n", + output += fmt.Sprintf("Session: %d/%d\n", pomodoro.Session, pomodoro.Settings.Sessions, ) - fmt.Printf("%s %s\n", modePrefix, pomodoro.Mode) - fmt.Printf(timerOutput) + output += fmt.Sprintf("%s %s\n", modePrefix, pomodoro.Mode) + output += fmt.Sprintf(timerOutput) - //footer - fmt.Printf(strings.Repeat("\n", 3)) - color.White("space: start/pause/resume • s: stop • r: reset pomodoro • q: quit") + return output } diff --git a/cmd/client/keys.go b/cmd/client/keys.go new file mode 100644 index 0000000..2a55fbe --- /dev/null +++ b/cmd/client/keys.go @@ -0,0 +1,44 @@ +package client + +import "github.com/charmbracelet/bubbles/key" + +// keyMap defines a set of keybindings. To work for help it must satisfy +// key.Map. It could also very easily be a map[string]key.Binding. +type keyMap struct { + Start key.Binding + Stop key.Binding + Reset key.Binding + Quit key.Binding +} + +var keys = keyMap{ + Start: key.NewBinding( + key.WithKeys(" "), + key.WithHelp("space", "start/pause/resume"), + ), + Stop: key.NewBinding( + key.WithKeys("s"), + key.WithHelp("s", "stop"), + ), + Reset: key.NewBinding( + key.WithKeys("r"), + key.WithHelp("r", "reset"), + ), + Quit: key.NewBinding( + key.WithKeys("q"), + key.WithHelp("q", "quit"), + ), +} + +func (k keyMap) ShortHelp() []key.Binding { + return []key.Binding{ + keys.Start, + keys.Stop, + keys.Reset, + keys.Quit, + } +} + +func (k keyMap) FullHelp() [][]key.Binding { + return [][]key.Binding{} +} diff --git a/cmd/client/main.go b/cmd/client/main.go deleted file mode 100644 index 82791b2..0000000 --- a/cmd/client/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package client - -import ( - "atomicgo.dev/cursor" - "flag" - - "git.smsvc.net/pomodoro/ChronoTomato/internal/frontend" - "git.smsvc.net/pomodoro/ChronoTomato/internal/helper" - "git.smsvc.net/pomodoro/ChronoTomato/internal/websocket" - - ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" - GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" -) - -var ( - config ChronoTomato.Config - - defaultConfigFile = "~/.config/ChronoTomato.yml" - - parameter_url = flag.String("url", "", "GoTomato Server URL (eg ws://localhost:8080/ws)") - parameter_password = flag.String("password", "", "Control password for pomodoro session") - configfile = flag.String("config", "", "Path to config file") -) - -func Start() { - flag.Parse() - - // show/hide cursor for nicer interface - cursor.Hide() - defer cursor.Show() - - // read passed config file or try to use default config - if *configfile != "" { - config = helper.ParseConfig(*configfile) - } else { - if helper.FileExists(defaultConfigFile) { - config = helper.ParseConfig(defaultConfigFile) - } - } - - // cli parameters always supersede config file - if *parameter_url != "" { - config.URL = *parameter_url - } - if *parameter_password != "" { - config.Password = *parameter_password - } - - // Create a client - client := websocket.Connect(config.URL) - client.Password = config.Password - - // Receive server messages und update the terminal - channel := make(chan GoTomato.ServerMessage) - go client.ProcessServerMessages(channel) - frontend.UpdateLoop(client, config, channel) - - // disconnect from server - client.Disconnect() -} diff --git a/cmd/client/update.go b/cmd/client/update.go new file mode 100644 index 0000000..b9ecee1 --- /dev/null +++ b/cmd/client/update.go @@ -0,0 +1,45 @@ +package client + +import ( + GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" + + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" +) + +// sends start/pause/resume based on the state of the pomodoro +func start_pause_resume(message GoTomato.ServerMessage) string { + if !message.Ongoing { + return "start" + } + if message.Paused { + return "resume" + } else { + return "pause" + } +} + +func (a app) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case GoTomato.ServerMessage: + a.pomodoro = msg + return a, a.waitForChannelSignal() + case tea.KeyMsg: + switch { + case key.Matches(msg, a.keys.Start): + cmd := start_pause_resume(a.pomodoro) + client.SendCmd(cmd) + case key.Matches(msg, a.keys.Stop): + client.SendCmd("stop") + case key.Matches(msg, a.keys.Reset): + if config.PomodoroConfig != (GoTomato.PomodoroConfig{}) { + client.SendSettingsUpdate(config.PomodoroConfig) + } + case key.Matches(msg, a.keys.Quit): + client.Disconnect() + return a, tea.Quit + } + } + + return a, nil +} diff --git a/cmd/client/view.go b/cmd/client/view.go new file mode 100644 index 0000000..eee5726 --- /dev/null +++ b/cmd/client/view.go @@ -0,0 +1,24 @@ +package client + +import ( + "strings" + + "git.smsvc.net/pomodoro/ChronoTomato/cmd/client/helper" + GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" +) + +func (a app) View() string { + var body string + + if a.pomodoro != (GoTomato.ServerMessage{}) { + body = helper.TerminalOutput(a.pomodoro) + helper.DesktopNotifications(a.pomodoro) + } else { + body = "Waiting for first server message..." + } + + helpView := a.help.View(a.keys) + height := 8 - strings.Count(body, "\n") - strings.Count(helpView, "\n") + + return body + strings.Repeat("\n", height) + helpView +} diff --git a/go.mod b/go.mod index 7e9a307..48db940 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module git.smsvc.net/pomodoro/ChronoTomato go 1.23.1 require ( - atomicgo.dev/cursor v0.2.0 git.smsvc.net/pomodoro/GoTomato v0.0.4 + 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/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 - github.com/fatih/color v1.18.0 github.com/gen2brain/beeep v0.0.0-20240516210008-9c006672e7f4 github.com/gorilla/websocket v1.5.3 gopkg.in/yaml.v3 v3.0.1 @@ -15,21 +15,27 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/lipgloss v0.10.0 // 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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/muesli/reflow v0.3.0 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + 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/rivo/uniseg v0.4.7 // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.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 bea4db9..095f75f 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,25 @@ -atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw= -atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU= git.smsvc.net/pomodoro/GoTomato v0.0.4 h1:+pCbPXUMtPteQilMe3QNo6fl67kB9gO6C/g0sNj1zck= git.smsvc.net/pomodoro/GoTomato v0.0.4/go.mod h1:rNFUjjBMKplygWYbgErWd4cD8JQ66h0KyiK54cGktJo= +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/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= -github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +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/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/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= -github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +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/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -31,23 +37,22 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +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/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.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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= @@ -57,10 +62,14 @@ github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG0 github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= 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/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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/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= diff --git a/internal/frontend/keyhandler.go b/internal/frontend/keyhandler.go deleted file mode 100644 index b2d8d10..0000000 --- a/internal/frontend/keyhandler.go +++ /dev/null @@ -1,40 +0,0 @@ -package frontend - -import ( - "github.com/eiannone/keyboard" - - "git.smsvc.net/pomodoro/ChronoTomato/internal/websocket" - - ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" - GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" -) - -// sends start/pause/resume based on the state of the pomodoro -func start_pause_resume(message GoTomato.ServerMessage) string { - if !message.Ongoing { - return "start" - } - if message.Paused { - return "resume" - } else { - return "pause" - } -} - -// reads a KeyEvent and sends the matching command -func menuHandler(key keyboard.KeyEvent, client websocket.Client, config ChronoTomato.Config, message GoTomato.ServerMessage) bool { - switch key.Rune { - case 0: // space - cmd := start_pause_resume(message) - client.SendCmd(cmd) - case 115: // s - client.SendCmd("stop") - case 114: // r - if config.PomodoroConfig != (GoTomato.PomodoroConfig{}) { - client.SendSettingsUpdate(config.PomodoroConfig) - } - case 113: // q - return false - } - return true -} diff --git a/internal/frontend/main.go b/internal/frontend/main.go deleted file mode 100644 index c9c941b..0000000 --- a/internal/frontend/main.go +++ /dev/null @@ -1,36 +0,0 @@ -package frontend - -import ( - "github.com/eiannone/keyboard" - - "git.smsvc.net/pomodoro/ChronoTomato/internal/websocket" - - ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" - GoTomato "git.smsvc.net/pomodoro/GoTomato/pkg/models" -) - -// Update the terminal and send desktop notifications until the websocket if closed or "quit" -func UpdateLoop(client websocket.Client, config ChronoTomato.Config, channel <-chan GoTomato.ServerMessage) { - var message GoTomato.ServerMessage - - // listen for key events - keysEvents, _ := keyboard.GetKeys(1) - defer keyboard.Close() - - for { - select { - case message = <-channel: - // for every received message - desktopNotifications(message) - terminalOutput(message) - case keypress := <-keysEvents: - // react to key pressed - if !menuHandler(keypress, client, config, message) { - return - } - case <-websocket.Done: - // connection closed - return - } - } -} diff --git a/internal/helper/files.go b/internal/helper/files.go index 7adad37..230fd93 100644 --- a/internal/helper/files.go +++ b/internal/helper/files.go @@ -10,7 +10,7 @@ import ( ChronoTomato "git.smsvc.net/pomodoro/ChronoTomato/pkg/models" ) -// Expands the `~` in a passed filename +// Expands the "~" in a passed filename func expandUnixPath(filename string) string { if strings.HasPrefix(filename, "~/") { dirname, _ := os.UserHomeDir() diff --git a/internal/websocket/connect.go b/internal/websocket/connect.go index 0110215..fdbc55d 100644 --- a/internal/websocket/connect.go +++ b/internal/websocket/connect.go @@ -12,13 +12,14 @@ type Client ChronoTomato.GoTomatoClient // New websocket client // Connects to websocket func Connect(url string) Client { - log.Info("Connected 󰖟 ", "host", url) - conn, _, err := websocket.DefaultDialer.Dial(url, nil) if err != nil { log.Fatal("Dial error!", "reason", err) } + log.Info("Connected 󰖟 ", "host", url) + time.Sleep(time.Second) + return Client{Conn: conn} } diff --git a/internal/websocket/receive.go b/internal/websocket/receive.go index 767f6cb..c21252b 100644 --- a/internal/websocket/receive.go +++ b/internal/websocket/receive.go @@ -2,7 +2,6 @@ package websocket import ( "encoding/json" - "fmt" "github.com/charmbracelet/log" "github.com/gorilla/websocket" @@ -11,7 +10,8 @@ import ( var Done = make(chan struct{}) -// Receives websocket messages and write them to `channel` +// 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 GoTomato.ServerMessage @@ -25,8 +25,8 @@ func (c Client) ProcessServerMessages(channel chan<- GoTomato.ServerMessage) { return } // Log any other errors - fmt.Println() log.Error("Read error!", "reason", err) + close(channel) return }