From 94b6786c7c8339def1bcee07c243ef066c90b74c Mon Sep 17 00:00:00 2001
From: Sebastian Mark <smark@posteo.net>
Date: Wed, 30 Oct 2024 07:46:29 +0100
Subject: [PATCH 1/2] feat: implement asynchronous timer start
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- add new StartAsync method to Timer for non-blocking execution
- use new method in RunPomodoro()

🤖
---
 internal/pomodoro/pomodoro.go | 6 +++---
 internal/pomodoro/timer.go    | 7 ++++++-
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/internal/pomodoro/pomodoro.go b/internal/pomodoro/pomodoro.go
index 8228249..a7bcc00 100644
--- a/internal/pomodoro/pomodoro.go
+++ b/internal/pomodoro/pomodoro.go
@@ -42,7 +42,7 @@ func RunPomodoro() {
 
 		// Work
 		shared.State.Mode = "Work"
-		go timer.Start(pomodoroConfig.Work)
+		timer.StartAsync(pomodoroConfig.Work)
 		if !waitForTimer(timer) {
 			break
 		}
@@ -50,13 +50,13 @@ func RunPomodoro() {
 		// Breaks
 		if session < pomodoroConfig.Sessions {
 			shared.State.Mode = "ShortBreak"
-			go timer.Start(pomodoroConfig.ShortBreak)
+			timer.StartAsync(pomodoroConfig.ShortBreak)
 			if !waitForTimer(timer) {
 				break
 			}
 		} else { // last phase, prepare for finish
 			shared.State.Mode = "LongBreak"
-			go timer.Start(pomodoroConfig.LongBreak)
+			timer.StartAsync(pomodoroConfig.LongBreak)
 			if !waitForTimer(timer) {
 				break
 			}
diff --git a/internal/pomodoro/timer.go b/internal/pomodoro/timer.go
index 09a50ee..11b1070 100644
--- a/internal/pomodoro/timer.go
+++ b/internal/pomodoro/timer.go
@@ -24,7 +24,12 @@ func (t Timer) Init() Timer {
 	}
 }
 
-// Start the timer
+// Start the timer (goroutine)
+func (t *Timer) StartAsync(duration int) {
+	go t.Start(duration)
+}
+
+// Start the timer (blocking)
 func (t *Timer) Start(duration int) {
 	tick := time.NewTicker(time.Second)
 	for timeLeft := duration; timeLeft > 0; {

From bdfd5c3b8448cff5d7903ec136bfcb8d32afa2fa Mon Sep 17 00:00:00 2001
From: Sebastian Mark <smark@posteo.net>
Date: Wed, 30 Oct 2024 08:09:41 +0100
Subject: [PATCH 2/2] feat: remove unused client mutex
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- remove unused Mutex from `models.Client` struct
- remove lock from `HandleConnection()`
- replace websocket.Conn with models.Client in `handleClientCommands()`

🤖
---
 internal/websocket/client_commands.go   | 4 ++--
 internal/websocket/handle_connection.go | 9 +++++----
 pkg/models/client.go                    | 7 +------
 3 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/internal/websocket/client_commands.go b/internal/websocket/client_commands.go
index 53464dd..4ea7e7d 100644
--- a/internal/websocket/client_commands.go
+++ b/internal/websocket/client_commands.go
@@ -3,7 +3,6 @@ package websocket
 import (
 	"encoding/json"
 	"github.com/charmbracelet/log"
-	"github.com/gorilla/websocket"
 
 	"git.smsvc.net/pomodoro/GoTomato/internal/pomodoro"
 	"git.smsvc.net/pomodoro/GoTomato/internal/shared"
@@ -11,7 +10,8 @@ import (
 )
 
 // Listens for commands from a client and handles them
-func handleClientCommands(ws *websocket.Conn) {
+func handleClientCommands(c models.Client) {
+	ws := c.Conn
 	for {
 		var clientCommand models.ClientCommand
 
diff --git a/internal/websocket/handle_connection.go b/internal/websocket/handle_connection.go
index 5e97df2..37160d8 100644
--- a/internal/websocket/handle_connection.go
+++ b/internal/websocket/handle_connection.go
@@ -31,12 +31,13 @@ func HandleConnection(w http.ResponseWriter, r *http.Request) {
 	log.Info("Client connected", "host", ws.NetConn().RemoteAddr(), "clients", len(Clients)+1)
 
 	// Register the new client
-	mu.Lock()
-	Clients[ws] = &models.Client{
-		Conn: ws, // Store the WebSocket connection
+	client := models.Client{
+		Conn: ws,
 	}
+	mu.Lock()
+	Clients[ws] = &client
 	mu.Unlock()
 
 	// Listen for commands from the connected client
-	handleClientCommands(ws)
+	handleClientCommands(client)
 }
diff --git a/pkg/models/client.go b/pkg/models/client.go
index 7a6ae21..f9eb116 100644
--- a/pkg/models/client.go
+++ b/pkg/models/client.go
@@ -3,7 +3,6 @@ package models
 import (
 	"github.com/charmbracelet/log"
 	"github.com/gorilla/websocket"
-	"sync"
 )
 
 // Represents a command from the client (start/stop)
@@ -15,16 +14,12 @@ type ClientCommand struct {
 
 // Represents a single client
 type Client struct {
-	Conn  *websocket.Conn // Websocket connection of the client
-	Mutex sync.Mutex      // Mutex used to lock
+	Conn *websocket.Conn
 }
 
 // Sends a message to the websocket.
 // Automatically locks and unlocks the client mutex, to ensure that only one goroutine can write at a time.
 func (c *Client) SendMessage(messageType int, data []byte) error {
-	c.Mutex.Lock()
-	defer c.Mutex.Unlock()
-
 	err := c.Conn.WriteMessage(messageType, data)
 	if err != nil {
 		log.Error("Error writing to WebSocket:", "msg", err)